Rev 2783: (Ian Clatworthy) Verbosity levels and standard options in file:///home/pqm/archives/thelove/bzr/%2Btrunk/

Canonical.com Patch Queue Manager pqm at pqm.ubuntu.com
Mon Sep 3 08:34:27 BST 2007


At file:///home/pqm/archives/thelove/bzr/%2Btrunk/

------------------------------------------------------------
revno: 2783
revision-id: pqm at pqm.ubuntu.com-20070903073425-ouk9qod51gqk18nn
parent: pqm at pqm.ubuntu.com-20070903070212-g8basoqekm8c489k
parent: ian.clatworthy at internode.on.net-20070903054111-xyba0ihklpfs73gc
committer: Canonical.com Patch Queue Manager <pqm at pqm.ubuntu.com>
branch nick: +trunk
timestamp: Mon 2007-09-03 08:34:25 +0100
message:
  (Ian Clatworthy) Verbosity levels and standard options
modified:
  NEWS                           NEWS-20050323055033-4e00b5db738777ff
  bzrlib/builtins.py             builtins.py-20050830033751-fc01482b9ca23183
  bzrlib/commands.py             bzr.py-20050309040720-d10f4714595cf8c3
  bzrlib/help_topics.py          help_topics.py-20060920210027-rnim90q9e0bwxvy4-1
  bzrlib/option.py               option.py-20051014052914-661fb36e76e7362f
  bzrlib/tests/blackbox/test_locale.py test_lang.py-20060824204205-80v50j25qkuop7yn-1
  bzrlib/tests/test_help.py      test_help.py-20070419045354-6q6rq15j9e2n5fna-1
  bzrlib/tests/test_options.py   testoptions.py-20051014093702-96457cfc86319a8f
  bzrlib/tests/test_trace.py     testtrace.py-20051110225523-a21117fc7a07eeff
  bzrlib/trace.py                trace.py-20050309040759-c8ed824bdcd4748a
    ------------------------------------------------------------
    revno: 2779.1.1
    merged: ian.clatworthy at internode.on.net-20070903054111-xyba0ihklpfs73gc
    parent: pqm at pqm.ubuntu.com-20070903051736-tnjjdk1ii0258an0
    parent: ian.clatworthy at internode.on.net-20070903034749-op16cqnb8fi0jmde
    committer: Ian Clatworthy <ian.clatworthy at internode.on.net>
    branch nick: ianc-integration
    timestamp: Mon 2007-09-03 15:41:11 +1000
    message:
      (Ian Clatworthy) Verbosity levels and standard options
    ------------------------------------------------------------
    revno: 2768.1.16
    merged: ian.clatworthy at internode.on.net-20070903034749-op16cqnb8fi0jmde
    parent: ian.clatworthy at internode.on.net-20070903015029-dmi0iqcqkejp27p3
    parent: pqm at pqm.ubuntu.com-20070903031921-8msn0bmzubicv5b1
    committer: Ian Clatworthy <ian.clatworthy at internode.on.net>
    branch nick: bzr.std-options
    timestamp: Mon 2007-09-03 13:47:49 +1000
    message:
      merge bzr.dev
    ------------------------------------------------------------
    revno: 2768.1.15
    merged: ian.clatworthy at internode.on.net-20070903015029-dmi0iqcqkejp27p3
    parent: ian.clatworthy at internode.on.net-20070903013349-o37wm1bxe04pctrm
    parent: pqm at pqm.ubuntu.com-20070902233606-wb062d366w5c83uc
    committer: Ian Clatworthy <ian.clatworthy at internode.on.net>
    branch nick: bzr.std-options
    timestamp: Mon 2007-09-03 11:50:29 +1000
    message:
      Merge bzr.dev
    ------------------------------------------------------------
    revno: 2768.1.14
    merged: ian.clatworthy at internode.on.net-20070903013349-o37wm1bxe04pctrm
    parent: ian.clatworthy at internode.on.net-20070831042259-9bal2zqm5kvgu5hl
    committer: Ian Clatworthy <ian.clatworthy at internode.on.net>
    branch nick: bzr.std-options
    timestamp: Mon 2007-09-03 11:33:49 +1000
    message:
      Incorporate feedback from poolie
    ------------------------------------------------------------
    revno: 2768.1.13
    merged: ian.clatworthy at internode.on.net-20070831042259-9bal2zqm5kvgu5hl
    parent: ian.clatworthy at internode.on.net-20070831041239-onw9xs8s142qqt1m
    committer: Ian Clatworthy <ian.clatworthy at internode.on.net>
    branch nick: bzr.std-options
    timestamp: Fri 2007-08-31 14:22:59 +1000
    message:
      Minor fixes to help
    ------------------------------------------------------------
    revno: 2768.1.12
    merged: ian.clatworthy at internode.on.net-20070831041239-onw9xs8s142qqt1m
    parent: ian.clatworthy at internode.on.net-20070831040757-30bmwliqsxd7zn6a
    parent: pqm at pqm.ubuntu.com-20070831020510-emrlta5dk6ta95zp
    committer: Ian Clatworthy <ian.clatworthy at internode.on.net>
    branch nick: bzr.std-options
    timestamp: Fri 2007-08-31 14:12:39 +1000
    message:
      merge bzr.dev
    ------------------------------------------------------------
    revno: 2768.1.11
    merged: ian.clatworthy at internode.on.net-20070831040757-30bmwliqsxd7zn6a
    parent: ian.clatworthy at internode.on.net-20070831020037-6drt2nnbu38907s5
    committer: Ian Clatworthy <ian.clatworthy at internode.on.net>
    branch nick: bzr.std-options
    timestamp: Fri 2007-08-31 14:07:57 +1000
    message:
      Add new option tests for custom help, callbacks and verbose/quiet linkage
    ------------------------------------------------------------
    revno: 2768.1.10
    merged: ian.clatworthy at internode.on.net-20070831020037-6drt2nnbu38907s5
    parent: ian.clatworthy at internode.on.net-20070831014737-79wyvq0fwat06alc
    committer: Ian Clatworthy <ian.clatworthy at internode.on.net>
    branch nick: bzr.std-options
    timestamp: Fri 2007-08-31 12:00:37 +1000
    message:
      Add tests for new methods in trace.py
    ------------------------------------------------------------
    revno: 2768.1.9
    merged: ian.clatworthy at internode.on.net-20070831014737-79wyvq0fwat06alc
    parent: ian.clatworthy at internode.on.net-20070831003537-29b1erv58zwb0kh7
    committer: Ian Clatworthy <ian.clatworthy at internode.on.net>
    branch nick: bzr.std-options
    timestamp: Fri 2007-08-31 11:47:37 +1000
    message:
      Update NEWS
    ------------------------------------------------------------
    revno: 2768.1.8
    merged: ian.clatworthy at internode.on.net-20070831003537-29b1erv58zwb0kh7
    parent: ian.clatworthy at internode.on.net-20070831003508-8r26w7ydnzfh5bbg
    committer: Ian Clatworthy <ian.clatworthy at internode.on.net>
    branch nick: bzr.std-options
    timestamp: Fri 2007-08-31 10:35:37 +1000
    message:
      Get test suite fully working again
    ------------------------------------------------------------
    revno: 2768.1.7
    merged: ian.clatworthy at internode.on.net-20070831003508-8r26w7ydnzfh5bbg
    parent: ian.clatworthy at internode.on.net-20070830135357-dqr7saq6ciipx09d
    committer: Ian Clatworthy <ian.clatworthy at internode.on.net>
    branch nick: bzr.std-options
    timestamp: Fri 2007-08-31 10:35:08 +1000
    message:
      Fix reset of verbosity levels
    ------------------------------------------------------------
    revno: 2768.1.6
    merged: ian.clatworthy at internode.on.net-20070830135357-dqr7saq6ciipx09d
    parent: ian.clatworthy at internode.on.net-20070830135258-t146laed1vd0nqx0
    committer: Ian Clatworthy <ian.clatworthy at internode.on.net>
    branch nick: bzr.std-options
    timestamp: Thu 2007-08-30 23:53:57 +1000
    message:
      Fix existing option and help tests
    ------------------------------------------------------------
    revno: 2768.1.5
    merged: ian.clatworthy at internode.on.net-20070830135258-t146laed1vd0nqx0
    parent: ian.clatworthy at internode.on.net-20070830134140-mjd6zw41nobxy51f
    committer: Ian Clatworthy <ian.clatworthy at internode.on.net>
    branch nick: bzr.std-options
    timestamp: Thu 2007-08-30 23:52:58 +1000
    message:
      Wrap new std verbose option with new help instead of declaring a new one
    ------------------------------------------------------------
    revno: 2768.1.4
    merged: ian.clatworthy at internode.on.net-20070830134140-mjd6zw41nobxy51f
    parent: ian.clatworthy at internode.on.net-20070830134041-y6wd24dz52fpkthx
    committer: Ian Clatworthy <ian.clatworthy at internode.on.net>
    branch nick: bzr.std-options
    timestamp: Thu 2007-08-30 23:41:40 +1000
    message:
      Process --verbose and --quiet standard options
    ------------------------------------------------------------
    revno: 2768.1.3
    merged: ian.clatworthy at internode.on.net-20070830134041-y6wd24dz52fpkthx
    parent: ian.clatworthy at internode.on.net-20070830133731-b2251gmd4zrlv4or
    committer: Ian Clatworthy <ian.clatworthy at internode.on.net>
    branch nick: bzr.std-options
    timestamp: Thu 2007-08-30 23:40:41 +1000
    message:
      Support custom help for commonly used options and standard option definition
    ------------------------------------------------------------
    revno: 2768.1.2
    merged: ian.clatworthy at internode.on.net-20070830133731-b2251gmd4zrlv4or
    parent: ian.clatworthy at internode.on.net-20070830060751-bsphtblostbmheh4
    committer: Ian Clatworthy <ian.clatworthy at internode.on.net>
    branch nick: bzr.std-options
    timestamp: Thu 2007-08-30 23:37:31 +1000
    message:
      Make noise levels a scale, not just a boolean in trace.py
    ------------------------------------------------------------
    revno: 2768.1.1
    merged: ian.clatworthy at internode.on.net-20070830060751-bsphtblostbmheh4
    parent: pqm at pqm.ubuntu.com-20070829235511-o6ftd800xa245p9h
    committer: Ian Clatworthy <ian.clatworthy at internode.on.net>
    branch nick: bzr.std-options
    timestamp: Thu 2007-08-30 16:07:51 +1000
    message:
      Document standard options
