From: Brian Warner <warner@lothar.com>
Date: Tue, 26 May 2015 18:29:49 +0000 (-0700)
Subject: scripts: improve rendering of synopsis/usage
X-Git-Tag: allmydata-tahoe-1.10.1b1~14^2~2
X-Git-Url: https://git.rkrishnan.org/%5B/frontends//%22file:/%22?a=commitdiff_plain;h=01619844de3d51df3528f0b691962e4b7b865dc5;p=tahoe-lafs%2Ftahoe-lafs.git

scripts: improve rendering of synopsis/usage

Subcommands "--help" is now rendered as:

```
 tahoe [global-options] COMMAND [options] ARGS
 (use 'tahoe --help' to view global options)
 USAGE (flags/options)
 DESCRIPTION
 DESCRIPTION_UNWRAPPED
```

The new .description and .description_unwrapped fields allow
commands (subclasses of twisted.python.usage.Usage) better control over
how their explanations are rendered: the old .longdesc field was wrapped
unpleasantly.
---

diff --git a/src/allmydata/scripts/common.py b/src/allmydata/scripts/common.py
index 2894d3dd..3ebb3e8c 100644
--- a/src/allmydata/scripts/common.py
+++ b/src/allmydata/scripts/common.py
@@ -1,5 +1,5 @@
 
-import os, sys, urllib
+import os, sys, urllib, textwrap
 import codecs
 from twisted.python import usage
 from allmydata.util.assertutil import precondition
@@ -10,6 +10,14 @@ from allmydata.scripts.default_nodedir import _default_nodedir
 def get_default_nodedir():
     return _default_nodedir
 
+def wrap_paragraphs(text, width):
+    # like textwrap.wrap(), but preserve paragraphs (delimited by double
+    # newlines) and leading whitespace, and remove internal whitespace.
+    text = textwrap.dedent(text)
+    if text.startswith("\n"):
+        text = text[1:]
+    return "\n\n".join([textwrap.fill(paragraph, width=width)
+                        for paragraph in text.split("\n\n")])
 
 class BaseOptions(usage.Options):
     def __init__(self):
@@ -22,6 +30,24 @@ class BaseOptions(usage.Options):
     def opt_version(self):
         raise usage.UsageError("--version not allowed on subcommands")
 
+    description = None
+    description_unwrapped = None
+
+    def __str__(self):
+        width = int(os.environ.get('COLUMNS', '80'))
+        s = (self.getSynopsis() + '\n' +
+             "(use 'tahoe --help' to view global options)\n" +
+             '\n' +
+             self.getUsage())
+        if self.description:
+            s += '\n' + wrap_paragraphs(self.description, width) + '\n'
+        if self.description_unwrapped:
+            du = textwrap.dedent(self.description_unwrapped)
+            if du.startswith("\n"):
+                du = du[1:]
+            s += '\n' + du + '\n'
+        return s
+
 class BasedirOptions(BaseOptions):
     default_nodedir = _default_nodedir
 
diff --git a/src/allmydata/scripts/runner.py b/src/allmydata/scripts/runner.py
index f295a980..4767e59e 100644
--- a/src/allmydata/scripts/runner.py
+++ b/src/allmydata/scripts/runner.py
@@ -66,11 +66,15 @@ class Options(usage.Options):
         print >>self.stdout, allmydata.get_package_versions_string(show_paths=True, debug=True)
         self.no_command_needed = True
 
-    def getSynopsis(self):
-        return "\nUsage: tahoe [global-opts] <command> [command-options]"
+    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):