------------------------------------------------------------
revno: 6762
committer: Barry Warsaw <[email protected]>
branch nick: 3.0
timestamp: Sun 2009-08-09 10:49:35 -0400
message:
  A start on the 'mailman' subcommand layout, with the help of argparse.  Right
  now the only subcommand is 'lists' which displays all mailing lists like the
  old bin/list_lists command did (which is now removed).
  
  Remove bin/version since 'bin/mailman --version' does this for us.
  
  Simplify the calculation of the bin scripts; there will be many fewer of them.
  
  Extend i18n to use a class based structure.  By default, all i18n strings are
  dedented after translation and substitution, which improves command line
  help.  The class structure allows for overriding this behavior.
removed:
  src/mailman/bin/list_lists.py
  src/mailman/bin/version.py
added:
  src/mailman/bin/mailman.py
  src/mailman/commands/docs/lists.txt
  src/mailman/commands/lists.py
modified:
  setup.py
  src/mailman/bin/__init__.py
  src/mailman/bin/master.py
  src/mailman/bin/qrunner.py
  src/mailman/i18n.py
  src/mailman/interfaces/command.py


--
lp:mailman
https://code.launchpad.net/~mailman-coders/mailman/3.0

Your team Mailman Checkins is subscribed to branch lp:mailman.
To unsubscribe from this branch go to 
https://code.launchpad.net/~mailman-coders/mailman/3.0/+edit-subscription.
=== modified file 'setup.py'
--- setup.py	2009-06-30 10:14:49 +0000
+++ setup.py	2009-08-09 14:49:35 +0000
@@ -23,7 +23,6 @@
 
 sys.path.insert(0, 'src')
 
-import mailman.bin
 from mailman.version import VERSION as __version__
 from setuptools import setup, find_packages
 
@@ -62,7 +61,7 @@
 template = Template('$script = mailman.bin.$script:main')
 scripts = set(
     template.substitute(script=script)
-    for script in mailman.bin.__all__
+    for script in ('mailman', 'mailmanctl', 'qrunner', 'master')
     )
 
 

=== modified file 'src/mailman/bin/__init__.py'
--- src/mailman/bin/__init__.py	2009-01-01 22:16:51 +0000
+++ src/mailman/bin/__init__.py	2009-08-09 14:49:35 +0000
@@ -1,61 +0,0 @@
-# Copyright (C) 2007-2009 by the Free Software Foundation, Inc.
-#
-# This file is part of GNU Mailman.
-#
-# GNU Mailman is free software: you can redistribute it and/or modify it under
-# the terms of the GNU General Public License as published by the Free
-# Software Foundation, either version 3 of the License, or (at your option)
-# any later version.
-#
-# GNU Mailman is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
-# more details.
-#
-# You should have received a copy of the GNU General Public License along with
-# GNU Mailman.  If not, see <http://www.gnu.org/licenses/>.
-
-__all__ = [
-    'add_members',
-    'arch',
-    'bounces',
-    'bumpdigests',
-    'check_perms',
-    'checkdbs',
-    'cleanarch',
-    'config_list',
-    'confirm',
-    'create_list',
-    'disabled',
-    'dumpdb',
-    'export',
-    'find_member',
-    'gate_news',
-    'genaliases',
-    'import',
-    'inject',
-    'join',
-    'leave',
-    'list_lists',
-    'list_members',
-    'list_owners',
-    'mailmanctl',
-    'make_instance',
-    'master',
-    'mmsitepass',
-    'nightly_gzip',
-    'owner',
-    'post',
-    'qrunner',
-    'remove_list',
-    'request',
-    'senddigests',
-    'set_members',
-    'show_config',
-    'show_qfiles',
-    'testall',
-    'unshunt',
-    'update',
-    'version',
-    'withlist',
-    ]

