Skip to content

Commit d223274

Browse files
committed
src-layout
Signed-off-by: Cristian Le <cristian.le@mpsd.mpg.de>
1 parent a378bde commit d223274

File tree

7 files changed

+183
-117
lines changed

7 files changed

+183
-117
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,4 +77,4 @@ dmypy.json
7777
.idea/
7878

7979
### Project specific
80-
click_option_group/_version.py
80+
src/click_option_group/_version.py

pyproject.toml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,7 @@ Issues = "https://github.com/click-contrib/click-option-group/issues"
6262

6363
[tool.hatch]
6464
version.source = "vcs"
65-
build.hooks.vcs.version-file = "click_option_group/_version.py"
66-
build.targets.wheel.packages = ['click-option-group']
65+
build.hooks.vcs.version-file = "src/click_option_group/_version.py"
6766

6867

6968
[tool.pytest.ini_options]

click_option_group/__init__.py renamed to src/click_option_group/__init__.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,13 @@
2525

2626

2727
__all__ = [
28-
'__version__',
29-
'optgroup',
30-
'GroupedOption',
31-
'OptionGroup',
32-
'RequiredAnyOptionGroup',
33-
'AllOptionGroup',
34-
'RequiredAllOptionGroup',
35-
'MutuallyExclusiveOptionGroup',
36-
'RequiredMutuallyExclusiveOptionGroup',
28+
"__version__",
29+
"optgroup",
30+
"GroupedOption",
31+
"OptionGroup",
32+
"RequiredAnyOptionGroup",
33+
"AllOptionGroup",
34+
"RequiredAllOptionGroup",
35+
"MutuallyExclusiveOptionGroup",
36+
"RequiredMutuallyExclusiveOptionGroup",
3737
]

click_option_group/_core.py renamed to src/click_option_group/_core.py

Lines changed: 93 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
get_callback_and_params,
2424
get_fake_option_name,
2525
raise_mixing_decorators_error,
26-
resolve_wrappers
26+
resolve_wrappers,
2727
)
2828

2929
FC = Union[Callable, click.Command]
@@ -39,26 +39,33 @@ class GroupedOption(click.Option):
3939
:param attrs: additional option attributes
4040
"""
4141

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+
):
4349
super().__init__(param_decls, **attrs)
4450

4551
for attr in group.forbidden_option_attrs:
4652
if attr in attrs:
4753
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+
)
4956

5057
self.__group = group
5158

5259
@property
53-
def group(self) -> 'OptionGroup':
60+
def group(self) -> "OptionGroup":
5461
"""Returns the reference to the group for this option
5562
5663
:return: `OptionGroup` the group instance for this option
5764
"""
5865
return self.__group
5966

6067
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]
6269
) -> Tuple[Any, List[str]]:
6370
with augment_usage_errors(ctx, param=self):
6471
if not ctx.resilient_parsing:
@@ -75,19 +82,24 @@ def get_help_record(self, ctx: click.Context) -> Optional[Tuple[str, str]]:
7582

7683
formatter = ctx.make_formatter()
7784
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
8087

8188

8289
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"""
8591

8692
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,
8898
) -> None:
8999
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+
)
91103

92104
# We remove parsed opts for the fake options just in case.
93105
# For example it is workaround for correct click-repl autocomplete
@@ -109,13 +121,19 @@ class OptionGroup:
109121
"""
110122

111123
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,
113129
) -> 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 "")
116132
self._hidden = hidden
117133

118-
self._options: Mapping[Any, Any] = collections.defaultdict(weakref.WeakValueDictionary)
134+
self._options: Mapping[Any, Any] = collections.defaultdict(
135+
weakref.WeakValueDictionary
136+
)
119137
self._group_title_options = weakref.WeakValueDictionary()
120138

121139
@property
@@ -136,14 +154,12 @@ def help(self) -> str:
136154

137155
@property
138156
def name_extra(self) -> List[str]:
139-
"""Returns extra name attributes for the group
140-
"""
157+
"""Returns extra name attributes for the group"""
141158
return []
142159

143160
@property
144161
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"""
147163
return []
148164

149165
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]]:
156172
return None
157173

158174
name = self.name
159-
help_ = self.help if self.help else ''
175+
help_ = self.help if self.help else ""
160176

161-
extra = ', '.join(self.name_extra)
177+
extra = ", ".join(self.name_extra)
162178
if extra:
163-
extra = f'[{extra}]'
179+
extra = f"[{extra}]"
164180

165181
if name:
166-
name = f'{name}: {extra}'
182+
name = f"{name}: {extra}"
167183
elif extra:
168-
name = f'{extra}:'
184+
name = f"{extra}:"
169185

170186
if not name and not help_:
171187
return None
@@ -180,12 +196,14 @@ def option(self, *param_decls: str, **attrs: Any) -> Callable:
180196

181197
def decorator(func: FC) -> FC:
182198
option_attrs = attrs.copy()
183-
option_attrs.setdefault('cls', GroupedOption)
199+
option_attrs.setdefault("cls", GroupedOption)
184200
if self._hidden:
185-
option_attrs.setdefault('hidden', self._hidden)
201+
option_attrs.setdefault("hidden", self._hidden)
186202

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+
)
189207

190208
self._check_mixing_decorators(func)
191209
func = click.option(*param_decls, group=self, **option_attrs)(func)
@@ -199,32 +217,33 @@ def decorator(func: FC) -> FC:
199217
return decorator
200218