=== modified file 'NEWS'
--- a/NEWS	2007-09-03 07:02:12 +0000
+++ b/NEWS	2007-09-03 07:34:25 +0000
@@ -14,6 +14,11 @@
      This format is compatible with Bazaar 0.15 and later.
      (Martin Pool)
 
+   * ``--quiet`` or ``-q`` is no longer a global option. If present, it
+     must now appear after the command name. Scripts doing things like
+     ``bzr -q missing`` need to be rewritten as ``bzr missing -q``.
+     (Ian Clatworthy)
+
   FEATURES:
 
    * New option ``--author`` in ``bzr commit`` to specify the author of the
@@ -21,6 +26,28 @@
      ``bzr annotate`` display the author instead of the committer.
      (Lukáš Lalinský)
 
+   * In addition to global options and command specific options, a set of
+     standard options are now supported. Standard options are legal for
+     all commands. The initial set of standard options are:
+     
+     * ``--help`` or ``-h`` - display help message
+     * ``--verbose`` or ``-v`` - display additional information
+     * ``--quiet``  or ``-q`` - only output warnings and errors.
+
+     Unlike global options, standard options can be used in aliases and
+     may have command-specific help. (Ian Clatworthy)
+
+   * Verbosity level processing has now been unified. If ``--verbose``
+     or ``-v`` is specified on the command line multiple times, the
+     verbosity level is made positive the first time then increased.
+     If ``--quiet`` or ``-q`` is specified on the command line
+     multiple times, the verbosity level is made negative the first
+     time then decreased. To get the default verbosity level of zero,
+     either specify none of the above , ``--no-verbose`` or ``--no-quiet``.
+     Note that most commands currently ignore the magnitude of the
+     verbosity level but do respect *quiet vs normal vs verbose* when
+     generating output. (Ian Clatworthy)
+
   BUG FIXES:
 
    * ``bzr plugins`` now lists the version number for each plugin in square
