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,211 @@
1
+ """Convert between different bit/byte units."""
2
+
3
+ from decimal import Decimal, getcontext
4
+ from typing import Any, Literal
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
+ getcontext().prec = 35
11
+
12
+ UNITS = {
13
+ "B": Decimal("1"),
14
+ "kB": Decimal("1000"),
15
+ "MB": Decimal("1000") ** 2,
16
+ "GB": Decimal("1000") ** 3,
17
+ "TB": Decimal("1000") ** 4,
18
+ "PB": Decimal("1000") ** 5,
19
+ "EB": Decimal("1000") ** 6,
20
+ "ZB": Decimal("1000") ** 7,
21
+ "YB": Decimal("1000") ** 8,
22
+ "RB": Decimal("1000") ** 9,
23
+ "QB": Decimal("1000") ** 10,
24
+ "KiB": Decimal("1024"),
25
+ "MiB": Decimal("1024") ** 2,
26
+ "GiB": Decimal("1024") ** 3,
27
+ "TiB": Decimal("1024") ** 4,
28
+ "PiB": Decimal("1024") ** 5,
29
+ "EiB": Decimal("1024") ** 6,
30
+ "ZiB": Decimal("1024") ** 7,
31
+ "YiB": Decimal("1024") ** 8,
32
+ # Bit units
33
+ "b": Decimal("0.125"),
34
+ "kb": Decimal("1000") * Decimal("0.125"),
35
+ "Mb": Decimal("1000") ** 2 * Decimal("0.125"),
36
+ "Gb": Decimal("1000") ** 3 * Decimal("0.125"),
37
+ "Tb": Decimal("1000") ** 4 * Decimal("0.125"),
38
+ "Pb": Decimal("1000") ** 5 * Decimal("0.125"),
39
+ "Eb": Decimal("1000") ** 6 * Decimal("0.125"),
40
+ "Zb": Decimal("1000") ** 7 * Decimal("0.125"),
41
+ "Yb": Decimal("1000") ** 8 * Decimal("0.125"),
42
+ "Rb": Decimal("1000") ** 9 * Decimal("0.125"),
43
+ "Qb": Decimal("1000") ** 10 * Decimal("0.125"),
44
+ "Kib": Decimal("1024") * Decimal("0.125"),
45
+ "Mib": Decimal("1024") ** 2 * Decimal("0.125"),
46
+ "Gib": Decimal("1024") ** 3 * Decimal("0.125"),
47
+ "Tib": Decimal("1024") ** 4 * Decimal("0.125"),
48
+ "Pib": Decimal("1024") ** 5 * Decimal("0.125"),
49
+ "Eib": Decimal("1024") ** 6 * Decimal("0.125"),
50
+ "Zib": Decimal("1024") ** 7 * Decimal("0.125"),
51
+ "Yib": Decimal("1024") ** 8 * Decimal("0.125"),
52
+ }
53
+
54
+
55
+ class ConvertBits(ChildNode):
56
+ """Convert between different bit/byte units."""
57
+
58
+ def handle_numeric(
59
+ self,
60
+ value: int | float,
61
+ context: Context,
62
+ *args: Any,
63
+ **kwargs: Any,
64
+ ) -> float:
65
+ from_unit = kwargs["from_unit"]
66
+ to_unit = kwargs["to_unit"]
67
+ val = Decimal(str(value))
68
+
69
+ if from_unit not in UNITS:
70
+ raise ValueError(f"Unknown unit '{from_unit}'")
71
+ if to_unit not in UNITS:
72
+ raise ValueError(f"Unknown unit '{to_unit}'")
73
+
74
+ bytes_val = val * UNITS[from_unit]
75
+ return float(bytes_val / UNITS[to_unit])
76
+
77
+
78
+ def convert_bits(
79
+ from_unit: Literal[
80
+ "B",
81
+ "kB",
82
+ "MB",
83
+ "GB",
84
+ "TB",
85
+ "PB",
86
+ "EB",
87
+ "ZB",
88
+ "YB",
89
+ "RB",
90
+ "QB",
91
+ "KiB",
92
+ "MiB",
93
+ "GiB",
94
+ "TiB",
95
+ "PiB",
96
+ "EiB",
97
+ "ZiB",
98
+ "YiB",
99
+ "b",
100
+ "kb",
101
+ "Mb",
102
+ "Gb",
103
+ "Tb",
104
+ "Pb",
105
+ "Eb",
106
+ "Zb",
107
+ "Yb",
108
+ "Rb",
109
+ "Qb",
110
+ "Kib",
111
+ "Mib",
112
+ "Gib",
113
+ "Tib",
114
+ "Pib",
115
+ "Eib",
116
+ "Zib",
117
+ "Yib",
118
+ ],
119
+ to_unit: Literal[
120
+ "B",
121
+ "kB",
122
+ "MB",
123
+ "GB",
124
+ "TB",
125
+ "PB",
126
+ "EB",
127
+ "ZB",
128
+ "YB",
129
+ "RB",
130
+ "QB",
131
+ "KiB",
132
+ "MiB",
133
+ "GiB",
134
+ "TiB",
135
+ "PiB",
136
+ "EiB",
137
+ "ZiB",
138
+ "YiB",
139
+ "b",
140
+ "kb",
141
+ "Mb",
142
+ "Gb",
143
+ "Tb",
144
+ "Pb",
145
+ "Eb",
146
+ "Zb",
147
+ "Yb",
148
+ "Rb",
149
+ "Qb",
150
+ "Kib",
151
+ "Mib",
152
+ "Gib",
153
+ "Tib",
154
+ "Pib",
155
+ "Eib",
156
+ "Zib",
157
+ "Yib",
158
+ ],
159
+ ) -> Decorator:
160
+ """
161
+ Convert between different bit/byte units.
162
+
163
+ Type: `ChildNode`
164
+
165
+ Supports: `int`, `float`
166
+
167
+ Units:
168
+ - **B**: Bytes
169
+ - **kB**: Kilobytes
170
+ - **MB**: Megabytes
171
+ - **GB**: Gigabytes
172
+ - **TB**: Terabytes
173
+ - **PB**: Petabytes
174
+ - **EB**: Exabytes
175
+ - **ZB**: Zettabytes
176
+ - **YB**: Yottabytes
177
+ - **RB**: Ronnabytes
178
+ - **QB**: Quettabytes
179
+ - **KiB**: Kibibytes
180
+ - **MiB**: Mebibytes
181
+ - **GiB**: Gibibytes
182
+ - **TiB**: Tebibytes
183
+ - **PiB**: Pebibytes
184
+ - **EiB**: Exbibytes
185
+ - **ZiB**: Zebibytes
186
+ - **YiB**: Yobibytes
187
+ - **b**: Bits
188
+ - **kb**: Kilobits
189
+ - **Mb**: Megabits
190
+ - **Gb**: Gigabits
191
+ - **Tb**: Terabits
192
+ - **Pb**: Petabits
193
+ - **Eb**: Exabits
194
+ - **Zb**: Zettabits
195
+ - **Yb**: Yottabits
196
+ - **Rb**: Ronnabits
197
+ - **Qb**: Quettabits
198
+ - **Kib**: Kibibits
199
+ - **Mib**: Mebibits
200
+ - **Gib**: Gibibits
201
+ - **Tib**: Tebibits
202
+ - **Pib**: Pebibits
203
+ - **Eib**: Exbibits
204
+ - **Zib**: Zebibits
205
+ - **Yib**: Yobibits
206
+
207
+ Returns:
208
+ Decorator:
209
+ The decorated function.
210
+ """
211
+ return ConvertBits.as_decorator(from_unit=from_unit, to_unit=to_unit)
@@ -0,0 +1,154 @@
1
+ """Convert between various distance units."""
2
+
3
+ from decimal import Decimal, getcontext
4
+ from typing import Any, Literal
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
+ getcontext().prec = 35
11
+
12
+ UNITS = {
13
+ "Qm": Decimal("1e30"),
14
+ "Rm": Decimal("1e27"),
15
+ "ym": Decimal("1e-24"),
16
+ "zm": Decimal("1e-21"),
17
+ "am": Decimal("1e-18"),
18
+ "fm": Decimal("1e-15"),
19
+ "pm": Decimal("1e-12"),
20
+ "nm": Decimal("1e-9"),
21
+ "us": Decimal("1e-6"),
22
+ "mm": Decimal("1e-3"),
23
+ "cm": Decimal("1e-2"),
24
+ "dm": Decimal("1e-1"),
25
+ "m": Decimal("1"),
26
+ "km": Decimal("1e3"),
27
+ "mil": Decimal("1e4"),
28
+ "AU": Decimal("149597870700"),
29
+ "ly": Decimal("9460730472580800"),
30
+ "pc": Decimal("3.0856775814913673e16"),
31
+ "in": Decimal("0.0254"),
32
+ "ft": Decimal("0.3048"),
33
+ "yd": Decimal("0.9144"),
34
+ "mi": Decimal("1609.344"),
35
+ "nmi": Decimal("1852"),
36
+ "ang": Decimal("1e-10"),
37
+ }
38
+
39
+
40
+ class ConvertDistance(ChildNode):
41
+ """Convert between various distance units."""
42
+
43
+ def handle_numeric(
44
+ self,
45
+ value: int | float,
46
+ context: Context,
47
+ *args: Any,
48
+ **kwargs: Any,
49
+ ) -> float:
50
+ from_unit = kwargs["from_unit"]
51
+ to_unit = kwargs["to_unit"]
52
+ val = Decimal(str(value))
53
+
54
+ if from_unit not in UNITS:
55
+ raise ValueError(f"Unknown unit '{from_unit}'")
56
+ if to_unit not in UNITS:
57
+ raise ValueError(f"Unknown unit '{to_unit}'")
58
+
59
+ meters = val * UNITS[from_unit]
60
+ return float(meters / UNITS[to_unit])
61
+
62
+
63
+ def convert_distance(
64
+ from_unit: Literal[
65
+ "Qm",
66
+ "Rm",
67
+ "ym",
68
+ "zm",
69
+ "am",
70
+ "fm",
71
+ "pm",
72
+ "nm",
73
+ "us",
74
+ "mm",
75
+ "cm",
76
+ "dm",
77
+ "m",
78
+ "km",
79
+ "mil",
80
+ "AU",
81
+ "ly",
82
+ "pc",
83
+ "in",
84
+ "ft",
85
+ "yd",
86
+ "mi",
87
+ "nmi",
88
+ "ang",
89
+ ],
90
+ to_unit: Literal[
91
+ "Qm",
92
+ "Rm",
93
+ "ym",
94
+ "zm",
95
+ "am",
96
+ "fm",
97
+ "pm",
98
+ "nm",
99
+ "us",
100
+ "mm",
101
+ "cm",
102
+ "dm",
103
+ "m",
104
+ "km",
105
+ "mil",
106
+ "AU",
107
+ "ly",
108
+ "pc",
109
+ "in",
110
+ "ft",
111
+ "yd",
112
+ "mi",
113
+ "nmi",
114
+ "ang",
115
+ ],
116
+ ) -> Decorator:
117
+ """
118
+ Convert between various distance units.
119
+
120
+ Type: `ChildNode`
121
+
122
+ Supports: `int`, `float`
123
+
124
+ Units:
125
+ - **Qm**: Quettameter
126
+ - **Rm**: Ronnameter
127
+ - **ym**: Yoctometer
128
+ - **zm**: Zeptometer
129
+ - **am**: Attometer
130
+ - **fm**: Femtometer
131
+ - **pm**: Picometer
132
+ - **nm**: Nanometer
133
+ - **us**: Micrometer
134
+ - **mm**: Millimeter
135
+ - **cm**: Centimeter
136
+ - **dm**: Decimeter
137
+ - **m**: Meter
138
+ - **km**: Kilometer
139
+ - **mil**: Swedish mile (10 kilometers)
140
+ - **AU**: Astronomical Unit
141
+ - **ly**: Light-year
142
+ - **pc**: Parsec
143
+ - **in**: Inch
144
+ - **ft**: Foot
145
+ - **yd**: Yard
146
+ - **mi**: Mile
147
+ - **nmi**: Nautical mile
148
+ - **ang**: Angstrom
149
+
150
+ Returns:
151
+ Decorator:
152
+ The decorated function.
153
+ """
154
+ return ConvertDistance.as_decorator(from_unit=from_unit, to_unit=to_unit)
@@ -0,0 +1,155 @@
1
+ """Convert between different energy units."""
2
+
3
+ from decimal import Decimal, getcontext
4
+ from typing import Any, Literal
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
+ getcontext().prec = 35
11
+
12
+ UNITS = {
13
+ "J": Decimal("1"),
14
+ "kJ": Decimal("1e3"),
15
+ "MJ": Decimal("1e6"),
16
+ "GJ": Decimal("1e9"),
17
+ "TJ": Decimal("1e12"),
18
+ "Wh": Decimal("3600"),
19
+ "kWh": Decimal("3.6e6"),
20
+ "MWh": Decimal("3.6e9"),
21
+ "GWh": Decimal("3.6e12"),
22
+ "TWh": Decimal("3.6e15"),
23
+ "cal": Decimal("4.184"),
24
+ "kcal": Decimal("4184"),
25
+ "eV": Decimal("1.602176634e-19"),
26
+ "keV": Decimal("1.602176634e-16"),
27
+ "MeV": Decimal("1.602176634e-13"),
28
+ "GeV": Decimal("1.602176634e-10"),
29
+ "TeV": Decimal("1.602176634e-7"),
30
+ "ftlb": Decimal("1.3558179483314004"),
31
+ "inlb": Decimal("0.1129848290276167"),
32
+ "Btu": Decimal("1055.05585262"),
33
+ "therm": Decimal("105505585.262"),
34
+ "erg": Decimal("1e-7"),
35
+ "ktTNT": Decimal("4.184e12"),
36
+ "MtTNT": Decimal("4.184e15"),
37
+ }
38
+
39
+
40
+ class ConvertEnergy(ChildNode):
41
+ """Convert between different energy units."""
42
+
43
+ def handle_numeric(
44
+ self,
45
+ value: int | float,
46
+ context: Context,
47
+ *args: Any,
48
+ **kwargs: Any,
49
+ ) -> float:
50
+ from_unit = kwargs["from_unit"]
51
+ to_unit = kwargs["to_unit"]
52
+ val = Decimal(str(value))
53
+
54
+ if from_unit not in UNITS:
55
+ raise ValueError(f"Unknown unit '{from_unit}'")
56
+ if to_unit not in UNITS:
57
+ raise ValueError(f"Unknown unit '{to_unit}'")
58
+
59
+ joules = val * UNITS[from_unit]
60
+
61
+ return float(joules / UNITS[to_unit])
62
+
63
+
64
+ def convert_energy(
65
+ from_unit: Literal[
66
+ "J",
67
+ "kJ",
68
+ "MJ",
69
+ "GJ",
70
+ "TJ",
71
+ "Wh",
72
+ "kWh",
73
+ "MWh",
74
+ "GWh",
75
+ "TWh",
76
+ "cal",
77
+ "kcal",
78
+ "eV",
79
+ "keV",
80
+ "MeV",
81
+ "GeV",
82
+ "TeV",
83
+ "ftlb",
84
+ "inlb",
85
+ "Btu",
86
+ "therm",
87
+ "erg",
88
+ "ktTNT",
89
+ "MtTNT",
90
+ ],
91
+ to_unit: Literal[
92
+ "J",
93
+ "kJ",
94
+ "MJ",
95
+ "GJ",
96
+ "TJ",
97
+ "Wh",
98
+ "kWh",
99
+ "MWh",
100
+ "GWh",
101
+ "TWh",
102
+ "cal",
103
+ "kcal",
104
+ "eV",
105
+ "keV",
106
+ "MeV",
107
+ "GeV",
108
+ "TeV",
109
+ "ftlb",
110
+ "inlb",
111
+ "Btu",
112
+ "therm",
113
+ "erg",
114
+ "ktTNT",
115
+ "MtTNT",
116
+ ],
117
+ ) -> Decorator:
118
+ """
119
+ Convert between different energy units.
120
+
121
+ Type: `ChildNode`
122
+
123
+ Supports: `int`, `float`
124
+
125
+ Units:
126
+ - **J**: Joule
127
+ - **kJ**: Kilojoule
128
+ - **MJ**: Megajoule
129
+ - **GJ**: Gigajoule
130
+ - **TJ**: Terajoule
131
+ - **Wh**: Watt-hour
132
+ - **kWh**: Kilowatt-hour
133
+ - **MWh**: Megawatt-hour
134
+ - **GWh**: Gigawatt-hour
135
+ - **TWh**: Terawatt-hour
136
+ - **cal**: Small calorie
137
+ - **kcal**: Kilocalorie
138
+ - **eV**: Electronvolt
139
+ - **keV**: Kiloelectronvolt
140
+ - **MeV**: Megaelectronvolt
141
+ - **GeV**: Gigaelectronvolt
142
+ - **TeV**: Teraelectronvolt
143
+ - **ftlb**: Foot-pound
144
+ - **inlb**: Inch-pound
145
+ - **Btu**: British thermal unit
146
+ - **therm**: Therm (natural gas energy unit)
147
+ - **erg**: Erg (centimeter-gram-second)
148
+ - **ktTNT**: Kiloton of TNT equivalent
149
+ - **MtTNT**: Megaton of TNT equivalent
150
+
151
+ Returns:
152
+ Decorator:
153
+ The decorated function.
154
+ """
155
+ return ConvertEnergy.as_decorator(from_unit=from_unit, to_unit=to_unit)
@@ -0,0 +1,128 @@
1
+ """Convert between different power units."""
2
+
3
+ from decimal import Decimal, getcontext
4
+ from typing import Any, Literal
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
+ getcontext().prec = 35
11
+
12
+ UNITS = {
13
+ "W": Decimal("1"),
14
+ "kW": Decimal("1e3"),
15
+ "MW": Decimal("1e6"),
16
+ "GW": Decimal("1e9"),
17
+ "TW": Decimal("1e12"),
18
+ "hp": Decimal("745.69987158227022"),
19
+ "hpM": Decimal("735.49875"),
20
+ "ftlbs": Decimal("1.3558179483314004"),
21
+ "Btuh": Decimal("0.2930710701722222"),
22
+ "Btus": Decimal("1055.05585262"),
23
+ "tonref": Decimal("3516.852842066666"),
24
+ }
25
+
26
+
27
+ class ConvertPower(ChildNode):
28
+ """Convert between different power units."""
29
+
30
+ def handle_numeric(
31
+ self,
32
+ value: int | float,
33
+ context: Context,
34
+ *args: Any,
35
+ **kwargs: Any,
36
+ ) -> float:
37
+ from_unit = kwargs["from_unit"]
38
+ to_unit = kwargs["to_unit"]
39
+ val = Decimal(str(value))
40
+
41
+ if from_unit == "dBW":
42
+ watts = Decimal(10) ** (val / 10)
43
+ elif from_unit == "dBm":
44
+ watts = Decimal(10) ** ((val - 30) / 10)
45
+ elif from_unit in UNITS:
46
+ watts = val * UNITS[from_unit]
47
+ else:
48
+ raise ValueError(f"Unknown unit '{from_unit}'")
49
+
50
+ if to_unit == "dBW":
51
+ if watts <= 0:
52
+ raise ValueError(
53
+ "Power must be positive for logarithmic conversion"
54
+ )
55
+ result = 10 * watts.log10()
56
+ elif to_unit == "dBm":
57
+ if watts <= 0:
58
+ raise ValueError(
59
+ "Power must be positive for logarithmic conversion"
60
+ )
61
+ result = 10 * (watts * 1000).log10()
62
+ elif to_unit in UNITS:
63
+ result = watts / UNITS[to_unit]
64
+ else:
65
+ raise ValueError(f"Unknown unit '{to_unit}'")
66
+
67
+ return float(result)
68
+
69
+
70
+ def convert_power(
71
+ from_unit: Literal[
72
+ "W",
73
+ "kW",
74
+ "MW",
75
+ "GW",
76
+ "TW",
77
+ "hp",
78
+ "hpM",
79
+ "dBW",
80
+ "dBm",
81
+ "Btuh",
82
+ "Btus",
83
+ "ftlbs",
84
+ "tonref",
85
+ ],
86
+ to_unit: Literal[
87
+ "W",
88
+ "kW",
89
+ "MW",
90
+ "GW",
91
+ "TW",
92
+ "hp",
93
+ "hpM",
94
+ "dBW",
95
+ "dBm",
96
+ "Btuh",
97
+ "Btus",
98
+ "ftlbs",
99
+ "tonref",
100
+ ],
101
+ ) -> Decorator:
102
+ """
103
+ Convert between different power units.
104
+
105
+ Type: `ChildNode`
106
+
107
+ Supports: `int`, `float`
108
+
109
+ Units:
110
+ - **W**: Watt
111
+ - **kW**: Kilowatt
112
+ - **MW**: Megawatt
113
+ - **GW**: Gigawatt
114
+ - **TW**: Terawatt
115
+ - **hp**: Horsepower
116
+ - **hpM**: Metric horsepower
117
+ - **dBW**: Decibel-watt
118
+ - **dBm**: Decibel-milliwatt
119
+ - **Btuh**: BTU per hour
120
+ - **Btus**: BTU per second
121
+ - **ftlbs**: Foot-pounds per second
122
+ - **tonref**: Ton of refrigeration (cooling capacity)
123
+
124
+ Returns:
125
+ Decorator:
126
+ The decorated function.
127
+ """
128
+ return ConvertPower.as_decorator(from_unit=from_unit, to_unit=to_unit)