summaryrefslogtreecommitdiff
path: root/src/mailman/utilities/options.py
diff options
context:
space:
mode:
authorBarry Warsaw2017-07-22 03:02:05 +0000
committerBarry Warsaw2017-07-22 03:02:05 +0000
commitf00b94f18e1d82d1488cbcee6053f03423bc2f49 (patch)
tree1a8e56dff0eab71e58e5fc9ecc5f3c614d7edca7 /src/mailman/utilities/options.py
parentf54c045519300f6f70947d1114f46c2b8ae0d368 (diff)
downloadmailman-f00b94f18e1d82d1488cbcee6053f03423bc2f49.tar.gz
mailman-f00b94f18e1d82d1488cbcee6053f03423bc2f49.tar.zst
mailman-f00b94f18e1d82d1488cbcee6053f03423bc2f49.zip
Diffstat (limited to 'src/mailman/utilities/options.py')
-rw-r--r--src/mailman/utilities/options.py156
1 files changed, 46 insertions, 110 deletions
diff --git a/src/mailman/utilities/options.py b/src/mailman/utilities/options.py
index 3d8392486..7f1f5a1c5 100644
--- a/src/mailman/utilities/options.py
+++ b/src/mailman/utilities/options.py
@@ -15,123 +15,59 @@
# You should have received a copy of the GNU General Public License along with
# GNU Mailman. If not, see <http://www.gnu.org/licenses/>.
-"""Common argument parsing."""
+"""Argument parsing utilities."""
-import os
-import sys
+import click
-from copy import copy
-from mailman.config import config
from mailman.core.i18n import _
-from mailman.core.initialize import initialize
-from mailman.version import MAILMAN_VERSION
-from optparse import Option, OptionParser, OptionValueError
from public import public
-def check_unicode(option, opt, value):
- """Check that the value is a unicode string."""
- if not isinstance(value, bytes):
+@public
+def validate_runner_spec(ctx, param, value):
+ # This validator handles two cases. First, for the runner script where
+ # only a single --runner option is allowed, and second the master script
+ # where multiple --runner options are allowed. When no --runner options
+ # are given, we'll either get None or the empty tuple. That's why we use
+ # false-iness here.
+ if not value:
return value
- try:
- return value.decode(sys.getdefaultencoding())
- except UnicodeDecodeError:
- raise OptionValueError(
- 'option {}: Cannot decode: {}'.format(opt, value))
-
-
-def check_yesno(option, opt, value):
- """Check that the value is 'yes' or 'no'."""
- value = value.lower()
- if value not in ('yes', 'no', 'y', 'n'):
- raise OptionValueError('option {}: invalid: {}'.format(opt, value))
- return value[0] == 'y'
-
-
-class MailmanOption(Option):
- """Extension types for unicode options."""
- TYPES = Option.TYPES + ('unicode', 'yesno')
- TYPE_CHECKER = copy(Option.TYPE_CHECKER)
- TYPE_CHECKER['unicode'] = check_unicode
- TYPE_CHECKER['yesno'] = check_yesno
-
-
-class SafeOptionParser(OptionParser):
- """A unicode-compatible `OptionParser`.
-
- Python's standard option parser does not accept unicode options. Rather
- than try to fix that, this class wraps the add_option() method and saves
- having to wrap the options in str() calls.
- """
- def add_option(self, *args, **kwargs):
- """See `OptionParser`."""
- # Check to see if the first or first two options are unicodes and turn
- # them into 8-bit strings before calling the superclass's method.
- if len(args) == 0:
- return OptionParser.add_option(self, *args, **kwargs)
- old_args = list(args)
- new_args = []
- arg0 = old_args.pop(0)
- new_args.append(str(arg0))
- if len(old_args) > 0:
- arg1 = old_args.pop(0)
- new_args.append(str(arg1))
- new_args.extend(old_args)
- return OptionParser.add_option(self, *new_args, **kwargs)
+ specs = []
+ for spec in ([value] if isinstance(value, str) else value):
+ parts = spec.split(':')
+ if len(parts) == 1:
+ specs.append((parts[0], 1, 1))
+ elif len(parts) == 3:
+ runner = parts[0]
+ try:
+ rslice = int(parts[1])
+ rrange = int(parts[2])
+ except ValueError:
+ raise click.BadParameter(
+ _('slice and range must be integers: $value'))
+ specs.append((runner, rslice, rrange))
+ else:
+ raise click.UsageError(_('Bad runner spec: $value'))
+ return specs[0] if isinstance(value, str) else specs
@public
-class Options:
- """Common argument parser."""
-
- # Subclasses should override.
- usage = None
-
- def __init__(self):
- self.parser = SafeOptionParser(
- version=MAILMAN_VERSION,
- option_class=MailmanOption,
- usage=self.usage)
- self.add_common_options()
- self.add_options()
- options, arguments = self.parser.parse_args()
- self.options = options
- self.arguments = arguments
- # Also, for convenience, place the options in the configuration file
- # because occasional global uses are necessary.
- config.options = self
-
- def add_options(self):
- """Allow the subclass to add its own specific arguments."""
- pass
-
- def sanity_check(self):
- """Allow subclasses to do sanity checking of arguments."""
- pass
-
- def add_common_options(self):
- """Add options common to all scripts."""
- # Python requires str types here.
- self.parser.add_option(
- '-C', '--config',
- help=_('Alternative configuration file to use'))
-
- def initialize(self, propagate_logs=None):
- """Initialize the configuration system.
-
- After initialization of the configuration system, perform sanity
- checks. We do it in this order because some sanity checks require the
- configuration to be initialized.
-
- :param propagate_logs: Optional flag specifying whether log messages
- in sub-loggers should be propagated to the master logger (and
- hence to the root logger). If not given, propagation is taken
- from the configuration files.
- :type propagate_logs: bool or None.
- """
- # Fall back to using the environment variable if -C is not given.
- config_file = (os.getenv('MAILMAN_CONFIG_FILE')
- if self.options.config is None
- else self.options.config)
- initialize(config_file, propagate_logs=propagate_logs)
- self.sanity_check()
+class I18nCommand(click.Command): # pragma: nocover
+ # https://github.com/pallets/click/issues/834
+ #
+ # Note that this handles the case for the `mailman <subcommand> --help`
+ # output. To handle `mailman --help` we override the same method in the
+ # `Subcommands` subclass over in src/mailman/bin/mailman.py. The test
+ # suite doesn't cover *this* copy of the method but who cares, since it
+ # will hopefully go away some day.
+ 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)