@@ -187,6 +214,26 @@
    * Knits with no annotation cache still produce correct annotations.
      (Aaron Bentley)
 
+   * Three new methods have been added to ``bzrlib.trace``:
+     ``set_verbosity_level``, ``get_verbosity_level`` and ``is_verbose``.
+     ``set_verbosity_level`` expects a numeric value: negative for quiet,
+     zero for normal, positive for verbose. The size of the number can be
+     used to determine just how quiet or verbose the application should be.
+     The existing ``be_quiet`` and ``is_quiet`` routines have been
+     integrated into this new scheme. (Ian Clatworthy)
+
+   * Options can now be delcared with a ``custom_callback`` parameter. If
+     set, this routine is called after the option is processed. This feature
+     is now used by the standard options ``verbose`` and ``quiet`` so that
+     setting one implicitly resets the other. (Ian Clatworthy)
+
+   * Rather than declaring a new option from scratch in order to provide
+     custom help, a centrally registered option can be decorated using the
+     new ``bzrlib.Option.custom_help`` routine. In particular, this routine
+     is useful when declaring better help for the ``verbose`` and ``quiet``
+     standard options as the base definition of these is now more complex
+     than before thanks to their use of a custom callback. (Ian Clatworthy)
+      
 
 bzr 0.90 2007-08-28
 ===================

=== modified file 'bzrlib/builtins.py'
--- a/bzrlib/builtins.py	2007-09-03 05:53:11 +0000
+++ b/bzrlib/builtins.py	2007-09-03 07:34:25 +0000
@@ -60,7 +60,7 @@
 """)
 
 from bzrlib.commands import Command, display_command
-from bzrlib.option import ListOption, Option, RegistryOption
+from bzrlib.option import ListOption, Option, RegistryOption, custom_help
 from bzrlib.progress import DummyProgress, ProgressPhase
 from bzrlib.trace import mutter, note, log_error, warning, is_quiet, info
 
@@ -573,7 +573,7 @@
 
     _see_also = ['push', 'update', 'status-flags']
     takes_options = ['remember', 'overwrite', 'revision',
-        Option('verbose', short_name='v',
+        custom_help('verbose',
             help='Show logs of pulled revisions.'),
         Option('directory',
             help='Branch to pull into, '
@@ -1058,10 +1058,14 @@
     takes_options = ['verbose']
 
     @display_command
-    def run(self, location=None, verbose=0):
+    def run(self, location=None, verbose=False):
+        if verbose:
+            noise_level = 2
+        else:
+            noise_level = 0
         from bzrlib.info import show_bzrdir_info
         show_bzrdir_info(bzrdir.BzrDir.open_containing(location)[0],
-                         verbose=verbose)
+                         verbose=noise_level)
 
 
 class cmd_remove(Command):
@@ -1626,8 +1630,7 @@
             Option('timezone',
                    type=str,
                    help='Display timezone as local, original, or utc.'),
-            Option('verbose',
-                   short_name='v',
+            custom_help('verbose',
                    help='Show files changed in each revision.'),
             'show-ids',
             'revision',
@@ -2227,7 +2230,7 @@
             properties.append('%s fixed' % bug_url)
         return '\n'.join(properties)
 
-    def run(self, message=None, file=None, verbose=True, selected_list=None,
+    def run(self, message=None, file=None, verbose=False, selected_list=None,
             unchanged=False, strict=False, local=False, fixes=None,
             author=None, show_diff=False):
         from bzrlib.commit import (
@@ -2287,7 +2290,7 @@
                 raise errors.BzrCommandError("empty commit message specified")
             return my_message
 
-        if verbose:
+        if verbose or not is_quiet():
             reporter = ReportCommitToLog()
         else:
             reporter = NullCommitReporter()

=== modified file 'bzrlib/commands.py'
--- a/bzrlib/commands.py	2007-08-21 12:32:42 +0000
+++ b/bzrlib/commands.py	2007-08-31 00:35:08 +0000
@@ -238,6 +238,8 @@
         """Construct an instance of this command."""
         if self.__doc__ == Command.__doc__:
             warn("No help message set for %r" % self)
+        # List of standard options directly supported
+        self.supported_std_options = []
 
     def _maybe_expand_globs(self, file_list):
         """Glob expand file_list if the platform does not do that itself.