201219
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"""
204221
return self._options.get(resolve_wrappers(ctx.command.callback), {})
205222

206223
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"""
209225
return list(reversed(list(self.get_options(ctx))))
210226

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:
212230
options = self.get_options(ctx)
213-
text = ''
231+
text = ""
214232

215233
for name, opt in reversed(list(options.items())):
216234
if option_names and name not in option_names:
217235
continue
218-
text += f' {opt.get_error_hint(ctx)}\n'
236+
text += f" {opt.get_error_hint(ctx)}\n"
219237

220238
if text:
221239
text = text[:-1]
222240

223241
return text
224242

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"""
228247

229248
def _check_mixing_decorators(self, func: Callable) -> None:
230249
func, params = get_callback_and_params(func)
@@ -243,8 +262,9 @@ def _add_title_fake_option(self, func: FC) -> None:
243262
callback, params = get_callback_and_params(func)
244263

245264
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)
248268

249269
_, params = get_callback_and_params(func)
250270
self._group_title_options[callback] = params[-1]
@@ -274,13 +294,15 @@ class RequiredAnyOptionGroup(OptionGroup):
274294

275295
@property
276296
def forbidden_option_attrs(self) -> List[str]:
277-
return ['required']
297+
return ["required"]
278298

279299
@property
280300
def name_extra(self) -> List[str]:
281-
return super().name_extra + ['required_any']
301+
return super().name_extra + ["required_any"]
282302

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:
284306
if option.name in opts:
285307
return
286308

@@ -300,7 +322,7 @@ def handle_parse_result(self, option: GroupedOption, ctx: click.Context, opts: M
300322

301323
raise click.UsageError(
302324
f"At least one of the following options from {group_name} option group is required:\n{option_info}",
303-
ctx=ctx
325+
ctx=ctx,
304326
)
305327

306328

@@ -312,13 +334,15 @@ class RequiredAllOptionGroup(OptionGroup):
312334

313335
@property
314336
def forbidden_option_attrs(self) -> List[str]:
315-
return ['required', 'hidden']
337+
return ["required", "hidden"]
316338

317339
@property
318340
def name_extra(self) -> List[str]:
319-
return super().name_extra + ['required_all']
341+
return super().name_extra + ["required_all"]
320342

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:
322346
option_names = set(self.get_options(ctx))
323347

324348
if not option_names.issubset(opts):
@@ -328,7 +352,7 @@ def handle_parse_result(self, option: GroupedOption, ctx: click.Context, opts: M
328352

329353
raise click.UsageError(
330354
f"Missing required options from {group_name} option group:\n{option_info}",
331-
ctx=ctx
355+
ctx=ctx,
332356
)
333357

334358

@@ -341,13 +365,15 @@ class MutuallyExclusiveOptionGroup(OptionGroup):
341365

342366
@property
343367
def forbidden_option_attrs(self) -> List[str]:
344-
return ['required']
368+
return ["required"]
345369

346370
@property
347371
def name_extra(self) -> List[str]:
348-
return super().name_extra + ['mutually_exclusive']
372+
return super().name_extra + ["mutually_exclusive"]
349373

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:
351377
option_names = set(self.get_options(ctx))
352378
given_option_names = option_names.intersection(opts)
353379
given_option_count = len(given_option_names)
@@ -359,7 +385,7 @@ def handle_parse_result(self, option: GroupedOption, ctx: click.Context, opts: M
359385
raise click.UsageError(
360386
f"Mutually exclusive options from {group_name} option group "
361387
f"cannot be used at the same time:\n{option_info}",
362-
ctx=ctx
388+
ctx=ctx,
363389
)
364390

365391

@@ -372,9 +398,11 @@ class RequiredMutuallyExclusiveOptionGroup(MutuallyExclusiveOptionGroup):
372398

373399
@property
374400
def name_extra(self) -> List[str]:
375-
return super().name_extra + ['required']
401+
return super().name_extra + ["required"]
376402

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:
378406
super().handle_parse_result(option, ctx, opts)
379407

380408
option_names = set(self.get_option_names(ctx))
@@ -387,7 +415,7 @@ def handle_parse_result(self, option: GroupedOption, ctx: click.Context, opts: M
387415
raise click.UsageError(
388416
"Missing one of the required mutually exclusive options from "
389417
f"{group_name} option group:\n{option_info}",
390-
ctx=ctx
418+
ctx=ctx,
391419
)
392420

393421

@@ -400,21 +428,26 @@ class AllOptionGroup(OptionGroup):
400428

401429
@property
402430
def forbidden_option_attrs(self) -> List[str]:
403-
return ['required', 'hidden']
431+
return ["required", "hidden"]
404432

405433
@property
406434
def name_extra(self) -> List[str]:
407-
return super().name_extra + ['all_or_none']
435+
return super().name_extra + ["all_or_none"]
408436

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:
410440
option_names = set(self.get_options(ctx))
411441

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+
):
413446
group_name = self._group_name_str()
414447
option_info = self.get_error_hint(ctx)
415448

416449
raise click.UsageError(
417450
f"All options from {group_name} option group should be specified or none should be specified. "
418451
f"Missing required options:\n{option_info}",
419-
ctx=ctx
452+
ctx=ctx,
420453
)

0 commit comments

Comments
 (0)