=== removed file 'src/mailman/bin/list_lists.py'
--- src/mailman/bin/list_lists.py	2009-01-05 00:41:05 +0000
+++ src/mailman/bin/list_lists.py	1970-01-01 00:00:00 +0000
@@ -1,104 +0,0 @@
-# Copyright (C) 1998-2009 by the Free Software Foundation, Inc.
-#
-# This file is part of GNU Mailman.
-#
-# GNU Mailman is free software: you can redistribute it and/or modify it under
-# the terms of the GNU General Public License as published by the Free
-# Software Foundation, either version 3 of the License, or (at your option)
-# any later version.
-#
-# GNU Mailman is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
-# more details.
-#
-# You should have received a copy of the GNU General Public License along with
-# GNU Mailman.  If not, see <http://www.gnu.org/licenses/>.
-
-from mailman.config import config
-from mailman.i18n import _
-from mailman.options import Options
-
-
-
-class ScriptOptions(Options):
-    usage = _("""\
-%prog [options]
-
-List all mailing lists.""")
-
-    def add_options(self):
-        super(ScriptOptions, self).add_options()
-        self.parser.add_option(
-            '-a', '--advertised',
-            default=False, action='store_true',
-            help=_("""\
-List only those mailing lists that are publicly advertised"""))
-        self.parser.add_option(
-            '-b', '--bare',
-            default=False, action='store_true',
-            help=_("""\
-Displays only the list name, with no description."""))
-        self.parser.add_option(
-            '-d', '--domain',
-            default=[], type='string', action='append',
-            dest='domains', help=_("""\
-List only those mailing lists that match the given virtual domain, which may
-be either the email host or the url host name.  Multiple -d options may be
-given."""))
-        self.parser.add_option(
-            '-f', '--full',
-            default=False, action='store_true',
-            help=_("""\
-Print the full list name, including the posting address."""))
-
-    def sanity_check(self):
-        if len(self.arguments) > 0:
-            self.parser.error(_('Unexpected arguments'))
-
-
-
-def main():
-    options = ScriptOptions()
-    options.initialize()
-
-    mlists = []
-    longest = 0
-
-    listmgr = config.db.list_manager
-    for fqdn_name in sorted(listmgr.names):
-        mlist = listmgr.get(fqdn_name)
-        if options.options.advertised and not mlist.advertised:
-            continue
-        if options.options.domains:
-            for domain in options.options.domains:
-                if domain in mlist.web_page_url or domain == mlist.host_name:
-                    mlists.append(mlist)
-                    break
-        else:
-            mlists.append(mlist)
-        if options.options.full:
-            name = mlist.fqdn_listname
-        else:
-            name = mlist.real_name
-        longest = max(len(name), longest)
-
-    if not mlists and not options.options.bare:
-        print _('No matching mailing lists found')
-        return
-
-    if not options.options.bare:
-        num_mlists = len(mlists)
-        print _('$num_mlists matching mailing lists found:')
-
-    format = '%%%ds - %%.%ds' % (longest, 77 - longest)
-    for mlist in mlists:
-        if options.options.full:
-            name = mlist.fqdn_listname
-        else:
-            name = mlist.real_name
-        if options.options.bare:
-            print name
-        else:
-            description = mlist.description or _('[no description available]')
-            print '   ', format % (name, description)

=== added file 'src/mailman/bin/mailman.py'
--- src/mailman/bin/mailman.py	1970-01-01 00:00:00 +0000
+++ src/mailman/bin/mailman.py	2009-08-09 14:49:35 +0000
@@ -0,0 +1,79 @@
+# Copyright (C) 2009 by the Free Software Foundation, Inc.
+#
+# This file is part of GNU Mailman.
+#
+# GNU Mailman is free software: you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free
+# Software Foundation, either version 3 of the License, or (at your option)
+# any later version.
+#
+# GNU Mailman is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+# more details.
+#
+# You should have received a copy of the GNU General Public License along with
+# GNU Mailman.  If not, see <http://www.gnu.org/licenses/>.
+
+"""The 'mailman' command dispatcher."""
+
+from __future__ import absolute_import, unicode_literals
+
+__metaclass__ = type
+__all__ = [
+    'main',
+    ]
+
+
+import os
+import argparse
+
+from zope.interface.verify import verifyObject
+
+from mailman.app.finder import find_components
+from mailman.core.initialize import initialize
+from mailman.i18n import _
+from mailman.interfaces.command import ICLISubCommand
+from mailman.version import MAILMAN_VERSION_FULL
+
+
+
+def main():
+    # Create the basic parser and add all globally common options.
+    parser = argparse.ArgumentParser(
+        description=_("""\
+        The GNU Mailman mailing list management system
+        Copyright 1998-2009 by the Free Software Foundation, Inc.
+        http://www.list.org
+        """),
+        formatter_class=argparse.RawDescriptionHelpFormatter,
+        version=MAILMAN_VERSION_FULL)
+    parser.add_argument(
+        '-C', '--config',
+        help=_("""\
+        Configuration file to use.  If not given, the environment variable
+        MAILMAN_CONFIG_FILE is consulted and used if set.  If neither are
+        given, a default configuration file is loaded."""))
+    # Look at all modules in the mailman.bin package and if they are prepared
+    # to add a subcommand, let them do so.  I'm still undecided as to whether
+    # this should be pluggable or not.  If so, then we'll probably have to
+    # partially parse the arguments now, then initialize the system, then find
+    # the plugins.  Punt on this for now.
+    subparser = parser.add_subparsers(title='Commands')
+    for command_class in find_components('mailman.commands', ICLISubCommand):
+        command = command_class()
+        verifyObject(ICLISubCommand, command)
+        command.add(subparser)
+    args = parser.parse_args()
+    if len(args.__dict__) == 0:
+        # No arguments or subcommands were given.
+        parser.print_help()
+        parser.exit()
+    # Before actually performing the subcommand, we need to initialize the
+    # Mailman system, and in particular, we must read the configuration file.
+    config_file = os.getenv('MAILMAN_CONFIG_FILE')
+    if config_file is None:
+        config_file = args.config
+    initialize(config_file)
+    # Perform the subcommand option.
+    args.func(args)

