1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152
|
from typing import Any, Hashable, List
from .attrdict import AttrDict
class Configuration(AttrDict):
"""Represents the running configuration.
This class never raises IndexError, instead it will return None if a
section or option does not yet exist.
"""
def __getitem__(self, key: Hashable) -> Any:
"""Returns a config section, creating it if it doesn't exist yet.
"""
if key not in self._data:
self._data[key] = ConfigurationSection(self)
return self._data[key]
class ConfigurationSection(Configuration):
def __init__(self, parent: AttrDict, *args: Any, **kwargs: Any) -> None:
super(ConfigurationSection, self).__init__(*args, **kwargs)
self._parent = parent
def __getitem__(self, key: Hashable) -> Any:
"""Returns a config value, pulling from the `user` section as a fallback.
This is called when the attribute is accessed either via the get method or through [ ] index.
"""
if key in self._data and self._data.get(key) is not None:
return self._data[key]
elif key in self._parent.user:
return self._parent.user[key]
return None
def __getattr__(self, key: str) -> Any:
"""Returns the config value from the `user` section.
This is called when the attribute is accessed via dot notation but does not exist.
"""
if key[0] != '_' and key in self._parent['user']:
return self._parent['user'][key]
return None
def __setattr__(self, key: str, value: Any) -> None:
"""Sets dictionary value when an attribute is set.
"""
super().__setattr__(key, value)
if key[0] != '_':
self._data[key] = value
class SubparserWrapper(object):
"""Wrap subparsers so we can track what options the user passed.
"""
# We type `cli` as Any instead of MILC to avoid a circular import
def __init__(self, cli: Any, submodule: Any, subparser: Any) -> None:
self.cli = cli
self.submodule = submodule
self.subparser = subparser
for attr in dir(subparser):
if not hasattr(self, attr):
setattr(self, attr, getattr(subparser, attr))
def completer(self, completer: Any) -> None:
"""Add an arpcomplete completer to this subcommand.
"""
self.subparser.completer = completer
def add_argument(self, *args: Any, **kwargs: Any) -> None:
"""Add an argument for this subcommand.
This also stores the default for the argument in `self.cli.default_arguments`.
"""
if kwargs.get('action') == 'store_boolean':
# Store boolean will call us again with the enable/disable flag arguments
handle_store_boolean(self, *args, **kwargs)
else:
completer = None
if kwargs.get('completer'):
completer = kwargs['completer']
del kwargs['completer']
self.cli.acquire_lock()
argument_name = get_argument_name(self.cli._arg_parser, *args, **kwargs)
if completer:
self.subparser.add_argument(*args, **kwargs).completer = completer
else:
self.subparser.add_argument(*args, **kwargs)
if kwargs.get('action') == 'store_false':
self.cli._config_store_false.append(argument_name)
if kwargs.get('action') == 'store_true':
self.cli._config_store_true.append(argument_name)
if self.submodule not in self.cli.default_arguments:
self.cli.default_arguments[self.submodule] = {}
self.cli.default_arguments[self.submodule][argument_name] = kwargs.get('default')
self.cli.release_lock()
def get_argument_strings(arg_parser: Any, *args: Any, **kwargs: Any) -> List[str]:
"""Takes argparse arguments and returns a list of argument strings or positional names.
"""
try:
return arg_parser._get_optional_kwargs(*args, **kwargs)['option_strings'] # type: ignore[no-any-return]
except ValueError:
return [arg_parser._get_positional_kwargs(*args, **kwargs)['dest']]
def get_argument_name(arg_parser: Any, *args: Any, **kwargs: Any) -> Any:
"""Takes argparse arguments and returns the dest name.
"""
try:
return arg_parser._get_optional_kwargs(*args, **kwargs)['dest']
except ValueError:
return arg_parser._get_positional_kwargs(*args, **kwargs)['dest']
# FIXME: We should not be using self in this way
def handle_store_boolean(self: Any, *args: Any, **kwargs: Any) -> Any:
"""Does the add_argument for action='store_boolean'.
"""
disabled_args = None
disabled_kwargs = kwargs.copy()
disabled_kwargs['action'] = 'store_false'
disabled_kwargs['dest'] = get_argument_name(getattr(self, 'cli', self)._arg_parser, *args, **kwargs)
disabled_kwargs['help'] = 'Disable ' + kwargs['help']
kwargs['action'] = 'store_true'
kwargs['help'] = 'Enable ' + kwargs['help']
for flag in args:
if flag[:2] == '--':
disabled_args = ('--no-' + flag[2:],)
break
self.add_argument(*args, **kwargs)
self.add_argument(*disabled_args, **disabled_kwargs)
return (args, kwargs, disabled_args, disabled_kwargs)
|