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.
- click_extended/__init__.py +2 -0
- 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/context.py +56 -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.2.dist-info}/METADATA +2 -1
- click_extended-1.0.2.dist-info/RECORD +150 -0
- click_extended-1.0.0.dist-info/RECORD +0 -10
- {click_extended-1.0.0.dist-info → click_extended-1.0.2.dist-info}/WHEEL +0 -0
- {click_extended-1.0.0.dist-info → click_extended-1.0.2.dist-info}/licenses/AUTHORS.md +0 -0
- {click_extended-1.0.0.dist-info → click_extended-1.0.2.dist-info}/licenses/LICENSE +0 -0
- {click_extended-1.0.0.dist-info → click_extended-1.0.2.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"""Split the string by a separator."""
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from click_extended.core.nodes.child_node import ChildNode
|
|
6
|
+
from click_extended.core.other.context import Context
|
|
7
|
+
from click_extended.types import Decorator
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Split(ChildNode):
|
|
11
|
+
"""Split the string by a separator."""
|
|
12
|
+
|
|
13
|
+
def handle_str(
|
|
14
|
+
self,
|
|
15
|
+
value: str,
|
|
16
|
+
context: Context,
|
|
17
|
+
*args: Any,
|
|
18
|
+
**kwargs: Any,
|
|
19
|
+
) -> list[str]:
|
|
20
|
+
sep = kwargs.get("sep")
|
|
21
|
+
maxsplit = kwargs.get("maxsplit", -1)
|
|
22
|
+
return value.split(sep, maxsplit)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def split(sep: str | None = None, maxsplit: int = -1) -> Decorator:
|
|
26
|
+
"""
|
|
27
|
+
Split the string by a separator.
|
|
28
|
+
|
|
29
|
+
Type: `ChildNode`
|
|
30
|
+
|
|
31
|
+
Supports: `str`
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
sep (str | None):
|
|
35
|
+
The delimiter string. If None, split by whitespace.
|
|
36
|
+
maxsplit (int):
|
|
37
|
+
Maximum number of splits. Defaults to `-1` (no limit).
|
|
38
|
+
|
|
39
|
+
Returns:
|
|
40
|
+
Decorator:
|
|
41
|
+
The decorated function.
|
|
42
|
+
"""
|
|
43
|
+
return Split.as_decorator(sep=sep, maxsplit=maxsplit)
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
"""Child decorators to strip characters from strings."""
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from click_extended.core.nodes.child_node import ChildNode
|
|
6
|
+
from click_extended.core.other.context import Context
|
|
7
|
+
from click_extended.types import Decorator
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Strip(ChildNode):
|
|
11
|
+
"""Child decorator to strip characters from both ends of a string."""
|
|
12
|
+
|
|
13
|
+
def handle_str(
|
|
14
|
+
self, value: str, context: Context, *args: Any, **kwargs: Any
|
|
15
|
+
) -> str:
|
|
16
|
+
chars: str | None = kwargs.get("chars")
|
|
17
|
+
return value.strip(chars)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class LStrip(ChildNode):
|
|
21
|
+
"""Child decorator to strip characters from the left end of a string."""
|
|
22
|
+
|
|
23
|
+
def handle_str(
|
|
24
|
+
self, value: str, context: Context, *args: Any, **kwargs: Any
|
|
25
|
+
) -> str:
|
|
26
|
+
chars: str | None = kwargs.get("chars")
|
|
27
|
+
return value.lstrip(chars)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class RStrip(ChildNode):
|
|
31
|
+
"""Child decorator to strip characters from the right end of a string."""
|
|
32
|
+
|
|
33
|
+
def handle_str(
|
|
34
|
+
self, value: str, context: Context, *args: Any, **kwargs: Any
|
|
35
|
+
) -> str:
|
|
36
|
+
chars: str | None = kwargs.get("chars")
|
|
37
|
+
return value.rstrip(chars)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def strip(chars: str | None = None) -> Decorator:
|
|
41
|
+
"""
|
|
42
|
+
Remove leading and trailing characters from a string.
|
|
43
|
+
|
|
44
|
+
Type: `ChildNode`
|
|
45
|
+
|
|
46
|
+
Supports: `str`
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
chars (str | None, optional):
|
|
50
|
+
A string specifying the set of characters to be removed.
|
|
51
|
+
If `None` (default), whitespace characters are removed.
|
|
52
|
+
|
|
53
|
+
Returns:
|
|
54
|
+
Decorator:
|
|
55
|
+
The decorator function.
|
|
56
|
+
|
|
57
|
+
Examples:
|
|
58
|
+
```python
|
|
59
|
+
@command()
|
|
60
|
+
@option("name", default=" hello ")
|
|
61
|
+
@strip()
|
|
62
|
+
def cmd(name: str) -> None:
|
|
63
|
+
click.echo(f"Name: '{name}'") # Output: Name: 'hello'
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
```python
|
|
67
|
+
@command()
|
|
68
|
+
@option("path", default="///path///")
|
|
69
|
+
@strip("/")
|
|
70
|
+
def cmd(path: str) -> None:
|
|
71
|
+
click.echo(f"Path: '{path}'") # Output: Path: 'path'
|
|
72
|
+
```
|
|
73
|
+
"""
|
|
74
|
+
return Strip.as_decorator(chars=chars)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def lstrip(chars: str | None = None) -> Decorator:
|
|
78
|
+
"""
|
|
79
|
+
Remove leading (left) characters from a string.
|
|
80
|
+
|
|
81
|
+
Type: `ChildNode`
|
|
82
|
+
|
|
83
|
+
Supports: `str`
|
|
84
|
+
|
|
85
|
+
Args:
|
|
86
|
+
chars (str | None, optional):
|
|
87
|
+
A string specifying the set of characters to be removed.
|
|
88
|
+
If `None` (default), whitespace characters are removed.
|
|
89
|
+
|
|
90
|
+
Returns:
|
|
91
|
+
Decorator:
|
|
92
|
+
The decorator function.
|
|
93
|
+
|
|
94
|
+
Examples:
|
|
95
|
+
```python
|
|
96
|
+
@command()
|
|
97
|
+
@option("name", default=" hello ")
|
|
98
|
+
@lstrip()
|
|
99
|
+
def cmd(name: str) -> None:
|
|
100
|
+
click.echo(f"Name: '{name}'") # Output: Name: 'hello '
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
```python
|
|
104
|
+
@command()
|
|
105
|
+
@option("path", default="///path///")
|
|
106
|
+
@lstrip("/")
|
|
107
|
+
def cmd(path: str) -> None:
|
|
108
|
+
click.echo(f"Path: '{path}'") # Output: Path: 'path///'
|
|
109
|
+
```
|
|
110
|
+
"""
|
|
111
|
+
return LStrip.as_decorator(chars=chars)
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def rstrip(chars: str | None = None) -> Decorator:
|
|
115
|
+
"""
|
|
116
|
+
Remove trailing (right) characters from a string.
|
|
117
|
+
|
|
118
|
+
Type: `ChildNode`
|
|
119
|
+
|
|
120
|
+
Supports: `str`
|
|
121
|
+
|
|
122
|
+
Args:
|
|
123
|
+
chars (str | None, optional):
|
|
124
|
+
A string specifying the set of characters to be removed.
|
|
125
|
+
If `None` (default), whitespace characters are removed.
|
|
126
|
+
|
|
127
|
+
Returns:
|
|
128
|
+
Decorator:
|
|
129
|
+
The decorator function.
|
|
130
|
+
|
|
131
|
+
Examples:
|
|
132
|
+
```python
|
|
133
|
+
@command()
|
|
134
|
+
@option("name", default=" hello ")
|
|
135
|
+
@rstrip()
|
|
136
|
+
def cmd(name: str) -> None:
|
|
137
|
+
click.echo(f"Name: '{name}'") # Output: Name: ' hello'
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
```python
|
|
141
|
+
@command()
|
|
142
|
+
@option("path", default="///path///")
|
|
143
|
+
@rstrip("/")
|
|
144
|
+
def cmd(path: str) -> None:
|
|
145
|
+
click.echo(f"Path: '{path}'") # Output: Path: '///path'
|
|
146
|
+
```
|
|
147
|
+
"""
|
|
148
|
+
return RStrip.as_decorator(chars=chars)
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
"""Child node to convert a string to one of many formats."""
|
|
2
|
+
|
|
3
|
+
from typing import Any, cast
|
|
4
|
+
|
|
5
|
+
from click_extended.core.nodes.child_node import ChildNode
|
|
6
|
+
from click_extended.core.other.context import Context
|
|
7
|
+
from click_extended.types import Decorator
|
|
8
|
+
from click_extended.utils.casing import Casing
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class ToCase(ChildNode):
|
|
12
|
+
"""Child node to convert a string to one of many formats."""
|
|
13
|
+
|
|
14
|
+
def handle_str(
|
|
15
|
+
self,
|
|
16
|
+
value: str,
|
|
17
|
+
context: Context,
|
|
18
|
+
*args: Any,
|
|
19
|
+
**kwargs: Any,
|
|
20
|
+
) -> str:
|
|
21
|
+
return cast(str, getattr(Casing, kwargs["method"])(str(value)))
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def to_lower_case() -> Decorator:
|
|
25
|
+
"""
|
|
26
|
+
Convert a string to `lower case`.
|
|
27
|
+
|
|
28
|
+
Type: `ChildNode`
|
|
29
|
+
|
|
30
|
+
Supports: `str`
|
|
31
|
+
|
|
32
|
+
Returns:
|
|
33
|
+
Decorator:
|
|
34
|
+
The decorator function.
|
|
35
|
+
"""
|
|
36
|
+
return ToCase.as_decorator(method="to_lower_case")
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def to_upper_case() -> Decorator:
|
|
40
|
+
"""
|
|
41
|
+
Convert a string to `UPPER CASE`.
|
|
42
|
+
|
|
43
|
+
Type: `ChildNode`
|
|
44
|
+
|
|
45
|
+
Supports: `str`
|
|
46
|
+
|
|
47
|
+
Returns:
|
|
48
|
+
Decorator:
|
|
49
|
+
The decorator function.
|
|
50
|
+
"""
|
|
51
|
+
return ToCase.as_decorator(method="to_upper_case")
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def to_meme_case() -> Decorator:
|
|
55
|
+
"""
|
|
56
|
+
Convert a string to `mEmE cAsE`.
|
|
57
|
+
|
|
58
|
+
Type: `ChildNode`
|
|
59
|
+
|
|
60
|
+
Supports: `str`
|
|
61
|
+
|
|
62
|
+
Returns:
|
|
63
|
+
Decorator:
|
|
64
|
+
The decorator function.
|
|
65
|
+
"""
|
|
66
|
+
return ToCase.as_decorator(method="to_meme_case")
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def to_snake_case() -> Decorator:
|
|
70
|
+
"""
|
|
71
|
+
Convert a string to `snake_case`.
|
|
72
|
+
|
|
73
|
+
Type: `ChildNode`
|
|
74
|
+
|
|
75
|
+
Supports: `str`
|
|
76
|
+
|
|
77
|
+
Returns:
|
|
78
|
+
Decorator:
|
|
79
|
+
The decorator function.
|
|
80
|
+
"""
|
|
81
|
+
return ToCase.as_decorator(method="to_snake_case")
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def to_screaming_snake_case() -> Decorator:
|
|
85
|
+
"""
|
|
86
|
+
Convert a string to `SCREAMING_SNAKE_CASE`.
|
|
87
|
+
|
|
88
|
+
Type: `ChildNode`
|
|
89
|
+
|
|
90
|
+
Supports: `str`
|
|
91
|
+
|
|
92
|
+
Returns:
|
|
93
|
+
Decorator:
|
|
94
|
+
The decorator function.
|
|
95
|
+
"""
|
|
96
|
+
return ToCase.as_decorator(method="to_screaming_snake_case")
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def to_camel_case() -> Decorator:
|
|
100
|
+
"""
|
|
101
|
+
Convert a string to `camelCase`.
|
|
102
|
+
|
|
103
|
+
Type: `ChildNode`
|
|
104
|
+
|
|
105
|
+
Supports: `str`
|
|
106
|
+
|
|
107
|
+
Returns:
|
|
108
|
+
Decorator:
|
|
109
|
+
The decorator function.
|
|
110
|
+
"""
|
|
111
|
+
return ToCase.as_decorator(method="to_camel_case")
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def to_pascal_case() -> Decorator:
|
|
115
|
+
"""
|
|
116
|
+
Convert a string to `PascalCase`.
|
|
117
|
+
|
|
118
|
+
Type: `ChildNode`
|
|
119
|
+
|
|
120
|
+
Supports: `str`
|
|
121
|
+
|
|
122
|
+
Returns:
|
|
123
|
+
Decorator:
|
|
124
|
+
The decorator function.
|
|
125
|
+
"""
|
|
126
|
+
return ToCase.as_decorator(method="to_pascal_case")
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def to_kebab_case() -> Decorator:
|
|
130
|
+
"""
|
|
131
|
+
Convert a string to `kebab-case`.
|
|
132
|
+
|
|
133
|
+
Type: `ChildNode`
|
|
134
|
+
|
|
135
|
+
Supports: `str`
|
|
136
|
+
|
|
137
|
+
Returns:
|
|
138
|
+
Decorator:
|
|
139
|
+
The decorator function.
|
|
140
|
+
"""
|
|
141
|
+
return ToCase.as_decorator(method="to_kebab_case")
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def to_train_case() -> Decorator:
|
|
145
|
+
"""
|
|
146
|
+
Convert a string to `Train-Case`.
|
|
147
|
+
|
|
148
|
+
Type: `ChildNode`
|
|
149
|
+
|
|
150
|
+
Supports: `str`
|
|
151
|
+
|
|
152
|
+
Returns:
|
|
153
|
+
Decorator:
|
|
154
|
+
The decorator function.
|
|
155
|
+
"""
|
|
156
|
+
return ToCase.as_decorator(method="to_train_case")
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def to_flat_case() -> Decorator:
|
|
160
|
+
"""
|
|
161
|
+
Convert a string to `flatcase`.
|
|
162
|
+
|
|
163
|
+
Type: `ChildNode`
|
|
164
|
+
|
|
165
|
+
Supports: `str`
|
|
166
|
+
|
|
167
|
+
Returns:
|
|
168
|
+
Decorator:
|
|
169
|
+
The decorator function.
|
|
170
|
+
"""
|
|
171
|
+
return ToCase.as_decorator(method="to_flat_case")
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def to_dot_case() -> Decorator:
|
|
175
|
+
"""
|
|
176
|
+
Convert a string to `dot.case`.
|
|
177
|
+
|
|
178
|
+
Type: `ChildNode`
|
|
179
|
+
|
|
180
|
+
Supports: `str`
|
|
181
|
+
|
|
182
|
+
Returns:
|
|
183
|
+
Decorator:
|
|
184
|
+
The decorator function.
|
|
185
|
+
"""
|
|
186
|
+
return ToCase.as_decorator(method="to_dot_case")
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
def to_title_case() -> Decorator:
|
|
190
|
+
"""
|
|
191
|
+
Convert a string to `Title Case`.
|
|
192
|
+
|
|
193
|
+
Type: `ChildNode`
|
|
194
|
+
|
|
195
|
+
Supports: `str`
|
|
196
|
+
|
|
197
|
+
Returns:
|
|
198
|
+
Decorator:
|
|
199
|
+
The decorator function.
|
|
200
|
+
"""
|
|
201
|
+
return ToCase.as_decorator(method="to_title_case")
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
def to_path_case() -> Decorator:
|
|
205
|
+
"""
|
|
206
|
+
Convert a string to `path/case`.
|
|
207
|
+
|
|
208
|
+
Type: `ChildNode`
|
|
209
|
+
|
|
210
|
+
Supports: `str`
|
|
211
|
+
|
|
212
|
+
Returns:
|
|
213
|
+
Decorator:
|
|
214
|
+
The decorator function.
|
|
215
|
+
"""
|
|
216
|
+
return ToCase.as_decorator(method="to_path_case")
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"""Child decorator to convert a string to a date."""
|
|
2
|
+
|
|
3
|
+
from datetime import date, datetime
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
from click_extended.core.nodes.child_node import ChildNode
|
|
7
|
+
from click_extended.core.other.context import Context
|
|
8
|
+
from click_extended.types import Decorator
|
|
9
|
+
from click_extended.utils import humanize_iterable
|
|
10
|
+
from click_extended.utils.time import normalize_datetime_format
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class ToDate(ChildNode):
|
|
14
|
+
"""Child decorator to convert a string to a date."""
|
|
15
|
+
|
|
16
|
+
def handle_str(
|
|
17
|
+
self,
|
|
18
|
+
value: str,
|
|
19
|
+
context: Context,
|
|
20
|
+
*args: Any,
|
|
21
|
+
**kwargs: Any,
|
|
22
|
+
) -> date:
|
|
23
|
+
formats = kwargs["formats"] or (
|
|
24
|
+
"%Y-%m-%d",
|
|
25
|
+
"%d/%m/%Y",
|
|
26
|
+
"%m/%d/%Y",
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
for fmt in formats:
|
|
30
|
+
try:
|
|
31
|
+
normalized_fmt = normalize_datetime_format(fmt)
|
|
32
|
+
dt = datetime.strptime(value, normalized_fmt)
|
|
33
|
+
return dt.date()
|
|
34
|
+
except ValueError:
|
|
35
|
+
continue
|
|
36
|
+
|
|
37
|
+
fmt_text = (
|
|
38
|
+
"either of the formats" if len(formats) != 1 else "in the format"
|
|
39
|
+
)
|
|
40
|
+
raise ValueError(
|
|
41
|
+
f"Invalid date '{value}', must be in "
|
|
42
|
+
f"{fmt_text} {humanize_iterable(formats, sep='or')}"
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def to_date(
|
|
47
|
+
*formats: str,
|
|
48
|
+
) -> Decorator:
|
|
49
|
+
"""
|
|
50
|
+
Convert a string to a date by trying multiple formats.
|
|
51
|
+
|
|
52
|
+
Type: `ChildNode`
|
|
53
|
+
|
|
54
|
+
Supports: `str`
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
*formats (str):
|
|
58
|
+
One or more date format strings to try. Supports both Python
|
|
59
|
+
strptime format (e.g., "%Y-%m-%d", "%d/%m/%Y") and simplified format
|
|
60
|
+
(e.g., "YYYY-MM-DD", "DD/MM/YYYY"). The decorator will attempt each
|
|
61
|
+
format in order until one succeeds. Defaults to `"%Y-%m-%d"`,
|
|
62
|
+
`"%d/%m/%Y"`, and `"%m/%d/%Y"`.
|
|
63
|
+
|
|
64
|
+
Returns:
|
|
65
|
+
Decorator:
|
|
66
|
+
The decorated function.
|
|
67
|
+
|
|
68
|
+
Example:
|
|
69
|
+
@to_date("YYYY-MM-DD", "DD/MM/YYYY")
|
|
70
|
+
# Or using Python strptime format:
|
|
71
|
+
@to_date("%Y-%m-%d", "%d/%m/%Y")
|
|
72
|
+
def process_date(date_val: date):
|
|
73
|
+
print(date_val)
|
|
74
|
+
"""
|
|
75
|
+
return ToDate.as_decorator(formats=formats)
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"""Child decorator to convert a string to a datetime."""
|
|
2
|
+
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
from typing import Any
|
|
5
|
+
from zoneinfo import ZoneInfo
|
|
6
|
+
|
|
7
|
+
from click_extended.core.nodes.child_node import ChildNode
|
|
8
|
+
from click_extended.core.other.context import Context
|
|
9
|
+
from click_extended.types import Decorator
|
|
10
|
+
from click_extended.utils import humanize_iterable
|
|
11
|
+
from click_extended.utils.time import normalize_datetime_format
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class ToDatetime(ChildNode):
|
|
15
|
+
"""Child decorator to convert a string to a datetime."""
|
|
16
|
+
|
|
17
|
+
def handle_str(
|
|
18
|
+
self,
|
|
19
|
+
value: str,
|
|
20
|
+
context: Context,
|
|
21
|
+
*args: Any,
|
|
22
|
+
**kwargs: Any,
|
|
23
|
+
) -> datetime:
|
|
24
|
+
formats = kwargs["formats"] or (
|
|
25
|
+
"%Y-%m-%d",
|
|
26
|
+
"%H:%M:%S",
|
|
27
|
+
"%Y-%m-%d %H:%M:%S",
|
|
28
|
+
)
|
|
29
|
+
tz = kwargs.get("tz")
|
|
30
|
+
|
|
31
|
+
for fmt in formats:
|
|
32
|
+
try:
|
|
33
|
+
normalized_fmt = normalize_datetime_format(fmt)
|
|
34
|
+
dt = datetime.strptime(value, normalized_fmt)
|
|
35
|
+
if tz:
|
|
36
|
+
dt = dt.replace(tzinfo=ZoneInfo(tz))
|
|
37
|
+
return dt
|
|
38
|
+
except ValueError:
|
|
39
|
+
continue
|
|
40
|
+
|
|
41
|
+
fmt = "either of the formats" if len(formats) != 1 else "in the format"
|
|
42
|
+
raise ValueError(
|
|
43
|
+
f"Invalid datetime '{value}', must be in "
|
|
44
|
+
f"{fmt} {humanize_iterable(formats, sep='or')}"
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def to_datetime(
|
|
49
|
+
*formats: str,
|
|
50
|
+
tz: str | None = None,
|
|
51
|
+
) -> Decorator:
|
|
52
|
+
"""
|
|
53
|
+
Convert a string to a datetime by trying multiple formats.
|
|
54
|
+
|
|
55
|
+
Type: `ChildNode`
|
|
56
|
+
|
|
57
|
+
Supports: `str`
|
|
58
|
+
|
|
59
|
+
Args:
|
|
60
|
+
*formats (str):
|
|
61
|
+
One or more datetime format strings to try. Supports both Python
|
|
62
|
+
strptime format (e.g., "%Y-%m-%d", "%d/%m/%Y") and simplified format
|
|
63
|
+
(e.g., "YYYY-MM-DD", "DD/MM/YYYY"). The decorator will attempt each
|
|
64
|
+
format in order until one succeeds. Defaults to `"%Y-%m-%d"`,
|
|
65
|
+
`"%H:%M:%S"` and `"%Y-%m-%d %H:%M:%S"`,
|
|
66
|
+
|
|
67
|
+
tz (str | None, optional):
|
|
68
|
+
Timezone name (e.g., "UTC", "America/New_York", "Europe/Stockholm")
|
|
69
|
+
to apply to the parsed datetime. Uses zoneinfo.ZoneInfo for timezone
|
|
70
|
+
handling. Defaults to `None` (naive datetime).
|
|
71
|
+
|
|
72
|
+
Returns:
|
|
73
|
+
Decorator:
|
|
74
|
+
The decorated function.
|
|
75
|
+
|
|
76
|
+
Example:
|
|
77
|
+
@to_datetime("YYYY-MM-DD", "DD/MM/YYYY", tz="America/New_York")
|
|
78
|
+
# Or using Python strptime format:
|
|
79
|
+
@to_datetime("%Y-%m-%d", "%d/%m/%Y", tz="America/New_York")
|
|
80
|
+
def process_date(date: datetime):
|
|
81
|
+
print(date)
|
|
82
|
+
"""
|
|
83
|
+
return ToDatetime.as_decorator(formats=formats, tz=tz)
|