=== modified file 'src/mailman/bin/master.py'
--- src/mailman/bin/master.py	2009-07-11 00:00:44 +0000
+++ src/mailman/bin/master.py	2009-08-09 14:49:35 +0000
@@ -17,10 +17,11 @@
 
 """Master sub-process watcher."""
 
+from __future__ import absolute_import, unicode_literals
+
 __metaclass__ = type
 __all__ = [
-    'Loop',
-    'get_lock_data',
+    'main',
     ]
 
 

=== modified file 'src/mailman/bin/qrunner.py'
--- src/mailman/bin/qrunner.py	2009-07-11 00:00:44 +0000
+++ src/mailman/bin/qrunner.py	2009-08-09 14:49:35 +0000
@@ -15,6 +15,16 @@
 # You should have received a copy of the GNU General Public License along with
 # GNU Mailman.  If not, see <http://www.gnu.org/licenses/>.
 
+"""The queue runner."""
+
+from __future__ import absolute_import, unicode_literals
+
+__metaclass__ = type
+__all__ = [
+    'main',
+    ]
+
+
 import sys
 import signal
 import logging

=== removed file 'src/mailman/bin/version.py'
--- src/mailman/bin/version.py	2009-07-11 00:00:44 +0000
+++ src/mailman/bin/version.py	1970-01-01 00:00:00 +0000
@@ -1,48 +0,0 @@
-# Copyright (C) 1998-2009 by the Free Software Foundation, Inc.
-#
-# This file is part of GNU Mailman.
-#
-# GNU Mailman is free software: you can redistribute it and/or modify it under
-# the terms of the GNU General Public License as published by the Free
-# Software Foundation, either version 3 of the License, or (at your option)
-# any later version.
-#
-# GNU Mailman is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
-# more details.
-#
-# You should have received a copy of the GNU General Public License along with
-# GNU Mailman.  If not, see <http://www.gnu.org/licenses/>.
-
-"""Print the Mailman version."""
-
-from __future__ import absolute_import, unicode_literals
-
-__metaclass__ = type
-__all__ = [
-    'main',
-    ]
-
-
-# pylint: disable-msg=W0611
-from mailman.core.system import system
-from mailman.i18n import _
-from mailman.options import Options
-
-
-
-class ScriptOptions(Options):
-    """See `Options`."""
-    usage = _("""\
-%prog
-
-Print the Mailman version and exit.""")
-
-
-
-def main():
-    """Main entry point."""
-    options = ScriptOptions()
-    options.initialize()
-    print _('Using $system.mailman_version')

