Merge pull request #418 from jodal/feature/show-deps

Update --show-deps to list deps of extensions
This commit is contained in:
Thomas Adamcik 2013-04-16 14:33:11 -07:00
commit c84cb95c3f
7 changed files with 138 additions and 197 deletions

View File

@ -55,6 +55,9 @@ and improved.
- The command option :option:`mopidy --list-settings` is now named
:option:`mopidy --show-config`.
- The command option :option:`mopidy --list-deps` is now named
:option:`mopidy --show-deps`.
- What configuration files to use can now be specified through the command
option :option:`mopidy --config`.

View File

@ -52,9 +52,9 @@ mopidy command
together to show the effective document. Secret values like passwords are
masked out. Config for disabled extensions are not included.
.. cmdoption:: --list-deps
.. cmdoption:: --show-deps
List dependencies and their versions.
Show dependencies, their versions and installation location.
.. cmdoption:: --config <file>

View File

@ -27,7 +27,7 @@ with others for debugging.
Installed dependencies
======================
The command :option:`mopidy --list-deps` will list the paths to and versions of
The command :option:`mopidy --show-deps` will list the paths to and versions of
any dependency Mopidy or the extensions might need to work. This is very useful
data for checking that you're using the right versions, and that you're using
the right installation if you have multiple installations of a dependency on

View File

@ -151,9 +151,9 @@ def parse_options():
action='callback', callback=show_config_callback,
help='show current config')
parser.add_option(
b'--list-deps',
action='callback', callback=deps.list_deps_optparse_callback,
help='list dependencies and their versions')
b'--show-deps',
action='callback', callback=deps.show_deps_optparse_callback,
help='show dependencies and their versions')
parser.add_option(
b'--config',
action='store', dest='config',

View File

@ -1,5 +1,6 @@
from __future__ import unicode_literals
import functools
import os
import platform
import sys
@ -8,16 +9,16 @@ import pygst
pygst.require('0.10')
import gst
import pykka
import pkg_resources
from . import formatting
def list_deps_optparse_callback(*args):
def show_deps_optparse_callback(*args):
"""
Prints a list of all dependencies.
Called by optparse when Mopidy is run with the :option:`--list-deps`
Called by optparse when Mopidy is run with the :option:`--show-deps`
option.
"""
print format_dependency_list()
@ -26,32 +27,47 @@ def list_deps_optparse_callback(*args):
def format_dependency_list(adapters=None):
if adapters is None:
dist_names = set([
ep.dist.project_name for ep in
pkg_resources.iter_entry_points('mopidy.ext')
if ep.dist.project_name != 'Mopidy'])
dist_infos = [
functools.partial(pkg_info, dist_name)
for dist_name in dist_names]
adapters = [
platform_info,
python_info,
functools.partial(pkg_info, 'Mopidy', True)
] + dist_infos + [
gstreamer_info,
pykka_info,
pyspotify_info,
pylast_info,
dbus_info,
serial_info,
cherrypy_info,
ws4py_info,
]
return '\n'.join([_format_dependency(a()) for a in adapters])
def _format_dependency(dep_info):
lines = []
for adapter in adapters:
dep_info = adapter()
lines.append('%(name)s: %(version)s' % {
'name': dep_info['name'],
'version': dep_info.get('version', 'not found'),
})
if 'path' in dep_info:
lines.append(' Imported from: %s' % (
os.path.dirname(dep_info['path'])))
if 'other' in dep_info:
lines.append(' Other: %s' % (
formatting.indent(dep_info['other'])),)
if 'version' not in dep_info:
lines.append('%s: not found' % dep_info['name'])
else:
lines.append('%s: %s from %s' % (
dep_info['name'],
dep_info['version'],
os.path.dirname(dep_info.get('path', 'none')),
))
if 'other' in dep_info:
lines.append(' Detailed information: %s' % (
formatting.indent(dep_info['other'], places=4)),)
if dep_info.get('dependencies', []):
for sub_dep_info in dep_info['dependencies']:
sub_dep_lines = _format_dependency(sub_dep_info)
lines.append(
formatting.indent(sub_dep_lines, places=2, singles=True))
return '\n'.join(lines)
@ -71,13 +87,46 @@ def python_info():
}
def pkg_info(project_name=None, include_extras=False):
if project_name is None:
project_name = 'Mopidy'
distribution = pkg_resources.get_distribution(project_name)
extras = include_extras and distribution.extras or []
dependencies = [
pkg_info(d) for d in distribution.requires(extras)]
return {
'name': distribution.project_name,
'version': distribution.version,
'path': distribution.location,
'dependencies': dependencies,
}
def gstreamer_info():
other = []
other.append('Python wrapper: gst-python %s' % (
'.'.join(map(str, gst.get_pygst_version()))))
other.append('Relevant elements:')
found_elements = []
missing_elements = []
for name, status in _gstreamer_check_elements():
other.append(' %s: %s' % (name, 'OK' if status else 'not found'))
if status:
found_elements.append(name)
else:
missing_elements.append(name)
other.append('Relevant elements:')
other.append(' Found:')
for element in found_elements:
other.append(' %s' % element)
else:
other.append(' none')
other.append(' Not found:')
for element in missing_elements:
other.append(' %s' % element)
else:
other.append(' none')
return {
'name': 'GStreamer',
'version': '.'.join(map(str, gst.get_gst_version())),
@ -134,82 +183,3 @@ def _gstreamer_check_elements():
gst.registry_get_default().get_feature_list(gst.TYPE_ELEMENT_FACTORY)]
return [
(element, element in known_elements) for element in elements_to_check]
def pykka_info():
return {
'name': 'Pykka',
'version': pykka.__version__,
'path': pykka.__file__,
}
def pyspotify_info():
dep_info = {'name': 'pyspotify'}
try:
import spotify
if hasattr(spotify, '__version__'):
dep_info['version'] = spotify.__version__
else:
dep_info['version'] = '< 1.3'
dep_info['path'] = spotify.__file__
dep_info['other'] = 'Built for libspotify API version %d' % (
spotify.api_version,)
except ImportError:
pass
return dep_info
def pylast_info():
dep_info = {'name': 'pylast'}
try:
import pylast
dep_info['version'] = pylast.__version__
dep_info['path'] = pylast.__file__
except ImportError:
pass
return dep_info
def dbus_info():
dep_info = {'name': 'dbus-python'}
try:
import dbus
dep_info['version'] = dbus.__version__
dep_info['path'] = dbus.__file__
except ImportError:
pass
return dep_info
def serial_info():
dep_info = {'name': 'pyserial'}
try:
import serial
dep_info['version'] = serial.VERSION
dep_info['path'] = serial.__file__
except ImportError:
pass
return dep_info
def cherrypy_info():
dep_info = {'name': 'cherrypy'}
try:
import cherrypy
dep_info['version'] = cherrypy.__version__
dep_info['path'] = cherrypy.__file__
except ImportError:
pass
return dep_info
def ws4py_info():
dep_info = {'name': 'ws4py'}
try:
import ws4py
dep_info['version'] = ws4py.__version__
dep_info['path'] = ws4py.__file__
except ImportError:
pass
return dep_info

