]> git.rkrishnan.org Git - tahoe-lafs/tahoe-lafs.git/blobdiff - src/allmydata/scripts/runner.py
scripts: improve rendering of synopsis/usage
[tahoe-lafs/tahoe-lafs.git] / src / allmydata / scripts / runner.py
index fcb5d14797d9e37fd0952ed0e4d92dd0739773d7..4767e59e067138d1ae6436249c980ae8c452b3f1 100644 (file)
 
-import sys
+import os, sys
 from cStringIO import StringIO
 
-import pkg_resources
-pkg_resources.require('twisted')
 from twisted.python import usage
 
-pkg_resources.require('allmydata-tahoe')
-from allmydata.scripts.common import BaseOptions
-import debug, create_node, startstop_node, cli, keygen
-
-_general_commands = ( create_node.subCommands
-                    + keygen.subCommands
-                    + debug.subCommands
-                    + cli.subCommands
+from allmydata.scripts.common import get_default_nodedir
+from allmydata.scripts import debug, create_node, startstop_node, cli, keygen, stats_gatherer, admin
+from allmydata.util.encodingutil import quote_output, quote_local_unicode_path, get_io_encoding
+
+def GROUP(s):
+    # Usage.parseOptions compares argv[1] against command[0], so it will
+    # effectively ignore any "subcommand" that starts with a newline. We use
+    # these to insert section headers into the --help output.
+    return [("\n(%s)" % s, None, None, None)]
+
+
+_default_nodedir = get_default_nodedir()
+
+NODEDIR_HELP = ("Specify which Tahoe node directory should be used. The "
+                "directory should either contain a full Tahoe node, or a "
+                "file named node.url that points to some other Tahoe node. "
+                "It should also contain a file named '"
+                + os.path.join('private', 'aliases') +
+                "' which contains the mapping from alias name to root "
+                "dirnode URI.")
+if _default_nodedir:
+    NODEDIR_HELP += " [default for most commands: " + quote_local_unicode_path(_default_nodedir) + "]"
+
+class Options(usage.Options):
+    # unit tests can override these to point at StringIO instances
+    stdin = sys.stdin
+    stdout = sys.stdout
+    stderr = sys.stderr
+
+    synopsis = "\nUsage:  tahoe <command> [command options]"
+    subCommands = ( GROUP("Administration")
+                    +   create_node.subCommands
+                    +   keygen.subCommands
+                    +   stats_gatherer.subCommands
+                    +   admin.subCommands
+                    + GROUP("Controlling a node")
+                    +   startstop_node.subCommands
+                    + GROUP("Debugging")
+                    +   debug.subCommands
+                    + GROUP("Using the filesystem")
+                    +   cli.subCommands
                     )
 
-class Options(BaseOptions, usage.Options):
-    synopsis = "Usage:  tahoe <command> [command options]"
+    optFlags = [
+        ["quiet", "q", "Operate silently."],
+        ["version", "V", "Display version numbers."],
+        ["version-and-path", None, "Display version numbers and paths to their locations."],
+    ]
+    optParameters = [
+        ["node-directory", "d", None, NODEDIR_HELP],
+    ]
+
+    def opt_version(self):
+        import allmydata
+        print >>self.stdout, allmydata.get_package_versions_string(debug=True)
+        self.no_command_needed = True
+
+    def opt_version_and_path(self):
+        import allmydata
+        print >>self.stdout, allmydata.get_package_versions_string(show_paths=True, debug=True)
+        self.no_command_needed = True
 
-    subCommands = []
-    subCommands += _general_commands
-    subCommands += startstop_node.subCommands
+    def __str__(self):
+        return ("\nUsage: tahoe [global-options] <command> [command-options]\n"
+                + self.getUsage())
+
+    synopsis = "\nUsage: tahoe [global-opts]" # used only for subcommands
+
+    def getUsage(self, **kwargs):
+        t = usage.Options.getUsage(self, **kwargs)
+        t = t.replace("Options:", "\nGlobal options:", 1)
+        return t + "\nPlease run 'tahoe <command> --help' for more details on each command.\n"
 
     def postOptions(self):
         if not hasattr(self, 'subOptions'):
-            raise usage.UsageError("must specify a command")
+            if not hasattr(self, 'no_command_needed'):
+                raise usage.UsageError("must specify a command")
+            sys.exit(0)
+
+
+create_dispatch = {}
+for module in (create_node, keygen, stats_gatherer):
+    create_dispatch.update(module.dispatch)
 
 def runner(argv,
            run_by_human=True,
-           stdin=sys.stdin, stdout=sys.stdout, stderr=sys.stderr,
+           stdin=None, stdout=None, stderr=None,
            install_node_control=True, additional_commands=None):
 
+    stdin  = stdin  or sys.stdin
+    stdout = stdout or sys.stdout
+    stderr = stderr or sys.stderr
+
     config = Options()
     if install_node_control:
         config.subCommands.extend(startstop_node.subCommands)
@@ -50,8 +115,12 @@ def runner(argv,
         c = config
         while hasattr(c, 'subOptions'):
             c = c.subOptions
-        print str(c)
-        print "%s:  %s" % (sys.argv[0], e)
+        print >>stdout, str(c)
+        try:
+            msg = e.args[0].decode(get_io_encoding())
+        except Exception:
+            msg = repr(e)
+        print >>stdout, "%s:  %s\n" % (sys.argv[0], quote_output(msg, quotemarks=False))
         return 1
 
     command = config.subCommand
@@ -64,19 +133,16 @@ def runner(argv,
     so.stderr = stderr
     so.stdin = stdin
 
-    rc = 0
-    if command in create_node.dispatch:
-        for basedir in so.basedirs:
-            f = create_node.dispatch[command]
-            rc = f(basedir, so, stdout, stderr) or rc
+    if command in create_dispatch:
+        rc = create_dispatch[command](so, stdout, stderr)
     elif command in startstop_node.dispatch:
         rc = startstop_node.dispatch[command](so, stdout, stderr)
     elif command in debug.dispatch:
         rc = debug.dispatch[command](so)
+    elif command in admin.dispatch:
+        rc = admin.dispatch[command](so)
     elif command in cli.dispatch:
         rc = cli.dispatch[command](so)
-    elif command in keygen.dispatch:
-        rc = keygen.dispatch[command](so, stdout, stderr)
     elif command in ac_dispatch:
         rc = ac_dispatch[command](so, stdout, stderr)
     else:
@@ -84,6 +150,17 @@ def runner(argv,
 
     return rc
 
+
 def run(install_node_control=True):
-    rc = runner(sys.argv[1:])
+    try:
+        if sys.platform == "win32":
+            from allmydata.windows.fixups import initialize
+            initialize()
+
+        rc = runner(sys.argv[1:], install_node_control=install_node_control)
+    except Exception:
+        import traceback
+        traceback.print_exc()
+        rc = 1
+
     sys.exit(rc)