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,119 @@
|
|
|
1
|
+
"""Parent node for generating a random UUID."""
|
|
2
|
+
|
|
3
|
+
import random
|
|
4
|
+
from typing import Any, Literal
|
|
5
|
+
from uuid import UUID, uuid1, uuid3, uuid5
|
|
6
|
+
|
|
7
|
+
from click_extended.classes import ParentNode
|
|
8
|
+
from click_extended.core.other.context import Context
|
|
9
|
+
from click_extended.types import Decorator
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class RandomUUID(ParentNode):
|
|
13
|
+
"""Parent node for generating a random UUID."""
|
|
14
|
+
|
|
15
|
+
@staticmethod
|
|
16
|
+
def _validate_namespace(namespace: UUID | str | None, version: int) -> UUID:
|
|
17
|
+
"""Validate and convert namespace to UUID object."""
|
|
18
|
+
if namespace is None:
|
|
19
|
+
raise ValueError(
|
|
20
|
+
f"namespace is required for UUID version {version}"
|
|
21
|
+
)
|
|
22
|
+
if isinstance(namespace, str):
|
|
23
|
+
try:
|
|
24
|
+
return UUID(namespace)
|
|
25
|
+
except ValueError as exc:
|
|
26
|
+
raise ValueError(
|
|
27
|
+
f"Invalid namespace UUID format: '{namespace}'. "
|
|
28
|
+
f"Expected a valid UUID string "
|
|
29
|
+
f"(e.g., '6ba7b810-9dad-11d1-80b4-00c04fd430c8') "
|
|
30
|
+
f"or use predefined constants like uuid.NAMESPACE_DNS"
|
|
31
|
+
) from exc
|
|
32
|
+
return namespace
|
|
33
|
+
|
|
34
|
+
def load(self, context: Context, *args: Any, **kwargs: Any) -> UUID:
|
|
35
|
+
if kwargs.get("seed") is not None:
|
|
36
|
+
random.seed(kwargs["seed"])
|
|
37
|
+
|
|
38
|
+
version = kwargs.get("version", 4)
|
|
39
|
+
|
|
40
|
+
if version == 1:
|
|
41
|
+
return uuid1()
|
|
42
|
+
|
|
43
|
+
if version == 3:
|
|
44
|
+
namespace = self._validate_namespace(kwargs.get("namespace"), 3)
|
|
45
|
+
uuid_name = kwargs.get("uuid_name")
|
|
46
|
+
if uuid_name is None:
|
|
47
|
+
raise ValueError("uuid_name is required for UUID version 3")
|
|
48
|
+
return uuid3(namespace, uuid_name)
|
|
49
|
+
|
|
50
|
+
if version == 4:
|
|
51
|
+
random_bytes = bytearray(random.getrandbits(8) for _ in range(16))
|
|
52
|
+
random_bytes[6] = (random_bytes[6] & 0x0F) | 0x40
|
|
53
|
+
random_bytes[8] = (random_bytes[8] & 0x3F) | 0x80
|
|
54
|
+
return UUID(bytes=bytes(random_bytes))
|
|
55
|
+
|
|
56
|
+
if version == 5:
|
|
57
|
+
namespace = self._validate_namespace(kwargs.get("namespace"), 5)
|
|
58
|
+
uuid_name = kwargs.get("uuid_name")
|
|
59
|
+
if uuid_name is None:
|
|
60
|
+
raise ValueError("uuid_name is required for UUID version 5")
|
|
61
|
+
return uuid5(namespace, uuid_name)
|
|
62
|
+
|
|
63
|
+
raise ValueError(
|
|
64
|
+
f"Unsupported UUID version: {version}. "
|
|
65
|
+
f"Supported versions are 1, 3, 4, and 5."
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def random_uuid(
|
|
70
|
+
name: str,
|
|
71
|
+
version: Literal[1, 3, 4, 5] = 4,
|
|
72
|
+
namespace: UUID | str | None = None,
|
|
73
|
+
uuid_name: str | None = None,
|
|
74
|
+
seed: int | None = None,
|
|
75
|
+
) -> Decorator:
|
|
76
|
+
"""
|
|
77
|
+
Generate a random UUID.
|
|
78
|
+
|
|
79
|
+
Type: `ParentNode`
|
|
80
|
+
|
|
81
|
+
Args:
|
|
82
|
+
name (str):
|
|
83
|
+
The name of the parent node.
|
|
84
|
+
version (Literal[1, 3, 4, 5], optional):
|
|
85
|
+
The version of the UUID. Defaults to 4.
|
|
86
|
+
- Version 1: Time-based UUID (includes MAC address and timestamp)
|
|
87
|
+
- Version 3: MD5 hash of namespace + name (deterministic)
|
|
88
|
+
- Version 4: Random UUID (recommended for most use cases)
|
|
89
|
+
- Version 5: SHA-1 hash of namespace + name (deterministic)
|
|
90
|
+
namespace (UUID | str | None, optional):
|
|
91
|
+
The namespace UUID for versions 3 and 5. Required for those
|
|
92
|
+
versions. Must be a valid UUID object, a valid UUID string in the
|
|
93
|
+
format 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx', or use predefined
|
|
94
|
+
constants from the uuid module (NAMESPACE_DNS, NAMESPACE_URL,
|
|
95
|
+
NAMESPACE_OID, NAMESPACE_X500). The namespace acts as a domain
|
|
96
|
+
identifier to generate deterministic UUIDs.
|
|
97
|
+
uuid_name (str | None, optional):
|
|
98
|
+
The name string for versions 3 and 5. Required for those versions.
|
|
99
|
+
This is combined with the namespace to generate a deterministic
|
|
100
|
+
UUID. The same namespace and name will always produce the same UUID.
|
|
101
|
+
seed (int | None, optional):
|
|
102
|
+
Optional seed for reproducible UUIDs (only affects version 4).
|
|
103
|
+
|
|
104
|
+
Returns:
|
|
105
|
+
Decorator:
|
|
106
|
+
The decorator function.
|
|
107
|
+
|
|
108
|
+
Raises:
|
|
109
|
+
ValueError:
|
|
110
|
+
If an unsupported UUID version is specified or if namespace/name
|
|
111
|
+
are missing for versions 3 or 5.
|
|
112
|
+
"""
|
|
113
|
+
return RandomUUID.as_decorator(
|
|
114
|
+
name=name,
|
|
115
|
+
version=version,
|
|
116
|
+
namespace=namespace,
|
|
117
|
+
uuid_name=uuid_name,
|
|
118
|
+
seed=seed,
|
|
119
|
+
)
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"""Initialization file for the `click_extended.decorators.transform` module."""
|
|
2
|
+
|
|
3
|
+
from click_extended.decorators.transform.add_prefix import add_prefix
|
|
4
|
+
from click_extended.decorators.transform.add_suffix import add_suffix
|
|
5
|
+
from click_extended.decorators.transform.apply import apply
|
|
6
|
+
from click_extended.decorators.transform.basename import basename
|
|
7
|
+
from click_extended.decorators.transform.dirname import dirname
|
|
8
|
+
from click_extended.decorators.transform.expand_vars import expand_vars
|
|
9
|
+
from click_extended.decorators.transform.remove_prefix import remove_prefix
|
|
10
|
+
from click_extended.decorators.transform.remove_suffix import remove_suffix
|
|
11
|
+
from click_extended.decorators.transform.replace import replace
|
|
12
|
+
from click_extended.decorators.transform.slugify import slugify
|
|
13
|
+
from click_extended.decorators.transform.split import split
|
|
14
|
+
from click_extended.decorators.transform.strip import lstrip, rstrip, strip
|
|
15
|
+
from click_extended.decorators.transform.to_case import (
|
|
16
|
+
to_camel_case,
|
|
17
|
+
to_dot_case,
|
|
18
|
+
to_flat_case,
|
|
19
|
+
to_kebab_case,
|
|
20
|
+
to_lower_case,
|
|
21
|
+
to_meme_case,
|
|
22
|
+
to_pascal_case,
|
|
23
|
+
to_path_case,
|
|
24
|
+
to_screaming_snake_case,
|
|
25
|
+
to_snake_case,
|
|
26
|
+
to_title_case,
|
|
27
|
+
to_train_case,
|
|
28
|
+
to_upper_case,
|
|
29
|
+
)
|
|
30
|
+
from click_extended.decorators.transform.to_date import to_date
|
|
31
|
+
from click_extended.decorators.transform.to_datetime import to_datetime
|
|
32
|
+
from click_extended.decorators.transform.to_path import to_path
|
|
33
|
+
from click_extended.decorators.transform.to_time import to_time
|
|
34
|
+
from click_extended.decorators.transform.to_timestamp import to_timestamp
|
|
35
|
+
from click_extended.decorators.transform.truncate import truncate
|
|
36
|
+
|
|
37
|
+
__all__ = [
|
|
38
|
+
"add_prefix",
|
|
39
|
+
"add_suffix",
|
|
40
|
+
"apply",
|
|
41
|
+
"basename",
|
|
42
|
+
"dirname",
|
|
43
|
+
"expand_vars",
|
|
44
|
+
"lstrip",
|
|
45
|
+
"remove_prefix",
|
|
46
|
+
"remove_suffix",
|
|
47
|
+
"replace",
|
|
48
|
+
"rstrip",
|
|
49
|
+
"slugify",
|
|
50
|
+
"split",
|
|
51
|
+
"strip",
|
|
52
|
+
"to_camel_case",
|
|
53
|
+
"to_dot_case",
|
|
54
|
+
"to_flat_case",
|
|
55
|
+
"to_kebab_case",
|
|
56
|
+
"to_lower_case",
|
|
57
|
+
"to_meme_case",
|
|
58
|
+
"to_pascal_case",
|
|
59
|
+
"to_path_case",
|
|
60
|
+
"to_screaming_snake_case",
|
|
61
|
+
"to_snake_case",
|
|
62
|
+
"to_title_case",
|
|
63
|
+
"to_train_case",
|
|
64
|
+
"to_upper_case",
|
|
65
|
+
"to_date",
|
|
66
|
+
"to_datetime",
|
|
67
|
+
"to_path",
|
|
68
|
+
"to_time",
|
|
69
|
+
"to_timestamp",
|
|
70
|
+
"truncate",
|
|
71
|
+
]
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"""Child node to add a prefix to a string."""
|
|
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 AddPrefix(ChildNode):
|
|
11
|
+
"""Child node to add a prefix to a string."""
|
|
12
|
+
|
|
13
|
+
def handle_str(
|
|
14
|
+
self, value: str, context: Context, *args: Any, **kwargs: Any
|
|
15
|
+
) -> Any:
|
|
16
|
+
prefix = kwargs["prefix"]
|
|
17
|
+
skip = kwargs["skip"]
|
|
18
|
+
case_sensitive = kwargs["case_sensitive"]
|
|
19
|
+
|
|
20
|
+
if skip:
|
|
21
|
+
if case_sensitive:
|
|
22
|
+
if value.startswith(prefix):
|
|
23
|
+
return value
|
|
24
|
+
else:
|
|
25
|
+
if value.lower().startswith(prefix.lower()):
|
|
26
|
+
return value
|
|
27
|
+
|
|
28
|
+
return prefix + value
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def add_prefix(
|
|
32
|
+
prefix: str,
|
|
33
|
+
skip: bool = True,
|
|
34
|
+
case_sensitive: bool = False,
|
|
35
|
+
) -> Decorator:
|
|
36
|
+
"""
|
|
37
|
+
Add a prefix to a string.
|
|
38
|
+
|
|
39
|
+
Type: `ChildNode`
|
|
40
|
+
|
|
41
|
+
Supports: `str`
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
prefix (str):
|
|
45
|
+
The prefix to add.
|
|
46
|
+
skip (bool, optional):
|
|
47
|
+
Skip adding the prefix if it already exists. Defaults to `True`.
|
|
48
|
+
case_sensitive (bool)
|
|
49
|
+
Check for exact case matching when `skip=True`. Defaults to `False`.
|
|
50
|
+
Returns:
|
|
51
|
+
Decorator:
|
|
52
|
+
The decorated function.
|
|
53
|
+
"""
|
|
54
|
+
return AddPrefix.as_decorator(
|
|
55
|
+
prefix=prefix,
|
|
56
|
+
skip=skip,
|
|
57
|
+
case_sensitive=case_sensitive,
|
|
58
|
+
)
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"""Child node to add a suffix to a string."""
|
|
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 AddSuffix(ChildNode):
|
|
11
|
+
"""Child node to add a suffix to a string."""
|
|
12
|
+
|
|
13
|
+
def handle_str(
|
|
14
|
+
self, value: str, context: Context, *args: Any, **kwargs: Any
|
|
15
|
+
) -> Any:
|
|
16
|
+
suffix = kwargs["suffix"]
|
|
17
|
+
skip = kwargs["skip"]
|
|
18
|
+
case_sensitive = kwargs["case_sensitive"]
|
|
19
|
+
|
|
20
|
+
if skip:
|
|
21
|
+
if case_sensitive:
|
|
22
|
+
if value.endswith(suffix):
|
|
23
|
+
return value
|
|
24
|
+
else:
|
|
25
|
+
if value.lower().endswith(suffix.lower()):
|
|
26
|
+
return value
|
|
27
|
+
|
|
28
|
+
return value + suffix
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def add_suffix(
|
|
32
|
+
suffix: str,
|
|
33
|
+
skip: bool = True,
|
|
34
|
+
case_sensitive: bool = False,
|
|
35
|
+
) -> Decorator:
|
|
36
|
+
"""
|
|
37
|
+
Add a suffix to a string.
|
|
38
|
+
|
|
39
|
+
Type: `ChildNode`
|
|
40
|
+
|
|
41
|
+
Supports: `str`
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
suffix (str):
|
|
45
|
+
The suffix to add.
|
|
46
|
+
skip (bool, optional):
|
|
47
|
+
Skip adding the suffix if it already exists. Defaults to `True`.
|
|
48
|
+
case_sensitive (bool)
|
|
49
|
+
Check for exact case matching when `skip=True`. Defaults to `False`.
|
|
50
|
+
Returns:
|
|
51
|
+
Decorator:
|
|
52
|
+
The decorated function.
|
|
53
|
+
"""
|
|
54
|
+
return AddSuffix.as_decorator(
|
|
55
|
+
suffix=suffix,
|
|
56
|
+
skip=skip,
|
|
57
|
+
case_sensitive=case_sensitive,
|
|
58
|
+
)
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"""Child decorator to apply an arbitrary function to any input."""
|
|
2
|
+
|
|
3
|
+
from typing import Any, Callable
|
|
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 Apply(ChildNode):
|
|
11
|
+
"""Child decorator to apply an arbitrary function to any input."""
|
|
12
|
+
|
|
13
|
+
def handle_all(
|
|
14
|
+
self, value: Any, context: Context, *args: Any, **kwargs: Any
|
|
15
|
+
) -> Any:
|
|
16
|
+
return kwargs["fn"](value)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def apply(fn: Callable[[Any], Any]) -> Decorator:
|
|
20
|
+
"""
|
|
21
|
+
A decorator to apply an arbitrary function to all input.
|
|
22
|
+
|
|
23
|
+
Type: `ChildNode`
|
|
24
|
+
|
|
25
|
+
Supports: `Any`
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
fn (Callable[[Any], Any]):
|
|
29
|
+
The function to apply.
|
|
30
|
+
|
|
31
|
+
Returns:
|
|
32
|
+
Decorator:
|
|
33
|
+
The decorated function.
|
|
34
|
+
"""
|
|
35
|
+
return Apply.as_decorator(fn=fn)
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"""Extract the base name from a path."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
import click
|
|
8
|
+
|
|
9
|
+
from click_extended.core.nodes.child_node import ChildNode
|
|
10
|
+
from click_extended.core.other.context import Context
|
|
11
|
+
from click_extended.types import Decorator
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class Basename(ChildNode):
|
|
15
|
+
"""Extract the base name from a path."""
|
|
16
|
+
|
|
17
|
+
def handle_str(
|
|
18
|
+
self, value: str, context: Context, *args: Any, **kwargs: Any
|
|
19
|
+
) -> str:
|
|
20
|
+
try:
|
|
21
|
+
return os.path.basename(value)
|
|
22
|
+
except ValueError as e:
|
|
23
|
+
raise click.BadParameter(str(e)) from e
|
|
24
|
+
|
|
25
|
+
def handle_path(
|
|
26
|
+
self, value: Path, context: Context, *args: Any, **kwargs: Any
|
|
27
|
+
) -> str:
|
|
28
|
+
return value.name
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def basename() -> Decorator:
|
|
32
|
+
"""
|
|
33
|
+
Extract the base name from a path.
|
|
34
|
+
|
|
35
|
+
Example:
|
|
36
|
+
>>> @basename()
|
|
37
|
+
... @option("--path")
|
|
38
|
+
... def cli(path):
|
|
39
|
+
... print(path)
|
|
40
|
+
...
|
|
41
|
+
>>> cli(["--path", "/foo/bar/baz.txt"])
|
|
42
|
+
baz.txt
|
|
43
|
+
"""
|
|
44
|
+
return Basename.as_decorator()
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"""Extract the directory name from a path."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
import click
|
|
8
|
+
|
|
9
|
+
from click_extended.core.nodes.child_node import ChildNode
|
|
10
|
+
from click_extended.core.other.context import Context
|
|
11
|
+
from click_extended.types import Decorator
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class Dirname(ChildNode):
|
|
15
|
+
"""Extract the directory name from a path."""
|
|
16
|
+
|
|
17
|
+
def handle_str(
|
|
18
|
+
self, value: str, context: Context, *args: Any, **kwargs: Any
|
|
19
|
+
) -> str:
|
|
20
|
+
try:
|
|
21
|
+
return os.path.dirname(value)
|
|
22
|
+
except ValueError as e:
|
|
23
|
+
raise click.BadParameter(str(e)) from e
|
|
24
|
+
|
|
25
|
+
def handle_path(
|
|
26
|
+
self, value: Path, context: Context, *args: Any, **kwargs: Any
|
|
27
|
+
) -> Path:
|
|
28
|
+
return value.parent
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def dirname() -> Decorator:
|
|
32
|
+
"""
|
|
33
|
+
Extract the directory name from a path.
|
|
34
|
+
|
|
35
|
+
Example:
|
|
36
|
+
>>> @dirname()
|
|
37
|
+
... @option("--path")
|
|
38
|
+
... def cli(path):
|
|
39
|
+
... print(path)
|
|
40
|
+
...
|
|
41
|
+
>>> cli(["--path", "/foo/bar/baz.txt"])
|
|
42
|
+
/foo/bar
|
|
43
|
+
"""
|
|
44
|
+
return Dirname.as_decorator()
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"""Expand environment variables in the string."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
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
|
+
|
|
10
|
+
|
|
11
|
+
class ExpandVars(ChildNode):
|
|
12
|
+
"""Expand environment variables in the string."""
|
|
13
|
+
|
|
14
|
+
def handle_str(
|
|
15
|
+
self,
|
|
16
|
+
value: str,
|
|
17
|
+
context: Context,
|
|
18
|
+
*args: Any,
|
|
19
|
+
**kwargs: Any,
|
|
20
|
+
) -> str:
|
|
21
|
+
return os.path.expandvars(value)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def expand_vars() -> Decorator:
|
|
25
|
+
"""
|
|
26
|
+
Expand environment variables in the string.
|
|
27
|
+
|
|
28
|
+
Type: `ChildNode`
|
|
29
|
+
|
|
30
|
+
Supports: `str`
|
|
31
|
+
|
|
32
|
+
Returns:
|
|
33
|
+
Decorator:
|
|
34
|
+
The decorated function.
|
|
35
|
+
"""
|
|
36
|
+
return ExpandVars.as_decorator()
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"""Child decorator to remove a prefix from a string."""
|
|
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 RemovePrefix(ChildNode):
|
|
11
|
+
"""Child decorator to remove a prefix from a string."""
|
|
12
|
+
|
|
13
|
+
def handle_str(
|
|
14
|
+
self, value: str, context: Context, *args: Any, **kwargs: Any
|
|
15
|
+
) -> str:
|
|
16
|
+
prefix: str = kwargs["prefix"]
|
|
17
|
+
|
|
18
|
+
if value.startswith(prefix):
|
|
19
|
+
return value[len(prefix) :]
|
|
20
|
+
|
|
21
|
+
return value
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def remove_prefix(prefix: str) -> Decorator:
|
|
25
|
+
"""
|
|
26
|
+
Remove a prefix from a string if it exists.
|
|
27
|
+
|
|
28
|
+
Type: `ChildNode`
|
|
29
|
+
|
|
30
|
+
Supports: `str`
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
prefix (str):
|
|
34
|
+
The prefix to remove from the beginning of the string.
|
|
35
|
+
|
|
36
|
+
Returns:
|
|
37
|
+
Decorator:
|
|
38
|
+
The decorator function.
|
|
39
|
+
|
|
40
|
+
Examples:
|
|
41
|
+
```python
|
|
42
|
+
@command()
|
|
43
|
+
@option("name", default="Mr. John")
|
|
44
|
+
@remove_prefix("Mr. ")
|
|
45
|
+
def cmd(name: str) -> None:
|
|
46
|
+
click.echo(f"Name: {name}") # Output: Name: John
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
```python
|
|
50
|
+
@command()
|
|
51
|
+
@option("url", default="https://example.com")
|
|
52
|
+
@remove_prefix("https://")
|
|
53
|
+
def cmd(url: str) -> None:
|
|
54
|
+
click.echo(f"Domain: {url}") # Output: Domain: example.com
|
|
55
|
+
```
|
|
56
|
+
"""
|
|
57
|
+
return RemovePrefix.as_decorator(prefix=prefix)
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"""Child decorator to remove a suffix from a string."""
|
|
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 RemoveSuffix(ChildNode):
|
|
11
|
+
"""Child decorator to remove a suffix from a string."""
|
|
12
|
+
|
|
13
|
+
def handle_str(
|
|
14
|
+
self, value: str, context: Context, *args: Any, **kwargs: Any
|
|
15
|
+
) -> str:
|
|
16
|
+
suffix: str = kwargs["suffix"]
|
|
17
|
+
|
|
18
|
+
if value.endswith(suffix):
|
|
19
|
+
return value[: -len(suffix)]
|
|
20
|
+
|
|
21
|
+
return value
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def remove_suffix(suffix: str) -> Decorator:
|
|
25
|
+
"""
|
|
26
|
+
Remove a suffix from a string if it exists.
|
|
27
|
+
|
|
28
|
+
Type: `ChildNode`
|
|
29
|
+
|
|
30
|
+
Supports: `str`
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
suffix (str):
|
|
34
|
+
The suffix to remove from the end of the string.
|
|
35
|
+
|
|
36
|
+
Returns:
|
|
37
|
+
Decorator:
|
|
38
|
+
The decorator function.
|
|
39
|
+
|
|
40
|
+
Examples:
|
|
41
|
+
```python
|
|
42
|
+
@command()
|
|
43
|
+
@option("filename", default="document.txt")
|
|
44
|
+
@remove_suffix(".txt")
|
|
45
|
+
def cmd(filename: str) -> None:
|
|
46
|
+
click.echo(f"Name: {filename}") # Output: Name: document
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
```python
|
|
50
|
+
@command()
|
|
51
|
+
@option("url", default="example.com/")
|
|
52
|
+
@remove_suffix("/")
|
|
53
|
+
def cmd(url: str) -> None:
|
|
54
|
+
click.echo(f"URL: {url}") # Output: URL: example.com
|
|
55
|
+
```
|
|
56
|
+
"""
|
|
57
|
+
return RemoveSuffix.as_decorator(suffix=suffix)
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"""Replace occurrences of a substring with another."""
|
|
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 Replace(ChildNode):
|
|
11
|
+
"""Replace occurrences of a substring with another."""
|
|
12
|
+
|
|
13
|
+
def handle_str(
|
|
14
|
+
self,
|
|
15
|
+
value: str,
|
|
16
|
+
context: Context,
|
|
17
|
+
*args: Any,
|
|
18
|
+
**kwargs: Any,
|
|
19
|
+
) -> str:
|
|
20
|
+
old = kwargs["old"]
|
|
21
|
+
new = kwargs["new"]
|
|
22
|
+
count = kwargs.get("count", -1)
|
|
23
|
+
return value.replace(old, new, count)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def replace(old: str, new: str, count: int = -1) -> Decorator:
|
|
27
|
+
"""
|
|
28
|
+
Replace occurrences of a substring with another.
|
|
29
|
+
|
|
30
|
+
Type: `ChildNode`
|
|
31
|
+
|
|
32
|
+
Supports: `str`
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
old (str):
|
|
36
|
+
The substring to replace.
|
|
37
|
+
new (str):
|
|
38
|
+
The replacement substring.
|
|
39
|
+
count (int):
|
|
40
|
+
Maximum number of occurrences to replace. Defaults to `-1` (all).
|
|
41
|
+
|
|
42
|
+
Returns:
|
|
43
|
+
Decorator:
|
|
44
|
+
The decorated function.
|
|
45
|
+
"""
|
|
46
|
+
return Replace.as_decorator(old=old, new=new, count=count)
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"""Convert the string to a slug."""
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from slugify import slugify as _slugify
|
|
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
|
+
|
|
11
|
+
|
|
12
|
+
class Slugify(ChildNode):
|
|
13
|
+
"""Convert the string to a slug."""
|
|
14
|
+
|
|
15
|
+
def handle_str(
|
|
16
|
+
self,
|
|
17
|
+
value: str,
|
|
18
|
+
context: Context,
|
|
19
|
+
*args: Any,
|
|
20
|
+
**kwargs: Any,
|
|
21
|
+
) -> str:
|
|
22
|
+
return _slugify(value, **kwargs)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def slugify(**kwargs: Any) -> Decorator:
|
|
26
|
+
"""
|
|
27
|
+
Convert the string to a slug.
|
|
28
|
+
|
|
29
|
+
This decorator uses the `python-slugify` library under the hood.
|
|
30
|
+
|
|
31
|
+
Read more about the library here: https://pypi.org/project/python-slugify/
|
|
32
|
+
|
|
33
|
+
Type: `ChildNode`
|
|
34
|
+
|
|
35
|
+
Supports: `str`
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
**kwargs (Any):
|
|
39
|
+
Additional keyword arguments passed to `slugify.slugify`.
|
|
40
|
+
|
|
41
|
+
Returns:
|
|
42
|
+
Decorator:
|
|
43
|
+
The decorated function.
|
|
44
|
+
"""
|
|
45
|
+
return Slugify.as_decorator(**kwargs)
|