cli: improve formatting of all commands
authorBrian Warner <warner@lothar.com>
Tue, 26 May 2015 18:31:06 +0000 (11:31 -0700)
committerBrian Warner <warner@lothar.com>
Tue, 26 May 2015 18:31:06 +0000 (11:31 -0700)
Also:

* do some light refactoring of create-client/node
* make it clear that these commands' --basedir options do the same as
  the global --node-directory option
* use "global-options" instead of "global-opts"

src/allmydata/scripts/admin.py
src/allmydata/scripts/cli.py
src/allmydata/scripts/common.py
src/allmydata/scripts/create_node.py
src/allmydata/scripts/debug.py
src/allmydata/scripts/runner.py
src/allmydata/scripts/startstop_node.py
src/allmydata/test/test_cli.py

index 092d90a9c143c10c7dd03e971a477098deac3a1d..6dab86ab7b03f3255220a14073bc538dbe63159e 100644 (file)
@@ -3,8 +3,6 @@ from twisted.python import usage
 from allmydata.scripts.common import BaseOptions
 
 class GenerateKeypairOptions(BaseOptions):
-    def getSynopsis(self):
-        return "Usage: tahoe [global-opts] admin generate-keypair"
 
     def getUsage(self, width=None):
         t = BaseOptions.getUsage(self, width)
@@ -26,7 +24,7 @@ class DerivePubkeyOptions(BaseOptions):
         self.privkey = privkey
 
     def getSynopsis(self):
-        return "Usage: tahoe [global-opts] admin derive-pubkey PRIVKEY"
+        return "Usage: tahoe [global-options] admin derive-pubkey PRIVKEY"
 
     def getUsage(self, width=None):
         t = BaseOptions.getUsage(self, width)
@@ -57,7 +55,7 @@ class AdminCommand(BaseOptions):
         if not hasattr(self, 'subOptions'):
             raise usage.UsageError("must specify a subcommand")
     def getSynopsis(self):
-        return "Usage: tahoe [global-opts] admin SUBCOMMAND"
+        return "Usage: tahoe [global-options] admin SUBCOMMAND"
     def getUsage(self, width=None):
         t = BaseOptions.getUsage(self, width)
         t += """
index 18ecc6590c573913015b782921cc0faaa1ed88f9..ef4fc71fe9ca2f7713fea2e9afae96f38400473a 100644 (file)
@@ -57,10 +57,8 @@ class MakeDirectoryOptions(FilesystemOptions):
             if self['format'].upper() not in ("SDMF", "MDMF"):
                 raise usage.UsageError("%s is an invalid format" % self['format'])
 
-    def getSynopsis(self):
-        return "Usage:  %s [global-opts] mkdir [options] [REMOTE_DIR]" % (self.command_name,)
-
-    longdesc = """Create a new directory, either unlinked or as a subdirectory."""
+    synopsis = "[options] [REMOTE_DIR]"
+    description = """Create a new directory, either unlinked or as a subdirectory."""
 
 class AddAliasOptions(FilesystemOptions):
     def parseArgs(self, alias, cap):
@@ -69,10 +67,8 @@ class AddAliasOptions(FilesystemOptions):
             self.alias = self.alias[:-1]
         self.cap = cap
 
-    def getSynopsis(self):
-        return "Usage:  %s [global-opts] add-alias [options] ALIAS[:] DIRCAP" % (self.command_name,)
-
-    longdesc = """Add a new alias for an existing directory."""
+    synopsis = "[options] ALIAS[:] DIRCAP"
+    description = """Add a new alias for an existing directory."""
 
 class CreateAliasOptions(FilesystemOptions):
     def parseArgs(self, alias):
@@ -80,16 +76,12 @@ class CreateAliasOptions(FilesystemOptions):
         if self.alias.endswith(u':'):
             self.alias = self.alias[:-1]
 
-    def getSynopsis(self):
-        return "Usage:  %s [global-opts] create-alias [options] ALIAS[:]" % (self.command_name,)
-
-    longdesc = """Create a new directory and add an alias for it."""
+    synopsis = "[options] ALIAS[:]"
+    description = """Create a new directory and add an alias for it."""
 
 class ListAliasesOptions(FilesystemOptions):
-    def getSynopsis(self):
-        return "Usage:  %s [global-opts] list-aliases [options]" % (self.command_name,)
-
-    longdesc = """Display a table of all configured aliases."""
+    synopsis = "[options]"
+    description = """Display a table of all configured aliases."""
 
 class ListOptions(FilesystemOptions):
     optFlags = [
@@ -102,10 +94,9 @@ class ListOptions(FilesystemOptions):
     def parseArgs(self, where=""):
         self.where = argv_to_unicode(where)
 
-    def getSynopsis(self):
-        return "Usage:  %s [global-opts] ls [options] [PATH]" % (self.command_name,)
+    synopsis = "[options] [PATH]"
 
-    longdesc = """
+    description = """
     List the contents of some portion of the grid.
 
     If PATH is omitted, "tahoe:" is assumed.
@@ -113,7 +104,7 @@ class ListOptions(FilesystemOptions):
     When the -l or --long option is used, each line is shown in the
     following format:
 
-    drwx <size> <date/time> <name in this directory>
+     drwx <size> <date/time> <name in this directory>
 
     where each of the letters on the left may be replaced by '-'.
     If 'd' is present, it indicates that the object is a directory.
@@ -146,24 +137,20 @@ class GetOptions(FilesystemOptions):
         self.from_file = argv_to_unicode(arg1)
         self.to_file   = None if arg2 is None else argv_to_abspath(arg2)
 
-    def getSynopsis(self):
-        return "Usage:  %s [global-opts] get [options] REMOTE_FILE LOCAL_FILE" % (self.command_name,)
+    synopsis = "[options] REMOTE_FILE LOCAL_FILE"
 
-    longdesc = """
+    description = """
     Retrieve a file from the grid and write it to the local filesystem. If
     LOCAL_FILE is omitted or '-', the contents of the file will be written to
     stdout."""
 
