click-extended 1.0.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/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/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-1.0.0.dist-info → click_extended-1.0.1.dist-info}/METADATA +2 -1
- click_extended-1.0.1.dist-info/RECORD +149 -0
- click_extended-1.0.0.dist-info/RECORD +0 -10
- {click_extended-1.0.0.dist-info → click_extended-1.0.1.dist-info}/WHEEL +0 -0
- {click_extended-1.0.0.dist-info → click_extended-1.0.1.dist-info}/licenses/AUTHORS.md +0 -0
- {click_extended-1.0.0.dist-info → click_extended-1.0.1.dist-info}/licenses/LICENSE +0 -0
- {click_extended-1.0.0.dist-info → click_extended-1.0.1.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
"""ArgumentNode abstract base class for CLI argument nodes."""
|
|
2
|
+
|
|
3
|
+
# pylint: disable=too-many-arguments
|
|
4
|
+
# pylint: disable=too-many-positional-arguments
|
|
5
|
+
# pylint: disable=redefined-builtin
|
|
6
|
+
# pylint: disable=arguments-differ
|
|
7
|
+
# pylint: disable=line-too-long
|
|
8
|
+
|
|
9
|
+
from abc import ABC, abstractmethod
|
|
10
|
+
from typing import TYPE_CHECKING, Any, Callable, ParamSpec, Type, TypeVar
|
|
11
|
+
|
|
12
|
+
from click_extended.core.nodes.parent_node import ParentNode
|
|
13
|
+
|
|
14
|
+
if TYPE_CHECKING:
|
|
15
|
+
from click_extended.core.other.context import Context
|
|
16
|
+
|
|
17
|
+
P = ParamSpec("P")
|
|
18
|
+
T = TypeVar("T")
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class ArgumentNode(ParentNode, ABC):
|
|
22
|
+
"""
|
|
23
|
+
Abstract base class for nodes that receive CLI argument values.
|
|
24
|
+
|
|
25
|
+
ArgumentNode extends ParentNode to handle command-line arguments.
|
|
26
|
+
The key difference is that the `load()` method receives a `value`
|
|
27
|
+
parameter containing the parsed CLI argument value.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
def __init__(
|
|
31
|
+
self,
|
|
32
|
+
name: str,
|
|
33
|
+
param: str | None = None,
|
|
34
|
+
nargs: int = 1,
|
|
35
|
+
type: Type[Any] | None = None,
|
|
36
|
+
help: str | None = None,
|
|
37
|
+
required: bool = True,
|
|
38
|
+
default: Any = None,
|
|
39
|
+
tags: str | list[str] | None = None,
|
|
40
|
+
**kwargs: Any,
|
|
41
|
+
):
|
|
42
|
+
"""
|
|
43
|
+
Initialize a new `ArgumentNode` instance.
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
name (str):
|
|
47
|
+
The argument name (parameter name for injection).
|
|
48
|
+
param (str, optional):
|
|
49
|
+
Custom parameter name for the function.
|
|
50
|
+
If not provided, uses `name`.
|
|
51
|
+
nargs (int):
|
|
52
|
+
Number of arguments to accept. Use `-1` for unlimited.
|
|
53
|
+
Defaults to `1`.
|
|
54
|
+
type (Type[Any], optional):
|
|
55
|
+
The type to convert the value to (`int`, `str`, `float`, `bool`).
|
|
56
|
+
help (str, optional):
|
|
57
|
+
Help text for this argument.
|
|
58
|
+
required (bool):
|
|
59
|
+
Whether this argument is required. Defaults to `True`.
|
|
60
|
+
default (Any):
|
|
61
|
+
Default value if not provided. Defaults to `None`.
|
|
62
|
+
tags (str | list[str], optional):
|
|
63
|
+
Tag(s) to associate with this argument for grouping.
|
|
64
|
+
**kwargs (Any):
|
|
65
|
+
Additional keyword arguments passed to parent class.
|
|
66
|
+
"""
|
|
67
|
+
super().__init__(
|
|
68
|
+
name=param if param is not None else name,
|
|
69
|
+
param=param,
|
|
70
|
+
help=help,
|
|
71
|
+
required=required,
|
|
72
|
+
default=default,
|
|
73
|
+
tags=tags,
|
|
74
|
+
**kwargs,
|
|
75
|
+
)
|
|
76
|
+
self.nargs = nargs
|
|
77
|
+
self.type = type
|
|
78
|
+
|
|
79
|
+
@abstractmethod
|
|
80
|
+
def load(
|
|
81
|
+
self,
|
|
82
|
+
value: str | int | float | bool | None,
|
|
83
|
+
context: "Context",
|
|
84
|
+
*args: Any,
|
|
85
|
+
**kwargs: Any,
|
|
86
|
+
) -> Any:
|
|
87
|
+
"""
|
|
88
|
+
Load and process the CLI argument value.
|
|
89
|
+
|
|
90
|
+
This method is called with the parsed CLI argument value and should
|
|
91
|
+
return the processed value to be injected into the function.
|
|
92
|
+
|
|
93
|
+
Args:
|
|
94
|
+
value (str | int | float | bool | None):
|
|
95
|
+
The parsed CLI argument value from Click.
|
|
96
|
+
context (Context):
|
|
97
|
+
The current context instance.
|
|
98
|
+
*args (Any):
|
|
99
|
+
Optional positional arguments.
|
|
100
|
+
**kwargs (Any):
|
|
101
|
+
Optional keyword arguments.
|
|
102
|
+
|
|
103
|
+
Returns:
|
|
104
|
+
Any:
|
|
105
|
+
The processed value to inject into the function.
|
|
106
|
+
"""
|
|
107
|
+
raise NotImplementedError
|
|
108
|
+
|
|
109
|
+
@classmethod
|
|
110
|
+
def as_decorator(
|
|
111
|
+
cls,
|
|
112
|
+
*,
|
|
113
|
+
name: str,
|
|
114
|
+
param: str | None = None,
|
|
115
|
+
nargs: int = 1,
|
|
116
|
+
type: Type[Any] | None = None,
|
|
117
|
+
help: str | None = None,
|
|
118
|
+
required: bool = True,
|
|
119
|
+
default: Any = None,
|
|
120
|
+
tags: str | list[str] | None = None,
|
|
121
|
+
**kwargs: Any,
|
|
122
|
+
) -> Callable[[Callable[P, T]], Callable[P, T]]:
|
|
123
|
+
"""
|
|
124
|
+
Return a decorator representation of the argument node.
|
|
125
|
+
|
|
126
|
+
Args:
|
|
127
|
+
name (str):
|
|
128
|
+
The argument name (parameter name for injection).
|
|
129
|
+
param (str, optional):
|
|
130
|
+
Custom parameter name for the function.
|
|
131
|
+
If not provided, uses `name`.
|
|
132
|
+
nargs (int):
|
|
133
|
+
Number of arguments to accept. Use `-1` for unlimited.
|
|
134
|
+
Defaults to `1`.
|
|
135
|
+
type (Type[Any], optional):
|
|
136
|
+
The type to convert the value to (`int`, `str`, `float`, `bool`).
|
|
137
|
+
help (str, optional):
|
|
138
|
+
Help text for this argument.
|
|
139
|
+
required (bool):
|
|
140
|
+
Whether this argument is required. Defaults to `True`.
|
|
141
|
+
default (Any):
|
|
142
|
+
Default value if not provided. Defaults to `None`.
|
|
143
|
+
tags (str | list[str], optional):
|
|
144
|
+
Tag(s) to associate with this argument for grouping.
|
|
145
|
+
**kwargs (Any):
|
|
146
|
+
Additional keyword arguments passed to __init__ and load().
|
|
147
|
+
|
|
148
|
+
Returns:
|
|
149
|
+
Callable:
|
|
150
|
+
A decorator function that registers the argument node.
|
|
151
|
+
"""
|
|
152
|
+
return super().as_decorator(
|
|
153
|
+
name=name,
|
|
154
|
+
param=param,
|
|
155
|
+
nargs=nargs,
|
|
156
|
+
type=type,
|
|
157
|
+
help=help,
|
|
158
|
+
required=required,
|
|
159
|
+
default=default,
|
|
160
|
+
tags=tags,
|
|
161
|
+
**kwargs,
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
__all__ = ["ArgumentNode"]
|
|
@@ -0,0 +1,555 @@
|
|
|
1
|
+
"""ChildNode class for handler-based value processing."""
|
|
2
|
+
|
|
3
|
+
# pylint: disable=too-many-public-methods
|
|
4
|
+
|
|
5
|
+
from abc import ABC
|
|
6
|
+
from datetime import date, datetime, time
|
|
7
|
+
from decimal import Decimal
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import TYPE_CHECKING, Any, Callable
|
|
10
|
+
from uuid import UUID
|
|
11
|
+
|
|
12
|
+
from click_extended.core.nodes.node import Node
|
|
13
|
+
from click_extended.core.other._tree import Tree
|
|
14
|
+
from click_extended.core.other.context import Context
|
|
15
|
+
from click_extended.utils.casing import Casing
|
|
16
|
+
|
|
17
|
+
if TYPE_CHECKING:
|
|
18
|
+
from click_extended.types import Decorator
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class ChildNode(Node, ABC):
|
|
22
|
+
"""
|
|
23
|
+
Base class for child nodes with type-specific handlers. Child nodes
|
|
24
|
+
process values through handler methods based on value type.
|
|
25
|
+
|
|
26
|
+
Handlers operate on a priority system with specificity deciding the order.
|
|
27
|
+
For example, `handle_flat_tuple` takes priority over `handle_tuple` which in
|
|
28
|
+
turn takes priority over `handle_all`.
|
|
29
|
+
|
|
30
|
+
All handlers are optional. Override only the handlers you need.
|
|
31
|
+
Returning None (or no return) passes the value through unchanged and makes
|
|
32
|
+
that type validation-only.
|
|
33
|
+
|
|
34
|
+
If a value is passed to the child in which it has not implemented a
|
|
35
|
+
relevant handler, an `UnhandledTypeError` exception is raised.
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
def __init__(
|
|
39
|
+
self,
|
|
40
|
+
name: str,
|
|
41
|
+
process_args: tuple[Any, ...] | None = None,
|
|
42
|
+
process_kwargs: dict[str, Any] | None = None,
|
|
43
|
+
**kwargs: Any,
|
|
44
|
+
) -> None:
|
|
45
|
+
"""
|
|
46
|
+
Initialize a new `ChildNode` instance.
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
name (str):
|
|
50
|
+
The name of the node.
|
|
51
|
+
process_args (tuple):
|
|
52
|
+
Positional arguments to pass to handler methods.
|
|
53
|
+
process_kwargs (dict[str, Any]):
|
|
54
|
+
Keyword arguments to pass to handler methods.
|
|
55
|
+
**kwargs (Any):
|
|
56
|
+
Additional keyword arguments for multiple inheritance.
|
|
57
|
+
"""
|
|
58
|
+
children = kwargs.pop("children", None)
|
|
59
|
+
super().__init__(name=name, children=children, **kwargs)
|
|
60
|
+
self.process_args = process_args or ()
|
|
61
|
+
self.process_kwargs = process_kwargs or {}
|
|
62
|
+
|
|
63
|
+
def handle_none(self, context: "Context", *args: Any, **kwargs: Any) -> Any:
|
|
64
|
+
"""
|
|
65
|
+
Handle None values explicitly.
|
|
66
|
+
|
|
67
|
+
Called when value is `None` before any other handler.
|
|
68
|
+
If not implemented, `None` values are auto-skipped.
|
|
69
|
+
|
|
70
|
+
Args:
|
|
71
|
+
context (Context):
|
|
72
|
+
Information about the current context.
|
|
73
|
+
*args (Any):
|
|
74
|
+
Additional positional arguments from decorator.
|
|
75
|
+
**kwargs (Any):
|
|
76
|
+
Additional keyword arguments from decorator.
|
|
77
|
+
|
|
78
|
+
Returns:
|
|
79
|
+
Any:
|
|
80
|
+
The processed value, or `None` to pass through unchanged.
|
|
81
|
+
"""
|
|
82
|
+
raise NotImplementedError
|
|
83
|
+
|
|
84
|
+
def handle_all(
|
|
85
|
+
self, value: Any, context: "Context", *args: Any, **kwargs: Any
|
|
86
|
+
) -> Any:
|
|
87
|
+
"""
|
|
88
|
+
Handle all value types. Called as fallback if no
|
|
89
|
+
specific handler is implemented.
|
|
90
|
+
|
|
91
|
+
Also catches `None` values if `handle_none` is not implemented.
|
|
92
|
+
|
|
93
|
+
Args:
|
|
94
|
+
value (Any):
|
|
95
|
+
The value to process.
|
|
96
|
+
context (Context):
|
|
97
|
+
Information about the current context.
|
|
98
|
+
*args (Any):
|
|
99
|
+
Additional positional arguments from decorator.
|
|
100
|
+
**kwargs (Any):
|
|
101
|
+
Additional keyword arguments from decorator.
|
|
102
|
+
|
|
103
|
+
Returns:
|
|
104
|
+
Any:
|
|
105
|
+
Processed value, or None to pass through unchanged.
|
|
106
|
+
"""
|
|
107
|
+
raise NotImplementedError
|
|
108
|
+
|
|
109
|
+
def handle_str(
|
|
110
|
+
self, value: str, context: "Context", *args: Any, **kwargs: Any
|
|
111
|
+
) -> Any:
|
|
112
|
+
"""
|
|
113
|
+
Handle string values.
|
|
114
|
+
|
|
115
|
+
Args:
|
|
116
|
+
value (str):
|
|
117
|
+
The string value to process.
|
|
118
|
+
context (Context):
|
|
119
|
+
Information about the current context.
|
|
120
|
+
*args (Any):
|
|
121
|
+
Additional positional arguments from decorator.
|
|
122
|
+
**kwargs (Any):
|
|
123
|
+
Additional keyword arguments from decorator.
|
|
124
|
+
|
|
125
|
+
Returns:
|
|
126
|
+
Any:
|
|
127
|
+
Processed value, or `None` to pass through unchanged.
|
|
128
|
+
"""
|
|
129
|
+
raise NotImplementedError
|
|
130
|
+
|
|
131
|
+
def handle_int(
|
|
132
|
+
self, value: int, context: "Context", *args: Any, **kwargs: Any
|
|
133
|
+
) -> Any:
|
|
134
|
+
"""
|
|
135
|
+
Handle integer values.
|
|
136
|
+
|
|
137
|
+
Args:
|
|
138
|
+
value (int):
|
|
139
|
+
The integer value to process.
|
|
140
|
+
context (Context):
|
|
141
|
+
Information about the current context.
|
|
142
|
+
*args (Any):
|
|
143
|
+
Additional positional arguments from decorator.
|
|
144
|
+
**kwargs (Any):
|
|
145
|
+
Additional keyword arguments from decorator.
|
|
146
|
+
|
|
147
|
+
Returns:
|
|
148
|
+
Any:
|
|
149
|
+
Processed value, or `None` to pass through unchanged.
|
|
150
|
+
"""
|
|
151
|
+
raise NotImplementedError
|
|
152
|
+
|
|
153
|
+
def handle_float(
|
|
154
|
+
self, value: float, context: "Context", *args: Any, **kwargs: Any
|
|
155
|
+
) -> Any:
|
|
156
|
+
"""
|
|
157
|
+
Handle float values.
|
|
158
|
+
|
|
159
|
+
Args:
|
|
160
|
+
value (float):
|
|
161
|
+
The float value to process.
|
|
162
|
+
context (Context):
|
|
163
|
+
Information about the current context.
|
|
164
|
+
*args (Any):
|
|
165
|
+
Additional positional arguments from decorator.
|
|
166
|
+
**kwargs (Any):
|
|
167
|
+
Additional keyword arguments from decorator.
|
|
168
|
+
|
|
169
|
+
Returns:
|
|
170
|
+
Any:
|
|
171
|
+
Processed value, or `None` to pass through unchanged.
|
|
172
|
+
"""
|
|
173
|
+
raise NotImplementedError
|
|
174
|
+
|
|
175
|
+
def handle_bool(
|
|
176
|
+
self, value: bool, context: "Context", *args: Any, **kwargs: Any
|
|
177
|
+
) -> Any:
|
|
178
|
+
"""
|
|
179
|
+
Handle boolean values.
|
|
180
|
+
|
|
181
|
+
Args:
|
|
182
|
+
value (bool):
|
|
183
|
+
The boolean value to process.
|
|
184
|
+
context (Context):
|
|
185
|
+
Information about the current context.
|
|
186
|
+
*args (Any):
|
|
187
|
+
Additional positional arguments from decorator.
|
|
188
|
+
**kwargs (Any):
|
|
189
|
+
Additional keyword arguments from decorator.
|
|
190
|
+
|
|
191
|
+
Returns:
|
|
192
|
+
Any:
|
|
193
|
+
Processed value, or `None` to pass through unchanged.
|
|
194
|
+
"""
|
|
195
|
+
raise NotImplementedError
|
|
196
|
+
|
|
197
|
+
def handle_numeric(
|
|
198
|
+
self, value: int | float, context: "Context", *args: Any, **kwargs: Any
|
|
199
|
+
) -> Any:
|
|
200
|
+
"""
|
|
201
|
+
Handle numeric values (int or float).
|
|
202
|
+
|
|
203
|
+
This is a union handler that works with both integers and floats.
|
|
204
|
+
Use this when your decorator logic applies to all numeric types.
|
|
205
|
+
|
|
206
|
+
Args:
|
|
207
|
+
value (int | float):
|
|
208
|
+
The numeric value to process.
|
|
209
|
+
context (Context):
|
|
210
|
+
Information about the current context.
|
|
211
|
+
*args (Any):
|
|
212
|
+
Additional positional arguments from decorator.
|
|
213
|
+
**kwargs (Any):
|
|
214
|
+
Additional keyword arguments from decorator.
|
|
215
|
+
|
|
216
|
+
Returns:
|
|
217
|
+
Any:
|
|
218
|
+
Processed value, or `None` to pass through unchanged.
|
|
219
|
+
"""
|
|
220
|
+
raise NotImplementedError
|
|
221
|
+
|
|
222
|
+
def handle_date(
|
|
223
|
+
self, value: date, context: "Context", *args: Any, **kwargs: Any
|
|
224
|
+
) -> Any:
|
|
225
|
+
"""
|
|
226
|
+
Handle date objects (datetime.date).
|
|
227
|
+
|
|
228
|
+
Args:
|
|
229
|
+
value (date):
|
|
230
|
+
The date object to process.
|
|
231
|
+
context (Context):
|
|
232
|
+
Information about the current context.
|
|
233
|
+
*args (Any):
|
|
234
|
+
Additional positional arguments from decorator.
|
|
235
|
+
**kwargs (Any):
|
|
236
|
+
Additional keyword arguments from decorator.
|
|
237
|
+
|
|
238
|
+
Returns:
|
|
239
|
+
Any:
|
|
240
|
+
Processed value, or `None` to pass through unchanged.
|
|
241
|
+
"""
|
|
242
|
+
raise NotImplementedError
|
|
243
|
+
|
|
244
|
+
def handle_time(
|
|
245
|
+
self, value: time, context: "Context", *args: Any, **kwargs: Any
|
|
246
|
+
) -> Any:
|
|
247
|
+
"""
|
|
248
|
+
Handle time objects (datetime.time).
|
|
249
|
+
|
|
250
|
+
Args:
|
|
251
|
+
value (time):
|
|
252
|
+
The time object to process.
|
|
253
|
+
context (Context):
|
|
254
|
+
Information about the current context.
|
|
255
|
+
*args (Any):
|
|
256
|
+
Additional positional arguments from decorator.
|
|
257
|
+
**kwargs (Any):
|
|
258
|
+
Additional keyword arguments from decorator.
|
|
259
|
+
|
|
260
|
+
Returns:
|
|
261
|
+
Any:
|
|
262
|
+
Processed value, or `None` to pass through unchanged.
|
|
263
|
+
"""
|
|
264
|
+
raise NotImplementedError
|
|
265
|
+
|
|
266
|
+
def handle_tuple(
|
|
267
|
+
self,
|
|
268
|
+
value: tuple[Any, ...],
|
|
269
|
+
context: "Context",
|
|
270
|
+
*args: Any,
|
|
271
|
+
**kwargs: Any,
|
|
272
|
+
) -> Any:
|
|
273
|
+
"""
|
|
274
|
+
Handle any tuple structure (fallback).
|
|
275
|
+
|
|
276
|
+
Called when:
|
|
277
|
+
|
|
278
|
+
- `handle_flat_tuple` not implemented and the tuple is flat
|
|
279
|
+
- `handle_nested_tuple` not implemented and the tuple is nested
|
|
280
|
+
- Tuple has mixed primitive and complex types
|
|
281
|
+
|
|
282
|
+
Use this when you want to handle all tuple types with the same logic,
|
|
283
|
+
or when you need to handle mixed-type tuples.
|
|
284
|
+
|
|
285
|
+
Examples:
|
|
286
|
+
|
|
287
|
+
- `(1, 2, 3)` if `handle_flat_tuple` not implemented
|
|
288
|
+
- `((1, 2),)` if `handle_nested_tuple` not implemented
|
|
289
|
+
- `(1, [2, 3], "hello")`: mixed types (only option)
|
|
290
|
+
|
|
291
|
+
Args:
|
|
292
|
+
value (tuple[Any, ...]):
|
|
293
|
+
The tuple to process.
|
|
294
|
+
context (Context):
|
|
295
|
+
Information about the current context.
|
|
296
|
+
*args (Any):
|
|
297
|
+
Additional positional arguments from decorator.
|
|
298
|
+
**kwargs (Any):
|
|
299
|
+
Additional keyword arguments from decorator.
|
|
300
|
+
|
|
301
|
+
Returns:
|
|
302
|
+
Any:
|
|
303
|
+
Processed value, or `None` to pass through unchanged.
|
|
304
|
+
"""
|
|
305
|
+
raise NotImplementedError
|
|
306
|
+
|
|
307
|
+
def handle_list(
|
|
308
|
+
self,
|
|
309
|
+
value: list[Any],
|
|
310
|
+
context: "Context",
|
|
311
|
+
*args: Any,
|
|
312
|
+
**kwargs: Any,
|
|
313
|
+
) -> Any:
|
|
314
|
+
"""
|
|
315
|
+
Handle list values.
|
|
316
|
+
|
|
317
|
+
Args:
|
|
318
|
+
value (list[Any]):
|
|
319
|
+
The list to process.
|
|
320
|
+
context (Context):
|
|
321
|
+
Information about the current context.
|
|
322
|
+
*args (Any):
|
|
323
|
+
Additional positional arguments from decorator.
|
|
324
|
+
**kwargs (Any):
|
|
325
|
+
Additional keyword arguments from decorator.
|
|
326
|
+
|
|
327
|
+
Returns:
|
|
328
|
+
Any:
|
|
329
|
+
Processed value, or `None` to pass through unchanged.
|
|
330
|
+
"""
|
|
331
|
+
raise NotImplementedError
|
|
332
|
+
|
|
333
|
+
def handle_dict(
|
|
334
|
+
self,
|
|
335
|
+
value: dict[Any, Any],
|
|
336
|
+
context: "Context",
|
|
337
|
+
*args: Any,
|
|
338
|
+
**kwargs: Any,
|
|
339
|
+
) -> Any:
|
|
340
|
+
"""
|
|
341
|
+
Handle dictionary values.
|
|
342
|
+
|
|
343
|
+
Args:
|
|
344
|
+
value (dict[Any, Any]):
|
|
345
|
+
The dictionary to process.
|
|
346
|
+
context (Context):
|
|
347
|
+
Information about the current context.
|
|
348
|
+
*args (Any):
|
|
349
|
+
Additional positional arguments from decorator.
|
|
350
|
+
**kwargs (Any):
|
|
351
|
+
Additional keyword arguments from decorator.
|
|
352
|
+
|
|
353
|
+
Returns:
|
|
354
|
+
Any:
|
|
355
|
+
Processed value, or `None` to pass through unchanged.
|
|
356
|
+
"""
|
|
357
|
+
raise NotImplementedError
|
|
358
|
+
|
|
359
|
+
def handle_tag(
|
|
360
|
+
self,
|
|
361
|
+
value: dict[str, Any],
|
|
362
|
+
context: "Context",
|
|
363
|
+
*args: Any,
|
|
364
|
+
**kwargs: Any,
|
|
365
|
+
) -> Any:
|
|
366
|
+
"""
|
|
367
|
+
Handle values from a `Tag` parent.
|
|
368
|
+
|
|
369
|
+
Called when the parent node is a `Tag`. The value is a dictionary
|
|
370
|
+
mapping parameter names to their values from all parent nodes
|
|
371
|
+
(`Option`, `Argument`, `Env`, etc.) that reference this tag.
|
|
372
|
+
|
|
373
|
+
Can be implemented as sync or async. This handler is validation-only
|
|
374
|
+
and must not return a value (or return None).
|
|
375
|
+
|
|
376
|
+
Args:
|
|
377
|
+
value (dict[str, Any]):
|
|
378
|
+
Dictionary mapping parameter names to values from
|
|
379
|
+
all parent nodes referencing this tag. Keys are parent
|
|
380
|
+
node names (e.g., "username"), values are `None` if not
|
|
381
|
+
provided by the user.
|
|
382
|
+
context (Context):
|
|
383
|
+
Information about the current context.
|
|
384
|
+
*args (Any):
|
|
385
|
+
Additional positional arguments from decorator.
|
|
386
|
+
**kwargs (Any):
|
|
387
|
+
Additional keyword arguments from decorator.
|
|
388
|
+
|
|
389
|
+
Returns:
|
|
390
|
+
None:
|
|
391
|
+
Must return None (validation-only).
|
|
392
|
+
"""
|
|
393
|
+
raise NotImplementedError
|
|
394
|
+
|
|
395
|
+
def handle_datetime(
|
|
396
|
+
self,
|
|
397
|
+
value: datetime,
|
|
398
|
+
context: "Context",
|
|
399
|
+
*args: Any,
|
|
400
|
+
**kwargs: Any,
|
|
401
|
+
) -> Any:
|
|
402
|
+
"""
|
|
403
|
+
Handle datetime objects (datetime.datetime).
|
|
404
|
+
|
|
405
|
+
Args:
|
|
406
|
+
value (datetime):
|
|
407
|
+
The datetime object to process.
|
|
408
|
+
context (Context):
|
|
409
|
+
Information about the current context.
|
|
410
|
+
*args (Any):
|
|
411
|
+
Additional positional arguments from decorator.
|
|
412
|
+
**kwargs (Any):
|
|
413
|
+
Additional keyword arguments from decorator.
|
|
414
|
+
|
|
415
|
+
Returns:
|
|
416
|
+
Any:
|
|
417
|
+
Processed value, or `None` to pass through unchanged.
|
|
418
|
+
"""
|
|
419
|
+
raise NotImplementedError
|
|
420
|
+
|
|
421
|
+
def handle_uuid(
|
|
422
|
+
self, value: UUID, context: "Context", *args: Any, **kwargs: Any
|
|
423
|
+
) -> Any:
|
|
424
|
+
"""
|
|
425
|
+
Handle UUID objects.
|
|
426
|
+
|
|
427
|
+
Args:
|
|
428
|
+
value (UUID):
|
|
429
|
+
The UUID object to process.
|
|
430
|
+
context (Context):
|
|
431
|
+
Information about the current context.
|
|
432
|
+
*args (Any):
|
|
433
|
+
Additional positional arguments from decorator.
|
|
434
|
+
**kwargs (Any):
|
|
435
|
+
Additional keyword arguments from decorator.
|
|
436
|
+
|
|
437
|
+
Returns:
|
|
438
|
+
Any:
|
|
439
|
+
Processed value, or `None` to pass through unchanged.
|
|
440
|
+
"""
|
|
441
|
+
raise NotImplementedError
|
|
442
|
+
|
|
443
|
+
def handle_path(
|
|
444
|
+
self, value: Path, context: "Context", *args: Any, **kwargs: Any
|
|
445
|
+
) -> Any:
|
|
446
|
+
"""
|
|
447
|
+
Handle Path objects.
|
|
448
|
+
|
|
449
|
+
Args:
|
|
450
|
+
value (Path):
|
|
451
|
+
The Path object to process.
|
|
452
|
+
context (Context):
|
|
453
|
+
Information about the current context.
|
|
454
|
+
*args (Any):
|
|
455
|
+
Additional positional arguments from decorator.
|
|
456
|
+
**kwargs (Any):
|
|
457
|
+
Additional keyword arguments from decorator.
|
|
458
|
+
|
|
459
|
+
Returns:
|
|
460
|
+
Any:
|
|
461
|
+
Processed value, or `None` to pass through unchanged.
|
|
462
|
+
"""
|
|
463
|
+
raise NotImplementedError
|
|
464
|
+
|
|
465
|
+
def handle_bytes(
|
|
466
|
+
self, value: bytes, context: "Context", *args: Any, **kwargs: Any
|
|
467
|
+
) -> Any:
|
|
468
|
+
"""
|
|
469
|
+
Handle bytes objects.
|
|
470
|
+
|
|
471
|
+
Args:
|
|
472
|
+
value (bytes):
|
|
473
|
+
The bytes object to process.
|
|
474
|
+
context (Context):
|
|
475
|
+
Information about the current context.
|
|
476
|
+
*args (Any):
|
|
477
|
+
Additional positional arguments from decorator.
|
|
478
|
+
**kwargs (Any):
|
|
479
|
+
Additional keyword arguments from decorator.
|
|
480
|
+
|
|
481
|
+
Returns:
|
|
482
|
+
Any:
|
|
483
|
+
Processed value, or `None` to pass through unchanged.
|
|
484
|
+
"""
|
|
485
|
+
raise NotImplementedError
|
|
486
|
+
|
|
487
|
+
def handle_decimal(
|
|
488
|
+
self, value: Decimal, context: "Context", *args: Any, **kwargs: Any
|
|
489
|
+
) -> Any:
|
|
490
|
+
"""
|
|
491
|
+
Handle Decimal objects.
|
|
492
|
+
|
|
493
|
+
Args:
|
|
494
|
+
value (Decimal):
|
|
495
|
+
The Decimal object to process.
|
|
496
|
+
context (Context):
|
|
497
|
+
Information about the current context.
|
|
498
|
+
*args (Any):
|
|
499
|
+
Additional positional arguments from decorator.
|
|
500
|
+
**kwargs (Any):
|
|
501
|
+
Additional keyword arguments from decorator.
|
|
502
|
+
|
|
503
|
+
Returns:
|
|
504
|
+
Any:
|
|
505
|
+
Processed value, or `None` to pass through unchanged.
|
|
506
|
+
"""
|
|
507
|
+
raise NotImplementedError
|
|
508
|
+
|
|
509
|
+
def get(self, _name: str) -> None:
|
|
510
|
+
"""
|
|
511
|
+
The ChildNode has no children, thus this method returns None.
|
|
512
|
+
|
|
513
|
+
Args:
|
|
514
|
+
_name (str):
|
|
515
|
+
The name of the child.
|
|
516
|
+
|
|
517
|
+
Returns:
|
|
518
|
+
None:
|
|
519
|
+
Always returns None as the ChildNode has no children.
|
|
520
|
+
"""
|
|
521
|
+
return None
|
|
522
|
+
|
|
523
|
+
def __getitem__(self, name: str | int) -> "Node":
|
|
524
|
+
raise KeyError("A ChildNode instance has no children.")
|
|
525
|
+
|
|
526
|
+
@classmethod
|
|
527
|
+
def as_decorator(cls, *args: Any, **kwargs: Any) -> "Decorator":
|
|
528
|
+
"""
|
|
529
|
+
Return a decorator representation of the child node.
|
|
530
|
+
|
|
531
|
+
The provided `args` and `kwargs` are stored and later passed to handler
|
|
532
|
+
methods when called by the parent processing.
|
|
533
|
+
|
|
534
|
+
Args:
|
|
535
|
+
*args (Any):
|
|
536
|
+
Positional arguments to pass to handler methods.
|
|
537
|
+
**kwargs (Any):
|
|
538
|
+
Keyword arguments to pass to handler methods.
|
|
539
|
+
|
|
540
|
+
Returns:
|
|
541
|
+
Decorator:
|
|
542
|
+
A decorator function that registers the child node.
|
|
543
|
+
"""
|
|
544
|
+
|
|
545
|
+
def decorator(func: Callable[..., Any]) -> Callable[..., Any]:
|
|
546
|
+
"""The actual decorator that wraps the function."""
|
|
547
|
+
name = Casing.to_snake_case(cls.__name__)
|
|
548
|
+
instance = cls(name=name, process_args=args, process_kwargs=kwargs)
|
|
549
|
+
Tree.queue_child(instance)
|
|
550
|
+
return func
|
|
551
|
+
|
|
552
|
+
return decorator
|
|
553
|
+
|
|
554
|
+
|
|
555
|
+
__all__ = ["ChildNode"]
|