summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDoug Hellmann <doug@doughellmann.com>2017-09-07 16:15:36 -0400
committerDean Troyer <dtroyer@gmail.com>2017-09-22 14:35:29 +0000
commit31ec27dc61cf561ecd699f98fcac627b2d6467fa (patch)
tree1cf65d96757a12e8d77ec92db59c1483d584f9fe
parentdacbd601d843e91b32a57859a2965055540dd8a2 (diff)
downloadcliff-31ec27dc61cf561ecd699f98fcac627b2d6467fa.tar.gz
show the distribution providing the command in help output
When listing all available commands, include the distribution name if it does not match the distribution of the main application. When showing the verbose help for a single command, include the distribution name if it does not match the distribution of the main application. Change-Id: I4ddb3327c62cfd0f82167c15e9513ece6a3689c4 Signed-off-by: Doug Hellmann <doug@doughellmann.com>
-rw-r--r--cliff/command.py56
-rw-r--r--cliff/help.py14
2 files changed, 62 insertions, 8 deletions
diff --git a/cliff/command.py b/cliff/command.py
index 1c6ac48..1867150 100644
--- a/cliff/command.py
+++ b/cliff/command.py
@@ -13,11 +13,47 @@
import abc
import inspect
+import pkg_resources
import six
from stevedore import extension
from cliff import _argparse
+_dists_by_mods = None
+
+
+def _get_distributions_by_modules():
+ """Return dict mapping module name to distribution names.
+
+ The python package name (the name used for importing) and the
+ distribution name (the name used with pip and PyPI) do not
+ always match. We want to report which distribution caused the
+ command to be installed, so we need to look up the values.
+
+ """
+ global _dists_by_mods
+ if _dists_by_mods is None:
+ results = {}
+ for dist in pkg_resources.working_set:
+ try:
+ mod_name = dist.get_metadata('top_level.txt').strip()
+ except KeyError:
+ # Could not retrieve metadata. Ignore.
+ pass
+ else:
+ results[mod_name] = dist.project_name
+ _dists_by_mods = results
+ return _dists_by_mods
+
+
+def _get_distribution_for_module(module):
+ "Return the distribution containing the module."
+ dist_name = None
+ if module:
+ pkg_name = module.__name__.partition('.')[0]
+ dist_name = _get_distributions_by_modules().get(pkg_name)
+ return dist_name
+
@six.add_metaclass(abc.ABCMeta)
class Command(object):
@@ -89,17 +125,23 @@ class Command(object):
def get_epilog(self):
"""Return the command epilog."""
+ # replace a None in self._epilog with an empty string
+ parts = [self._epilog or '']
hook_epilogs = filter(
None,
(h.obj.get_epilog() for h in self._hooks),
)
- if hook_epilogs:
- # combine them, replacing a None in self._epilog with an
- # empty string
- parts = [self._epilog or '']
- parts.extend(hook_epilogs)
- return '\n\n'.join(parts)
- return self._epilog
+ parts.extend(hook_epilogs)
+ app_dist_name = _get_distribution_for_module(
+ inspect.getmodule(self.app)
+ )
+ dist_name = _get_distribution_for_module(inspect.getmodule(self))
+ if dist_name and dist_name != app_dist_name:
+ parts.append(
+ 'This command is provided by the %s plugin.' %
+ (dist_name,)
+ )
+ return '\n\n'.join(parts)
def get_parser(self, prog_name):
"""Return an :class:`argparse.ArgumentParser`.
diff --git a/cliff/help.py b/cliff/help.py
index 40c4874..971e9ad 100644
--- a/cliff/help.py
+++ b/cliff/help.py
@@ -29,6 +29,13 @@ class HelpAction(argparse.Action):
app = self.default
parser.print_help(app.stdout)
app.stdout.write('\nCommands:\n')
+ dists_by_module = command._get_distributions_by_modules()
+
+ def dist_for_obj(obj):
+ name = inspect.getmodule(obj).__name__.partition('.')[0]
+ return dists_by_module.get(name)
+
+ app_dist = dist_for_obj(app)
command_manager = app.command_manager
for name, ep in sorted(command_manager):
try:
@@ -51,7 +58,12 @@ class HelpAction(argparse.Action):
traceback.print_exc(file=app.stdout)
continue
one_liner = cmd.get_description().split('\n')[0]
- app.stdout.write(' %-13s %s\n' % (name, one_liner))
+ dist_name = dist_for_obj(factory)
+ if dist_name and dist_name != app_dist:
+ dist_info = ' (' + dist_name + ')'
+ else:
+ dist_info = ''
+ app.stdout.write(' %-13s %s%s\n' % (name, one_liner, dist_info))
sys.exit(0)