-    def getUsage(self, width=None):
-        t = FilesystemOptions.getUsage(self, width)
-        t += """
-Examples:
- % tahoe get FOO |less            # write to stdout
- % tahoe get tahoe:FOO |less      # same
- % tahoe get FOO bar              # write to local file
- % tahoe get tahoe:FOO bar        # same
-"""
-        return t
+    description_unwrapped = """
+    Examples:
+     % tahoe get FOO |less            # write to stdout
+     % tahoe get tahoe:FOO |less      # same
+     % tahoe get FOO bar              # write to local file
+     % tahoe get tahoe:FOO bar        # same
+    """
 
 class PutOptions(FilesystemOptions):
     optFlags = [
@@ -186,33 +173,30 @@ class PutOptions(FilesystemOptions):
             if self['format'].upper() not in ("SDMF", "MDMF", "CHK"):
                 raise usage.UsageError("%s is an invalid format" % self['format'])
 
-    def getSynopsis(self):
-        return "Usage:  %s [global-opts] put [options] LOCAL_FILE REMOTE_FILE" % (self.command_name,)
+    synopsis = "[options] LOCAL_FILE REMOTE_FILE"
 
-    longdesc = """
+    description = """
     Put a file into the grid, copying its contents from the local filesystem.
     If REMOTE_FILE is missing, upload the file but do not link it into a
     directory; also print the new filecap to stdout. If LOCAL_FILE is missing
     or '-', data will be copied from stdin. REMOTE_FILE is assumed to start
     with tahoe: unless otherwise specified.
 
-    If the destination file already exists and is mutable, it will be modified
-    in-place, whether or not --mutable is specified. (--mutable only affects
-    creation of new files.)"""
-
-    def getUsage(self, width=None):
-        t = FilesystemOptions.getUsage(self, width)
-        t += """
-Examples:
- % cat FILE | tahoe put                # create unlinked file from stdin
- % cat FILE | tahoe put -              # same
- % tahoe put bar                       # create unlinked file from local 'bar'
- % cat FILE | tahoe put - FOO          # create tahoe:FOO from stdin
- % tahoe put bar FOO                   # copy local 'bar' to tahoe:FOO
- % tahoe put bar tahoe:FOO             # same
- % tahoe put bar MUTABLE-FILE-WRITECAP # modify the mutable file in-place
-"""
-        return t
+    If the destination file already exists and is mutable, it will be
+    modified in-place, whether or not --mutable is specified. (--mutable only
+    affects creation of new files.)
+    """
+
+    description_unwrapped = """
+    Examples:
+     % cat FILE | tahoe put                # create unlinked file from stdin
+     % cat FILE | tahoe put -              # same
+     % tahoe put bar                       # create unlinked file from local 'bar'
+     % cat FILE | tahoe put - FOO          # create tahoe:FOO from stdin
+     % tahoe put bar FOO                   # copy local 'bar' to tahoe:FOO
+     % tahoe put bar tahoe:FOO             # same
+     % tahoe put bar MUTABLE-FILE-WRITECAP # modify the mutable file in-place
+    """
 
 class CpOptions(FilesystemOptions):
     optFlags = [
@@ -229,10 +213,9 @@ class CpOptions(FilesystemOptions):
         self.sources = map(argv_to_unicode, args[:-1])
         self.destination = argv_to_unicode(args[-1])
 
-    def getSynopsis(self):
-        return "Usage: %s [global-opts] cp [options] FROM.. TO" % (self.command_name,)
+    synopsis = "[options] FROM.. TO"
 
-    longdesc = """
+    description = """
     Use 'tahoe cp' to copy files between a local filesystem and a Tahoe grid.
     Any FROM/TO arguments that begin with an alias indicate Tahoe-side
     files or non-file arguments. Directories will be copied recursively.
@@ -240,15 +223,15 @@ class CpOptions(FilesystemOptions):
     you have previously set up an alias 'home' with 'tahoe create-alias home',
     here are some examples:
 
-    tahoe cp ~/foo.txt home:  # creates tahoe-side home:foo.txt
+     tahoe cp ~/foo.txt home:  # creates tahoe-side home:foo.txt
 
-    tahoe cp ~/foo.txt /tmp/bar.txt home:  # copies two files to home:
+     tahoe cp ~/foo.txt /tmp/bar.txt home:  # copies two files to home:
 
-    tahoe cp ~/Pictures home:stuff/my-pictures  # copies directory recursively
+     tahoe cp ~/Pictures home:stuff/my-pictures  # copies directory recursively
 
     You can also use a dircap as either FROM or TO target:
 
-    tahoe cp URI:DIR2-RO:ixqhc4kdbjxc7o65xjnveoewym:5x6lwoxghrd5rxhwunzavft2qygfkt27oj3fbxlq4c6p45z5uneq/blog.html ./   # copy Zooko's wiki page to a local file
+     tahoe cp URI:DIR2-RO:ixqhc4kdbjxc7o65xjnveoewym:5x6lwoxghrd5rxhwunzavft2qygfkt27oj3fbxlq4c6p45z5uneq/blog.html ./   # copy Zooko's wiki page to a local file
 
     This command still has some limitations: symlinks and special files
     (device nodes, named pipes) are not handled very well. Arguments should
@@ -266,22 +249,21 @@ class UnlinkOptions(FilesystemOptions):
     def parseArgs(self, where):
         self.where = argv_to_unicode(where)
 
-    def getSynopsis(self):
-        return "Usage:  %s [global-opts] unlink [options] REMOTE_FILE" % (self.command_name,)
+    synopsis = "[options] REMOTE_FILE"
+    description = "Remove a named file from its parent directory."
 
 class RmOptions(UnlinkOptions):
-    def getSynopsis(self):
-        return "Usage:  %s [global-opts] rm [options] REMOTE_FILE" % (self.command_name,)
+    synopsis = "[options] REMOTE_FILE"
+    description = "Remove a named file from its parent directory."
 
 class MvOptions(FilesystemOptions):
     def parseArgs(self, frompath, topath):
         self.from_file = argv_to_unicode(frompath)
         self.to_file = argv_to_unicode(topath)
 
-    def getSynopsis(self):
-        return "Usage:  %s [global-opts] mv [options] FROM TO" % (self.command_name,)
+    synopsis = "[options] FROM TO"
 
-    longdesc = """
+    description = """
     Use 'tahoe mv' to move files that are already on the grid elsewhere on
     the grid, e.g., 'tahoe mv alias:some_file alias:new_file'.
 
@@ -298,10 +280,9 @@ class LnOptions(FilesystemOptions):
         self.from_file = argv_to_unicode(frompath)
         self.to_file = argv_to_unicode(topath)
 
-    def getSynopsis(self):
-        return "Usage:  %s [global-opts] ln [options] FROM_LINK TO_LINK" % (self.command_name,)
+    synopsis = "[options] FROM_LINK TO_LINK"
 
-    longdesc = """
+    description = """
     Use 'tahoe ln' to duplicate a link (directory entry) already on the grid
     to elsewhere on the grid. For example 'tahoe ln alias:some_file
     alias:new_file'. causes 'alias:new_file' to point to the same object that
@@ -345,8 +326,7 @@ class BackupOptions(FilesystemOptions):
         self.from_dir = argv_to_abspath(localdir)
         self.to_dir = argv_to_unicode(topath)
 
-    def getSynopsis(self):
-        return "Usage:  %s [global-opts] backup [options] FROM ALIAS:TO" % (self.command_name,)
+    synopsis = "[options] FROM ALIAS:TO"
 
     def opt_exclude(self, pattern):
         """Ignore files matching a glob pattern. You may give multiple
@@ -388,7 +368,7 @@ class BackupOptions(FilesystemOptions):
             else:
                 yield filename
 
-    longdesc = """
+    description = """
     Add a versioned backup of the local FROM directory to a timestamped
     subdirectory of the TO/Archives directory on the grid, sharing as many
     files and directories as possible with earlier backups. Create TO/Latest
@@ -403,10 +383,10 @@ class WebopenOptions(FilesystemOptions):
     def parseArgs(self, where=''):
         self.where = argv_to_unicode(where)
 
-    def getSynopsis(self):
-        return "Usage:  %s [global-opts] webopen [options] [ALIAS:PATH]" % (self.command_name,)
+    synopsis = "[options] [ALIAS:PATH]"
 
-    longdesc = """Open a web browser to the contents of some file or
+    description = """
+    Open a web browser to the contents of some file or
     directory on the grid. When run without arguments, open the Welcome
     page."""
 
@@ -420,11 +400,10 @@ class ManifestOptions(FilesystemOptions):
     def parseArgs(self, where=''):
         self.where = argv_to_unicode(where)
 
-    def getSynopsis(self):
-        return "Usage:  %s [global-opts] manifest [options] [ALIAS:PATH]" % (self.command_name,)
-
-    longdesc = """Print a list of all files and directories reachable from
-    the given starting point."""
+    synopsis = "[options] [ALIAS:PATH]"
+    description = """
+    Print a list of all files and directories reachable from the given
+    starting point."""
 
 class StatsOptions(FilesystemOptions):
     optFlags = [
@@ -433,11 +412,10 @@ class StatsOptions(FilesystemOptions):
     def parseArgs(self, where=''):
         self.where = argv_to_unicode(where)
 
-    def getSynopsis(self):
-        return "Usage:  %s [global-opts] stats [options] [ALIAS:PATH]" % (self.command_name,)
-
-    longdesc = """Print statistics about of all files and directories
-    reachable from the given starting point."""
+    synopsis = "[options] [ALIAS:PATH]"
+    description = """
+    Print statistics about of all files and directories reachable from the
+    given starting point."""
 
 class CheckOptions(FilesystemOptions):
     optFlags = [
@@ -449,10 +427,8 @@ class CheckOptions(FilesystemOptions):
     def parseArgs(self, *locations):
         self.locations = map(argv_to_unicode, locations)
 
-    def getSynopsis(self):
-        return "Usage:  %s [global-opts] check [options] [ALIAS:PATH]" % (self.command_name,)
-
-    longdesc = """
+    synopsis = "[options] [ALIAS:PATH]"
+    description = """
     Check a single file or directory: count how many shares are available and
     verify their hashes. Optionally repair the file if any problems were
     found."""
@@ -468,10 +444,8 @@ class DeepCheckOptions(FilesystemOptions):
     def parseArgs(self, *locations):
         self.locations = map(argv_to_unicode, locations)
 
-    def getSynopsis(self):
-        return "Usage:  %s [global-opts] deep-check [options] [ALIAS:PATH]" % (self.command_name,)
-
-    longdesc = """
+    synopsis = "[options] [ALIAS:PATH]"
+    description = """
     Check all files and directories reachable from the given starting point
     (which must be a directory), like 'tahoe check' but for multiple files.
     Optionally repair any problems found."""
index 3ebb3e8ca807650478ca56e2ad38bbba7a30c58a..d6246fc05f6198621080fd1d0e4389a03c26ff74 100644 (file)
@@ -93,7 +93,7 @@ class NoDefaultBasedirOptions(BasedirOptions):
         BasedirOptions.parseArgs(self, basedir)
 
     def getSynopsis(self):
-        return "Usage:  %s [global-opts] %s [options] NODEDIR" % (self.command_name, self.subcommand_name)
+        return "Usage:  %s [global-options] %s [options] NODEDIR" % (self.command_name, self.subcommand_name)
 
 
 DEFAULT_ALIAS = u"tahoe"
index 6663d125d2d5278dd6240c82c77d1a85b845f34e..65cbb63ae740d1e8630dffde99ee820e59a08914 100644 (file)
@@ -1,11 +1,12 @@
 
 import os, sys
 from allmydata.scripts.common import BasedirOptions, NoDefaultBasedirOptions
+from allmydata.scripts.default_nodedir import _default_nodedir
 from allmydata.util.assertutil import precondition
-from allmydata.util.encodingutil import listdir_unicode, argv_to_unicode, quote_output
+from allmydata.util.encodingutil import listdir_unicode, argv_to_unicode, quote_output, quote_local_unicode_path
 import allmydata
 
-class CreateClientOptions(BasedirOptions):
+class _CreateBaseOptions(BasedirOptions):
     optParameters = [
         # we provide 'create-node'-time options for the most common
         # configuration knobs. The rest can be controlled by editing
@@ -14,28 +15,30 @@ class CreateClientOptions(BasedirOptions):
         ("introducer", "i", None, "Specify the introducer FURL to use."),
         ("webport", "p", "tcp:3456:interface=127.0.0.1",
          "Specify which TCP port to run the HTTP interface on. Use 'none' to disable."),
-        ]
+        ("basedir", "C", None, "Specify which Tahoe base directory should be used. This has the same effect as the global --node-directory option. [default: %s]"
+         % quote_local_unicode_path(_default_nodedir)),
 
-    def getSynopsis(self):
-        return "Usage:  %s [global-opts] create-client [options] [NODEDIR]" % (self.command_name,)
+        ]
 
-    # This is overridden in order to ensure we get a "Wrong number of arguments."
-    # error when more than one argument is given.
+    # This is overridden in order to ensure we get a "Wrong number of
+    # arguments." error when more than one argument is given.
     def parseArgs(self, basedir=None):
         BasedirOptions.parseArgs(self, basedir)
 
+class CreateClientOptions(_CreateBaseOptions):
+    synopsis = "[options] [NODEDIR]"
+    description = "Create a client-only Tahoe-LAFS node (no storage server)."
 
 class CreateNodeOptions(CreateClientOptions):
     optFlags = [
         ("no-storage", None, "Do not offer storage service to other nodes."),
         ]
-
-    def getSynopsis(self):
-        return "Usage:  %s [global-opts] create-node [options] [NODEDIR]" % (self.command_name,)
-
+    synopsis = "[options] [NODEDIR]"
+    description = "Create a full Tahoe-LAFS node (client+server)."
 
 class CreateIntroducerOptions(NoDefaultBasedirOptions):
     subcommand_name = "create-introducer"
+    description = "Create a Tahoe-LAFS introducer."
 
 
 client_tac = """
index fedd69851e312c28ca6b871d74fce84a4a39c198..738f911e39307b60a35b6ae07b92bfe307de5c79 100644 (file)
@@ -11,25 +11,21 @@ from allmydata.scripts.common import BaseOptions
 
 class DumpOptions(BaseOptions):
     def getSynopsis(self):
-        return "Usage: tahoe [global-opts] debug dump-share SHARE_FILENAME"
+        return "Usage: tahoe [global-options] debug dump-share SHARE_FILENAME"
 
     optFlags = [
         ["offsets", None, "Display a table of section offsets."],
         ["leases-only", None, "Dump leases but not CHK contents."],
         ]
 
-    def getUsage(self, width=None):
-        t = BaseOptions.getUsage(self, width)
-        t += """
+    description = """
 Print lots of information about the given share, by parsing the share's
 contents. This includes share type, lease information, encoding parameters,
 hash-tree roots, public keys, and segment sizes. This command also emits a
 verify-cap for the file that uses the share.
 
  tahoe debug dump-share testgrid/node-3/storage/shares/4v/4vozh77tsrw7mdhnj7qvp5ky74/0
-
 """
-        return t
 
     def parseArgs(self, filename):
         from allmydata.util.encodingutil import argv_to_abspath
@@ -408,7 +404,7 @@ def dump_MDMF_share(m, length, options):
 
 class DumpCapOptions(BaseOptions):
     def getSynopsis(self):
-        return "Usage: tahoe [global-opts] debug dump-cap [options] FILECAP"
+        return "Usage: tahoe [global-options] debug dump-cap [options] FILECAP"
     optParameters = [
         ["nodeid", "n",
          None, "Specify the storage server nodeid (ASCII), to construct WE and secrets."],
@@ -420,9 +416,7 @@ class DumpCapOptions(BaseOptions):
     def parseArgs(self, cap):
         self.cap = cap
 
-    def getUsage(self, width=None):
-        t = BaseOptions.getUsage(self, width)
-        t += """
+    description = """
 Print information about the given cap-string (aka: URI, file-cap, dir-cap,
 read-cap, write-cap). The URI string is parsed and unpacked. This prints the
 type of the cap, its storage index, and any derived keys.
@@ -437,7 +431,6 @@ If additional information is provided (storage server nodeid and/or client
 base secret), this command will compute the shared secrets used for the
 write-enabler and for lease-renewal.
 """
-        return t
 
 
 def dump_cap(options):
@@ -610,16 +603,14 @@ def dump_uri_instance(u, nodeid, secret, out, show_header=True):
 
 class FindSharesOptions(BaseOptions):
     def getSynopsis(self):
-        return "Usage: tahoe [global-opts] debug find-shares STORAGE_INDEX NODEDIRS.."
+        return "Usage: tahoe [global-options] debug find-shares STORAGE_INDEX NODEDIRS.."
 
     def parseArgs(self, storage_index_s, *nodedirs):
         from allmydata.util.encodingutil import argv_to_abspath
         self.si_s = storage_index_s
         self.nodedirs = map(argv_to_abspath, nodedirs)
 
-    def getUsage(self, width=None):
-        t = BaseOptions.getUsage(self, width)
-        t += """
+    description = """
 Locate all shares for the given storage index. This command looks through one
 or more node directories to find the shares. It returns a list of filenames,
 one per line, for each share file found.
@@ -630,7 +621,6 @@ It may be useful during testing, when running a test grid in which all the
 nodes are on a local disk. The share files thus located can be counted,
 examined (with dump-share), or corrupted/deleted to test checker/repairer.
 """
-        return t
 
 def find_shares(options):
     """Given a storage index and a list of node directories, emit a list of
@@ -659,9 +649,6 @@ def find_shares(options):
 
 
 class CatalogSharesOptions(BaseOptions):
-    """
-
-    """
     def parseArgs(self, *nodedirs):
         from allmydata.util.encodingutil import argv_to_abspath
         self.nodedirs = map(argv_to_abspath, nodedirs)
@@ -669,11 +656,9 @@ class CatalogSharesOptions(BaseOptions):
             raise usage.UsageError("must specify at least one node directory")
 
     def getSynopsis(self):
-        return "Usage: tahoe [global-opts] debug catalog-shares NODEDIRS.."
+        return "Usage: tahoe [global-options] debug catalog-shares NODEDIRS.."
 
-    def getUsage(self, width=None):
-        t = BaseOptions.getUsage(self, width)
-        t += """
+    description = """
 Locate all shares in the given node directories, and emit a one-line summary
 of each share. Run it like this:
 
@@ -691,7 +676,6 @@ you see shares with the same SI but different parameters/filesize/UEB_hash,
 then something is wrong. The misc/find-share/anomalies.py script may be
 useful for purpose.
 """
-        return t
 
 def call(c, *args, **kwargs):
     # take advantage of the fact that ImmediateReadBucketProxy returns
@@ -882,15 +866,13 @@ def catalog_shares_one_abbrevdir(si_s, si_dir, now, out, err):
 
 class CorruptShareOptions(BaseOptions):
     def getSynopsis(self):
-        return "Usage: tahoe [global-opts] debug corrupt-share SHARE_FILENAME"
+        return "Usage: tahoe [global-options] debug corrupt-share SHARE_FILENAME"
 
     optParameters = [
         ["offset", "o", "block-random", "Specify which bit to flip."],
         ]
 
-    def getUsage(self, width=None):
-        t = BaseOptions.getUsage(self, width)
-        t += """
+    description = """
 Corrupt the given share by flipping a bit. This will cause a
 verifying/downloading client to log an integrity-check failure incident, and
 downloads will proceed with a different share.
@@ -902,7 +884,6 @@ to flip a single random bit of the block data.
 
 Obviously, this command should not be used in normal operation.
 """
-        return t
     def parseArgs(self, filename):
         self['filename'] = filename
 
@@ -962,7 +943,7 @@ def corrupt_share(options):
 
 class ReplOptions(BaseOptions):
     def getSynopsis(self):
-        return "Usage: tahoe [global-opts] debug repl"
+        return "Usage: tahoe [global-options] debug repl"
 
 def repl(options):
     import code
@@ -973,7 +954,7 @@ DEFAULT_TESTSUITE = 'allmydata'
 
 class TrialOptions(twisted_trial.Options):
     def getSynopsis(self):
-        return "Usage: tahoe [global-opts] debug trial [options] [[file|package|module|TestCase|testmethod]...]"
+        return "Usage: tahoe [global-options] debug trial [options] [[file|package|module|TestCase|testmethod]...]"
 
     def parseOptions(self, all_subargs, *a, **kw):
         self.trial_args = list(all_subargs)
@@ -985,13 +966,10 @@ class TrialOptions(twisted_trial.Options):
         if not nonoption_args:
             self.trial_args.append(DEFAULT_TESTSUITE)
 
-    def getUsage(self, width=None):
-        t = twisted_trial.Options.getUsage(self, width)
-        t += """
-The 'tahoe debug trial' command uses the correct imports for this instance of
-Tahoe-LAFS. The default test suite is '%s'.
-""" % (DEFAULT_TESTSUITE,)
-        return t
+    longdesc = twisted_trial.Options.longdesc + "\n\n" + (
+        "The 'tahoe debug trial' command uses the correct imports for this "
+        "instance of Tahoe-LAFS. The default test suite is '%s'."
+        % DEFAULT_TESTSUITE)
 
 def trial(config):
     sys.argv = ['trial'] + config.trial_args
@@ -1014,9 +992,9 @@ def fixOptionsClass( (subcmd, shortcut, OptionsClass, desc) ):
             t = OptionsClass.getSynopsis(self)
             i = t.find("Usage: flogtool ")
             if i >= 0:
-                return "Usage: tahoe [global-opts] debug flogtool " + t[i+len("Usage: flogtool "):]
+                return "Usage: tahoe [global-options] debug flogtool " + t[i+len("Usage: flogtool "):]
             else:
-                return "Usage: tahoe [global-opts] debug flogtool %s [options]" % (subcmd,)
+                return "Usage: tahoe [global-options] debug flogtool %s [options]" % (subcmd,)
     return (subcmd, shortcut, FixedOptionsClass, desc)
 
 class FlogtoolOptions(foolscap_cli.Options):
@@ -1025,7 +1003,7 @@ class FlogtoolOptions(foolscap_cli.Options):
         self.subCommands = map(fixOptionsClass, self.subCommands)
 
     def getSynopsis(self):
-        return "Usage: tahoe [global-opts] debug flogtool (%s) [command options]" % ("|".join([x[0] for x in self.subCommands]))
+        return "Usage: tahoe [global-options] debug flogtool COMMAND [flogtool-options]"
 
     def parseOptions(self, all_subargs, *a, **kw):
         self.flogtool_args = list(all_subargs)
@@ -1037,7 +1015,7 @@ class FlogtoolOptions(foolscap_cli.Options):
 The 'tahoe debug flogtool' command uses the correct imports for this instance
 of Tahoe-LAFS.
 
-Please run 'tahoe debug flogtool SUBCOMMAND --help' for more details on each
+Please run 'tahoe debug flogtool COMMAND --help' for more details on each
 subcommand.
 """
         return t
@@ -1066,20 +1044,11 @@ class DebugCommand(BaseOptions):
     def postOptions(self):
         if not hasattr(self, 'subOptions'):
             raise usage.UsageError("must specify a subcommand")
-    def getSynopsis(self):
-        return ""
+    synopsis = "COMMAND"
+
     def getUsage(self, width=None):
-        #t = BaseOptions.getUsage(self, width)
-        t = """Usage: tahoe debug SUBCOMMAND
-Subcommands:
-    tahoe debug dump-share      Unpack and display the contents of a share.
-    tahoe debug dump-cap        Unpack a read-cap or write-cap.
-    tahoe debug find-shares     Locate sharefiles in node directories.
-    tahoe debug catalog-shares  Describe all shares in node dirs.
-    tahoe debug corrupt-share   Corrupt a share by flipping a bit.
-    tahoe debug repl            Open a Python interpreter.
-    tahoe debug trial           Run tests using Twisted Trial with the right imports.
-    tahoe debug flogtool        Utilities to access log files.
+        t = BaseOptions.getUsage(self, width)
+        t += """\
 
 Please run e.g. 'tahoe debug dump-share --help' for more details on each
 subcommand.
index 4767e59e067138d1ae6436249c980ae8c452b3f1..c331eee72684ec1ac4a8c8e01da7ed0ad2557e0d 100644 (file)
@@ -70,7 +70,7 @@ class Options(usage.Options):
         return ("\nUsage: tahoe [global-options] <command> [command-options]\n"
                 + self.getUsage())
 
-    synopsis = "\nUsage: tahoe [global-opts]" # used only for subcommands
+    synopsis = "\nUsage: tahoe [global-options]" # used only for subcommands
 
     def getUsage(self, **kwargs):
         t = usage.Options.getUsage(self, **kwargs)
index 45b7fd11233753ebca334342f9c2ae1011625d64..b1aad6acdc35cb4d252ce69152348bafd22e2547 100644 (file)
@@ -3,12 +3,17 @@ import os, sys, signal, time
 from allmydata.scripts.common import BasedirOptions
 from twisted.scripts import twistd
 from twisted.python import usage
+from allmydata.scripts.default_nodedir import _default_nodedir
 from allmydata.util import fileutil
 from allmydata.util.encodingutil import listdir_unicode, quote_local_unicode_path
 
 
 class StartOptions(BasedirOptions):
     subcommand_name = "start"
+    optParameters = [
+        ("basedir", "C", None, "Specify which Tahoe base directory should be used. This has the same effect as the global --node-directory option. [default: %s]"
+         % quote_local_unicode_path(_default_nodedir)),
+        ]
 
     def parseArgs(self, basedir=None, *twistd_args):
         # This can't handle e.g. 'tahoe start --nodaemon', since '--nodaemon'
@@ -21,7 +26,7 @@ class StartOptions(BasedirOptions):
         self.twistd_args = twistd_args
 
     def getSynopsis(self):
-        return "Usage:  %s [global-opts] %s [options] [NODEDIR [twistd-options]]" % (self.command_name, self.subcommand_name)
+        return "Usage:  %s [global-options] %s [options] [NODEDIR [twistd-options]]" % (self.command_name, self.subcommand_name)
 
     def getUsage(self, width=None):
         t = BasedirOptions.getUsage(self, width) + "\n"
@@ -40,7 +45,7 @@ class StopOptions(BasedirOptions):
         BasedirOptions.parseArgs(self, basedir)
 
     def getSynopsis(self):
-        return "Usage:  %s [global-opts] stop [options] [NODEDIR]" % (self.command_name,)
+        return "Usage:  %s [global-options] stop [options] [NODEDIR]" % (self.command_name,)
 
 class RestartOptions(StartOptions):
     subcommand_name = "restart"
index 3c73d7069f66d268c08773e01f4b766a23767730..3caf7687ff9fb33a20a3fa1267e0d5568ed92be6 100644 (file)
@@ -563,124 +563,124 @@ class CLI(CLITestMixin, unittest.TestCase):
 class Help(unittest.TestCase):
     def test_get(self):
         help = str(cli.GetOptions())
-        self.failUnlessIn(" [global-opts] get [options] REMOTE_FILE LOCAL_FILE", help)
+        self.failUnlessIn("[options] REMOTE_FILE LOCAL_FILE", help)
         self.failUnlessIn("% tahoe get FOO |less", help)
 
     def test_put(self):
         help = str(cli.PutOptions())
-        self.failUnlessIn(" [global-opts] put [options] LOCAL_FILE REMOTE_FILE", help)
+        self.failUnlessIn("[options] LOCAL_FILE REMOTE_FILE", help)
         self.failUnlessIn("% cat FILE | tahoe put", help)
 
     def test_ls(self):
         help = str(cli.ListOptions())
-        self.failUnlessIn(" [global-opts] ls [options] [PATH]", help)
+        self.failUnlessIn("[options] [PATH]", help)
 
     def test_unlink(self):
         help = str(cli.UnlinkOptions())
-        self.failUnlessIn(" [global-opts] unlink [options] REMOTE_FILE", help)
+        self.failUnlessIn("[options] REMOTE_FILE", help)
 
     def test_rm(self):
         help = str(cli.RmOptions())
-        self.failUnlessIn(" [global-opts] rm [options] REMOTE_FILE", help)
+        self.failUnlessIn("[options] REMOTE_FILE", help)
 
     def test_mv(self):
         help = str(cli.MvOptions())
-        self.failUnlessIn(" [global-opts] mv [options] FROM TO", help)
+        self.failUnlessIn("[options] FROM TO", help)
         self.failUnlessIn("Use 'tahoe mv' to move files", help)
 
     def test_cp(self):
         help = str(cli.CpOptions())
-        self.failUnlessIn(" [global-opts] cp [options] FROM.. TO", help)
+        self.failUnlessIn("[options] FROM.. TO", help)
         self.failUnlessIn("Use 'tahoe cp' to copy files", help)
 
     def test_ln(self):
         help = str(cli.LnOptions())
-        self.failUnlessIn(" [global-opts] ln [options] FROM_LINK TO_LINK", help)
+        self.failUnlessIn("[options] FROM_LINK TO_LINK", help)
         self.failUnlessIn("Use 'tahoe ln' to duplicate a link", help)
 
     def test_mkdir(self):
         help = str(cli.MakeDirectoryOptions())
-        self.failUnlessIn(" [global-opts] mkdir [options] [REMOTE_DIR]", help)
+        self.failUnlessIn("[options] [REMOTE_DIR]", help)
         self.failUnlessIn("Create a new directory", help)
 
     def test_backup(self):
         help = str(cli.BackupOptions())
-        self.failUnlessIn(" [global-opts] backup [options] FROM ALIAS:TO", help)
+        self.failUnlessIn("[options] FROM ALIAS:TO", help)
 
     def test_webopen(self):
         help = str(cli.WebopenOptions())
-        self.failUnlessIn(" [global-opts] webopen [options] [ALIAS:PATH]", help)
+        self.failUnlessIn("[options] [ALIAS:PATH]", help)
 
     def test_manifest(self):
         help = str(cli.ManifestOptions())
-        self.failUnlessIn(" [global-opts] manifest [options] [ALIAS:PATH]", help)
+        self.failUnlessIn("[options] [ALIAS:PATH]", help)
 
     def test_stats(self):
         help = str(cli.StatsOptions())
-        self.failUnlessIn(" [global-opts] stats [options] [ALIAS:PATH]", help)
+        self.failUnlessIn("[options] [ALIAS:PATH]", help)
 
     def test_check(self):
         help = str(cli.CheckOptions())
-        self.failUnlessIn(" [global-opts] check [options] [ALIAS:PATH]", help)
+        self.failUnlessIn("[options] [ALIAS:PATH]", help)
 
     def test_deep_check(self):
         help = str(cli.DeepCheckOptions())
-        self.failUnlessIn(" [global-opts] deep-check [options] [ALIAS:PATH]", help)
+        self.failUnlessIn("[options] [ALIAS:PATH]", help)
 
     def test_create_alias(self):
         help = str(cli.CreateAliasOptions())
-        self.failUnlessIn(" [global-opts] create-alias [options] ALIAS[:]", help)
+        self.failUnlessIn("[options] ALIAS[:]", help)
 
     def test_add_alias(self):
         help = str(cli.AddAliasOptions())
-        self.failUnlessIn(" [global-opts] add-alias [options] ALIAS[:] DIRCAP", help)
+        self.failUnlessIn("[options] ALIAS[:] DIRCAP", help)
 
     def test_list_aliases(self):
         help = str(cli.ListAliasesOptions())
-        self.failUnlessIn(" [global-opts] list-aliases [options]", help)
+        self.failUnlessIn("[options]", help)
 
     def test_start(self):
         help = str(startstop_node.StartOptions())
-        self.failUnlessIn(" [global-opts] start [options] [NODEDIR [twistd-options]]", help)
+        self.failUnlessIn("[options] [NODEDIR [twistd-options]]", help)
 
     def test_stop(self):
         help = str(startstop_node.StopOptions())
-        self.failUnlessIn(" [global-opts] stop [options] [NODEDIR]", help)
+        self.failUnlessIn("[options] [NODEDIR]", help)
 
     def test_restart(self):
         help = str(startstop_node.RestartOptions())
-        self.failUnlessIn(" [global-opts] restart [options] [NODEDIR [twistd-options]]", help)
+        self.failUnlessIn("[options] [NODEDIR [twistd-options]]", help)
 
     def test_run(self):
         help = str(startstop_node.RunOptions())
-        self.failUnlessIn(" [global-opts] run [options] [NODEDIR [twistd-options]]", help)
+        self.failUnlessIn("[options] [NODEDIR [twistd-options]]", help)
 
     def test_create_client(self):
         help = str(create_node.CreateClientOptions())
-        self.failUnlessIn(" [global-opts] create-client [options] [NODEDIR]", help)
+        self.failUnlessIn("[options] [NODEDIR]", help)
 
     def test_create_node(self):
         help = str(create_node.CreateNodeOptions())
-        self.failUnlessIn(" [global-opts] create-node [options] [NODEDIR]", help)
+        self.failUnlessIn("[options] [NODEDIR]", help)
 
     def test_create_introducer(self):
         help = str(create_node.CreateIntroducerOptions())
-        self.failUnlessIn(" [global-opts] create-introducer [options] NODEDIR", help)
+        self.failUnlessIn("[options] NODEDIR", help)
 
     def test_debug_trial(self):
         help = str(debug.TrialOptions())
-        self.failUnlessIn(" [global-opts] debug trial [options] [[file|package|module|TestCase|testmethod]...]", help)
+        self.failUnlessIn(" [global-options] debug trial [options] [[file|package|module|TestCase|testmethod]...]", help)
         self.failUnlessIn("The 'tahoe debug trial' command uses the correct imports", help)
 
     def test_debug_flogtool(self):
         options = debug.FlogtoolOptions()
         help = str(options)
-        self.failUnlessIn(" [global-opts] debug flogtool ", help)
+        self.failUnlessIn(" [global-options] debug flogtool ", help)
         self.failUnlessIn("The 'tahoe debug flogtool' command uses the correct imports", help)
 
         for (option, shortcut, oClass, desc) in options.subCommands:
             subhelp = str(oClass())
-            self.failUnlessIn(" [global-opts] debug flogtool %s " % (option,), subhelp)
+            self.failUnlessIn(" [global-options] debug flogtool %s " % (option,), subhelp)
 
 
 class Ln(GridTestMixin, CLITestMixin, unittest.TestCase):