@@ -417,12 +419,14 @@
         """Return dict of valid options for this command.
 
         Maps from long option name to option object."""
-        r = dict()
-        r['help'] = option._help_option
+        r = Option.STD_OPTIONS.copy()
+        std_names = r.keys()
         for o in self.takes_options:
             if isinstance(o, basestring):
                 o = option.Option.OPTIONS[o]
             r[o.name] = o
+            if o.name in std_names:
+                self.supported_std_options.append(o.name)
         return r
 
     def _setup_outf(self):
@@ -458,9 +462,21 @@
                  DeprecationWarning, stacklevel=2)
             argv = []
         args, opts = parse_args(self, argv, alias_argv)
+
+        # Process the standard options
         if 'help' in opts:  # e.g. bzr add --help
             sys.stdout.write(self.get_help_text())
             return 0
+        trace.set_verbosity_level(option._verbosity_level)
+        if 'verbose' in self.supported_std_options:
+            opts['verbose'] = trace.is_verbose()
+        elif opts.has_key('verbose'):
+            del opts['verbose']
+        if 'quiet' in self.supported_std_options:
+            opts['quiet'] = trace.is_quiet()
+        elif opts.has_key('quiet'):
+            del opts['quiet']
+
         # mix arguments and options into one dictionary
         cmdargs = _match_argform(self.name(), self.takes_args, args)
         cmdopts = {}
@@ -691,8 +707,6 @@
             opt_no_aliases = True
         elif a == '--builtin':
             opt_builtin = True
-        elif a in ('--quiet', '-q'):
-            trace.be_quiet()
         elif a.startswith('-D'):
             debug.debug_flags.add(a[2:])
         else:
@@ -744,7 +758,7 @@
         return ret or 0
     finally:
         # reset, in case we may do other commands later within the same process
-        trace.be_quiet(False)
+        option._verbosity_level = 0
 
 def display_command(func):
     """Decorator that suppresses pipe/interrupt errors."""

=== modified file 'bzrlib/help_topics.py'
--- a/bzrlib/help_topics.py	2007-08-28 00:49:10 +0000
+++ b/bzrlib/help_topics.py	2007-09-03 01:33:49 +0000
@@ -240,24 +240,14 @@
 """Global Options
 
 These options may be used with any command, and may appear in front of any
-command.  (e.g. "bzr --quiet help").
-
---quiet        Suppress informational output; only print errors and warnings.
---version      Print the version number.
-
+command.  (e.g. "bzr --profile help").
+
+--version      Print the version number. Must be supplied before the command.
 --no-aliases   Do not process command aliases when running this command.
 --builtin      Use the built-in version of a command, not the plugin version.
                This does not suppress other plugin effects.
 --no-plugins   Do not process any plugins.
 
--Devil         Capture call sites that do expensive or badly-scaling
-               operations.
--Derror        Instead of normal error handling, always print a traceback on
-               error.
--Dhooks        Trace hook execution.
--Dhpss         Trace smart protocol requests and responses.
--Dindex        Trace major index operations.
--Dlock         Trace when lockdir locks are taken or released.
 --profile      Profile execution using the hotshot profiler.
 --lsprof       Profile execution using the lsprof profiler.
 --lsprof-file  Profile execution using the lsprof profiler, and write the
@@ -268,9 +258,31 @@
                will be a pickle.
 
 See doc/developers/profiling.txt for more information on profiling.
-
-Note: --version must be supplied before any command.
-"""
+A number of debug flags are also available to assist troubleshooting and
+development.
+
+-Derror        Instead of normal error handling, always print a traceback on
+               error.
+-Devil         Capture call sites that do expensive or badly-scaling
+               operations.
+-Dhooks        Trace hook execution.
+-Dhpss         Trace smart protocol requests and responses.
+-Dindex        Trace major index operations.
+-Dlock         Trace when lockdir locks are taken or released.
+"""
+
+_standard_options = \
+"""Standard Options
+
+Standard options are legal for all commands.
+      
+--help, -h     Show help message.
+--verbose, -v  Display more information.
+--quiet, -q    Only display errors and warnings.
+
+Unlike global options, standard options can be used in aliases.
+"""
+
 
 _checkouts = \
 """Checkouts
