config: add decorator to add_option_* functions
authorGuido Günther <agx@sigxcpu.org>
Wed, 2 Apr 2014 20:13:46 +0000 (22:13 +0200)
committerGuido Günther <agx@sigxcpu.org>
Thu, 3 Apr 2014 18:34:07 +0000 (20:34 +0200)
This allows us to build an internal list of valid options and print
these.

13 files changed:
docs/manpages/gbp-config.sgml
gbp/config.py
gbp/scripts/buildpackage.py
gbp/scripts/clone.py
gbp/scripts/config.py
gbp/scripts/create_remote_repo.py
gbp/scripts/dch.py
gbp/scripts/import_dsc.py
gbp/scripts/import_orig.py
gbp/scripts/pq.py
gbp/scripts/pull.py
tests/18_test_Config.py
tests/19_test_gbp_scripts_config.py

index c07d758..d68ac64 100644 (file)
 
       <arg><option>--verbose</option></arg>
       <arg><option>--color=</option><replaceable>[auto|on|off]</replaceable></arg>
-      <arg choice="plain"><replaceable>command.option</replaceable></arg>
+      <group choice='req'>
+       <arg choice="plain"><replaceable>command.option</replaceable></arg>
+       <arg choice="plain"><replaceable>command</replaceable></arg>
+      </group>
     </cmdsynopsis>
   </refsynopsisdiv>
   <refsect1>
     would use:</para>
     <screen>
     $ gbp config buildpackage.upstream-branch
-    upstream
+    buildpackage.upstream-branch=upstream
+    </screen>
+    <para>Print the values of all of &gbp-buildpackage;s options</para>
+    <screen>
+    $ gbp config buildpackage
+    buildpackage.upstream-branch=upstream
+    buildpackage.debian-branch=master
+    ...
     </screen>
   </refsect1>
   <refsect1>
index 710eddf..fc31076 100644 (file)
@@ -45,6 +45,27 @@ def check_tristate(option, opt, value):
     else:
         return val
 
+
+def safe_option(f):
+    def _decorator(self, *args, **kwargs):
+        obj = self
+        option_name = kwargs.get('option_name')
+        if not option_name and len(args):
+            option_name = args[0]
+
+        # We're decorating GbpOption not GbpOptionParser
+        if not hasattr(obj, 'valid_options'):
+            if not hasattr(obj, 'parser'):
+                raise ValueError("Can only decorete GbpOptionParser and GbpOptionGroup not %s" % obj)
+            else:
+                obj = obj.parser
+
+        if option_name and not option_name.startswith('no-'):
+            obj.valid_options.append(option_name)
+        return f(self, *args, **kwargs)
+    return _decorator
+
+
 class GbpOption(Option):
     TYPES = Option.TYPES + ('path', 'tristate')
     TYPE_CHECKER = copy(Option.TYPE_CHECKER)
@@ -373,6 +394,7 @@ class GbpOptionParser(OptionParser):
         self.config = {}
         self.config_files = self.get_config_files()
         self.parse_config_files()
+        self.valid_options = []
 
         if self.command.startswith('git-') or self.command.startswith('gbp-'):
             prog = self.command
@@ -426,15 +448,16 @@ class GbpOptionParser(OptionParser):
             default = self.config[option_name]
         return default
 
+    @safe_option
     def add_config_file_option(self, option_name, dest, help=None, **kwargs):
         """
         set a option for the command line parser, the default is read from the config file
-        @param option_name: name of the option
-        @type option_name: string
-        @param dest: where to store this option
-        @type dest: string
-        @param help: help text
-        @type help: string
+        param option_name: name of the option
+        type option_name: string
+        param dest: where to store this option
+        type dest: string
+        param help: help text
+        type help: string
         """
         if not help:
             help = self.help[option_name]
@@ -460,15 +483,16 @@ class GbpOptionParser(OptionParser):
 
 
 class GbpOptionGroup(OptionGroup):
+    @safe_option
     def add_config_file_option(self, option_name, dest, help=None, **kwargs):
         """
         set a option for the command line parser, the default is read from the config file
-        @param option_name: name of the option
-        @type option_name: string
-        @param dest: where to store this option
-        @type dest: string
-        @param help: help text
-        @type help: string
+        param option_name: name of the option
+        type option_name: string
+        param dest: where to store this option
+        type dest: string
+        param help: help text
+        type help: string
         """
         if not help:
             help = self.parser.help[option_name]
