Re: [PATCH] %default keyword expansion

Greg Ward <[email protected]> Sun, 16 May 2004 22:01:41 -0400
Newsgroups gmane.comp.python.optik.user
Message-ID <[email protected]>
ARrgh, forgot the attachment.  !#$@^!^%#@%#!$
expand-defaults.patch (text/plain, 8 KB)
Index: lib/help.py
===================================================================
RCS file: /cvsroot/optik/optik/lib/help.py,v
retrieving revision 1.6
diff -u -r1.6 help.py
--- lib/help.py	27 Aug 2003 02:35:41 -0000	1.6
+++ lib/help.py	17 May 2004 01:58:17 -0000
@@ -4,6 +4,7 @@
 
 __all__ = ['HelpFormatter', 'IndentedHelpFormatter', 'TitledHelpFormatter']
 
+from optik.option import NO_DEFAULT
 
 class HelpFormatter:
 
@@ -13,6 +14,8 @@
     formatting help; by default IndentedHelpFormatter is used.
 
     Instance attributes:
+      parser : OptionParser
+        the controlling OptionParser instance
       indent_increment : int
         the number of columns to indent per nesting level
       max_help_position : int
@@ -28,13 +31,19 @@
         current indentation level (in columns)
       help_width : int
         number of columns available for option help text (calculated)