@@ -512,8 +524,10 @@
     from bzrlib import bzrdir
     return "Storage Formats\n\n" + bzrdir.format_registry.help_topic(topic)
 topic_registry.register('formats', get_format_topic, 'Directory formats')
+topic_registry.register('standard-options', _standard_options,
+                        'Options that can be used with any command')
 topic_registry.register('global-options', _global_options,
-                        'Options that can be used with any command')
+                    'Options that control how Bazaar runs')
 topic_registry.register('checkouts', _checkouts,
                         'Information on what a checkout is', SECT_CONCEPT)
 topic_registry.register('urlspec', _help_on_transport,

=== modified file 'bzrlib/option.py'
--- a/bzrlib/option.py	2007-09-01 16:04:44 +0000
+++ b/bzrlib/option.py	2007-09-03 01:50:29 +0000
@@ -142,27 +142,38 @@
     Otherwise None.
     """
 
-    # TODO: Some way to show in help a description of the option argument
+    # The dictionary of standard options. These are always legal.
+    STD_OPTIONS = {}
 
+    # The dictionary of commonly used options. these are only legal
+    # if a command explicitly references them by name in the list
+    # of supported options.
     OPTIONS = {}
 
     def __init__(self, name, help='', type=None, argname=None,
-                 short_name=None, param_name=None):
+                 short_name=None, param_name=None, custom_callback=None):
         """Make a new command option.
 
-        name -- regular name of the command, used in the double-dash
+        :param name: regular name of the command, used in the double-dash
             form and also as the parameter to the command's run() 
             method (unless param_name is specified).
 
-        help -- help message displayed in command help
+        :param help: help message displayed in command help
 
-        type -- function called to parse the option argument, or 
+        :param type: function called to parse the option argument, or 
             None (default) if this option doesn't take an argument.
 
-        argname -- name of option argument, if any
-
-        param_name -- name of the parameter which will be passed to
+        :param argname: name of option argument, if any
+
+        :param short_name: short option code for use with a single -, e.g.
+            short_name="v" to enable parsing of -v.
+
+        :param param_name: name of the parameter which will be passed to
             the command's run() method.
+
+        :param custom_callback: a callback routine to be called after normal
+            processing. The signature of the callback routine is
+            (option, name, new_value, parser).
         """
         self.name = name
         self.help = help
@@ -177,6 +188,7 @@
             self._param_name = self.name
         else:
             self._param_name = param_name
+        self.custom_callback = custom_callback
 
     def short_name(self):
         if self._short_name:
@@ -198,12 +210,15 @@
             option_strings.append('-%s' % short_name)
         optargfn = self.type
         if optargfn is None:
-            parser.add_option(action='store_true', dest=self.name, 
+            parser.add_option(action='callback', 
+                              callback=self._optparse_bool_callback, 
+                              callback_args=(True,),
                               help=self.help,
-                              default=OptionParser.DEFAULT_VALUE,
                               *option_strings)
             negation_strings = ['--%s' % self.get_negation_name()]
-            parser.add_option(action='store_false', dest=self.name, 
+            parser.add_option(action='callback', 
+                              callback=self._optparse_bool_callback, 
+                              callback_args=(False,),
                               help=optparse.SUPPRESS_HELP, *negation_strings)
         else:
             parser.add_option(action='callback', 
@@ -213,8 +228,16 @@
                               default=OptionParser.DEFAULT_VALUE, 
                               *option_strings)
 
+    def _optparse_bool_callback(self, option, opt_str, value, parser, bool_v):
+        setattr(parser.values, self._param_name, bool_v)
+        if self.custom_callback is not None:
+            self.custom_callback(option, self._param_name, bool_v, parser)
+
     def _optparse_callback(self, option, opt, value, parser):
-        setattr(parser.values, self._param_name, self.type(value))
+        v = self.type(value)
+        setattr(parser.values, self._param_name, v)
+        if self.custom_callback is not None:
+            self.custom_callback(option, self.name, v, parser)
 
     def iter_switches(self):
         """Iterate through the list of switches provided by the option
@@ -253,11 +276,13 @@
                           *option_strings)
 
     def _optparse_callback(self, option, opt, value, parser):
-        values = getattr(parser.values, self.name)
+        values = getattr(parser.values, self._param_name)
         if value == '-':
             del values[:]
         else:
             values.append(self.type(value))
+        if self.custom_callback is not None:
+            self.custom_callback(option, self._param_name, values, parser)
 
 
 class RegistryOption(Option):
@@ -343,7 +368,10 @@
 
     def _optparse_value_callback(self, cb_value):
         def cb(option, opt, value, parser):
-            setattr(parser.values, self.name, self.type(cb_value))
+            v = self.type(cb_value)
+            setattr(parser.values, self._param_name, v)
+            if self.custom_callback is not None:
+                self.custom_callback(option, self._param_name, v, parser)
         return cb
 
     def iter_switches(self):
@@ -382,8 +410,23 @@
     return parser
 
 
+def custom_help(name, help):
+    """Clone a common option overriding the help."""
+    import copy
+    o = copy.copy(Option.OPTIONS[name])
+    o.help = help
+    return o
+
+
+def _standard_option(name, **kwargs):
+    """Register a standard option."""
+    # All standard options are implicitly 'global' ones
+    Option.STD_OPTIONS[name] = Option(name, **kwargs)
+    Option.OPTIONS[name] = Option.STD_OPTIONS[name]
+
+
 def _global_option(name, **kwargs):