index 753ad64..c077b9e 100755 (executable)
@@ -365,20 +365,12 @@ def changes_file_suffix(dpkg_args):
         return os.getenv('ARCH', None) or du.get_arch()
 
 
-def parse_args(argv, prefix):
-    args = [ arg for arg in argv[1:] if arg.find('--%s' % prefix) == 0 ]
-    dpkg_args = [ arg for arg in argv[1:] if arg.find('--%s' % prefix) == -1 ]
-
-    # We handle these although they don't have a --git- prefix
-    for arg in [ "--help", "-h", "--version" ]:
-        if arg in dpkg_args:
-            args.append(arg)
-
+def build_parser(name, prefix=None):
     try:
-        parser = GbpOptionParserDebian(command=os.path.basename(argv[0]), prefix=prefix)
+        parser = GbpOptionParserDebian(command=os.path.basename(name), prefix=prefix)
     except ConfigParser.ParsingError as err:
         gbp.log.err(err)
-        return None, None, None
+        return None
 
     tag_group = GbpOptionGroup(parser, "tag options", "options related to git tag creation")
     branch_group = GbpOptionGroup(parser, "branch options", "branch layout options")
@@ -453,6 +445,21 @@ def parse_args(argv, prefix):
     export_group.add_option("--git-dont-purge", action="store_true", dest="dont_purge", default=False,
                             help="deprecated, use --git-no-purge instead")
     export_group.add_boolean_config_file_option(option_name="overlay", dest="overlay")
+    return parser
+
+
+def parse_args(argv, prefix):
+    args = [ arg for arg in argv[1:] if arg.find('--%s' % prefix) == 0 ]
+    dpkg_args = [ arg for arg in argv[1:] if arg.find('--%s' % prefix) == -1 ]
+
+    # We handle these although they don't have a --git- prefix
+    for arg in [ "--help", "-h", "--version" ]:
+        if arg in dpkg_args:
+            args.append(arg)
+
+    parser = build_parser(argv[0], prefix=prefix)
+    if not parser:
+        return None, None, None
     options, args = parser.parse_args(args)
 
     gbp.log.setup(options.color, options.verbose, options.color_scheme)
index 251cef2..62d0dcc 100755 (executable)
@@ -29,13 +29,13 @@ from gbp.errors import GbpError
 import gbp.log
 
 
-def parse_args (argv):
+def build_parser(name):
     try:
