summaryrefslogtreecommitdiff
path: root/src/mailman/bin/mailman.py
diff options
context:
space:
mode:
authorBarry Warsaw2017-07-22 03:02:06 +0000
committerBarry Warsaw2017-07-22 03:02:06 +0000
commit02826321d0430d7ffc1f674eeff4221941689ef7 (patch)
tree1a8e56dff0eab71e58e5fc9ecc5f3c614d7edca7 /src/mailman/bin/mailman.py
parentf54c045519300f6f70947d1114f46c2b8ae0d368 (diff)
parentf00b94f18e1d82d1488cbcee6053f03423bc2f49 (diff)
downloadmailman-02826321d0430d7ffc1f674eeff4221941689ef7.tar.gz
mailman-02826321d0430d7ffc1f674eeff4221941689ef7.tar.zst
mailman-02826321d0430d7ffc1f674eeff4221941689ef7.zip
Diffstat (limited to 'src/mailman/bin/mailman.py')
-rw-r--r--src/mailman/bin/mailman.py142
1 files changed, 77 insertions, 65 deletions
diff --git a/src/mailman/bin/mailman.py b/src/mailman/bin/mailman.py
index 62066175e..e012237f3 100644
--- a/src/mailman/bin/mailman.py
+++ b/src/mailman/bin/mailman.py
@@ -17,81 +17,93 @@
"""The 'mailman' command dispatcher."""
-import os
-import argparse
+import click
-from functools import cmp_to_key
+from contextlib import ExitStack
+from mailman.config import config
from mailman.core.i18n import _
from mailman.core.initialize import initialize
from mailman.database.transaction import transaction
from mailman.interfaces.command import ICLISubCommand
-from mailman.utilities.modules import find_components
+from mailman.utilities.modules import add_components
from mailman.version import MAILMAN_VERSION_FULL
from public import public
-# --help should display the subcommands by alphabetical order, except that
-# 'mailman help' should be first.
-def _help_sorter(command, other):
- """Sorting helper."""
- if command.name == 'help':
- return -1
- elif other.name == 'help':
- return 1
- elif command.name < other.name:
- return -1
- elif command.name == other.name:
- return 0
- else:
- assert command.name > other.name
- return 1
+class Subcommands(click.MultiCommand):
+ # Handle dynamic listing and loading of `mailman` subcommands.
+ def __init__(self, *args, **kws):
+ super().__init__(*args, **kws)
+ self._commands = {}
+ # 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.
+ add_components('mailman.commands', ICLISubCommand, self._commands)
+ def list_commands(self, ctx):
+ return sorted(self._commands) # pragma: nocover
+ def get_command(self, ctx, name):
+ try:
+ return self._commands[name].command
+ except KeyError as error:
+ # Returning None here signals click to report usage information
+ # and a "No such command" error message.
+ return None
+
+ # This is here to hook command parsing into the Mailman database
+ # transaction system. If the subcommand succeeds, the transaction is
+ # committed, otherwise it's aborted.
+ def invoke(self, ctx):
+ with ExitStack() as resources:
+ # If given a bogus subcommand, the database won't have been
+ # initialized so there's no transaction to commit.
+ if config.db is not None:
+ resources.enter_context(transaction())
+ return super().invoke(ctx)
+
+ # https://github.com/pallets/click/issues/834
+ #
+ # Note that this only handles the case for the `mailman --help` output.
+ # To handle `mailman <subcommand> --help` we create a custom click.Command
+ # subclass and override this method there too. See
+ # src/mailman/utilities/options.py
+ def format_options(self, ctx, formatter):
+ """Writes all the options into the formatter if they exist."""
+ opts = []
+ for param in self.get_params(ctx):
+ rv = param.get_help_record(ctx)
+ if rv is not None:
+ part_a, part_b = rv
+ opts.append((part_a, part_b.replace('\n', ' ')))
+ if opts:
+ with formatter.section('Options'):
+ formatter.write_dl(opts)
+
+
+@click.group(
+ cls=Subcommands,
+ context_settings=dict(help_option_names=['-h', '--help']))
+@click.pass_context
+@click.option(
+ '-C', '--config', 'config_file',
+ envvar='MAILMAN_CONFIG_FILE',
+ type=click.Path(exists=True, dir_okay=False, resolve_path=True),
+ 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."""))
+@click.version_option(MAILMAN_VERSION_FULL, message='%(version)s')
@public
-def main():
- """The `mailman` command dispatcher."""
- # Create the basic parser and add all globally common options.
- parser = argparse.ArgumentParser(
- description=_("""\
- The GNU Mailman mailing list management system
- Copyright 1998-2017 by the Free Software Foundation, Inc.
- http://www.list.org
- """),
- formatter_class=argparse.RawDescriptionHelpFormatter)
- parser.add_argument(
- '-v', '--version',
- action='version', version=MAILMAN_VERSION_FULL,
- help=_('Print this version string and exit'))
- 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')
- subcommands = []
- for command in find_components('mailman.commands', ICLISubCommand):
- subcommands.append(command)
- subcommands.sort(key=cmp_to_key(_help_sorter))
- for command in subcommands:
- command_parser = subparser.add_parser(
- command.name, help=_(command.__doc__))
- command.add(parser, command_parser)
- command_parser.set_defaults(func=command.process)
- args = parser.parse_args()
- if len(args.__dict__) <= 1:
- # No arguments or subcommands were given.
- parser.print_help()
- parser.exit()
+def main(ctx, config_file):
+ # XXX https://github.com/pallets/click/issues/303
+ """\
+ The GNU Mailman mailing list management system
+ Copyright 1998-2017 by the Free Software Foundation, Inc.
+ http://www.list.org
+ """
# Initialize the system. Honor the -C flag if given.
- config_path = (None if args.config is None
- else os.path.abspath(os.path.expanduser(args.config)))
- initialize(config_path)
- # Perform the subcommand option.
- with transaction():
- args.func(args)
+ initialize(config_file)
+ # click handles dispatching to the subcommand via the Subcommands class.