changeset 99:8703be118430

Changes to `help` command and extendable commands: - The `help` command has a prettier output, with descriptions of help topics. - `ExtendableChefCommand` is not assuming extensions are sub-commands anymore.
author Ludovic Chabant <ludovic@chabant.com>
date Sat, 13 Sep 2014 14:47:01 -0700
parents 5959a117a943
children 69d5eecfa449
files piecrust/chefutil.py piecrust/commands/base.py piecrust/main.py
diffstat 3 files changed, 47 insertions(+), 24 deletions(-) [+]
line wrap: on
line diff
--- a/piecrust/chefutil.py	Mon Sep 08 00:04:29 2014 -0700
+++ b/piecrust/chefutil.py	Sat Sep 13 14:47:01 2014 -0700
@@ -17,3 +17,16 @@
         indent += '  '
         ex = ex.__cause__
 
+
+def print_help_item(s, title, description, margin=4, align=25):
+    s.write(margin * ' ')
+    s.write(title)
+    spacer = (align - margin - len(title) - 1)
+    if spacer <= 0:
+        s.write("\n")
+        s.write(' ' * align)
+    else:
+        s.write(' ' * spacer)
+    s.write(description)
+    s.write("\n")
+
--- a/piecrust/commands/base.py	Mon Sep 08 00:04:29 2014 -0700
+++ b/piecrust/commands/base.py	Sat Sep 13 14:47:01 2014 -0700
@@ -38,13 +38,15 @@
         super(ExtendableChefCommand, self).__init__()
         self._extensions = None
 
-    def setupParser(self, parser, app):
+    def getExtensions(self, app):
         self._loadExtensions(app)
-        subparsers = parser.add_subparsers()
-        for e in self._extensions:
+        return self._extensions
+
+    def setupExtensionParsers(self, subparsers, app):
+        for e in self.getExtensions(app):
             p = subparsers.add_parser(e.name, help=e.description)
             e.setupParser(p, app)
-            p.set_defaults(func=e.checkedRun)
+            p.set_defaults(sub_func=e.checkedRun)
 
     def _loadExtensions(self, app):
         if self._extensions is not None:
@@ -55,36 +57,36 @@
                 self._extensions.append(e)
 
 
-class ChefCommandExtension(ChefCommand):
-    def __init__(self):
-        super(ChefCommandExtension, self).__init__()
-        self.command_name = '__unknown__'
+class ChefCommandExtension(object):
+    command_name = '__unknown__'
 
     def supports(self, app):
         return True
 
 
-class HelpCommand(ChefCommand):
+class HelpCommand(ExtendableChefCommand):
     def __init__(self):
         super(HelpCommand, self).__init__()
         self.name = 'help'
         self.description = "Prints help about PieCrust's chef."
-        self._topic_providers = {}
+        self._topic_providers = []
 
     @property
     def has_topics(self):
-        return any(self._topic_providers)
+        return len(self._topic_providers) > 0
 
-    def addTopic(self, name, provider):
-        self._topic_providers[name] = provider
-
-    def getTopicNames(self):
-        return self._topic_providers.keys()
+    def getTopics(self):
+        return [(n, d) for (n, d, e) in self._topic_providers]
 
     def setupParser(self, parser, app):
         parser.add_argument('topic', nargs='?',
                 help="The command name or topic on which to get help.")
 
+        extensions = self.getExtensions(app)
+        for ext in extensions:
+            for name, desc in ext.getHelpTopics():
+                self._topic_providers.append((name, desc, ext))
+
     def run(self, ctx):
         topic = ctx.args.topic
 
@@ -92,9 +94,10 @@
             ctx.parser.print_help()
             return 0
 
-        if topic in self._topic_providers:
-            print(self._topic_providers[topic].getHelpTopic(topic, ctx.app))
-            return 0
+        for name, desc, ext in self._topic_providers:
+            if name == topic:
+                print(ext.getHelpTopic(topic, ctx.app))
+                return 0
 
         for c in ctx.app.plugin_loader.getCommands():
             if c.name == topic:
--- a/piecrust/main.py	Mon Sep 08 00:04:29 2014 -0700
+++ b/piecrust/main.py	Sat Sep 13 14:47:01 2014 -0700
@@ -1,3 +1,4 @@
+import io
 import sys
 import time
 import os.path
@@ -6,7 +7,8 @@
 import colorama
 from piecrust import APP_VERSION
 from piecrust.app import PieCrust, PieCrustConfiguration
-from piecrust.chefutil import format_timed, log_friendly_exception
+from piecrust.chefutil import (format_timed, log_friendly_exception,
+        print_help_item)
 from piecrust.commands.base import CommandContext
 from piecrust.environment import StandardEnvironment
 from piecrust.pathutil import SiteNotFoundError, find_app_root
@@ -167,7 +169,8 @@
     # Setup the arg parser.
     parser = argparse.ArgumentParser(
             prog='chef',
-            description="The PieCrust chef manages your website.")
+            description="The PieCrust chef manages your website.",
+            formatter_class=argparse.RawDescriptionHelpFormatter)
     parser.add_argument('--version', action='version', version=('%(prog)s ' + APP_VERSION))
     parser.add_argument('--root', help="The root directory of the website.")
     parser.add_argument('--config', help="The configuration variant to use for this command.")
@@ -179,15 +182,19 @@
 
     commands = sorted(app.plugin_loader.getCommands(),
             key=lambda c: c.name)
-    subparsers = parser.add_subparsers()
+    subparsers = parser.add_subparsers(title='list of commands')
     for c in commands:
         p = subparsers.add_parser(c.name, help=c.description)
         c.setupParser(p, app)
         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()))
+        with io.StringIO() as epilog:
+            epilog.write("additional help topics:\n")
+            for name, desc in help_cmd.getTopics():
+                print_help_item(epilog, name, desc)
+            parser.epilog = epilog.getvalue()
 
 
     # Parse the command line.