changeset 57:c8c522dacfea

Add `help` function, cleanup argument handling.
author Ludovic Chabant <ludovic@chabant.com>
date Tue, 26 Aug 2014 23:18:32 -0700
parents 2d617b889b00
children 95590732e4c9
files piecrust/commands/base.py piecrust/main.py
diffstat 2 files changed, 63 insertions(+), 11 deletions(-) [+]
line wrap: on
line diff
--- a/piecrust/commands/base.py	Tue Aug 26 23:17:20 2014 -0700
+++ b/piecrust/commands/base.py	Tue Aug 26 23:18:32 2014 -0700
@@ -1,4 +1,5 @@
 import logging
+import argparse
 import functools
 from piecrust.pathutil import SiteNotFoundError
 
@@ -7,10 +8,10 @@
 
 
 class CommandContext(object):
-    def __init__(self, app, args):
+    def __init__(self, app, parser, args):
         self.app = app
+        self.parser = parser
         self.args = args
-        self.result = 0
 
 
 class ChefCommand(object):
@@ -23,14 +24,13 @@
         raise NotImplementedError()
 
     def run(self, ctx):
-        raise NotImplementedError()
+        raise NotImplementedError("Command '%s' doesn't implement the `run` "
+                "method." % type(self))
 
-    def _runFromChef(self, app, res):
-        if app.root_dir is None and self.requires_website:
+    def checkedRun(self, ctx):
+        if ctx.app.root_dir is None and self.requires_website:
             raise SiteNotFoundError()
-        ctx = CommandContext(app, res)
-        self.run(ctx)
-        return ctx.result
+        return self.run(ctx)
 
 
 class ExtendableChefCommand(ChefCommand):
@@ -44,7 +44,7 @@
         for e in self._extensions:
             p = subparsers.add_parser(e.name, help=e.description)
             e.setupParser(p, app)
-            p.set_defaults(func=e._runFromChef)
+            p.set_defaults(func=e.checkedRun)
 
     def _loadExtensions(self, app):
         if self._extensions is not None:
@@ -64,6 +64,50 @@
         return True
 
 
+class HelpCommand(ChefCommand):
+    def __init__(self):
+        super(HelpCommand, self).__init__()
+        self.name = 'help'
+        self.description = "Prints help about PieCrust's chef."
+        self._topic_providers = {}
+
+    @property
+    def has_topics(self):
+        return any(self._topic_providers)
+
+    def addTopic(self, name, provider):
+        self._topic_providers[name] = provider
+
+    def getTopicNames(self):
+        return self._topic_providers.keys()
+
+    def setupParser(self, parser, app):
+        parser.add_argument('topic', nargs='?',
+                help="The command name or topic on which to get help.")
+
+    def run(self, ctx):
+        topic = ctx.args.topic
+
+        if topic is None:
+            ctx.parser.print_help()
+            return 0
+
+        if topic in self._topic_providers:
+            print(self._topic_providers[topic].getHelpTopic(topic, ctx.app))
+            return 0
+
+        for c in ctx.app.plugin_loader.getCommands():
+            if c.name == topic:
+                fake = argparse.ArgumentParser(
+                        prog='%s %s' % (ctx.parser.prog, c.name),
+                        description=c.description)
+                c.setupParser(fake, ctx.app)
+                fake.print_help()
+                return 0
+
+        raise Exception("No such command or topic: %s" % topic)
+
+
 class _WrappedCommand(ChefCommand):
     def __init__(self, func, name, description):
         super(_WrappedCommand, self).__init__()
--- a/piecrust/main.py	Tue Aug 26 23:17:20 2014 -0700
+++ b/piecrust/main.py	Tue Aug 26 23:18:32 2014 -0700
@@ -6,6 +6,7 @@
 import colorama
 from piecrust.app import PieCrust, PieCrustConfiguration, APP_VERSION
 from piecrust.chefutil import format_timed, log_friendly_exception
+from piecrust.commands.base import CommandContext
 from piecrust.environment import StandardEnvironment
 from piecrust.pathutil import SiteNotFoundError, find_app_root
 from piecrust.plugins.base import PluginLoader
@@ -151,6 +152,7 @@
 
     # Setup the arg parser.
     parser = argparse.ArgumentParser(
+            prog='chef',
             description="The PieCrust chef manages your website.")
     parser.add_argument('--version', action='version', version=('%(prog)s ' + APP_VERSION))
     parser.add_argument('--root', help="The root directory of the website.")
@@ -166,13 +168,19 @@
     for c in commands:
         p = subparsers.add_parser(c.name, help=c.description)
         c.setupParser(p, app)
-        p.set_defaults(func=c._runFromChef)
+        p.set_defaults(func=c.checkedRun)
+    help_cmd = next(filter(lambda c: c.name == 'help', commands), None)
+    if help_cmd and help_cmd.has_topics:
+        parser.epilog = ("Additional help topics: " +
+                ', '.join(help_cmd.getTopicNames()))
+
 
     # Parse the command line.
     result = parser.parse_args()
     logger.debug(format_timed(start_time, 'initialized PieCrust', colored=False))
 
     # Run the command!
-    exit_code = result.func(app, result)
+    ctx = CommandContext(app, parser, result)
+    exit_code = result.func(ctx)
     return exit_code