-    """Register o as a global option."""
+    """Register a global option."""
     Option.OPTIONS[name] = Option(name, **kwargs)
 
 
@@ -396,6 +439,34 @@
     pass
 
 
+# This is the verbosity level detected during command line parsing.
+# Note that the final value is dependent on the order in which the
+# various flags (verbose, quiet, no-verbose, no-quiet) are given.
+# The final value will be one of the following:
+#
+# * -ve for quiet
+# * 0 for normal
+# * +ve for verbose
+_verbosity_level = 0
+
+
+def _verbosity_level_callback(option, opt_str, value, parser):
+    global _verbosity_level
+    if not value:
+        # Either --no-verbose or --no-quiet was specified
+        _verbosity_level = 0
+    elif opt_str == "verbose":
+        if _verbosity_level > 0:
+            _verbosity_level += 1
+        else:
+            _verbosity_level = 1
+    else:
+        if _verbosity_level < 0:
+            _verbosity_level -= 1
+        else:
+            _verbosity_level = -1
+
+
 _merge_type_registry = MergeTypeRegistry()
 _merge_type_registry.register_lazy('merge3', 'bzrlib.merge', 'Merge3Merger',
                                    "Native diff3-style merge")
@@ -404,6 +475,17 @@
 _merge_type_registry.register_lazy('weave', 'bzrlib.merge', 'WeaveMerger',
                                    "Weave-based merge")
 
+# Declare the standard options
+_standard_option('help', short_name='h',
+                 help='Show help message.')
+_standard_option('verbose', short_name='v',
+                 help='Display more information.',
+                 custom_callback=_verbosity_level_callback)
+_standard_option('quiet', short_name='q',
+                 help="Only display errors and warnings.",
+                 custom_callback=_verbosity_level_callback)
+
+# Declare commonly used options
 _global_option('all')
 _global_option('overwrite', help='Ignore differences between branches and '
                'overwrite unconditionally.')
@@ -435,9 +517,6 @@
                type=str,
                help='display timezone as local, original, or utc')
 _global_option('unbound')
-_global_option('verbose',
-               help='Display more information.',
-               short_name='v')
 _global_option('version')
 _global_option('email')
 _global_option('update')
@@ -454,7 +533,6 @@
                         _merge_type_registry, value_switches=True,
                         title='Merge algorithm')
 _global_option('pattern', type=str)
-_global_option('quiet', short_name='q')
 _global_option('remember', help='Remember the specified location as a'
                ' default.')
 _global_option('reprocess', help='Reprocess to reduce spurious conflicts.')
@@ -462,7 +540,3 @@
 _global_option('dry-run',
                help="Show what would be done, but don't actually do anything.")
 _global_option('name-from-revision', help='The path name in the old tree.')
-
-_help_option = Option('help',
-                      help='Show help message.',
-                      short_name='h')

=== modified file 'bzrlib/tests/blackbox/test_locale.py'
--- a/bzrlib/tests/blackbox/test_locale.py	2007-08-16 18:50:22 +0000
+++ b/bzrlib/tests/blackbox/test_locale.py	2007-08-31 00:35:37 +0000
@@ -42,7 +42,7 @@
 
     def test_log_C(self):
         out, err = self.run_bzr_subprocess(
-            '--no-aliases --no-plugins -q log --log-format=long tree',
+            '--no-aliases --no-plugins log -q --log-format=long tree',
                env_changes={'LANG':'C', 'BZR_PROGRESS_BAR':'none',
                             'LC_ALL':None, 'LC_CTYPE':None, 'LANGUAGE':None})
         self.assertEqual('', err)
@@ -58,7 +58,7 @@
 
     def test_log_BOGUS(self):
         out, err = self.run_bzr_subprocess(
-            '--no-aliases --no-plugins -q log --log-format=long tree',
+            '--no-aliases --no-plugins log -q --log-format=long tree',
                env_changes={'LANG':'BOGUS', 'BZR_PROGRESS_BAR':'none',
                             'LC_ALL':None, 'LC_CTYPE':None, 'LANGUAGE':None})
         # XXX: This depends on the exact formatting of a locale.Error

=== modified file 'bzrlib/tests/test_help.py'
--- a/bzrlib/tests/test_help.py	2007-08-06 10:02:13 +0000
+++ b/bzrlib/tests/test_help.py	2007-08-30 13:53:57 +0000
@@ -40,7 +40,9 @@
         helptext = cmd.get_help_text()
         self.assertEndsWith(
             helptext,
-            '  -h, --help  Show help message.\n'
+            '  -v, --verbose  Display more information.\n'
+            '  -q, --quiet    Only display errors and warnings.\n'
+            '  -h, --help     Show help message.\n'
             '\n'
             'See also: bar, foo\n')
 
@@ -53,7 +55,8 @@
         self.assertStartsWith(helptext,
             'Purpose: A sample command.\n'
             'Usage:   bzr Demo')
-        self.assertEndsWith(helptext, 'Show help message.\n\n')
+        self.assertEndsWith(helptext,
+            '  -h, --help     Show help message.\n\n')
 
     def test_command_with_additional_see_also(self):
         class cmd_WithSeeAlso(commands.Command):