=== added file 'src/mailman/commands/docs/lists.txt'
--- src/mailman/commands/docs/lists.txt	1970-01-01 00:00:00 +0000
+++ src/mailman/commands/docs/lists.txt	2009-08-09 14:49:35 +0000
@@ -0,0 +1,127 @@
+=========================
+Command line list display
+=========================
+
+A system administrator can display all the mailing lists via the command
+line.  When there are no mailing lists, a helpful message is displayed.
+
+    >>> class FakeArgs:
+    ...     advertised = False
+    ...     bare = False
+    ...     domains = None
+    ...     full = False
+
+    >>> from mailman.commands.lists import Lists
+    >>> command = Lists()
+    >>> command.process(FakeArgs)
+    No matching mailing lists found
+
+When there are a few mailing lists, they are shown in alphabetical order by
+their fully qualified list names, with a description.
+
+    >>> from mailman.config import config
+    >>> from mailman.interfaces.domain import IDomainManager
+    >>> domain_mgr = IDomainManager(config)
+    >>> domain_mgr.add('example.net')
+    <Domain example.net...>
+
+    >>> from mailman.app.lifecycle import create_list
+    >>> mlist_1 = create_list('[email protected]')
+    >>> mlist_1.description = 'List One'
+
+    >>> mlist_2 = create_list('[email protected]')
+    >>> mlist_2.description = 'List Two'
+
+    >>> mlist_3 = create_list('[email protected]')
+    >>> mlist_3.description = 'List One in Example.Net'
+    >>> transaction.commit()
+
+    >>> command.process(FakeArgs)
+    3 matching mailing lists found:
+    List-one - List One
+    List-one - List One in Example.Net
+    List-two - List Two
+
+
+Full names
+==========
+
+You can display the mailing lists' full names, i.e. their posting addresses,
+with the --full switch.
+
+    >>> FakeArgs.full = True
+    >>> command.process(FakeArgs)
+    3 matching mailing lists found:
+    [email protected] - List One
+    [email protected] - List One in Example.Net
+    [email protected] - List Two
+
+
+Bare names
+==========
+
+You can print less verbose output, such that only the mailing list's name is
+shown, with the --bare option.
+
+    >>> FakeArgs.bare = True
+    >>> FakeArgs.full = False
+    >>> command.process(FakeArgs)
+    List-one
+    List-one
+    List-two
+
+--full and --bare can be combined.
+
+    >>> FakeArgs.full = True
+    >>> command.process(FakeArgs)
+    [email protected]
+    [email protected]
+    [email protected]
+
+
+Specific domain
+===============
+
+You can narrow the search down to a specific domain with the --domain option.
+A helpful message is displayed if no matching domains are given.
+
+    >>> FakeArgs.bare = False
+    >>> FakeArgs.domains = ['example.org']
+    >>> command.process(FakeArgs)
+    No matching mailing lists found
+
+But if a matching domain is given, only mailing lists in that domain are
+shown.
+
+    >>> FakeArgs.domains = ['example.net']
+    >>> command.process(FakeArgs)
+    1 matching mailing lists found:
+    [email protected] - List One in Example.Net
+
+More than one --domain argument can be given; then all mailing lists in
+matching domains are shown.
+
+    >>> FakeArgs.domains = ['example.com', 'example.net']
+    >>> command.process(FakeArgs)
+    3 matching mailing lists found:
+    [email protected] - List One
+    [email protected] - List One in Example.Net
+    [email protected] - List Two
+
+
+Advertised lists
+================
+
+Mailing lists can be 'advertised' meaning their existence is public
+knowledge.  Non-advertised lists are considered private.  Display through the
+command line can select on this attribute.
+
+    >>> FakeArgs.domains = []
+    >>> FakeArgs.advertised = True
+    >>> mlist_1.advertised = False
+    >>> transaction.commit()
+
+    >>> command.process(FakeArgs)
+    2 matching mailing lists found:
+    [email protected] - List One in Example.Net
+    [email protected] - List Two