-        parser = GbpOptionParser(command=os.path.basename(argv[0]), prefix='',
+        parser = GbpOptionParser(command=os.path.basename(name), prefix='',
                                  usage='%prog [options] repository - clone a remote repository')
     except ConfigParser.ParsingError as err:
         gbp.log.err(err)
-        return None, None
+        return None
 
     branch_group = GbpOptionGroup(parser, "branch options", "branch tracking and layout options")
     parser.add_option_group(branch_group)
@@ -53,10 +53,16 @@ def parse_args (argv):
     parser.add_config_file_option(option_name="color", dest="color", type='tristate')
     parser.add_config_file_option(option_name="color-scheme",
                                   dest="color_scheme")
+    return parser
+
+
+def parse_args (argv):
+    parser = build_parser(argv[0])
+    if not parser:
+        return None, None
 
     (options, args) = parser.parse_args(argv)
     gbp.log.setup(options.color, options.verbose, options.color_scheme)
-
     return (options, args)
 
 
index 19966fe..0ebca13 100755 (executable)
@@ -22,49 +22,88 @@ import sys
 import os, os.path
 from gbp.config import (GbpOptionParser, GbpOptionGroup)
 from gbp.errors import GbpError
+from gbp.scripts.supercommand import import_command
 import gbp.log
 
-def parse_args(argv):
+
+def build_parser(name):
     try:
-        parser = GbpOptionParser(command=os.path.basename(argv[0]), prefix='',
+        parser = GbpOptionParser(command=os.path.basename(name), prefix='',
                              usage='%prog [options] - display configuration settings')
     except ConfigParser.ParsingError as err:
         gbp.log.err(err)
-        return None, None
+        return None
 
     parser.add_option("-v", "--verbose", action="store_true", dest="verbose", default=False,
                       help="verbose command execution")
     parser.add_config_file_option(option_name="color", dest="color", type='tristate')
     parser.add_config_file_option(option_name="color-scheme",
                                   dest="color_scheme")
+    return parser
+
+
+def parse_args(argv):
+    parser = build_parser(argv[0])
+    if not parser:
+        return None, None
     return parser.parse_args(argv)
 
 
-def parse_config(command):
+def parse_cmd_config(command):
+    """Make a command parse it's config files"""
     parser = GbpOptionParser(command)
     parser.parse_config_files()
     return parser
 
 
-def print_single_value(query, printer):
+def print_cmd_single_value(query, printer):
+    """Print a single configuration value of a command
+
+    @param query: the cmd to print the value for
+    @param printer: the printer to output the value
+    """
     try:
         cmd, option = query.split('.')
     except ValueError:
         return 2
 
-    parser = parse_config(cmd)
+    parser = parse_cmd_config(cmd)
     value = parser.get_config_file_value(option)
-    printer(value)
+    printer("%s=%s" % (query, value))
     return 0 if value else 1
 
 
-def single_value_printer(value):
+def print_cmd_all_values(cmd, printer):
+    """
+    Print all configuration values of a command
+
+    @param cmd: the cmd to print the values for
+    @param printer: the printer to output the values
+    """
+    if not cmd:
+        return 2
+    try:
+        # Populae the parset to get a list of
+        # valid options
+        module = import_command(cmd)
+        parser = module.build_parser(cmd)
+    except (AttributeError, ImportError):
+        return 2
+
+    for option in parser.valid_options:
+        value = parser.get_config_file_value(option)
+        if value != '':
+            printer("%s.%s=%s" % (cmd, option, value))
+    return 0
+
+
+def value_printer(value):
     if (value):
         print(value)
 
 
 def main(argv):
-    retval = 0
+    retval = 1
 
     (options, args) = parse_args(argv)
     gbp.log.setup(options.color, options.verbose, options.color_scheme)
@@ -73,12 +112,15 @@ def main(argv):
         gbp.log.error("No command given")
         return 2
     elif len(args) != 2:
-        gbp.log.error("Can only print a single value")
+        gbp.log.error("Can only take a single argument")
         return 2
     else:
         query = args[1]
 
-    retval = print_single_value(query, single_value_printer)
+    if '.' in query:
+        retval = print_cmd_single_value(query, value_printer)
+    else:
+        retval = print_cmd_all_values(query, value_printer)
     return retval
 
 if __name__ == '__main__':
index c8c4a36..f0e680b 100644 (file)
@@ -18,6 +18,7 @@
 # Based on the aa-create-git-repo and dom-new-git-repo shell scripts
 """Create a remote repo based on the current one"""
 
+import ConfigParser
 import sys
 import os, os.path
 import urlparse
@@ -225,30 +226,16 @@ def push_branches(remote, branches):
     gitPush([remote['url'], '--tags'])
 
 
-def parse_args(argv, sections=[]):
-    """
-    Parse the command line arguments and config files.
-
-    @param argv: the command line arguments
-    @type argv: C{list} of C{str}
-    @param sections: additional sections to add to the config file parser
-        besides the command name
-    @type sections: C{list} of C{str}
-    """
-
-    # We simpley handle the template section as an additional config file
-    # section to parse, this makes e.g. --help work as expected:
-    for arg in argv:
-        if arg.startswith('--remote-config='):
-            sections = ['remote-config %s' % arg.split('=',1)[1]]
-            break
-    else:
-        sections = []
+def build_parser(name, sections=[]):
+    try:
+        parser = GbpOptionParserDebian(command=os.path.basename(name), prefix='',
+                                       usage='%prog [options] - '
+                                       'create a remote repository',
+                                       sections=sections)
+    except ConfigParser.ParsingError as err:
+        gbp.log.err(err)
+        return None
 
-    parser = GbpOptionParserDebian(command=os.path.basename(argv[0]), prefix='',
-                                   usage='%prog [options] - '
-                                   'create a remote repository',
-                                   sections=sections)
     branch_group = GbpOptionGroup(parser,
                                   "branch options",
                                   "branch layout and tracking options")
@@ -281,10 +268,34 @@ def parse_args(argv, sections=[]):
                                   dest="template_dir")
     parser.add_config_file_option(option_name="remote-config",
                                   dest="remote_config")
+    return parser
+
+
+def parse_args(argv, sections=[]):
+    """
+    Parse the command line arguments and config files.
+
+    @param argv: the command line arguments
+    @type argv: C{list} of C{str}
+    @param sections: additional sections to add to the config file parser
+        besides the command name
+    @type sections: C{list} of C{str}
+    """
+
+    # We simpley handle the template section as an additional config file
+    # section to parse, this makes e.g. --help work as expected:
+    for arg in argv:
+        if arg.startswith('--remote-config='):
+            sections = ['remote-config %s' % arg.split('=',1)[1]]
+            break
+    else:
+        sections = []
 
-    (options, args) = parser.parse_args(argv)
+    parser = build_parser(argv[0], sections)
+    if not parser:
+        return None, None
 
-    return options, args
+    return parser.parse_args(argv)
 
 
 def main(argv):
index a848d6d..f36f287 100644 (file)
@@ -283,20 +283,14 @@ def changelog_commit_msg(options, version):
     return options.commit_msg % dict(version=version)
 
 
-def main(argv):
-    ret = 0
-    changelog = 'debian/changelog'
-    until = 'HEAD'
-    found_snapshot_banner = False
-    version_change = {}
-    branch = None
-
+def build_parser(name):
     try:
-        parser = GbpOptionParserDebian(command=os.path.basename(argv[0]), prefix='',
+        parser = GbpOptionParserDebian(command=os.path.basename(name),
                                        usage='%prog [options] paths')
     except ConfigParser.ParsingError as err:
         gbp.log.err(err)
-        return 1
+        return None
+
     range_group = GbpOptionGroup(parser, "commit range options",
                                  "which commits to add to the changelog")
     version_group = GbpOptionGroup(parser, "release & version number options",
@@ -374,10 +368,31 @@ def main(argv):
                                         dest="customization_file",
                                         help=help_msg)
 
+
+    return parser
+
+
+def parse_args(argv):
+    parser = build_parser(argv[0])
+    if not parser:
+        return None, None
+
     (options, args) = parser.parse_args(argv[1:])
     gbp.log.setup(options.color, options.verbose, options.color_scheme)
     dch_options = process_options(options, parser)
     editor_cmd = process_editor_option(options)
+    return options, args, dch_options, editor_cmd
+
+def main(argv):
+    ret = 0
+    changelog = 'debian/changelog'
+    until = 'HEAD'
+    found_snapshot_banner = False
+    version_change = {}
+    branch = None
+
+
+    options, args, dch_options, editor_cmd = parse_args(argv)
 
     try:
         try:
index 630422b..600b394 100644 (file)
@@ -204,14 +204,13 @@ def set_bare_repo_options(options):
                       % (["", " '--no-pristine-tar'"][options.pristine_tar], ))
         options.pristine_tar = False
 
-
-def parse_args(argv):
+def build_parser(name):
     try:
-        parser = GbpOptionParserDebian(command=os.path.basename(argv[0]), prefix='',
+        parser = GbpOptionParserDebian(command=os.path.basename(name), prefix='',
                                        usage='%prog [options] /path/to/package.dsc')
     except ConfigParser.ParsingError as err:
         gbp.log.err(err)
-        return None, None
+        return None
 
     import_group = GbpOptionGroup(parser, "import options",
                       "pristine-tar and filtering")
@@ -263,9 +262,15 @@ def parse_args(argv):
                       dest="author_committer_date")
     import_group.add_boolean_config_file_option(option_name="allow-unauthenticated",
                       dest="allow_unauthenticated")
+    return parser
+
+
+def parse_args(argv):
+    parser = build_parser(argv[0])
+    if not parser:
+        return None, None
 
     (options, args) = parser.parse_args(argv[1:])
-    gbp.log.setup(options.color, options.verbose)
     gbp.log.setup(options.color, options.verbose, options.color_scheme)
     return options, args
 
index aae93fa..542896e 100644 (file)
@@ -181,13 +181,13 @@ def set_bare_repo_options(options):
         options.merge = False
 
 
-def parse_args(argv):
+def build_parser(name):
     try:
-        parser = GbpOptionParserDebian(command=os.path.basename(argv[0]), prefix='',
+        parser = GbpOptionParserDebian(command=os.path.basename(name), prefix='',
                                        usage='%prog [options] /path/to/upstream-version.tar.gz | --uscan')
     except ConfigParser.ParsingError as err:
         gbp.log.err(err)
-        return None, None
+        return None
 
     import_group = GbpOptionGroup(parser, "import options",
                       "pristine-tar and filtering")
@@ -241,6 +241,13 @@ def parse_args(argv):
                       default=False, help="deprecated - don't use.")
     parser.add_option("--uscan", dest='uscan', action="store_true",
                       default=False, help="use uscan(1) to download the new tarball.")
+    return parser
+
+
+def parse_args(argv):
+    parser = build_parser(argv[0])
+    if not parser:
+        return None, None
 
     (options, args) = parser.parse_args(argv[1:])
     gbp.log.setup(options.color, options.verbose, options.color_scheme)
index 41d3ddf..fc205bf 100755 (executable)
@@ -210,9 +210,9 @@ def switch_pq(repo, current):
         switch_to_pq_branch(repo, current)
 
 
-def parse_args(argv):
+def build_parser(name):
     try:
-        parser = GbpOptionParserDebian(command=os.path.basename(argv[0]), prefix='',
+        parser = GbpOptionParserDebian(command=os.path.basename(name),
                                    usage="%prog [options] action - maintain patches on a patch queue branch\n"
         "Actions:\n"
         "  export         export the patch queue associated to the current branch\n"
@@ -226,7 +226,7 @@ def parse_args(argv):
         "  switch         switch to patch-queue branch and vice versa")
     except ConfigParser.ParsingError as err:
         gbp.log.err(err)
-        return None, None
+        return None
 
     parser.add_boolean_config_file_option(option_name="patch-numbers", dest="patch_numbers")
     parser.add_option("-v", "--verbose", action="store_true", dest="verbose", default=False,
@@ -238,6 +238,13 @@ def parse_args(argv):
     parser.add_config_file_option(option_name="color", dest="color", type='tristate')
     parser.add_config_file_option(option_name="color-scheme",
                                   dest="color_scheme")
+    return parser
+
+
+def parse_args(argv):
+    parser = build_parser(argv[0])
+    if not parser:
+        return None, None
     return parser.parse_args(argv)
 
 
index 03be9fc..fb0d827 100755 (executable)
@@ -68,13 +68,14 @@ def fast_forward_branch(branch, repo, options):
                             msg="gbp: forward %s to %s" % (branch, remote))
     return update
 
-def parse_args(argv):
+
+def build_parser(name):
     try:
-        parser = GbpOptionParser(command=os.path.basename(argv[0]), prefix='',
+        parser = GbpOptionParser(command=os.path.basename(name), prefix='',
                              usage='%prog [options] - safely update a repository from remote')
     except ConfigParser.ParsingError as err:
         gbp.log.err(err)
-        return None, None
+        return None
 
     branch_group = GbpOptionGroup(parser, "branch options", "branch update and layout options")
     parser.add_option_group(branch_group)
@@ -93,6 +94,13 @@ def parse_args(argv):
     parser.add_config_file_option(option_name="color", dest="color", type='tristate')
     parser.add_config_file_option(option_name="color-scheme",
                                   dest="color_scheme")
+    return parser
+
+
+def parse_args(argv):
+    parser = build_parser(argv[0])
+    if not parser:
+        return None, None
     return parser.parse_args(argv)
 
 
index ae39b5a..f7ba8c9 100644 (file)
@@ -2,7 +2,7 @@
 
 import os
 import unittest
-from gbp.config import GbpOptionParser
+from gbp.config import GbpOptionParser, GbpOptionGroup
 from . import context
 
 class TestConfigParser(unittest.TestCase):
@@ -71,3 +71,17 @@ class TestConfigParser(unittest.TestCase):
         self.assertEqual(parser.get_config_file_value('new_overrides_git_option1'),
                          'new_overrides_git_value1')
         self.assertEqual(parser.get_config_file_value('doesnotexist'), None)
+
+    def test_param_list(self):
+        parser = GbpOptionParser('cmd4')
+
+        branch_group = GbpOptionGroup(parser, "branch options", "branch update and layout options")
+        parser.add_option_group(branch_group)
+        branch_group.add_config_file_option(option_name="upstream-branch", dest="upstream_branch")
+        branch_group.add_config_file_option("debian-branch", dest="upstream_branch")
+        parser.add_config_file_option(option_name="color", dest="color", type='tristate')
+
+        params = parser.valid_options
+        self.assertTrue('upstream-branch' in params)
+        self.assertTrue('debian-branch' in params)
+        self.assertTrue('color' in params)
index 1c3369b..f503ef5 100644 (file)
@@ -21,6 +21,21 @@ import unittest
 import gbp.scripts.config
 
 class TestGbpConfigCommand(unittest.TestCase):
+    class SingleValuePrintStub(object):
+        def __init__(self):
+            self.result = None
+
+        def __call__(self, arg):
+            self.result = arg
+
+    class AllValuesPrintStub(object):
+        def __init__(self, cmd):
+            self.cmd = cmd
+            self.result = {}
+
+        def __call__(self, arg):
+            k, v = arg.split('=', 1)
+            self.result[k] = v
 
     def setUp(self):
         self.conffiles_save = os.environ.get('GBP_CONF_FILES')
@@ -29,28 +44,43 @@ class TestGbpConfigCommand(unittest.TestCase):
         os.environ['GBP_CONF_FILES'] = self.confname
 
     def test_invocation_single_value(self):
-        """Test if we an invoke it without error"""
+        """Can invoke it for a sngle value  without error"""
         ret = gbp.scripts.config.main(['doesnotmatter', 'coolcommand.branchname'])
         self.assertEqual(ret, 0)
 
     def test_invocation_missing_value(self):
-        """Test if we an invoke it without error"""
+        """Can we detect a missing value"""
         ret = gbp.scripts.config.main(['doesnotmatter', 'coolcommand.doesnotexist'])
         self.assertEqual(ret, 1)
 
-    def test_invocation_parse_error(self):
-        """Test if we an invoke it without error"""
-        ret = gbp.scripts.config.main(['doesnotmatter', 'mustcontaindot'])
-        self.assertEqual(ret, 2)
+    def test_print_cmd_single_value(self):
+        """Can we fetch a single configuration value"""
+        printstub = self.SingleValuePrintStub()
+        query = 'coolcommand.branchname'
+        ret = gbp.scripts.config.print_cmd_single_value(query, printstub)
+        self.assertEqual(printstub.result, '%s=abranch' % query)
+        self.assertEqual(ret, 0)
 
-    def test_print_single_value(self):
-        class Printstub(object):
-            result = None
-            def __call__(self, arg):
-                self.result = arg
+    def test_print_cmd_all_values(self):
+        """Can we fetch the configuration for all commands"""
+        for cmd in [ 'buildpackage',
+                     'clone',
+                     'config',
+                     'create_remote_repo',
+                     'dch',
+                     'import_dsc',
+                     'import_orig',
+                     'pq',
+                     'pull' ]:
+            printstub = self.AllValuesPrintStub(cmd)
+            ret = gbp.scripts.config.print_cmd_all_values(cmd, printstub)
+            self.assertTrue('%s.color' % cmd in printstub.result.keys())
+            self.assertEqual(ret, 0)
 
-        printstub = Printstub()
-        ret = gbp.scripts.config.print_single_value('coolcommand.branchname', printstub)
-        self.assertEqual(printstub.result, 'abranch')
-        self.assertEqual(ret, 0)
+    def test_invalid_cms(self):
+        """Invalid commands or those not using the config should rerturn an error code"""
+        for cmd in [ "import_dscs", "supercommand" ]:
+            printstub = self.AllValuesPrintStub(cmd)
+            ret = gbp.scripts.config.print_cmd_all_values(cmd, printstub)
+            self.assertEqual(ret, 2)