@@ -63,7 +66,9 @@
         helptext = cmd.get_help_text(['gam'])
         self.assertEndsWith(
             helptext,
-            '  -h, --help  Show help message.\n'
+            '  -v, --verbose  Display more information.\n'
+            '  -q, --quiet    Only display errors and warnings.\n'
+            '  -h, --help     Show help message.\n'
             '\n'
             'See also: bar, foo, gam\n')
 
@@ -74,7 +79,9 @@
         helptext = cmd.get_help_text(['gam'])
         self.assertEndsWith(
             helptext,
-            '  -h, --help  Show help message.\n'
+            '  -v, --verbose  Display more information.\n'
+            '  -q, --quiet    Only display errors and warnings.\n'
+            '  -h, --help     Show help message.\n'
             '\n'
             'See also: gam\n')
 
@@ -107,7 +114,9 @@
             'Usage:   bzr Demo\n'
             '\n'
             'Options:\n'
-            '  -h, --help  Show help message.\n'
+            '  -v, --verbose  Display more information.\n'
+            '  -q, --quiet    Only display errors and warnings.\n'
+            '  -h, --help     Show help message.\n'
             '\n'
             'Examples:\n'
             '    Example 1:\n'
@@ -124,7 +133,9 @@
             ':Usage:   bzr Demo\n'
             '\n'
             ':Options:\n'
-            '  -h, --help  Show help message.\n'
+            '  -v, --verbose  Display more information.\n'
+            '  -q, --quiet    Only display errors and warnings.\n'
+            '  -h, --help     Show help message.\n'
             '\n'
             ':Examples:\n'
             '    Example 1::\n'
@@ -159,7 +170,9 @@
             '\n'
             '\n'
             'Options:\n'
-            '  -h, --help  Show help message.\n'
+            '  -v, --verbose  Display more information.\n'
+            '  -q, --quiet    Only display errors and warnings.\n'
+            '  -h, --help     Show help message.\n'
             '\n'
             'Description:\n'
             '  Blah blah blah.\n\n')

=== modified file 'bzrlib/tests/test_options.py'
--- a/bzrlib/tests/test_options.py	2007-09-01 16:04:44 +0000
+++ b/bzrlib/tests/test_options.py	2007-09-03 01:50:29 +0000
@@ -93,7 +93,7 @@
         opts, args = self.parse(options, ['--no-hello', '--hello'])
         self.assertEqual(True, opts.hello)
         opts, args = self.parse(options, [])
-        self.assertEqual(option.OptionParser.DEFAULT_VALUE, opts.hello)
+        self.assertFalse(hasattr(opts, 'hello'))
         opts, args = self.parse(options, ['--hello', '--no-hello'])
         self.assertEqual(False, opts.hello)
         options = [option.Option('number', type=int)]
@@ -214,6 +214,37 @@
                           ('two', None, None, 'two help'),
                           ])
 
+    def test_option_callback_bool(self):
+        "Test booleans get True and False passed correctly to a callback."""
+        cb_calls = []
+        def cb(option, name, value, parser):
+            cb_calls.append((option,name,value,parser))
+        options = [option.Option('hello', custom_callback=cb)]
+        opts, args = self.parse(options, ['--hello', '--no-hello'])
+        self.assertEqual(2, len(cb_calls))
+        opt,name,value,parser = cb_calls[0]
+        self.assertEqual('hello', name)
+        self.assertTrue(value)
+        opt,name,value,parser = cb_calls[1]
+        self.assertEqual('hello', name)
+        self.assertFalse(value)
+
+    def test_option_callback_str(self):
+        """Test callbacks work for string options both long and short."""
+        cb_calls = []
+        def cb(option, name, value, parser):
+            cb_calls.append((option,name,value,parser))
+        options = [option.Option('hello', type=str, custom_callback=cb,
+            short_name='h')]
+        opts, args = self.parse(options, ['--hello', 'world', '-h', 'mars'])
+        self.assertEqual(2, len(cb_calls))
+        opt,name,value,parser = cb_calls[0]
+        self.assertEqual('hello', name)
+        self.assertEqual('world', value)
+        opt,name,value,parser = cb_calls[1]
+        self.assertEqual('hello', name)
+        self.assertEqual('mars', value)
+
 
 class TestListOptions(TestCase):
     """Tests for ListOption, used to specify lists on the command-line."""
@@ -250,6 +281,26 @@
             options, ['--hello=a', '--hello=b', '--hello=-', '--hello=c'])
         self.assertEqual(['c'], opts.hello)
 
+    def test_option_callback_list(self):
+        """Test callbacks work for list options."""
+        cb_calls = []
+        def cb(option, name, value, parser):
+            # Note that the value is a reference so copy to keep it
+            cb_calls.append((option,name,value[:],parser))
+        options = [option.ListOption('hello', type=str, custom_callback=cb)]
+        opts, args = self.parse(options, ['--hello=world', '--hello=mars',
+            '--hello=-'])
+        self.assertEqual(3, len(cb_calls))
+        opt,name,value,parser = cb_calls[0]
+        self.assertEqual('hello', name)
+        self.assertEqual(['world'], value)
+        opt,name,value,parser = cb_calls[1]
+        self.assertEqual('hello', name)
+        self.assertEqual(['world', 'mars'], value)
+        opt,name,value,parser = cb_calls[2]
+        self.assertEqual('hello', name)
+        self.assertEqual([], value)
+
 
 class TestOptionDefinitions(TestCase):
     """Tests for options in the Bazaar codebase."""
