23
23
get_callback_and_params ,
24
24
get_fake_option_name ,
25
25
raise_mixing_decorators_error ,
26
- resolve_wrappers
26
+ resolve_wrappers ,
27
27
)
28
28
29
29
FC = Union [Callable , click .Command ]
@@ -39,26 +39,33 @@ class GroupedOption(click.Option):
39
39
:param attrs: additional option attributes
40
40
"""
41
41
42
- def __init__ (self , param_decls : Optional [Sequence [str ]] = None , * , group : 'OptionGroup' , ** attrs : Any ):
42
+ def __init__ (
43
+ self ,
44
+ param_decls : Optional [Sequence [str ]] = None ,
45
+ * ,
46
+ group : "OptionGroup" ,
47
+ ** attrs : Any ,
48
+ ):
43
49
super ().__init__ (param_decls , ** attrs )
44
50
45
51
for attr in group .forbidden_option_attrs :
46
52
if attr in attrs :
47
53
raise TypeError (
48
- f"'{ attr } ' attribute is not allowed for '{ type (group ).__name__ } ' option `{ self .name } '." )
54
+ f"'{ attr } ' attribute is not allowed for '{ type (group ).__name__ } ' option `{ self .name } '."
55
+ )
49
56
50
57
self .__group = group
51
58
52
59
@property
53
- def group (self ) -> ' OptionGroup' :
60
+ def group (self ) -> " OptionGroup" :
54
61
"""Returns the reference to the group for this option
55
62
56
63
:return: `OptionGroup` the group instance for this option
57
64
"""
58
65
return self .__group
59
66
60
67
def handle_parse_result (
61
- self , ctx : click .Context , opts : Mapping [str , Any ], args : List [str ]
68
+ self , ctx : click .Context , opts : Mapping [str , Any ], args : List [str ]
62
69
) -> Tuple [Any , List [str ]]:
63
70
with augment_usage_errors (ctx , param = self ):
64
71
if not ctx .resilient_parsing :
@@ -75,19 +82,24 @@ def get_help_record(self, ctx: click.Context) -> Optional[Tuple[str, str]]:
75
82
76
83
formatter = ctx .make_formatter ()
77
84
with formatter .indentation ():
78
- indent = ' ' * formatter .current_indent
79
- return f' { indent } { opts } ' , opt_help
85
+ indent = " " * formatter .current_indent
86
+ return f" { indent } { opts } " , opt_help
80
87
81
88
82
89
class _GroupTitleFakeOption (click .Option ):
83
- """The helper `Option` class to display option group title in help
84
- """
90
+ """The helper `Option` class to display option group title in help"""
85
91
86
92
def __init__ (
87
- self , param_decls : Optional [Sequence [str ]] = None , * , group : 'OptionGroup' , ** attrs : Any
93
+ self ,
94
+ param_decls : Optional [Sequence [str ]] = None ,
95
+ * ,
96
+ group : "OptionGroup" ,
97
+ ** attrs : Any ,
88
98
) -> None :
89
99
self .__group = group
90
- super ().__init__ (param_decls , hidden = True , expose_value = False , help = group .help , ** attrs )
100
+ super ().__init__ (
101
+ param_decls , hidden = True , expose_value = False , help = group .help , ** attrs
102
+ )
91
103
92
104
# We remove parsed opts for the fake options just in case.
93
105
# For example it is workaround for correct click-repl autocomplete
@@ -109,13 +121,19 @@ class OptionGroup:
109
121
"""
110
122
111
123
def __init__ (
112
- self , name : Optional [str ] = None , * , hidden : bool = False , help : Optional [str ] = None
124
+ self ,
125
+ name : Optional [str ] = None ,
126
+ * ,
127
+ hidden : bool = False ,
128
+ help : Optional [str ] = None ,
113
129
) -> None :
114
- self ._name = name if name else ''
115
- self ._help = inspect .cleandoc (help if help else '' )
130
+ self ._name = name if name else ""
131
+ self ._help = inspect .cleandoc (help if help else "" )
116
132
self ._hidden = hidden
117
133
118
- self ._options : Mapping [Any , Any ] = collections .defaultdict (weakref .WeakValueDictionary )
134
+ self ._options : Mapping [Any , Any ] = collections .defaultdict (
135
+ weakref .WeakValueDictionary
136
+ )
119
137
self ._group_title_options = weakref .WeakValueDictionary ()
120
138
121
139
@property
@@ -136,14 +154,12 @@ def help(self) -> str:
136
154
137
155
@property
138
156
def name_extra (self ) -> List [str ]:
139
- """Returns extra name attributes for the group
140
- """
157
+ """Returns extra name attributes for the group"""
141
158
return []
142
159
143
160
@property
144
161
def forbidden_option_attrs (self ) -> List [str ]:
145
- """Returns the list of forbidden option attributes for the group
146
- """
162
+ """Returns the list of forbidden option attributes for the group"""
147
163
return []
148
164
149
165
def get_help_record (self , ctx : click .Context ) -> Optional [Tuple [str , str ]]:
@@ -156,16 +172,16 @@ def get_help_record(self, ctx: click.Context) -> Optional[Tuple[str, str]]:
156
172
return None
157
173
158
174
name = self .name
159
- help_ = self .help if self .help else ''
175
+ help_ = self .help if self .help else ""
160
176
161
- extra = ', ' .join (self .name_extra )
177
+ extra = ", " .join (self .name_extra )
162
178
if extra :
163
- extra = f' [{ extra } ]'
179
+ extra = f" [{ extra } ]"
164
180
165
181
if name :
166
- name = f' { name } : { extra } '
182
+ name = f" { name } : { extra } "
167
183
elif extra :
168
- name = f' { extra } :'
184
+ name = f" { extra } :"
169
185
170
186
if not name and not help_ :
171
187
return None
@@ -180,12 +196,14 @@ def option(self, *param_decls: str, **attrs: Any) -> Callable:
180
196
181
197
def decorator (func : FC ) -> FC :
182
198
option_attrs = attrs .copy ()
183
- option_attrs .setdefault (' cls' , GroupedOption )
199
+ option_attrs .setdefault (" cls" , GroupedOption )
184
200
if self ._hidden :
185
- option_attrs .setdefault (' hidden' , self ._hidden )
201
+ option_attrs .setdefault (" hidden" , self ._hidden )
186
202
187
- if not issubclass (option_attrs ['cls' ], GroupedOption ):
188
- raise TypeError ("'cls' argument must be a subclass of 'GroupedOption' class." )
203
+ if not issubclass (option_attrs ["cls" ], GroupedOption ):
204
+ raise TypeError (
205
+ "'cls' argument must be a subclass of 'GroupedOption' class."
206
+ )
189
207
190
208
self ._check_mixing_decorators (func )
191
209
func = click .option (* param_decls , group = self , ** option_attrs )(func )
@@ -199,32 +217,33 @@ def decorator(func: FC) -> FC:
199
217
return decorator
200
218
201
219
def get_options (self , ctx : click .Context ) -> Dict [str , GroupedOption ]:
202
- """Returns the dictionary with group options
203
- """
220
+ """Returns the dictionary with group options"""
204
221
return self ._options .get (resolve_wrappers (ctx .command .callback ), {})
205
222
206
223
def get_option_names (self , ctx : click .Context ) -> List [str ]:
207
- """Returns the list with option names ordered by addition in the group
208
- """
224
+ """Returns the list with option names ordered by addition in the group"""
209
225
return list (reversed (list (self .get_options (ctx ))))
210
226
211
- def get_error_hint (self , ctx : click .Context , option_names : Optional [Set [str ]] = None ) -> str :
227
+ def get_error_hint (
228
+ self , ctx : click .Context , option_names : Optional [Set [str ]] = None
229
+ ) -> str :
212
230
options = self .get_options (ctx )
213
- text = ''
231
+ text = ""
214
232
215
233
for name , opt in reversed (list (options .items ())):
216
234
if option_names and name not in option_names :
217
235
continue
218
- text += f' { opt .get_error_hint (ctx )} \n '
236
+ text += f" { opt .get_error_hint (ctx )} \n "
219
237
220
238
if text :
221
239
text = text [:- 1 ]
222
240
223
241
return text
224
242
225
- def handle_parse_result (self , option : GroupedOption , ctx : click .Context , opts : Mapping [str , Any ]) -> None :
226
- """The method should be used for adding specific behavior and relation for options in the group
227
- """
243
+ def handle_parse_result (
244
+ self , option : GroupedOption , ctx : click .Context , opts : Mapping [str , Any ]
245
+ ) -> None :
246
+ """The method should be used for adding specific behavior and relation for options in the group"""
228
247
229
248
def _check_mixing_decorators (self , func : Callable ) -> None :
230
249
func , params = get_callback_and_params (func )
@@ -243,8 +262,9 @@ def _add_title_fake_option(self, func: FC) -> None:
243
262
callback , params = get_callback_and_params (func )
244
263
245
264
if callback not in self ._group_title_options :
246
- func = click .option (get_fake_option_name (),
247
- group = self , cls = _GroupTitleFakeOption )(func )
265
+ func = click .option (
266
+ get_fake_option_name (), group = self , cls = _GroupTitleFakeOption
267
+ )(func )
248
268
249
269
_ , params = get_callback_and_params (func )
250
270
self ._group_title_options [callback ] = params [- 1 ]
@@ -274,13 +294,15 @@ class RequiredAnyOptionGroup(OptionGroup):
274
294
275
295
@property
276
296
def forbidden_option_attrs (self ) -> List [str ]:
277
- return [' required' ]
297
+ return [" required" ]
278
298
279
299
@property
280
300
def name_extra (self ) -> List [str ]:
281
- return super ().name_extra + [' required_any' ]
301
+ return super ().name_extra + [" required_any" ]
282
302
283
- def handle_parse_result (self , option : GroupedOption , ctx : click .Context , opts : Mapping [str , Any ]) -> None :
303
+ def handle_parse_result (
304
+ self , option : GroupedOption , ctx : click .Context , opts : Mapping [str , Any ]
305
+ ) -> None :
284
306
if option .name in opts :
285
307
return
286
308
@@ -300,7 +322,7 @@ def handle_parse_result(self, option: GroupedOption, ctx: click.Context, opts: M
300
322
301
323
raise click .UsageError (
302
324
f"At least one of the following options from { group_name } option group is required:\n { option_info } " ,
303
- ctx = ctx
325
+ ctx = ctx ,
304
326
)
305
327
306
328
@@ -312,13 +334,15 @@ class RequiredAllOptionGroup(OptionGroup):
312
334
313
335
@property
314
336
def forbidden_option_attrs (self ) -> List [str ]:
315
- return [' required' , ' hidden' ]
337
+ return [" required" , " hidden" ]
316
338
317
339
@property
318
340
def name_extra (self ) -> List [str ]:
319
- return super ().name_extra + [' required_all' ]
341
+ return super ().name_extra + [" required_all" ]
320
342
321
- def handle_parse_result (self , option : GroupedOption , ctx : click .Context , opts : Mapping [str , Any ]) -> None :
343
+ def handle_parse_result (
344
+ self , option : GroupedOption , ctx : click .Context , opts : Mapping [str , Any ]
345
+ ) -> None :
322
346
option_names = set (self .get_options (ctx ))
323
347
324
348
if not option_names .issubset (opts ):
@@ -328,7 +352,7 @@ def handle_parse_result(self, option: GroupedOption, ctx: click.Context, opts: M
328
352
329
353
raise click .UsageError (
330
354
f"Missing required options from { group_name } option group:\n { option_info } " ,
331
- ctx = ctx
355
+ ctx = ctx ,
332
356
)
333
357
334
358
@@ -341,13 +365,15 @@ class MutuallyExclusiveOptionGroup(OptionGroup):
341
365
342
366
@property
343
367
def forbidden_option_attrs (self ) -> List [str ]:
344
- return [' required' ]
368
+ return [" required" ]
345
369
346
370
@property
347
371
def name_extra (self ) -> List [str ]:
348
- return super ().name_extra + [' mutually_exclusive' ]
372
+ return super ().name_extra + [" mutually_exclusive" ]
349
373
350
- def handle_parse_result (self , option : GroupedOption , ctx : click .Context , opts : Mapping [str , Any ]) -> None :
374
+ def handle_parse_result (
375
+ self , option : GroupedOption , ctx : click .Context , opts : Mapping [str , Any ]
376
+ ) -> None :
351
377
option_names = set (self .get_options (ctx ))
352
378
given_option_names = option_names .intersection (opts )
353
379
given_option_count = len (given_option_names )
@@ -359,7 +385,7 @@ def handle_parse_result(self, option: GroupedOption, ctx: click.Context, opts: M
359
385
raise click .UsageError (
360
386
f"Mutually exclusive options from { group_name } option group "
361
387
f"cannot be used at the same time:\n { option_info } " ,
362
- ctx = ctx
388
+ ctx = ctx ,
363
389
)
364
390
365
391
@@ -372,9 +398,11 @@ class RequiredMutuallyExclusiveOptionGroup(MutuallyExclusiveOptionGroup):
372
398
373
399
@property
374
400
def name_extra (self ) -> List [str ]:
375
- return super ().name_extra + [' required' ]
401
+ return super ().name_extra + [" required" ]
376
402
377
- def handle_parse_result (self , option : GroupedOption , ctx : click .Context , opts : Mapping [str , Any ]) -> None :
403
+ def handle_parse_result (
404
+ self , option : GroupedOption , ctx : click .Context , opts : Mapping [str , Any ]
405
+ ) -> None :
378
406
super ().handle_parse_result (option , ctx , opts )
379
407
380
408
option_names = set (self .get_option_names (ctx ))
@@ -387,7 +415,7 @@ def handle_parse_result(self, option: GroupedOption, ctx: click.Context, opts: M
387
415
raise click .UsageError (
388
416
"Missing one of the required mutually exclusive options from "
389
417
f"{ group_name } option group:\n { option_info } " ,
390
- ctx = ctx
418
+ ctx = ctx ,
391
419
)
392
420
393
421
@@ -400,21 +428,26 @@ class AllOptionGroup(OptionGroup):
400
428
401
429
@property
402
430
def forbidden_option_attrs (self ) -> List [str ]:
403
- return [' required' , ' hidden' ]
431
+ return [" required" , " hidden" ]
404
432
405
433
@property
406
434
def name_extra (self ) -> List [str ]:
407
- return super ().name_extra + [' all_or_none' ]
435
+ return super ().name_extra + [" all_or_none" ]
408
436
409
- def handle_parse_result (self , option : GroupedOption , ctx : click .Context , opts : Mapping [str , Any ]) -> None :
437
+ def handle_parse_result (
438
+ self , option : GroupedOption , ctx : click .Context , opts : Mapping [str , Any ]
439
+ ) -> None :
410
440
option_names = set (self .get_options (ctx ))
411
441
412
- if not option_names .isdisjoint (opts ) and option_names .intersection (opts ) != option_names :
442
+ if (
443
+ not option_names .isdisjoint (opts )
444
+ and option_names .intersection (opts ) != option_names
445
+ ):
413
446
group_name = self ._group_name_str ()
414
447
option_info = self .get_error_hint (ctx )
415
448
416
449
raise click .UsageError (
417
450
f"All options from { group_name } option group should be specified or none should be specified. "
418
451
f"Missing required options:\n { option_info } " ,
419
- ctx = ctx
452
+ ctx = ctx ,
420
453
)
0 commit comments