diff options
| author | Barry Warsaw | 2017-07-22 03:02:06 +0000 |
|---|---|---|
| committer | Barry Warsaw | 2017-07-22 03:02:06 +0000 |
| commit | 02826321d0430d7ffc1f674eeff4221941689ef7 (patch) | |
| tree | 1a8e56dff0eab71e58e5fc9ecc5f3c614d7edca7 /src/mailman/bin/mailman.py | |
| parent | f54c045519300f6f70947d1114f46c2b8ae0d368 (diff) | |
| parent | f00b94f18e1d82d1488cbcee6053f03423bc2f49 (diff) | |
| download | mailman-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.py | 142 |
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. |