View File

@ -4,13 +4,15 @@ import re
import unicodedata
def indent(string, places=4, linebreak='\n'):
def indent(string, places=4, linebreak='\n', singles=False):
lines = string.split(linebreak)
if len(lines) == 1:
if not singles and len(lines) == 1:
return string
result = ''
for line in lines:
result += linebreak + ' ' * places + line
for i, line in enumerate(lines):
lines[i] = ' ' * places + line
result = linebreak.join(lines)
if not singles:
result = linebreak + result
return result

View File

@ -5,37 +5,8 @@ import platform
import pygst
pygst.require('0.10')
import gst
import pykka
try:
import dbus
except ImportError:
dbus = False
try:
import pylast
except ImportError:
pylast = False
try:
import serial
except ImportError:
serial = False
try:
import spotify
except ImportError:
spotify = False
try:
import cherrypy
except ImportError:
cherrypy = False
try:
import ws4py
except ImportError:
ws4py = False
import mock
from mopidy.utils import deps
@ -47,17 +18,32 @@ class DepsTest(unittest.TestCase):
adapters = [
lambda: dict(name='Python', version='FooPython 2.7.3'),
lambda: dict(name='Platform', version='Loonix 4.0.1'),
lambda: dict(name='Pykka', path='/foo/bar/baz.py', other='Quux')
lambda: dict(
name='Pykka', version='1.1',
path='/foo/bar/baz.py', other='Quux'),
lambda: dict(name='Foo'),
lambda: dict(name='Mopidy', version='0.13', dependencies=[
dict(name='pylast', version='0.5', dependencies=[
dict(name='setuptools', version='0.6')
])
])
]
result = deps.format_dependency_list(adapters)
self.assertIn('Python: FooPython 2.7.3', result)
self.assertIn('Platform: Loonix 4.0.1', result)
self.assertIn('Pykka: not found', result)
self.assertIn('Imported from: /foo/bar', result)
self.assertIn('Pykka: 1.1 from /foo/bar', result)
self.assertNotIn('/baz.py', result)
self.assertIn('Quux', result)
self.assertIn('Detailed information: Quux', result)
self.assertIn('Foo: not found', result)
self.assertIn('Mopidy: 0.13', result)
self.assertIn(' pylast: 0.5', result)
self.assertIn(' setuptools: 0.6', result)
def test_platform_info(self):
result = deps.platform_info()
@ -85,59 +71,39 @@ class DepsTest(unittest.TestCase):
'.'.join(map(str, gst.get_pygst_version())), result['other'])
self.assertIn('Relevant elements:', result['other'])
def test_pykka_info(self):
result = deps.pykka_info()
@mock.patch('pkg_resources.get_distribution')
def test_pkg_info(self, get_distribution_mock):
dist_mopidy = mock.Mock()
dist_mopidy.project_name = 'Mopidy'
dist_mopidy.version = '0.13'
dist_mopidy.location = '/tmp/example/mopidy'
dist_mopidy.requires.return_value = ['Pykka']
self.assertEquals('Pykka', result['name'])
self.assertEquals(pykka.__version__, result['version'])
self.assertIn('pykka', result['path'])
dist_pykka = mock.Mock()
dist_pykka.project_name = 'Pykka'
dist_pykka.version = '1.1'
dist_pykka.location = '/tmp/example/pykka'
dist_pykka.requires.return_value = ['setuptools']
@unittest.skipUnless(spotify, 'pyspotify not found')
def test_pyspotify_info(self):
result = deps.pyspotify_info()
dist_setuptools = mock.Mock()
dist_setuptools.project_name = 'setuptools'
dist_setuptools.version = '0.6'
dist_setuptools.location = '/tmp/example/setuptools'
dist_setuptools.requires.return_value = []
self.assertEquals('pyspotify', result['name'])
self.assertEquals(spotify.__version__, result['version'])
self.assertIn('spotify', result['path'])
self.assertIn('Built for libspotify API version', result['other'])
self.assertIn(str(spotify.api_version), result['other'])
get_distribution_mock.side_effect = [
dist_mopidy, dist_pykka, dist_setuptools]
@unittest.skipUnless(pylast, 'pylast not found')
def test_pylast_info(self):
result = deps.pylast_info()
result = deps.pkg_info()
self.assertEquals('pylast', result['name'])
self.assertEquals(pylast.__version__, result['version'])
self.assertIn('pylast', result['path'])
self.assertEquals('Mopidy', result['name'])
self.assertEquals('0.13', result['version'])
self.assertIn('mopidy', result['path'])
@unittest.skipUnless(dbus, 'dbus not found')
def test_dbus_info(self):
result = deps.dbus_info()
dep_info_pykka = result['dependencies'][0]
self.assertEquals('Pykka', dep_info_pykka['name'])
self.assertEquals('1.1', dep_info_pykka['version'])
self.assertEquals('dbus-python', result['name'])
self.assertEquals(dbus.__version__, result['version'])
self.assertIn('dbus', result['path'])
@unittest.skipUnless(serial, 'serial not found')
def test_serial_info(self):
result = deps.serial_info()
self.assertEquals('pyserial', result['name'])
self.assertEquals(serial.VERSION, result['version'])
self.assertIn('serial', result['path'])
@unittest.skipUnless(cherrypy, 'cherrypy not found')
def test_cherrypy_info(self):
result = deps.cherrypy_info()
self.assertEquals('cherrypy', result['name'])
self.assertEquals(cherrypy.__version__, result['version'])
self.assertIn('cherrypy', result['path'])
@unittest.skipUnless(ws4py, 'ws4py not found')
def test_ws4py_info(self):
result = deps.ws4py_info()
self.assertEquals('ws4py', result['name'])
self.assertEquals(ws4py.__version__, result['version'])
self.assertIn('ws4py', result['path'])
dep_info_setuptools = dep_info_pykka['dependencies'][0]
self.assertEquals('setuptools', dep_info_setuptools['name'])
self.assertEquals('0.6', dep_info_setuptools['version'])