mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
* Implemented a wrapper module around the standard argparse package, exposing the same interface with some extras on top, including a new builder type with expressive DSL for constructing complex argument parsers. * Fixed imports in build_swift/argparse/__init__.py to make flake8 happy. * More re-formmating to meet the exacting standards of the python_lint script. * Added doc-strings to all the modules in the build_swift argparse overlay. * Implemented a new BoolType for the argparse module which handles boolean-like values and replaces the hard-coded boolean values in the _ToggleAction class. * Fixed the mess of imports in the tests sub-package to favor relative imports, so now the unit-tests will actually run as expected. The README has also been updated with a better command for executing the unit-test suite. * Updated the add_positional method on the ArgumentParser builder class to only take a single action or default to the store action. * Cleaned up the set_defaults method. * Added validation test to run the build_swift unit-tests. * Updated validation-test for the build_swift unit-test suite to use %utils. * Fixed hard-coded default values in the expected_options module used for generating argument parser tests. * Updated the comment in the Python validation test to run the build_swift unit-tests.
228 lines
7.1 KiB
Python
228 lines
7.1 KiB
Python
# This source file is part of the Swift.org open source project
|
|
#
|
|
# Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
|
|
# Licensed under Apache License v2.0 with Runtime Library Exception
|
|
#
|
|
# See https://swift.org/LICENSE.txt for license information
|
|
# See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
|
|
|
|
|
"""
|
|
Extensions to the standard argparse ArgumentParer class to support multiple
|
|
destination actions as well as a new builder DSL for declaratively
|
|
constructing complex parsers.
|
|
"""
|
|
|
|
|
|
import argparse
|
|
from contextlib import contextmanager
|
|
|
|
from . import Namespace, SUPPRESS, actions
|
|
from .actions import Action
|
|
|
|
|
|
__all__ = [
|
|
'ArgumentParser',
|
|
]
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
|
|
|
class _ActionContainer(object):
|
|
"""Container object holding partially applied actions used as a part of the
|
|
builder DSL.
|
|
"""
|
|
|
|
def __init__(self):
|
|
self.append = _PartialAction(actions.AppendAction)
|
|
self.custom_call = _PartialAction(actions.CustomCallAction)
|
|
self.store = _PartialAction(actions.StoreAction)
|
|
self.store_int = _PartialAction(actions.StoreIntAction)
|
|
self.store_true = _PartialAction(actions.StoreTrueAction)
|
|
self.store_false = _PartialAction(actions.StoreFalseAction)
|
|
self.store_path = _PartialAction(actions.StorePathAction)
|
|
self.toggle_true = _PartialAction(actions.ToggleTrueAction)
|
|
self.toggle_false = _PartialAction(actions.ToggleFalseAction)
|
|
self.unsupported = _PartialAction(actions.UnsupportedAction)
|
|
|
|
|
|
class _CompoundAction(Action):
|
|
"""Action composed of multiple actions. Default attributes are derived
|
|
from the first action.
|
|
"""
|
|
|
|
def __init__(self, actions, **kwargs):
|
|
_actions = []
|
|
for action in actions:
|
|
_actions.append(action(**kwargs))
|
|
|
|
kwargs.setdefault('nargs', kwargs[0].nargs)
|
|
kwargs.setdefault('metavar', kwargs[0].metavar)
|
|
kwargs.setdefault('choices', kwargs[0].choices)
|
|
|
|
super(_CompoundAction, self).__init__(**kwargs)
|
|
|
|
self.actions = _actions
|
|
|
|
def __call__(self, *args):
|
|
for action in self.actions:
|
|
action(*args)
|
|
|
|
|
|
class _PartialAction(Action):
|
|
"""Action that is partially applied, creating a factory closure used to
|
|
defer initialization of acitons in the builder DSL.
|
|
"""
|
|
|
|
def __init__(self, action_class):
|
|
self.action_class = action_class
|
|
|
|
def __call__(self, dests=None, *call_args, **call_kwargs):
|
|
def factory(**kwargs):
|
|
kwargs.update(call_kwargs)
|
|
if dests is not None:
|
|
return self.action_class(dests=dests, *call_args, **kwargs)
|
|
return self.action_class(*call_args, **kwargs)
|
|
|
|
return factory
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
|
|
|
class _Builder(object):
|
|
"""Builder object for constructing complex ArgumentParser instances with
|
|
a more friendly and descriptive DSL.
|
|
"""
|
|
|
|
def __init__(self, parser, **kwargs):
|
|
assert isinstance(parser, ArgumentParser)
|
|
|
|
self._parser = parser
|
|
self._current_group = self._parser
|
|
self._defaults = dict()
|
|
|
|
self.actions = _ActionContainer()
|
|
|
|
def build(self):
|
|
self._parser.set_defaults(**self._defaults)
|
|
return self._parser
|
|
|
|
def _add_argument(self, names, *actions, **kwargs):
|
|
# Unwrap partial actions
|
|
_actions = []
|
|
for action in actions:
|
|
if isinstance(action, _PartialAction):
|
|
action = action()
|
|
_actions.append(action)
|
|
|
|
if len(_actions) == 0:
|
|
# Default to store action
|
|
action = actions.StoreAction
|
|
elif len(_actions) == 1:
|
|
action = _actions[0]
|
|
else:
|
|
def thunk(**kwargs):
|
|
return _CompoundAction(_actions, **kwargs)
|
|
action = thunk
|
|
|
|
return self._current_group.add_argument(
|
|
*names, action=action, **kwargs)
|
|
|
|
def add_positional(self, dests, action=None, **kwargs):
|
|
if isinstance(dests, str):
|
|
dests = [dests]
|
|
|
|
if any(dest.startswith('-') for dest in dests):
|
|
raise ValueError("add_positional can't add optional arguments")
|
|
|
|
if action is None:
|
|
action = actions.StoreAction
|
|
|
|
return self._add_argument(dests, action, **kwargs)
|
|
|
|
def add_option(self, option_strings, *actions, **kwargs):
|
|
if isinstance(option_strings, str):
|
|
option_strings = [option_strings]
|
|
|
|
if not all(opt.startswith('-') for opt in option_strings):
|
|
raise ValueError("add_option can't add positional arguments")
|
|
|
|
return self._add_argument(option_strings, *actions, **kwargs)
|
|
|
|
def set_defaults(self, *args, **kwargs):
|
|
if len(args) == 1:
|
|
raise TypeError('set_defaults takes at least 2 arguments')
|
|
|
|
if len(args) >= 2:
|
|
dests, value = args[:-1], args[-1]
|
|
for dest in dests:
|
|
kwargs[dest] = value
|
|
|
|
self._defaults.update(**kwargs)
|
|
|
|
def in_group(self, description):
|
|
self._current_group = self._parser.add_argument_group(description)
|
|
return self._current_group
|
|
|
|
def reset_group(self):
|
|
self._current_group = self._parser
|
|
|
|
@contextmanager
|
|
def argument_group(self, description):
|
|
previous_group = self._current_group
|
|
self._current_group = self._parser.add_argument_group(description)
|
|
yield self._current_group
|
|
self._current_group = previous_group
|
|
|
|
@contextmanager
|
|
def mutually_exclusive_group(self, **kwargs):
|
|
previous_group = self._current_group
|
|
self._current_group = previous_group \
|
|
.add_mutually_exclusive_group(**kwargs)
|
|
yield self._current_group
|
|
self._current_group = previous_group
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
|
|
|
class ArgumentParser(argparse.ArgumentParser):
|
|
"""A thin extension class to the standard ArgumentParser which incluldes
|
|
methods to interact with a builder instance.
|
|
"""
|
|
|
|
@classmethod
|
|
def builder(cls, **kwargs):
|
|
"""Create a new builder instance using this parser class.
|
|
"""
|
|
|
|
return _Builder(parser=cls(**kwargs))
|
|
|
|
def to_builder(self):
|
|
"""Construct and return a builder instance with this parser.
|
|
"""
|
|
|
|
return _Builder(parser=self)
|
|
|
|
def parse_known_args(self, args=None, namespace=None):
|
|
"""Thin wrapper around parse_known_args which shims-in support for
|
|
actions with multiple destinations.
|
|
"""
|
|
|
|
if namespace is None:
|
|
namespace = Namespace()
|
|
|
|
# Add action defaults not present in namespace
|
|
for action in self._actions:
|
|
if not hasattr(action, 'dests'):
|
|
continue
|
|
|
|
for dest in action.dests:
|
|
if hasattr(namespace, dest):
|
|
continue
|
|
if action.default is SUPPRESS:
|
|
continue
|
|
|
|
setattr(namespace, dest, action.default)
|
|
|
|
return super(ArgumentParser, self).parse_known_args(args, namespace)
|