click-extended 0.4.0__py3-none-any.whl → 1.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- click_extended/__init__.py +10 -6
- click_extended/classes.py +12 -8
- click_extended/core/__init__.py +10 -0
- click_extended/core/decorators/__init__.py +21 -0
- click_extended/core/decorators/argument.py +227 -0
- click_extended/core/decorators/command.py +93 -0
- click_extended/core/decorators/env.py +155 -0
- click_extended/core/decorators/group.py +96 -0
- click_extended/core/decorators/option.py +347 -0
- click_extended/core/decorators/prompt.py +69 -0
- click_extended/core/decorators/selection.py +155 -0
- click_extended/core/decorators/tag.py +109 -0
- click_extended/core/nodes/__init__.py +21 -0
- click_extended/core/nodes/_root_node.py +1012 -0
- click_extended/core/nodes/argument_node.py +165 -0
- click_extended/core/nodes/child_node.py +555 -0
- click_extended/core/nodes/child_validation_node.py +100 -0
- click_extended/core/nodes/node.py +55 -0
- click_extended/core/nodes/option_node.py +205 -0
- click_extended/core/nodes/parent_node.py +220 -0
- click_extended/core/nodes/validation_node.py +124 -0
- click_extended/core/other/__init__.py +7 -0
- click_extended/core/other/_click_command.py +60 -0
- click_extended/core/other/_click_group.py +246 -0
- click_extended/core/other/_tree.py +491 -0
- click_extended/core/other/context.py +496 -0
- click_extended/decorators/__init__.py +29 -0
- click_extended/decorators/check/__init__.py +57 -0
- click_extended/decorators/check/conflicts.py +149 -0
- click_extended/decorators/check/contains.py +69 -0
- click_extended/decorators/check/dependencies.py +115 -0
- click_extended/decorators/check/divisible_by.py +48 -0
- click_extended/decorators/check/ends_with.py +85 -0
- click_extended/decorators/check/exclusive.py +75 -0
- click_extended/decorators/check/falsy.py +37 -0
- click_extended/decorators/check/is_email.py +43 -0
- click_extended/decorators/check/is_hex_color.py +41 -0
- click_extended/decorators/check/is_hostname.py +47 -0
- click_extended/decorators/check/is_ipv4.py +46 -0
- click_extended/decorators/check/is_ipv6.py +46 -0
- click_extended/decorators/check/is_json.py +40 -0
- click_extended/decorators/check/is_mac_address.py +40 -0
- click_extended/decorators/check/is_negative.py +37 -0
- click_extended/decorators/check/is_non_zero.py +37 -0
- click_extended/decorators/check/is_port.py +39 -0
- click_extended/decorators/check/is_positive.py +37 -0
- click_extended/decorators/check/is_url.py +75 -0
- click_extended/decorators/check/is_uuid.py +40 -0
- click_extended/decorators/check/length.py +68 -0
- click_extended/decorators/check/not_empty.py +49 -0
- click_extended/decorators/check/regex.py +47 -0
- click_extended/decorators/check/requires.py +190 -0
- click_extended/decorators/check/starts_with.py +87 -0
- click_extended/decorators/check/truthy.py +37 -0
- click_extended/decorators/compare/__init__.py +15 -0
- click_extended/decorators/compare/at_least.py +57 -0
- click_extended/decorators/compare/at_most.py +57 -0
- click_extended/decorators/compare/between.py +119 -0
- click_extended/decorators/compare/greater_than.py +183 -0
- click_extended/decorators/compare/less_than.py +183 -0
- click_extended/decorators/convert/__init__.py +31 -0
- click_extended/decorators/convert/convert_angle.py +94 -0
- click_extended/decorators/convert/convert_area.py +123 -0
- click_extended/decorators/convert/convert_bits.py +211 -0
- click_extended/decorators/convert/convert_distance.py +154 -0
- click_extended/decorators/convert/convert_energy.py +155 -0
- click_extended/decorators/convert/convert_power.py +128 -0
- click_extended/decorators/convert/convert_pressure.py +131 -0
- click_extended/decorators/convert/convert_speed.py +122 -0
- click_extended/decorators/convert/convert_temperature.py +89 -0
- click_extended/decorators/convert/convert_time.py +108 -0
- click_extended/decorators/convert/convert_volume.py +218 -0
- click_extended/decorators/convert/convert_weight.py +158 -0
- click_extended/decorators/load/__init__.py +13 -0
- click_extended/decorators/load/load_csv.py +117 -0
- click_extended/decorators/load/load_json.py +61 -0
- click_extended/decorators/load/load_toml.py +47 -0
- click_extended/decorators/load/load_yaml.py +72 -0
- click_extended/decorators/math/__init__.py +37 -0
- click_extended/decorators/math/absolute.py +35 -0
- click_extended/decorators/math/add.py +48 -0
- click_extended/decorators/math/ceil.py +36 -0
- click_extended/decorators/math/clamp.py +51 -0
- click_extended/decorators/math/divide.py +42 -0
- click_extended/decorators/math/floor.py +36 -0
- click_extended/decorators/math/maximum.py +39 -0
- click_extended/decorators/math/minimum.py +39 -0
- click_extended/decorators/math/modulo.py +39 -0
- click_extended/decorators/math/multiply.py +51 -0
- click_extended/decorators/math/normalize.py +76 -0
- click_extended/decorators/math/power.py +39 -0
- click_extended/decorators/math/rounded.py +39 -0
- click_extended/decorators/math/sqrt.py +39 -0
- click_extended/decorators/math/subtract.py +39 -0
- click_extended/decorators/math/to_percent.py +63 -0
- click_extended/decorators/misc/__init__.py +17 -0
- click_extended/decorators/misc/choice.py +139 -0
- click_extended/decorators/misc/confirm_if.py +147 -0
- click_extended/decorators/misc/default.py +95 -0
- click_extended/decorators/misc/deprecated.py +131 -0
- click_extended/decorators/misc/experimental.py +79 -0
- click_extended/decorators/misc/now.py +42 -0
- click_extended/decorators/random/__init__.py +21 -0
- click_extended/decorators/random/random_bool.py +49 -0
- click_extended/decorators/random/random_choice.py +63 -0
- click_extended/decorators/random/random_datetime.py +140 -0
- click_extended/decorators/random/random_float.py +62 -0
- click_extended/decorators/random/random_integer.py +56 -0
- click_extended/decorators/random/random_prime.py +196 -0
- click_extended/decorators/random/random_string.py +77 -0
- click_extended/decorators/random/random_uuid.py +119 -0
- click_extended/decorators/transform/__init__.py +71 -0
- click_extended/decorators/transform/add_prefix.py +58 -0
- click_extended/decorators/transform/add_suffix.py +58 -0
- click_extended/decorators/transform/apply.py +35 -0
- click_extended/decorators/transform/basename.py +44 -0
- click_extended/decorators/transform/dirname.py +44 -0
- click_extended/decorators/transform/expand_vars.py +36 -0
- click_extended/decorators/transform/remove_prefix.py +57 -0
- click_extended/decorators/transform/remove_suffix.py +57 -0
- click_extended/decorators/transform/replace.py +46 -0
- click_extended/decorators/transform/slugify.py +45 -0
- click_extended/decorators/transform/split.py +43 -0
- click_extended/decorators/transform/strip.py +148 -0
- click_extended/decorators/transform/to_case.py +216 -0
- click_extended/decorators/transform/to_date.py +75 -0
- click_extended/decorators/transform/to_datetime.py +83 -0
- click_extended/decorators/transform/to_path.py +274 -0
- click_extended/decorators/transform/to_time.py +77 -0
- click_extended/decorators/transform/to_timestamp.py +114 -0
- click_extended/decorators/transform/truncate.py +47 -0
- click_extended/types.py +1 -1
- click_extended/utils/__init__.py +13 -0
- click_extended/utils/casing.py +169 -0
- click_extended/utils/checks.py +48 -0
- click_extended/utils/dispatch.py +1016 -0
- click_extended/utils/format.py +101 -0
- click_extended/utils/humanize.py +209 -0
- click_extended/utils/naming.py +238 -0
- click_extended/utils/process.py +294 -0
- click_extended/utils/selection.py +267 -0
- click_extended/utils/time.py +46 -0
- {click_extended-0.4.0.dist-info → click_extended-1.0.1.dist-info}/METADATA +100 -29
- click_extended-1.0.1.dist-info/RECORD +149 -0
- click_extended-0.4.0.dist-info/RECORD +0 -10
- {click_extended-0.4.0.dist-info → click_extended-1.0.1.dist-info}/WHEEL +0 -0
- {click_extended-0.4.0.dist-info → click_extended-1.0.1.dist-info}/licenses/AUTHORS.md +0 -0
- {click_extended-0.4.0.dist-info → click_extended-1.0.1.dist-info}/licenses/LICENSE +0 -0
- {click_extended-0.4.0.dist-info → click_extended-1.0.1.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"""Click Command class for integration with RootNode."""
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING, Any
|
|
4
|
+
|
|
5
|
+
import click
|
|
6
|
+
|
|
7
|
+
if TYPE_CHECKING:
|
|
8
|
+
from click_extended.core.nodes._root_node import RootNode
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class ClickCommand(click.Command):
|
|
12
|
+
"""
|
|
13
|
+
A Click Command that integrates with the `RootNode`.
|
|
14
|
+
|
|
15
|
+
This is a regular `click.Command` that works everywhere Click works,
|
|
16
|
+
with built-in support for aliasing and `click-extended` features.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
def __init__(
|
|
20
|
+
self, *args: Any, root_instance: "RootNode | None" = None, **kwargs: Any
|
|
21
|
+
) -> None:
|
|
22
|
+
"""
|
|
23
|
+
Initialize a new `ClickCommand` instance..
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
*args (Any):
|
|
27
|
+
Positional arguments for `click.Command`
|
|
28
|
+
root_instance (RootNode, optional):
|
|
29
|
+
The RootNode instance that manages this command
|
|
30
|
+
**kwargs (Any):
|
|
31
|
+
Keyword arguments for `click.Command`
|
|
32
|
+
"""
|
|
33
|
+
if root_instance is None:
|
|
34
|
+
raise ValueError("root_instance is required for ClickCommand")
|
|
35
|
+
|
|
36
|
+
self.root = root_instance
|
|
37
|
+
self.aliases = root_instance.aliases
|
|
38
|
+
|
|
39
|
+
kwargs.pop("aliases", None)
|
|
40
|
+
super().__init__(*args, **kwargs)
|
|
41
|
+
|
|
42
|
+
def format_help(
|
|
43
|
+
self, ctx: click.Context, formatter: click.HelpFormatter
|
|
44
|
+
) -> None:
|
|
45
|
+
"""
|
|
46
|
+
Format help text with aliases.
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
ctx (click.Context):
|
|
50
|
+
The Click context.
|
|
51
|
+
formatter (click.HelpFormatter):
|
|
52
|
+
The Click help formatter instance.
|
|
53
|
+
"""
|
|
54
|
+
original_name = self.name
|
|
55
|
+
|
|
56
|
+
if self.aliases:
|
|
57
|
+
self.name = self.root.format_name_with_aliases()
|
|
58
|
+
|
|
59
|
+
super().format_help(ctx, formatter)
|
|
60
|
+
self.name = original_name
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
"""Click Group class for integration with RootNode."""
|
|
2
|
+
|
|
3
|
+
# pylint: disable=cyclic-import
|
|
4
|
+
# pylint: disable=redefined-builtin
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
from typing import TYPE_CHECKING, Any, Callable
|
|
9
|
+
|
|
10
|
+
import click
|
|
11
|
+
|
|
12
|
+
if TYPE_CHECKING:
|
|
13
|
+
from click_extended.core.nodes._root_node import RootNode
|
|
14
|
+
from click_extended.core.other._click_command import ClickCommand
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class ClickGroup(click.Group):
|
|
18
|
+
"""
|
|
19
|
+
A Click Group that integrates with the `RootNode`.
|
|
20
|
+
|
|
21
|
+
This is a regular Click group that works everywhere Click works,
|
|
22
|
+
with built-in support for aliasing, `click-extended` features,
|
|
23
|
+
and convenience methods for building command hierarchies.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
def __init__(
|
|
27
|
+
self, *args: Any, root_instance: "RootNode | None" = None, **kwargs: Any
|
|
28
|
+
) -> None:
|
|
29
|
+
"""
|
|
30
|
+
Initialize a new `ClickGroup` instance.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
*args (Any):
|
|
34
|
+
Positional arguments for `click.Group`
|
|
35
|
+
root_instance (RootNode):
|
|
36
|
+
The `RootNode` instance that manages this group
|
|
37
|
+
**kwargs (Any):
|
|
38
|
+
Keyword arguments for `click.Group`
|
|
39
|
+
"""
|
|
40
|
+
if root_instance is None:
|
|
41
|
+
raise ValueError("root_instance is required for ClickGroup")
|
|
42
|
+
|
|
43
|
+
self.root = root_instance
|
|
44
|
+
self.aliases = root_instance.aliases
|
|
45
|
+
|
|
46
|
+
kwargs.pop("aliases", None)
|
|
47
|
+
super().__init__(*args, **kwargs)
|
|
48
|
+
|
|
49
|
+
def format_help( # type: ignore[override]
|
|
50
|
+
self, ctx: click.Context, formatter: click.HelpFormatter
|
|
51
|
+
) -> None:
|
|
52
|
+
"""
|
|
53
|
+
Format help text with aliases.
|
|
54
|
+
|
|
55
|
+
Args:
|
|
56
|
+
ctx (click.Context):
|
|
57
|
+
The Click context.
|
|
58
|
+
formatter (click.HelpFormatter):
|
|
59
|
+
The Click help message formatter instance.
|
|
60
|
+
"""
|
|
61
|
+
original_name = self.name
|
|
62
|
+
|
|
63
|
+
if self.aliases:
|
|
64
|
+
self.name = self.root.format_name_with_aliases()
|
|
65
|
+
|
|
66
|
+
super().format_help(ctx, formatter)
|
|
67
|
+
self.name = original_name
|
|
68
|
+
|
|
69
|
+
def add_command(self, cmd: click.Command, name: str | None = None) -> None:
|
|
70
|
+
"""
|
|
71
|
+
Add a command to the group, including its aliases.
|
|
72
|
+
|
|
73
|
+
Args:
|
|
74
|
+
cmd (click.Command):
|
|
75
|
+
The command to add to the group.
|
|
76
|
+
name (str, optional):
|
|
77
|
+
The name to use for the command.
|
|
78
|
+
"""
|
|
79
|
+
super().add_command(cmd, name)
|
|
80
|
+
|
|
81
|
+
aliases = getattr(cmd, "aliases", None)
|
|
82
|
+
if aliases is not None:
|
|
83
|
+
aliases_list = [aliases] if isinstance(aliases, str) else aliases
|
|
84
|
+
for alias in aliases_list:
|
|
85
|
+
if alias:
|
|
86
|
+
super().add_command(cmd, alias)
|
|
87
|
+
|
|
88
|
+
def format_commands( # type: ignore[override]
|
|
89
|
+
self, _ctx: click.Context, formatter: click.HelpFormatter
|
|
90
|
+
) -> None:
|
|
91
|
+
"""
|
|
92
|
+
Format the command list for display in help text.
|
|
93
|
+
|
|
94
|
+
Args:
|
|
95
|
+
_ctx (click.Context):
|
|
96
|
+
The Click context containing command information.
|
|
97
|
+
formatter (click.HelpFormatter):
|
|
98
|
+
The formatter to write command information to.
|
|
99
|
+
"""
|
|
100
|
+
commands: dict[str, click.Command] = {}
|
|
101
|
+
for name, cmd in self.commands.items():
|
|
102
|
+
if name == cmd.name:
|
|
103
|
+
aliases = getattr(cmd, "aliases", None)
|
|
104
|
+
display_name = name
|
|
105
|
+
|
|
106
|
+
if aliases is not None:
|
|
107
|
+
aliases_list = (
|
|
108
|
+
[aliases] if isinstance(aliases, str) else aliases
|
|
109
|
+
)
|
|
110
|
+
valid_aliases = [a for a in aliases_list if a]
|
|
111
|
+
if valid_aliases:
|
|
112
|
+
display_name = f"{name} ({', '.join(valid_aliases)})"
|
|
113
|
+
|
|
114
|
+
commands[display_name] = cmd
|
|
115
|
+
|
|
116
|
+
rows: list[tuple[str, str]] = [
|
|
117
|
+
(name, cmd.get_short_help_str()) for name, cmd in commands.items()
|
|
118
|
+
]
|
|
119
|
+
|
|
120
|
+
if rows:
|
|
121
|
+
with formatter.section("Commands"):
|
|
122
|
+
formatter.write_dl(rows)
|
|
123
|
+
|
|
124
|
+
def add(self, cmd: click.Command | click.Group) -> "ClickGroup":
|
|
125
|
+
"""
|
|
126
|
+
A method to add a command or group and return self for chaining.
|
|
127
|
+
|
|
128
|
+
Args:
|
|
129
|
+
cmd (click.Command | click.Group):
|
|
130
|
+
The command or group to add.
|
|
131
|
+
|
|
132
|
+
Returns:
|
|
133
|
+
Self:
|
|
134
|
+
The instance to allow chaining.
|
|
135
|
+
|
|
136
|
+
Example:
|
|
137
|
+
```python
|
|
138
|
+
@group()
|
|
139
|
+
def cli():
|
|
140
|
+
pass
|
|
141
|
+
|
|
142
|
+
@command()
|
|
143
|
+
def cmd1():
|
|
144
|
+
pass
|
|
145
|
+
|
|
146
|
+
@group()
|
|
147
|
+
def subgroup():
|
|
148
|
+
pass
|
|
149
|
+
|
|
150
|
+
cli.add(cmd1).add(subgroup)
|
|
151
|
+
```
|
|
152
|
+
"""
|
|
153
|
+
self.add_command(cmd)
|
|
154
|
+
return self
|
|
155
|
+
|
|
156
|
+
def command( # type: ignore[override]
|
|
157
|
+
self,
|
|
158
|
+
name: str | None = None,
|
|
159
|
+
*,
|
|
160
|
+
aliases: str | list[str] | None = None,
|
|
161
|
+
help: str | None = None,
|
|
162
|
+
**kwargs: Any,
|
|
163
|
+
) -> Callable[[Callable[..., Any]], ClickCommand]:
|
|
164
|
+
"""
|
|
165
|
+
A decorator to create and add a child command.
|
|
166
|
+
|
|
167
|
+
Args:
|
|
168
|
+
name (str, optional):
|
|
169
|
+
The name of the child command.
|
|
170
|
+
aliases (str | list[str], optional):
|
|
171
|
+
Alternative name(s) for the command. Can be a single
|
|
172
|
+
string or a list of strings.
|
|
173
|
+
help (str, optional):
|
|
174
|
+
The help message for the command. If not provided,
|
|
175
|
+
uses the first line of the function's docstring.
|
|
176
|
+
**kwargs (Any):
|
|
177
|
+
Additional arguments for the command.
|
|
178
|
+
|
|
179
|
+
Returns:
|
|
180
|
+
Decorator:
|
|
181
|
+
A decorator function.
|
|
182
|
+
"""
|
|
183
|
+
if aliases is not None:
|
|
184
|
+
kwargs["aliases"] = aliases
|
|
185
|
+
if help is not None:
|
|
186
|
+
kwargs["help"] = help
|
|
187
|
+
|
|
188
|
+
def decorator(func: Callable[..., Any]) -> ClickCommand:
|
|
189
|
+
from click_extended.core.decorators.command import Command
|
|
190
|
+
|
|
191
|
+
if help is None and func.__doc__:
|
|
192
|
+
first_line = func.__doc__.strip().split("\n")[0].strip()
|
|
193
|
+
if first_line:
|
|
194
|
+
kwargs["help"] = first_line
|
|
195
|
+
|
|
196
|
+
cmd = Command.as_decorator(name, **kwargs)(func)
|
|
197
|
+
self.add_command(cmd)
|
|
198
|
+
return cmd
|
|
199
|
+
|
|
200
|
+
return decorator
|
|
201
|
+
|
|
202
|
+
def group( # type: ignore[override]
|
|
203
|
+
self,
|
|
204
|
+
name: str | None = None,
|
|
205
|
+
*,
|
|
206
|
+
aliases: str | list[str] | None = None,
|
|
207
|
+
help: str | None = None,
|
|
208
|
+
**kwargs: Any,
|
|
209
|
+
) -> Callable[[Callable[..., Any]], ClickGroup]:
|
|
210
|
+
"""
|
|
211
|
+
A decorator to create and add a child group.
|
|
212
|
+
|
|
213
|
+
Args:
|
|
214
|
+
name (str, optional):
|
|
215
|
+
The name of the child group.
|
|
216
|
+
aliases (str | list[str], optional):
|
|
217
|
+
Alternative name(s) for the group. Can be a single
|
|
218
|
+
string or a list of strings.
|
|
219
|
+
help (str, optional):
|
|
220
|
+
The help message for the group. If not provided,
|
|
221
|
+
uses the first line of the function's docstring.
|
|
222
|
+
**kwargs (Any):
|
|
223
|
+
Additional arguments for the group
|
|
224
|
+
|
|
225
|
+
Returns:
|
|
226
|
+
Decorator:
|
|
227
|
+
A decorator function
|
|
228
|
+
"""
|
|
229
|
+
if aliases is not None:
|
|
230
|
+
kwargs["aliases"] = aliases
|
|
231
|
+
if help is not None:
|
|
232
|
+
kwargs["help"] = help
|
|
233
|
+
|
|
234
|
+
def decorator(func: Callable[..., Any]) -> ClickGroup:
|
|
235
|
+
from click_extended.core.decorators.group import Group
|
|
236
|
+
|
|
237
|
+
if help is None and func.__doc__:
|
|
238
|
+
first_line = func.__doc__.strip().split("\n")[0].strip()
|
|
239
|
+
if first_line:
|
|
240
|
+
kwargs["help"] = first_line
|
|
241
|
+
|
|
242
|
+
grp = Group.as_decorator(name, **kwargs)(func)
|
|
243
|
+
self.add_command(grp)
|
|
244
|
+
return grp
|
|
245
|
+
|
|
246
|
+
return decorator
|