Skip to content

Argparse "Overlay" Module #12873

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Nov 28, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion utils/build_swift/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@ the Swift build-script.
You may run the unit test suite using the command:

```sh
$ python -m unittest discover -s utils/build_swift
$ python -m unittest discover -s utils/build_swift/ -t utils/
```
53 changes: 53 additions & 0 deletions utils/build_swift/argparse/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# 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


Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a docstring explaining what this module is intended to do so it is used appropriately in the future :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No problem, I'll add those in a bit.

"""
Wrapper module around the standard argparse that extends the default
functionality with support for multi-destination actions, an expressive DSL for
constructing parsers and more argument types. This module exposes a strict
super-set of the argparse API and is meant to be used as a drop-in replacement.
"""


from argparse import (ArgumentDefaultsHelpFormatter, ArgumentError,
ArgumentTypeError, FileType, HelpFormatter,
Namespace, RawDescriptionHelpFormatter,
RawTextHelpFormatter)
from argparse import ONE_OR_MORE, OPTIONAL, SUPPRESS, ZERO_OR_MORE

from .actions import Action, Nargs
from .parser import ArgumentParser
from .types import (BoolType, ClangVersionType, PathType, RegexType,
SwiftVersionType)


__all__ = [
'Action',
'ArgumentDefaultsHelpFormatter',
'ArgumentError',
'ArgumentParser',
'ArgumentTypeError',
'HelpFormatter',
'Namespace',
'Nargs',
'RawDescriptionHelpFormatter',
'RawTextHelpFormatter',

'BoolType',
'FileType',
'PathType',
'RegexType',
'ClangVersionType',
'SwiftVersionType',

'SUPPRESS',
'OPTIONAL',
'ZERO_OR_MORE',
'ONE_OR_MORE',
]
337 changes: 337 additions & 0 deletions utils/build_swift/argparse/actions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,337 @@
# 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


Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

docstring here as well.

"""
Hierarchy of action classes which support multiple destinations, similar to the
default actions provided by the standard argparse.
"""


import argparse
import copy

from .types import BoolType, PathType


__all__ = [
'Action',
'Nargs',

'AppendAction',
'CustomCallAction',
'StoreAction',
'StoreIntAction',
'StoreTrueAction',
'StoreFalseAction',
'StorePathAction',
'ToggleTrueAction',
'ToggleFalseAction',
'UnsupportedAction',
]


# -----------------------------------------------------------------------------

class Nargs(object):
"""Container class holding valid values for the number of arguments
actions can accept. Other possible values include integer literals.
"""

ZERO = 0
SINGLE = None
OPTIONAL = argparse.OPTIONAL
ZERO_OR_MORE = argparse.ZERO_OR_MORE
ONE_OR_MORE = argparse.ONE_OR_MORE


# -----------------------------------------------------------------------------

class Action(argparse.Action):
"""Slightly modified version of the Action class from argparse which has
support for multiple destinations available rather than just a single one.
"""

def __init__(self,
option_strings,
dests=None,
nargs=Nargs.SINGLE,
const=None,
default=None,
type=None,
choices=None,
required=False,
help=None,
metavar=None,
dest=None): # exists for compatibility purposes

if dests is None and dest is None:
raise TypeError('action must supply either dests or dest')

dests = dests or dest
if dests == argparse.SUPPRESS:
dests = []
elif isinstance(dests, str):
dests = [dests]

super(Action, self).__init__(
option_strings=option_strings,
dest=argparse.SUPPRESS,
nargs=nargs,
const=const,
default=default,
type=type,
choices=choices,
required=required,
help=help,
metavar=metavar)

self.dests = dests

def _get_kwargs(self):
"""Unofficial method used for pretty-printing out actions.
"""

names = [
'option_strings',
'dests',
'nargs',
'const',
'default',
'type',
'choices',
'required',
'help',
'metavar',
]

return [(name, getattr(self, name)) for name in names]

def __call__(self, parser, namespace, values, option_string=None):
raise NotImplementedError('__call__ not defined')


# -----------------------------------------------------------------------------

class AppendAction(Action):
"""Action that appends
"""

def __init__(self, option_strings, join=None, **kwargs):

kwargs['nargs'] = Nargs.SINGLE
kwargs.setdefault('default', [])

super(AppendAction, self).__init__(
option_strings=option_strings,
**kwargs)

def __call__(self, parser, namespace, values, option_string=None):
if isinstance(values, str):
values = [values]

for dest in self.dests:
if getattr(namespace, dest) is None:
setattr(namespace, dest, [])

items = copy.copy(getattr(namespace, dest))
items += values

setattr(namespace, dest, items)


# -----------------------------------------------------------------------------