=== added file 'src/mailman/commands/lists.py'
--- src/mailman/commands/lists.py	1970-01-01 00:00:00 +0000
+++ src/mailman/commands/lists.py	2009-08-09 14:49:35 +0000
@@ -0,0 +1,102 @@
+# Copyright (C) 2009 by the Free Software Foundation, Inc.
+#
+# This file is part of GNU Mailman.
+#
+# GNU Mailman is free software: you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free
+# Software Foundation, either version 3 of the License, or (at your option)
+# any later version.
+#
+# GNU Mailman is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+# more details.
+#
+# You should have received a copy of the GNU General Public License along with
+# GNU Mailman.  If not, see <http://www.gnu.org/licenses/>.
+
+"""The 'lists' subcommand."""
+
+from __future__ import absolute_import, unicode_literals
+
+__metaclass__ = type
+__all__ = [
+    'Lists',
+    ]
+
+
+from zope.interface import implements
+
+from mailman.config import config
+from mailman.i18n import _
+from mailman.interfaces.command import ICLISubCommand
+
+
+
+class Lists:
+    """The `lists` subcommand."""
+
+    implements(ICLISubCommand)
+
+    def add(self, subparser):
+        """See `ICLISubCommand`."""
+        lists_parser = subparser.add_parser(
+            'lists', help=_('List all mailing lists'))
+        lists_parser.add_argument(
+            '-a', '--advertised',
+            default=False, action='store_true',
+            help=_(
+                'List only those mailing lists that are publicly advertised'))
+        lists_parser.add_argument(
+            '-b', '--bare',
+            default=False, action='store_true',
+            help=_('Show only the list name, with no description'))
+        lists_parser.add_argument(
+            '-d', '--domain',
+            action='append', help=_("""\
+            List only those mailing lists hosted on the given domain, which
+            must be the email host name.  Multiple -d options may be given.
+            """))
+        lists_parser.add_argument(
+            '-f', '--full',
+            default=False, action='store_true',
+            help=_(
+                'Show the full mailing list name (i.e. the posting address'))
+        lists_parser.set_defaults(func=self.process)
+
+    def process(self, args):
+        """See `ICLISubCommand`."""
+        mailing_lists = []
+        list_manager = config.db.list_manager
+        # Gather the matching mailing lists.
+        for fqdn_name in sorted(list_manager.names):
+            mlist = list_manager.get(fqdn_name)
+            if args.advertised and not mlist.advertised:
+                continue
+            if args.domains and mlist.host_name not in args.domains:
+                continue
+            mailing_lists.append(mlist)
+        # Maybe no mailing lists matched.
+        if len(mailing_lists) == 0:
+            if not args.bare:
+                print _('No matching mailing lists found')
+            return
+        if not args.bare:
+            count = len(mailing_lists)
+            print _('$count matching mailing lists found:')
+        # Calculate the longest mailing list name.
+        longest = len(
+            max(mlist.fqdn_listname for mlist in mailing_lists)
+            if args.full else
+            max(mlist.real_name for mlist in mailing_lists))
+        # Print it out.
+        for mlist in mailing_lists:
+            name = (mlist.fqdn_listname if args.full else mlist.real_name)
+            if args.bare:
+                print name
+            else:
+                description = (mlist.description
+                               if mlist.description is not None
+                               else _('[no description available]'))
+                print '{0:{2}} - {1:{3}}'.format(
+                    name, description, longest, 77 - longest)

=== modified file 'src/mailman/i18n.py'
--- src/mailman/i18n.py	2009-07-01 03:14:26 +0000
+++ src/mailman/i18n.py	2009-08-09 14:49:35 +0000
@@ -17,10 +17,11 @@
 
 """Internationalization support."""
 