+      default_tag : str
+        text to replace with each option's default value, "%default"
+        by default.  Set to false value to disable default value expansion.
     """
 
+    NO_DEFAULT_VALUE = "none"
+
     def __init__ (self,
                   indent_increment,
                   max_help_position,
                   width,
                   short_first):
+        self.parser = None
         self.indent_increment = indent_increment
         self.help_position = self.max_help_position = max_help_position
         self.width = width
@@ -42,6 +51,10 @@
         self.level = 0
         self.help_width = width - max_help_position
         self.short_first = short_first
+        self.default_tag = "%default"
+
+    def set_parser(self, parser):
+        self.parser = parser
 
     def indent (self):
         self.current_indent += self.indent_increment
@@ -65,6 +78,16 @@
                              initial_indent=indent,
                              subsequent_indent=indent) + "\n"
 
+    def expand_default(self, option):
+        if self.parser is None or not self.default_tag:
+            return option.help
+
+        default_value = self.parser.defaults.get(option.dest)
+        if default_value in (NO_DEFAULT, None):
+            default_value = self.NO_DEFAULT_VALUE
+
+        return option.help.replace(self.default_tag, str(default_value))
+
     def format_option (self, option):
         # The help for each option consists of two parts:
         #   * the opt strings and metavars
@@ -91,7 +114,8 @@
             indent_first = 0
         result.append(opts)
         if option.help:
-            help_lines = textwrap.wrap(option.help, self.help_width)
+            help_text = self.expand_default(option)
+            help_lines = textwrap.wrap(help_text, self.help_width)
             result.append("%*s%s\n" % (indent_first, "", help_lines[0]))
             result.extend(["%*s%s\n" % (self.help_position, "", line)
                            for line in help_lines[1:]])
Index: lib/option_parser.py
===================================================================
RCS file: /cvsroot/optik/optik/lib/option_parser.py,v
retrieving revision 1.57
diff -u -r1.57 option_parser.py
--- lib/option_parser.py	27 Aug 2003 02:35:41 -0000	1.57
+++ lib/option_parser.py	17 May 2004 01:58:18 -0000
@@ -359,6 +359,7 @@
         if formatter is None:
             formatter = IndentedHelpFormatter()
         self.formatter = formatter
+        self.formatter.set_parser(self)
 
         # Populate the option list; initial sources are the
         # standard_option_list class attribute, the 'option_list'
Index: test/test_optik.py
===================================================================
RCS file: /cvsroot/optik/optik/test/test_optik.py,v
retrieving revision 1.22
diff -u -r1.22 test_optik.py
--- test/test_optik.py	7 Sep 2003 18:18:19 -0000	1.22
+++ test/test_optik.py	17 May 2004 01:58:18 -0000
@@ -144,6 +144,14 @@
         """Assert a TypeError is raised when executing func."""
         self.assertRaises(func, TypeError, expected_output, funcargs=args)
 
+    def assertHelp(self, parser, expected_help):
+        actual_help = parser.format_help()
+        if actual_help != expected_help:
+            raise self.failureException(
+                'help text failure; expected:\n"' +
+                expected_help + '"; got:\n"' +
+                actual_help + '"\n')
+
 # -- Test make_option() aka Option -------------------------------------
 
 # It's not necessary to test correct options here. All the tests in the
@@ -332,9 +340,6 @@
     def assertVersion(self, parser, expected_version):
         self.assertEqual(parser.get_version(), expected_version)
 
-    def assertHelp(self, parser, expected_help):
-        self.assertEqual(parser.format_help(), expected_help)
-
 
     def test_default_progname(self):
         # Make sure that program name taken from sys.argv[0] by default.
@@ -361,6 +366,82 @@
         self.assertHelp(parser, expected_usage + "\n")
 
 
+class TestDefaultValues(BaseTest):
+    def setUp(self):
+        self.parser = OptionParser(prog="test")
+        self.help_prefix = """\
+usage: test [options]
+
+options:
+  -h, --help           show this help message and exit
+"""
+        self.file_help = "read from FILE [default: %default]"
+        self.expected_help_file = self.help_prefix + \
+            "  -fFILE, --file=FILE  read from FILE [default: foo.txt]\n"
+        self.expected_help_none = self.help_prefix + \
+            "  -fFILE, --file=FILE  read from FILE [default: none]\n"
+
+    def test_option_default(self):
+        self.parser.add_option("-f", "--file",
+                               default="foo.txt",
+                               help=self.file_help)
+        self.assertHelp(self.parser, self.expected_help_file)
+
+    def test_parser_default_1(self):
+        self.parser.add_option("-f", "--file",
+                               help=self.file_help)
+        self.parser.set_default('file', "foo.txt")
+        self.assertHelp(self.parser, self.expected_help_file)
+
+    def test_parser_default_2(self):
+        self.parser.add_option("-f", "--file",
+                               help=self.file_help)
+        self.parser.set_defaults(file="foo.txt")
+        self.assertHelp(self.parser, self.expected_help_file)
+
+    def test_no_default(self):
+        self.parser.add_option("-f", "--file",
+                               help=self.file_help)
+        self.assertHelp(self.parser, self.expected_help_none)
+
+    def test_default_none_1(self):
+        self.parser.add_option("-f", "--file",
+                               default=None,
+                               help=self.file_help)
+        self.assertHelp(self.parser, self.expected_help_none)
+        
+    def test_default_none_2(self):
+        self.parser.add_option("-f", "--file",
+                               help=self.file_help)
+        self.parser.set_defaults(file=None)
+        self.assertHelp(self.parser, self.expected_help_none)
+
+    def test_float_default(self):
+        self.parser.add_option(
+            "-p", "--prob",
+            help="blow up with probability PROB [default: %default]")
+        self.parser.set_defaults(prob=0.43)
+        expected_help = self.help_prefix + \
+            "  -pPROB, --prob=PROB  blow up with probability PROB [default: 0.43]\n"
+        self.assertHelp(self.parser, expected_help)
+
+    def test_alt_expand(self):
+        self.parser.add_option("-f", "--file",
+                               default="foo.txt",
+                               help="read from FILE [default: *DEFAULT*]")
+        self.parser.formatter.default_tag = "*DEFAULT*"
+        self.assertHelp(self.parser, self.expected_help_file)
+
+    def test_no_expand(self):
+        self.parser.add_option("-f", "--file",
+                               default="foo.txt",
+                               help="read from %default file")
+        self.parser.formatter.default_tag = None
+        expected_help = self.help_prefix + \
+            "  -fFILE, --file=FILE  read from %default file\n"
+        self.assertHelp(self.parser, expected_help)
+
+
 # -- Test parser.parse_args() ------------------------------------------
 
 class TestStandard(BaseTest):