@@ -318,3 +369,31 @@
         format = option.RegistryOption('format', '', registry, str)
         self.assertTrue(format.is_hidden('hidden'))
         self.assertFalse(format.is_hidden('visible'))
+
+    def test_option_custom_help(self):
+        the_opt = option.Option.OPTIONS['help']
+        orig_help = the_opt.help[:]
+        my_opt = option.custom_help('help', 'suggest lottery numbers')
+        # Confirm that my_opt has my help and the original is unchanged
+        self.assertEqual('suggest lottery numbers', my_opt.help)
+        self.assertEqual(orig_help, the_opt.help)
+
+
+class TestVerboseQuietLinkage(TestCase):
+
+    def check(self, parser, level, args):
+        option._verbosity_level = 0
+        opts, args = parser.parse_args(args)
+        self.assertEqual(level, option._verbosity_level)
+
+    def test_verbose_quiet_linkage(self):
+        parser = option.get_optparser(option.Option.STD_OPTIONS)
+        self.check(parser, 0, [])
+        self.check(parser, 1, ['-v'])
+        self.check(parser, 2, ['-v', '-v'])
+        self.check(parser, -1, ['-q'])
+        self.check(parser, -2, ['-qq'])
+        self.check(parser, -1, ['-v', '-v', '-q'])
+        self.check(parser, 2, ['-q', '-v', '-v'])
+        self.check(parser, 0, ['--no-verbose'])
+        self.check(parser, 0, ['-v', '-q', '--no-quiet'])

=== modified file 'bzrlib/tests/test_trace.py'
--- a/bzrlib/tests/test_trace.py	2007-08-23 14:10:48 +0000
+++ b/bzrlib/tests/test_trace.py	2007-08-31 02:00:37 +0000
@@ -27,7 +27,10 @@
     errors,
     )
 from bzrlib.tests import TestCaseInTempDir, TestCase
-from bzrlib.trace import mutter, mutter_callsite, report_exception
+from bzrlib.trace import (
+    mutter, mutter_callsite, report_exception,
+    set_verbosity_level, get_verbosity_level, is_quiet, is_verbose, be_quiet,
+    )
 
 
 def _format_exception():
@@ -148,3 +151,26 @@
         self.assertContainsRe(log, "But fails in an ascii string")
         self.assertContainsRe(log, u"ascii argument: \xb5")
 
+
+class TestVerbosityLevel(TestCase):
+
+    def test_verbosity_level(self):
+        set_verbosity_level(1)
+        self.assertEqual(1, get_verbosity_level())
+        self.assertTrue(is_verbose())
+        self.assertFalse(is_quiet())
+        set_verbosity_level(-1)
+        self.assertEqual(-1, get_verbosity_level())
+        self.assertFalse(is_verbose())
+        self.assertTrue(is_quiet())
+        set_verbosity_level(0)
+        self.assertEqual(0, get_verbosity_level())
+        self.assertFalse(is_verbose())
+        self.assertFalse(is_quiet())
+
+    def test_be_quiet(self):
+        # Confirm the old API still works
+        be_quiet(True)
+        self.assertEqual(-1, get_verbosity_level())
+        be_quiet(False)
+        self.assertEqual(0, get_verbosity_level())

=== modified file 'bzrlib/trace.py'
--- a/bzrlib/trace.py	2007-08-20 03:43:40 +0000
+++ b/bzrlib/trace.py	2007-08-31 02:00:37 +0000
@@ -70,7 +70,7 @@
 
 _file_handler = None
 _stderr_handler = None
-_stderr_quiet = False
+_verbosity_level = 0
 _trace_file = None
 _trace_depth = 0
 _bzr_log_file = None
@@ -218,10 +218,34 @@
     _bzr_logger.setLevel(logging.DEBUG)
 
 
+def set_verbosity_level(level):
+    """Set the verbosity level.
+
+    :param level: -ve for quiet, 0 for normal, +ve for verbose
+    """
+    global _verbosity_level
+    _verbosity_level = level
+    _update_logging_level(level < 0)
+
+
+def get_verbosity_level():
+    """Get the verbosity level.
+
+    See set_verbosity_level() for values.
+    """
+    return _verbosity_level
+
+
 def be_quiet(quiet=True):
-    global _stderr_handler, _stderr_quiet
-    
-    _stderr_quiet = quiet
+    # Perhaps this could be deprecated now ...
+    if quiet:
+        set_verbosity_level(-1)
+    else:
+        set_verbosity_level(0)
+
+
+def _update_logging_level(quiet=True):
+    """Hide INFO messages if quiet."""
     if quiet:
         _stderr_handler.setLevel(logging.WARNING)
     else:
@@ -229,8 +253,13 @@
 
 
 def is_quiet():
-    global _stderr_quiet
-    return _stderr_quiet
+    """Is the verbosity level negative?"""
+    return _verbosity_level < 0
+
+
+def is_verbose():
+    """Is the verbosity level positive?"""
+    return _verbosity_level > 0
 
 
 def disable_default_logging():




More information about the bazaar-commits mailing list