class CustomCallAction(Action):
"""Action that allows for instances to implement custom call functionality.
The call_func must follow the same calling convention as implementing the
__call__ method for an Action.
"""

def __init__(self, option_strings, call_func, **kwargs):

if not callable(call_func):
raise TypeError('call_func must be callable')

super(CustomCallAction, self).__init__(
option_strings=option_strings,
**kwargs)

self.call_func = call_func

def __call__(self, parser, namespace, values, option_string=None):
self.call_func(self, parser, namespace, values, option_string)


# -----------------------------------------------------------------------------

class StoreAction(Action):
"""Action that stores a string.
"""

def __init__(self, option_strings, **kwargs):

if 'choices' in kwargs:
kwargs['nargs'] = Nargs.OPTIONAL
if 'const' in kwargs:
kwargs['nargs'] = Nargs.ZERO

super(StoreAction, self).__init__(
option_strings=option_strings,
**kwargs)

def __call__(self, parser, namespace, values, option_string=None):
for dest in self.dests:
if self.nargs == Nargs.ZERO and self.const is not None:
values = self.const

setattr(namespace, dest, values)


class StoreIntAction(StoreAction):
"""Action that stores an int.
"""

def __init__(self, option_strings, **kwargs):
kwargs['nargs'] = Nargs.SINGLE
kwargs['type'] = int
kwargs.setdefault('metavar', 'N')

super(StoreAction, self).__init__(
option_strings=option_strings,
**kwargs)


class StoreTrueAction(StoreAction):
"""Action that stores True when called and False by default.
"""

def __init__(self, option_strings, **kwargs):
kwargs['nargs'] = Nargs.ZERO
kwargs['const'] = True
kwargs['default'] = False

super(StoreTrueAction, self).__init__(
option_strings=option_strings,
**kwargs)


class StoreFalseAction(StoreAction):
"""Action that stores False when called and True by default.
"""

def __init__(self, option_strings, **kwargs):
kwargs['nargs'] = Nargs.ZERO
kwargs['const'] = False
kwargs['default'] = True

super(StoreFalseAction, self).__init__(
option_strings=option_strings,
**kwargs)


class StorePathAction(StoreAction):
"""Action that stores a path, which it will attempt to expand.
"""

def __init__(self, option_strings, **kwargs):
kwargs['nargs'] = Nargs.SINGLE
kwargs['type'] = PathType()

super(StorePathAction, self).__init__(
option_strings=option_strings,
**kwargs)


# -----------------------------------------------------------------------------

class _ToggleAction(Action):
"""Action that can be toggled on or off, either implicitly when called or
explicitly when an optional boolean value is parsed.
"""

def __init__(self, option_strings, on_value, off_value, **kwargs):
kwargs['nargs'] = Nargs.OPTIONAL
kwargs['type'] = BoolType()
kwargs.setdefault('default', off_value)
kwargs.setdefault('metavar', 'BOOL')

super(_ToggleAction, self).__init__(
option_strings=option_strings,
**kwargs)

self.on_value = on_value
self.off_value = off_value

def __call__(self, parser, namespace, values, option_string=None):
if values is None:
values = self.on_value
elif values is True:
values = self.on_value
elif values is False:
values = self.off_value
else:
raise argparse.ArgumentTypeError(
'{} is not a boolean value'.format(values))

for dest in self.dests:
setattr(namespace, dest, values)


class ToggleTrueAction(_ToggleAction):
"""Action that toggles True when called or False otherwise, with the
option to explicitly override the value.
"""

def __init__(self, option_strings, **kwargs):

super(ToggleTrueAction, self).__init__(
option_strings=option_strings,
on_value=True,
off_value=False,
**kwargs)


class ToggleFalseAction(_ToggleAction):
"""Action that toggles False when called or True otherwise, with the
option to explicitly override the value.
"""

def __init__(self, option_strings, **kwargs):

super(ToggleFalseAction, self).__init__(
option_strings=option_strings,
on_value=False,
off_value=True,
**kwargs)


# -----------------------------------------------------------------------------

class UnsupportedAction(Action):
"""Action that denotes an unsupported argument or opiton and subsequently
raises an ArgumentError.
"""

def __init__(self, option_strings, message=None, **kwargs):
kwargs['nargs'] = Nargs.ZERO
kwargs['default'] = argparse.SUPPRESS

super(UnsupportedAction, self).__init__(
option_strings=option_strings,
dests=argparse.SUPPRESS,
**kwargs)

self.message = message

def __call__(self, parser, namespace, values, option_string=None):
if self.message is not None:
parser.error(self.message)

arg = option_string or str(values)
parser.error('unsupported argument: {}'.format(arg))
Loading