-
Notifications
You must be signed in to change notification settings - Fork 4.3k
/
Copy pathtoplevelbool.py
148 lines (122 loc) · 5.76 KB
/
toplevelbool.py
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
# language governing permissions and limitations under the License.
"""
Top Level Boolean Parameters
----------------------------
This customization will take a parameter that has
a structure of a single boolean element and allow the argument
to be specified without a value.
Instead of having to say::
--ebs-optimized '{"Value": true}'
--ebs-optimized '{"Value": false}'
You can instead say `--ebs-optimized/--no-ebs-optimized`.
"""
import logging
from functools import partial
from awscli.argprocess import detect_shape_structure
from awscli import arguments
from awscli.customizations.utils import validate_mutually_exclusive_handler
LOG = logging.getLogger(__name__)
# This sentinel object is used to distinguish when
# a parameter is not specified vs. specified with no value
# (a value of None).
_NOT_SPECIFIED = object()
def register_bool_params(event_handler):
event_handler.register('building-argument-table.ec2.*',
partial(pull_up_bool,
event_handler=event_handler))
def _qualifies_for_simplification(arg_model):
if detect_shape_structure(arg_model) == 'structure(scalar)':
members = arg_model.members
if (len(members) == 1 and
list(members.keys())[0] == 'Value' and
list(members.values())[0].type_name == 'boolean'):
return True
return False
def pull_up_bool(argument_table, event_handler, **kwargs):
# List of tuples of (positive_bool, negative_bool)
# This is used to validate that we don't specify
# an --option and a --no-option.
boolean_pairs = []
event_handler.register(
'operation-args-parsed.ec2.*',
partial(validate_boolean_mutex_groups,
boolean_pairs=boolean_pairs))
for value in list(argument_table.values()):
if hasattr(value, 'argument_model'):
arg_model = value.argument_model
if _qualifies_for_simplification(arg_model):
# Swap out the existing CLIArgument for two args:
# one that supports --option and --option <some value>
# and another arg of --no-option.
new_arg = PositiveBooleanArgument(
value.name, arg_model, value._operation_model,
value._event_emitter,
group_name=value.name,
serialized_name=value._serialized_name)
argument_table[value.name] = new_arg
negative_name = 'no-%s' % value.name
negative_arg = NegativeBooleanParameter(
negative_name, arg_model, value._operation_model,
value._event_emitter,
action='store_true', dest='no_%s' % new_arg.py_name,
group_name=value.name,
serialized_name=value._serialized_name)
argument_table[negative_name] = negative_arg
# If we've pulled up a structure(scalar) arg
# into a pair of top level boolean args, we need
# to validate that a user only provides the argument
# once. They can't say --option/--no-option, nor
# can they say --option --option Value=false.
boolean_pairs.append((new_arg, negative_arg))
def validate_boolean_mutex_groups(boolean_pairs, parsed_args, **kwargs):
# Validate we didn't pass in an --option and a --no-option.
for positive, negative in boolean_pairs:
if getattr(parsed_args, positive.py_name) is not _NOT_SPECIFIED and \
getattr(parsed_args, negative.py_name) is not _NOT_SPECIFIED:
raise ValueError(
'Cannot specify both the "%s" option and '
'the "%s" option.' % (positive.cli_name, negative.cli_name))
class PositiveBooleanArgument(arguments.CLIArgument):
def __init__(self, name, argument_model, operation_model,
event_emitter, serialized_name, group_name):
super(PositiveBooleanArgument, self).__init__(
name, argument_model, operation_model, event_emitter,
serialized_name=serialized_name)
self._group_name = group_name
@property
def group_name(self):
return self._group_name
def add_to_parser(self, parser):
# We need to support three forms:
# --option-name
# --option-name Value=(true|false)
parser.add_argument(self.cli_name,
help=self.documentation,
action='store',
default=_NOT_SPECIFIED,
nargs='?')
def add_to_params(self, parameters, value):
if value is _NOT_SPECIFIED:
return
elif value is None:
# Then this means that the user explicitly
# specified this arg with no value,
# e.g. --boolean-parameter
# which means we should add a true value
# to the parameters dict.
parameters[self._serialized_name] = {'Value': True}
else:
# Otherwise the arg was specified with a value.
parameters[self._serialized_name] = self._unpack_argument(
value)
class NegativeBooleanParameter(arguments.BooleanArgument):
def __init__(self, name, argument_model, operation_model,
event_emitter, serialized_name, action='store_true',
dest=None, group_name=None):
super(NegativeBooleanParameter, self).__init__(
name, argument_model, operation_model, event_emitter,
default=_NOT_SPECIFIED, serialized_name=serialized_name)
self._group_name = group_name
def add_to_params(self, parameters, value):
if value is not _NOT_SPECIFIED and value:
parameters[self._serialized_name] = {'Value': False}