-from __future__ import unicode_literals
+from __future__ import absolute_import, unicode_literals
 
 __metaclass__ = type
 __all__ = [
+    'Translator',
     '_',
     'get_translation',
     'set_language',
@@ -35,6 +36,8 @@
 import string
 import gettext
 
+from textwrap import dedent
+
 import mailman.messages
 from mailman.utilities.string import expand
 
@@ -127,48 +130,68 @@
 
 
 
-def _(s):
-    """Translate the string.
-
-    :param s: The string to transate
-    :type s: string
-    :return: The translated string
-    :rtype: string
-    """
-    if s == '':
-        return ''
-    assert s, 'Cannot translate: {0}'.format(s)
-    # Do translation of the given string into the current language, and do PEP
-    # 292 style $-string interpolation into the resulting string.
-    #
-    # This lets you write something like:
-    #
-    #     now = time.ctime(time.time())
-    #     print _('The current time is: $now')
-    #
-    # and have it Just Work.  Note that the lookup order for keys in the
-    # original string is 1) locals dictionary, 2) globals dictionary.
-    #
-    # Get the frame of the caller.
-    # pylint: disable-msg=W0212
-    frame = sys._getframe(1)
-    # A `safe' dictionary is used so we won't get an exception if there's a
-    # missing key in the dictionary.
-    raw_dict = frame.f_globals.copy()
-    raw_dict.update(frame.f_locals)
-    # Mailman must be unicode safe internally (i.e. all strings inside Mailman
-    # must be unicodes).  The translation service is one boundary to the
-    # outside world, so to honor this constraint, make sure that all strings
-    # to come out of _() are unicodes, even if the translated string or
-    # dictionary values are 8-bit strings.
-    tns = _translation.ugettext(s)
-    charset = _translation.charset() or 'us-ascii'
-    # Python requires ** dictionaries to have str, not unicode keys.  For our
-    # purposes, keys should always be ascii.  Values though should be unicode.
-    translated_string = expand(tns, attrdict(raw_dict), Template)
-    if isinstance(translated_string, str):
-        translated_string = unicode(translated_string, charset)
-    return translated_string
+class Translator:
+    def __init__(self, dedent=True):
+        """Create a translation context.
+
+        :param dedent: Whether the input string should be dedented.
+        :type dedent: bool
+        """
+        self.dedent = dedent
+
+    def _(self, original):
+        """Translate the string.
+
+        :param original: The original string to translate.
+        :type original: string
+        :return: The translated string.
+        :rtype: string
+        """
+        if original == '':
+            return ''
+        assert original, 'Cannot translate: {0}'.format(s)
+        # Because the original string is what the text extractors put into the
+        # catalog, we must first look up the original unadulterated string in
+        # the catalog.  Use the global translation context for this.
+        #
+        # Mailman must be unicode safe internally (i.e. all strings inside
+        # Mailman are unicodes).  The translation service is one boundary to
+        # the outside world, so to honor this constraint, make sure that all
+        # strings to come out of _() are unicodes, even if the translated
+        # string or dictionary values are 8-bit strings.
+        tns = _translation.ugettext(original)
+        charset = _translation.charset() or 'us-ascii'
+        # Do PEP 292 style $-string interpolation into the resulting string.
+        #
+        # This lets you write something like:
+        #
+        #     now = time.ctime(time.time())
+        #     print _('The current time is: $now')
+        #
+        # and have it Just Work.  Note that the lookup order for keys in the
+        # original string is 1) locals dictionary, 2) globals dictionary.
+        #
+        # Get the frame of the caller.
+        # pylint: disable-msg=W0212
+        frame = sys._getframe(1)
+        # A 'safe' dictionary is used so we won't get an exception if there's
+        # a missing key in the dictionary.
+        raw_dict = frame.f_globals.copy()
+        raw_dict.update(frame.f_locals)
+        # Python requires ** dictionaries to have str, not unicode keys.  For
+        # our purposes, keys should always be ascii.  Values though should be
+        # unicode.
+        translated_string = expand(tns, attrdict(raw_dict), Template)
+        if isinstance(translated_string, str):
+            translated_string = unicode(translated_string, charset)
+        # Dedent the string if so desired.
+        if self.dedent:
+            translated_string = dedent(translated_string)
+        return translated_string
+
+
+# Global defaults.
+_ = Translator()._
 
 
 

=== modified file 'src/mailman/interfaces/command.py'
--- src/mailman/interfaces/command.py	2009-03-29 20:40:22 +0000
+++ src/mailman/interfaces/command.py	2009-08-09 14:49:35 +0000
@@ -22,6 +22,7 @@
 __metaclass__ = type
 __all__ = [
     'ContinueProcessing',
+    'ICLISubCommand',
     'IEmailCommand',
     'IEmailResults',
     ]
@@ -69,5 +70,19 @@
 
 
 
-class IBinCommand(Interface):
-    """A command line (i.e. bin) command."""
+class ICLISubCommand(Interface):
+    """A command line interface subcommand."""
+
+    def add(subparser):
+        """Add the subcommand to the subparser.
+
+        :param subparser: The argument subparser.
+        :type subparser: `argparse.ArgumentParser`
+        """
+
+    def process(args):
+        """Process the subcommand.
+
+        :param args: The namespace, as passed in by argparse.
+        :type args: `argparse.Namespace`
+        """

_______________________________________________
Mailman-checkins mailing list
[email protected]
Unsubscribe: 
http://mail.python.org/mailman/options/mailman-checkins/archive%40jab.org

Reply via email to