click-extended 1.0.0__py3-none-any.whl → 1.0.2__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.
Files changed (148) hide show
  1. click_extended/__init__.py +2 -0
  2. click_extended/core/__init__.py +10 -0
  3. click_extended/core/decorators/__init__.py +21 -0
  4. click_extended/core/decorators/argument.py +227 -0
  5. click_extended/core/decorators/command.py +93 -0
  6. click_extended/core/decorators/context.py +56 -0
  7. click_extended/core/decorators/env.py +155 -0
  8. click_extended/core/decorators/group.py +96 -0
  9. click_extended/core/decorators/option.py +347 -0
  10. click_extended/core/decorators/prompt.py +69 -0
  11. click_extended/core/decorators/selection.py +155 -0
  12. click_extended/core/decorators/tag.py +109 -0
  13. click_extended/core/nodes/__init__.py +21 -0
  14. click_extended/core/nodes/_root_node.py +1012 -0
  15. click_extended/core/nodes/argument_node.py +165 -0
  16. click_extended/core/nodes/child_node.py +555 -0
  17. click_extended/core/nodes/child_validation_node.py +100 -0
  18. click_extended/core/nodes/node.py +55 -0
  19. click_extended/core/nodes/option_node.py +205 -0
  20. click_extended/core/nodes/parent_node.py +220 -0
  21. click_extended/core/nodes/validation_node.py +124 -0
  22. click_extended/core/other/__init__.py +7 -0
  23. click_extended/core/other/_click_command.py +60 -0
  24. click_extended/core/other/_click_group.py +246 -0
  25. click_extended/core/other/_tree.py +491 -0
  26. click_extended/core/other/context.py +496 -0
  27. click_extended/decorators/__init__.py +29 -0
  28. click_extended/decorators/check/__init__.py +57 -0
  29. click_extended/decorators/check/conflicts.py +149 -0
  30. click_extended/decorators/check/contains.py +69 -0
  31. click_extended/decorators/check/dependencies.py +115 -0
  32. click_extended/decorators/check/divisible_by.py +48 -0
  33. click_extended/decorators/check/ends_with.py +85 -0
  34. click_extended/decorators/check/exclusive.py +75 -0
  35. click_extended/decorators/check/falsy.py +37 -0
  36. click_extended/decorators/check/is_email.py +43 -0
  37. click_extended/decorators/check/is_hex_color.py +41 -0
  38. click_extended/decorators/check/is_hostname.py +47 -0
  39. click_extended/decorators/check/is_ipv4.py +46 -0
  40. click_extended/decorators/check/is_ipv6.py +46 -0
  41. click_extended/decorators/check/is_json.py +40 -0
  42. click_extended/decorators/check/is_mac_address.py +40 -0
  43. click_extended/decorators/check/is_negative.py +37 -0
  44. click_extended/decorators/check/is_non_zero.py +37 -0
  45. click_extended/decorators/check/is_port.py +39 -0
  46. click_extended/decorators/check/is_positive.py +37 -0
  47. click_extended/decorators/check/is_url.py +75 -0
  48. click_extended/decorators/check/is_uuid.py +40 -0
  49. click_extended/decorators/check/length.py +68 -0
  50. click_extended/decorators/check/not_empty.py +49 -0
  51. click_extended/decorators/check/regex.py +47 -0
  52. click_extended/decorators/check/requires.py +190 -0
  53. click_extended/decorators/check/starts_with.py +87 -0
  54. click_extended/decorators/check/truthy.py +37 -0
  55. click_extended/decorators/compare/__init__.py +15 -0
  56. click_extended/decorators/compare/at_least.py +57 -0
  57. click_extended/decorators/compare/at_most.py +57 -0
  58. click_extended/decorators/compare/between.py +119 -0
  59. click_extended/decorators/compare/greater_than.py +183 -0
  60. click_extended/decorators/compare/less_than.py +183 -0
  61. click_extended/decorators/convert/__init__.py +31 -0
  62. click_extended/decorators/convert/convert_angle.py +94 -0
  63. click_extended/decorators/convert/convert_area.py +123 -0
  64. click_extended/decorators/convert/convert_bits.py +211 -0
  65. click_extended/decorators/convert/convert_distance.py +154 -0
  66. click_extended/decorators/convert/convert_energy.py +155 -0
  67. click_extended/decorators/convert/convert_power.py +128 -0
  68. click_extended/decorators/convert/convert_pressure.py +131 -0
  69. click_extended/decorators/convert/convert_speed.py +122 -0
  70. click_extended/decorators/convert/convert_temperature.py +89 -0
  71. click_extended/decorators/convert/convert_time.py +108 -0
  72. click_extended/decorators/convert/convert_volume.py +218 -0
  73. click_extended/decorators/convert/convert_weight.py +158 -0
  74. click_extended/decorators/load/__init__.py +13 -0
  75. click_extended/decorators/load/load_csv.py +117 -0
  76. click_extended/decorators/load/load_json.py +61 -0
  77. click_extended/decorators/load/load_toml.py +47 -0
  78. click_extended/decorators/load/load_yaml.py +72 -0
  79. click_extended/decorators/math/__init__.py +37 -0
  80. click_extended/decorators/math/absolute.py +35 -0
  81. click_extended/decorators/math/add.py +48 -0
  82. click_extended/decorators/math/ceil.py +36 -0
  83. click_extended/decorators/math/clamp.py +51 -0
  84. click_extended/decorators/math/divide.py +42 -0
  85. click_extended/decorators/math/floor.py +36 -0
  86. click_extended/decorators/math/maximum.py +39 -0
  87. click_extended/decorators/math/minimum.py +39 -0
  88. click_extended/decorators/math/modulo.py +39 -0
  89. click_extended/decorators/math/multiply.py +51 -0
  90. click_extended/decorators/math/normalize.py +76 -0
  91. click_extended/decorators/math/power.py +39 -0
  92. click_extended/decorators/math/rounded.py +39 -0
  93. click_extended/decorators/math/sqrt.py +39 -0
  94. click_extended/decorators/math/subtract.py +39 -0
  95. click_extended/decorators/math/to_percent.py +63 -0
  96. click_extended/decorators/misc/__init__.py +17 -0
  97. click_extended/decorators/misc/choice.py +139 -0
  98. click_extended/decorators/misc/confirm_if.py +147 -0
  99. click_extended/decorators/misc/default.py +95 -0
  100. click_extended/decorators/misc/deprecated.py +131 -0
  101. click_extended/decorators/misc/experimental.py +79 -0
  102. click_extended/decorators/misc/now.py +42 -0
  103. click_extended/decorators/random/__init__.py +21 -0
  104. click_extended/decorators/random/random_bool.py +49 -0
  105. click_extended/decorators/random/random_choice.py +63 -0
  106. click_extended/decorators/random/random_datetime.py +140 -0
  107. click_extended/decorators/random/random_float.py +62 -0
  108. click_extended/decorators/random/random_integer.py +56 -0
  109. click_extended/decorators/random/random_prime.py +196 -0
  110. click_extended/decorators/random/random_string.py +77 -0
  111. click_extended/decorators/random/random_uuid.py +119 -0
  112. click_extended/decorators/transform/__init__.py +71 -0
  113. click_extended/decorators/transform/add_prefix.py +58 -0
  114. click_extended/decorators/transform/add_suffix.py +58 -0
  115. click_extended/decorators/transform/apply.py +35 -0
  116. click_extended/decorators/transform/basename.py +44 -0
  117. click_extended/decorators/transform/dirname.py +44 -0
  118. click_extended/decorators/transform/expand_vars.py +36 -0
  119. click_extended/decorators/transform/remove_prefix.py +57 -0
  120. click_extended/decorators/transform/remove_suffix.py +57 -0
  121. click_extended/decorators/transform/replace.py +46 -0
  122. click_extended/decorators/transform/slugify.py +45 -0
  123. click_extended/decorators/transform/split.py +43 -0
  124. click_extended/decorators/transform/strip.py +148 -0
  125. click_extended/decorators/transform/to_case.py +216 -0
  126. click_extended/decorators/transform/to_date.py +75 -0
  127. click_extended/decorators/transform/to_datetime.py +83 -0
  128. click_extended/decorators/transform/to_path.py +274 -0
  129. click_extended/decorators/transform/to_time.py +77 -0
  130. click_extended/decorators/transform/to_timestamp.py +114 -0
  131. click_extended/decorators/transform/truncate.py +47 -0
  132. click_extended/utils/__init__.py +13 -0
  133. click_extended/utils/casing.py +169 -0
  134. click_extended/utils/checks.py +48 -0
  135. click_extended/utils/dispatch.py +1016 -0
  136. click_extended/utils/format.py +101 -0
  137. click_extended/utils/humanize.py +209 -0
  138. click_extended/utils/naming.py +238 -0
  139. click_extended/utils/process.py +294 -0
  140. click_extended/utils/selection.py +267 -0
  141. click_extended/utils/time.py +46 -0
  142. {click_extended-1.0.0.dist-info → click_extended-1.0.2.dist-info}/METADATA +2 -1
  143. click_extended-1.0.2.dist-info/RECORD +150 -0
  144. click_extended-1.0.0.dist-info/RECORD +0 -10
  145. {click_extended-1.0.0.dist-info → click_extended-1.0.2.dist-info}/WHEEL +0 -0
  146. {click_extended-1.0.0.dist-info → click_extended-1.0.2.dist-info}/licenses/AUTHORS.md +0 -0
  147. {click_extended-1.0.0.dist-info → click_extended-1.0.2.dist-info}/licenses/LICENSE +0 -0
  148. {click_extended-1.0.0.dist-info → click_extended-1.0.2.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,7 @@
1
+ """Initialization file for the `click_extended.core.other` module."""
2
+
3
+ from click_extended.core.other.context import Context
4
+
5
+ __all__ = [
6
+ "Context",
7
+ ]
@@ -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