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.
Files changed (146) hide show
  1. click_extended/core/__init__.py +10 -0
  2. click_extended/core/decorators/__init__.py +21 -0
  3. click_extended/core/decorators/argument.py +227 -0
  4. click_extended/core/decorators/command.py +93 -0
  5. click_extended/core/decorators/env.py +155 -0
  6. click_extended/core/decorators/group.py +96 -0
  7. click_extended/core/decorators/option.py +347 -0
  8. click_extended/core/decorators/prompt.py +69 -0
  9. click_extended/core/decorators/selection.py +155 -0
  10. click_extended/core/decorators/tag.py +109 -0
  11. click_extended/core/nodes/__init__.py +21 -0
  12. click_extended/core/nodes/_root_node.py +1012 -0
  13. click_extended/core/nodes/argument_node.py +165 -0
  14. click_extended/core/nodes/child_node.py +555 -0
  15. click_extended/core/nodes/child_validation_node.py +100 -0
  16. click_extended/core/nodes/node.py +55 -0
  17. click_extended/core/nodes/option_node.py +205 -0
  18. click_extended/core/nodes/parent_node.py +220 -0
  19. click_extended/core/nodes/validation_node.py +124 -0
  20. click_extended/core/other/__init__.py +7 -0
  21. click_extended/core/other/_click_command.py +60 -0
  22. click_extended/core/other/_click_group.py +246 -0
  23. click_extended/core/other/_tree.py +491 -0
  24. click_extended/core/other/context.py +496 -0
  25. click_extended/decorators/__init__.py +29 -0
  26. click_extended/decorators/check/__init__.py +57 -0
  27. click_extended/decorators/check/conflicts.py +149 -0
  28. click_extended/decorators/check/contains.py +69 -0
  29. click_extended/decorators/check/dependencies.py +115 -0
  30. click_extended/decorators/check/divisible_by.py +48 -0
  31. click_extended/decorators/check/ends_with.py +85 -0
  32. click_extended/decorators/check/exclusive.py +75 -0
  33. click_extended/decorators/check/falsy.py +37 -0
  34. click_extended/decorators/check/is_email.py +43 -0
  35. click_extended/decorators/check/is_hex_color.py +41 -0
  36. click_extended/decorators/check/is_hostname.py +47 -0
  37. click_extended/decorators/check/is_ipv4.py +46 -0
  38. click_extended/decorators/check/is_ipv6.py +46 -0
  39. click_extended/decorators/check/is_json.py +40 -0
  40. click_extended/decorators/check/is_mac_address.py +40 -0
  41. click_extended/decorators/check/is_negative.py +37 -0
  42. click_extended/decorators/check/is_non_zero.py +37 -0
  43. click_extended/decorators/check/is_port.py +39 -0
  44. click_extended/decorators/check/is_positive.py +37 -0
  45. click_extended/decorators/check/is_url.py +75 -0
  46. click_extended/decorators/check/is_uuid.py +40 -0
  47. click_extended/decorators/check/length.py +68 -0
  48. click_extended/decorators/check/not_empty.py +49 -0
  49. click_extended/decorators/check/regex.py +47 -0
  50. click_extended/decorators/check/requires.py +190 -0
  51. click_extended/decorators/check/starts_with.py +87 -0
  52. click_extended/decorators/check/truthy.py +37 -0
  53. click_extended/decorators/compare/__init__.py +15 -0
  54. click_extended/decorators/compare/at_least.py +57 -0
  55. click_extended/decorators/compare/at_most.py +57 -0
  56. click_extended/decorators/compare/between.py +119 -0
  57. click_extended/decorators/compare/greater_than.py +183 -0
  58. click_extended/decorators/compare/less_than.py +183 -0
  59. click_extended/decorators/convert/__init__.py +31 -0
  60. click_extended/decorators/convert/convert_angle.py +94 -0
  61. click_extended/decorators/convert/convert_area.py +123 -0
  62. click_extended/decorators/convert/convert_bits.py +211 -0
  63. click_extended/decorators/convert/convert_distance.py +154 -0
  64. click_extended/decorators/convert/convert_energy.py +155 -0
  65. click_extended/decorators/convert/convert_power.py +128 -0
  66. click_extended/decorators/convert/convert_pressure.py +131 -0
  67. click_extended/decorators/convert/convert_speed.py +122 -0
  68. click_extended/decorators/convert/convert_temperature.py +89 -0
  69. click_extended/decorators/convert/convert_time.py +108 -0
  70. click_extended/decorators/convert/convert_volume.py +218 -0
  71. click_extended/decorators/convert/convert_weight.py +158 -0
  72. click_extended/decorators/load/__init__.py +13 -0
  73. click_extended/decorators/load/load_csv.py +117 -0
  74. click_extended/decorators/load/load_json.py +61 -0
  75. click_extended/decorators/load/load_toml.py +47 -0
  76. click_extended/decorators/load/load_yaml.py +72 -0
  77. click_extended/decorators/math/__init__.py +37 -0
  78. click_extended/decorators/math/absolute.py +35 -0
  79. click_extended/decorators/math/add.py +48 -0
  80. click_extended/decorators/math/ceil.py +36 -0
  81. click_extended/decorators/math/clamp.py +51 -0
  82. click_extended/decorators/math/divide.py +42 -0
  83. click_extended/decorators/math/floor.py +36 -0
  84. click_extended/decorators/math/maximum.py +39 -0
  85. click_extended/decorators/math/minimum.py +39 -0
  86. click_extended/decorators/math/modulo.py +39 -0
  87. click_extended/decorators/math/multiply.py +51 -0
  88. click_extended/decorators/math/normalize.py +76 -0
  89. click_extended/decorators/math/power.py +39 -0
  90. click_extended/decorators/math/rounded.py +39 -0
  91. click_extended/decorators/math/sqrt.py +39 -0
  92. click_extended/decorators/math/subtract.py +39 -0
  93. click_extended/decorators/math/to_percent.py +63 -0
  94. click_extended/decorators/misc/__init__.py +17 -0
  95. click_extended/decorators/misc/choice.py +139 -0
  96. click_extended/decorators/misc/confirm_if.py +147 -0
  97. click_extended/decorators/misc/default.py +95 -0
  98. click_extended/decorators/misc/deprecated.py +131 -0
  99. click_extended/decorators/misc/experimental.py +79 -0
  100. click_extended/decorators/misc/now.py +42 -0
  101. click_extended/decorators/random/__init__.py +21 -0
  102. click_extended/decorators/random/random_bool.py +49 -0
  103. click_extended/decorators/random/random_choice.py +63 -0
  104. click_extended/decorators/random/random_datetime.py +140 -0
  105. click_extended/decorators/random/random_float.py +62 -0
  106. click_extended/decorators/random/random_integer.py +56 -0
  107. click_extended/decorators/random/random_prime.py +196 -0
  108. click_extended/decorators/random/random_string.py +77 -0
  109. click_extended/decorators/random/random_uuid.py +119 -0
  110. click_extended/decorators/transform/__init__.py +71 -0
  111. click_extended/decorators/transform/add_prefix.py +58 -0
  112. click_extended/decorators/transform/add_suffix.py +58 -0
  113. click_extended/decorators/transform/apply.py +35 -0
  114. click_extended/decorators/transform/basename.py +44 -0
  115. click_extended/decorators/transform/dirname.py +44 -0
  116. click_extended/decorators/transform/expand_vars.py +36 -0
  117. click_extended/decorators/transform/remove_prefix.py +57 -0
  118. click_extended/decorators/transform/remove_suffix.py +57 -0
  119. click_extended/decorators/transform/replace.py +46 -0
  120. click_extended/decorators/transform/slugify.py +45 -0
  121. click_extended/decorators/transform/split.py +43 -0
  122. click_extended/decorators/transform/strip.py +148 -0
  123. click_extended/decorators/transform/to_case.py +216 -0
  124. click_extended/decorators/transform/to_date.py +75 -0
  125. click_extended/decorators/transform/to_datetime.py +83 -0
  126. click_extended/decorators/transform/to_path.py +274 -0
  127. click_extended/decorators/transform/to_time.py +77 -0
  128. click_extended/decorators/transform/to_timestamp.py +114 -0
  129. click_extended/decorators/transform/truncate.py +47 -0
  130. click_extended/utils/__init__.py +13 -0
  131. click_extended/utils/casing.py +169 -0
  132. click_extended/utils/checks.py +48 -0
  133. click_extended/utils/dispatch.py +1016 -0
  134. click_extended/utils/format.py +101 -0
  135. click_extended/utils/humanize.py +209 -0
  136. click_extended/utils/naming.py +238 -0
  137. click_extended/utils/process.py +294 -0
  138. click_extended/utils/selection.py +267 -0
  139. click_extended/utils/time.py +46 -0
  140. {click_extended-1.0.0.dist-info → click_extended-1.0.1.dist-info}/METADATA +2 -1
  141. click_extended-1.0.1.dist-info/RECORD +149 -0
  142. click_extended-1.0.0.dist-info/RECORD +0 -10
  143. {click_extended-1.0.0.dist-info → click_extended-1.0.1.dist-info}/WHEEL +0 -0
  144. {click_extended-1.0.0.dist-info → click_extended-1.0.1.dist-info}/licenses/AUTHORS.md +0 -0
  145. {click_extended-1.0.0.dist-info → click_extended-1.0.1.dist-info}/licenses/LICENSE +0 -0
  146. {click_extended-1.0.0.dist-info → click_extended-1.0.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,131 @@
1
+ """Convert between different pressure 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
+ "Pa": Decimal("1"),
14
+ "kPa": Decimal("1000"),
15
+ "MPa": Decimal("1e6"),
16
+ "GPa": Decimal("1e9"),
17
+ "hPa": Decimal("100"),
18
+ "bar": Decimal("100000"),
19
+ "mbar": Decimal("100"),
20
+ "Ba": Decimal("0.1"),
21
+ "psi": Decimal("6894.757293168"),
22
+ "ksi": Decimal("6894757.293168"),
23
+ "psf": Decimal("47.88025898"),
24
+ "mmHg": Decimal("133.322387415"),
25
+ "inHg": Decimal("3386.388666666"),
26
+ "mmH2O": Decimal("9.80665"),
27
+ "inH2O": Decimal("249.08891"),
28
+ "atm": Decimal("101325"),
29
+ "at": Decimal("98066.5"),
30
+ "torr": Decimal("133.322368421"),
31
+ }
32
+
33
+
34
+ class ConvertPressure(ChildNode):
35
+ """Convert between different pressure units."""
36
+
37
+ def handle_numeric(
38
+ self,
39
+ value: int | float,
40
+ context: Context,
41
+ *args: Any,
42
+ **kwargs: Any,
43
+ ) -> float:
44
+ from_unit = kwargs["from_unit"]
45
+ to_unit = kwargs["to_unit"]
46
+ val = Decimal(str(value))
47
+
48
+ if from_unit not in UNITS:
49
+ raise ValueError(f"Unknown unit '{from_unit}'")
50
+ if to_unit not in UNITS:
51
+ raise ValueError(f"Unknown unit '{to_unit}'")
52
+
53
+ pa = val * UNITS[from_unit]
54
+
55
+ return float(pa / UNITS[to_unit])
56
+
57
+
58
+ def convert_pressure(
59
+ from_unit: Literal[
60
+ "Pa",
61
+ "kPa",
62
+ "MPa",
63
+ "GPa",
64
+ "bar",
65
+ "mbar",
66
+ "hPa",
67
+ "psi",
68
+ "ksi",
69
+ "psf",
70
+ "mmHg",
71
+ "inHg",
72
+ "mmH2O",
73
+ "inH2O",
74
+ "atm",
75
+ "at",
76
+ "torr",
77
+ "Ba",
78
+ ],
79
+ to_unit: Literal[
80
+ "Pa",
81
+ "kPa",
82
+ "MPa",
83
+ "GPa",
84
+ "bar",
85
+ "mbar",
86
+ "hPa",
87
+ "psi",
88
+ "ksi",
89
+ "psf",
90
+ "mmHg",
91
+ "inHg",
92
+ "mmH2O",
93
+ "inH2O",
94
+ "atm",
95
+ "at",
96
+ "torr",
97
+ "Ba",
98
+ ],
99
+ ) -> Decorator:
100
+ """
101
+ Convert between different pressure units.
102
+
103
+ Type: `ChildNode`
104
+
105
+ Supports: `int`, `float`
106
+
107
+ Units:
108
+ - **Pa**: Pascal
109
+ - **kPa**: Kilopascal
110
+ - **MPa**: Megapascal
111
+ - **GPa**: Gigapascal
112
+ - **bar**: Bar
113
+ - **mbar**: Millibar
114
+ - **hPa**: Hectopascal
115
+ - **psi**: Pounds per square inch
116
+ - **ksi**: Kilopounds per square inch
117
+ - **psf**: Pounds per square foot
118
+ - **mmHg**: Millimeters of mercury
119
+ - **inHg**: Inches of mercury
120
+ - **mmH2O**: Millimeters of water
121
+ - **inH2O**: Inches of water
122
+ - **atm**: Standard atmosphere
123
+ - **at**: Technical atmosphere
124
+ - **torr**: Torr (1/760 atm)
125
+ - **Ba**: Barye (dyn/cm2)
126
+
127
+ Returns:
128
+ Decorator:
129
+ The decorated function.
130
+ """
131
+ return ConvertPressure.as_decorator(from_unit=from_unit, to_unit=to_unit)
@@ -0,0 +1,122 @@
1
+ """Convert between different speed 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
+ "mps": Decimal("1"),
14
+ "kmh": Decimal("1000") / Decimal("3600"),
15
+ "kmps": Decimal("1000"),
16
+ "cmps": Decimal("0.01"),
17
+ "mmps": Decimal("0.001"),
18
+ "kmday": Decimal("1000") / Decimal("86400"),
19
+ "mph": Decimal("1609.344") / Decimal("3600"),
20
+ "fps": Decimal("0.3048"),
21
+ "ftmin": Decimal("0.3048") / Decimal("60"),
22
+ "inps": Decimal("0.0254"),
23
+ "kn": Decimal("1852") / Decimal("3600"),
24
+ "kt": Decimal("1852") / Decimal("3600"),
25
+ "mach": Decimal("343"),
26
+ "c": Decimal("299792458"),
27
+ "auday": Decimal("149597870700") / Decimal("86400"),
28
+ "pcyr": Decimal("3.0856775814913673e16") / Decimal("31557600"),
29
+ }
30
+
31
+
32
+ class ConvertSpeed(ChildNode):
33
+ """Convert between different speed units."""
34
+
35
+ def handle_numeric(
36
+ self,
37
+ value: int | float,
38
+ context: Context,
39
+ *args: Any,
40
+ **kwargs: Any,
41
+ ) -> float:
42
+ from_unit = kwargs["from_unit"]
43
+ to_unit = kwargs["to_unit"]
44
+ val = Decimal(str(value))
45
+
46
+ if from_unit not in UNITS:
47
+ raise ValueError(f"Unknown unit '{from_unit}'")
48
+ if to_unit not in UNITS:
49
+ raise ValueError(f"Unknown unit '{to_unit}'")
50
+
51
+ mps = val * UNITS[from_unit]
52
+ return float(mps / UNITS[to_unit])
53
+
54
+
55
+ def convert_speed(
56
+ from_unit: Literal[
57
+ "mps",
58
+ "kmh",
59
+ "kmps",
60
+ "cmps",
61
+ "mmps",
62
+ "mph",
63
+ "fps",
64
+ "ftmin",
65
+ "inps",
66
+ "kn",
67
+ "kt",
68
+ "mach",
69
+ "c",
70
+ "auday",
71
+ "kmday",
72
+ "pcyr",
73
+ ],
74
+ to_unit: Literal[
75
+ "mps",
76
+ "kmh",
77
+ "kmps",
78
+ "cmps",
79
+ "mmps",
80
+ "mph",
81
+ "fps",
82
+ "ftmin",
83
+ "inps",
84
+ "kn",
85
+ "kt",
86
+ "mach",
87
+ "c",
88
+ "auday",
89
+ "kmday",
90
+ "pcyr",
91
+ ],
92
+ ) -> Decorator:
93
+ """
94
+ Convert between different speed units.
95
+
96
+ Type: `ChildNode`
97
+
98
+ Supports: `int`, `float`
99
+
100
+ Units:
101
+ - **mps**: Meters per second
102
+ - **kmh**: Kilometers per hour
103
+ - **kmps**: Kilometers per second
104
+ - **cmps**: Centimeters per second
105
+ - **mmps**: Millimeters per second
106
+ - **mph**: Miles per hour
107
+ - **fps**: Feet per second
108
+ - **ftmin**: Feet per minute
109
+ - **inps**: Inches per second
110
+ - **kn**: Knot
111
+ - **kt**: Knot
112
+ - **mach**: Mach number (ratio to speed of sound)
113
+ - **c**: Fraction of the speed of light
114
+ - **auday**: Astronomical units per day
115
+ - **kmday**: Kilometers per day
116
+ - **pcyr**: Parsecs per year
117
+
118
+ Returns:
119
+ Decorator:
120
+ The decorated function.
121
+ """
122
+ return ConvertSpeed.as_decorator(from_unit=from_unit, to_unit=to_unit)
@@ -0,0 +1,89 @@
1
+ """Convert between temperature 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 = 28
11
+
12
+
13
+ class ConvertTemperature(ChildNode):
14
+ """Convert between temperature units."""
15
+
16
+ def _to_celsius(self, value: float, unit: str) -> Decimal:
17
+ val = Decimal(str(value))
18
+ if unit == "C":
19
+ return val
20
+ if unit == "F":
21
+ return (val - 32) * Decimal("5") / Decimal("9")
22
+ if unit == "K":
23
+ return val - Decimal("273.15")
24
+ if unit == "R":
25
+ return (val - Decimal("491.67")) * Decimal("5") / Decimal("9")
26
+ if unit == "Re":
27
+ return val * Decimal("5") / Decimal("4")
28
+ if unit == "De":
29
+ return 100 - val * Decimal("2") / Decimal("3")
30
+ raise ValueError(f"Unknown unit '{unit}'")
31
+
32
+ def _from_celsius(self, value: Decimal, unit: str) -> Decimal:
33
+ if unit == "C":
34
+ return value
35
+ if unit == "F":
36
+ return (value * Decimal("9") / Decimal("5")) + 32
37
+ if unit == "K":
38
+ return value + Decimal("273.15")
39
+ if unit == "R":
40
+ return (value + Decimal("273.15")) * Decimal("9") / Decimal("5")
41
+ if unit == "Re":
42
+ return value * Decimal("4") / Decimal("5")
43
+ if unit == "De":
44
+ return (100 - value) * Decimal("3") / Decimal("2")
45
+ raise ValueError(f"Unknown unit '{unit}'")
46
+
47
+ def handle_numeric(
48
+ self, value: int | float, context: Context, *args: Any, **kwargs: Any
49
+ ) -> float:
50
+ celsius = self._to_celsius(value, kwargs["from_unit"])
51
+
52
+ if celsius < Decimal("-273.15"):
53
+ raise ValueError(
54
+ "Temperature cannot be below absolute zero (-273.15°C)."
55
+ )
56
+
57
+ return float(self._from_celsius(celsius, kwargs["to_unit"]))
58
+
59
+
60
+ def convert_temperature(
61
+ from_unit: Literal["C", "F", "K", "R", "Re", "De"],
62
+ to_unit: Literal["C", "F", "K", "R", "Re", "De"],
63
+ ) -> Decorator:
64
+ """
65
+ Convert between temperature units.
66
+
67
+ Type: `ChildNode`
68
+
69
+ Supports: `int`, `float`
70
+
71
+ Units:
72
+ - **C**: Celcius
73
+ - **F**: Fahrenheit
74
+ - **K**: Kelvin
75
+ - **R**: Rankine
76
+ - **Re**: Reaumur
77
+ - **De**: Delisle
78
+
79
+ Args:
80
+ from_unit (str):
81
+ The unit to convert from.
82
+ to_unit (str):
83
+ The unit to convert to.
84
+
85
+ Returns:
86
+ Decorator:
87
+ The decorated function.
88
+ """
89
+ return ConvertTemperature.as_decorator(from_unit=from_unit, to_unit=to_unit)
@@ -0,0 +1,108 @@
1
+ """Convert between time units."""
2
+
3
+ import re
4
+ from decimal import Decimal, getcontext
5
+ from typing import Any, Literal
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
+ # Set precision high enough for time conversions
12
+ getcontext().prec = 28
13
+
14
+ UNITS = {
15
+ "ns": Decimal("1e-9"),
16
+ "us": Decimal("1e-6"),
17
+ "ms": Decimal("1e-3"),
18
+ "s": Decimal("1"),
19
+ "m": Decimal("60"),
20
+ "h": Decimal("3600"),
21
+ "d": Decimal("86400"),
22
+ "w": Decimal("604800"),
23
+ "M": Decimal("2592000"),
24
+ "y": Decimal("31536000"),
25
+ }
26
+
27
+
28
+ class ConvertTime(ChildNode):
29
+ """Convert between time units."""
30
+
31
+ def _convert(self, value: float, from_unit: str, to_unit: str) -> float:
32
+ if from_unit not in UNITS:
33
+ raise ValueError(f"Unknown unit '{from_unit}'")
34
+ if to_unit not in UNITS:
35
+ raise ValueError(f"Unknown unit '{to_unit}'")
36
+
37
+ val = Decimal(str(value))
38
+ seconds = val * UNITS[from_unit]
39
+ result = seconds / UNITS[to_unit]
40
+ return float(result)
41
+
42
+ def handle_str(
43
+ self, value: str, context: Context, *args: Any, **kwargs: Any
44
+ ) -> float:
45
+ to_unit = str(kwargs["to_unit"])
46
+ parts = re.findall(r"(\d+(?:\.\d+)?)\s*([a-zA-Z]+)", value)
47
+
48
+ if not parts:
49
+ try:
50
+ val = float(value)
51
+ from_unit = str(kwargs["from_unit"])
52
+ return self._convert(val, from_unit, to_unit)
53
+ except ValueError as e:
54
+ raise ValueError(
55
+ f"Could not parse time string '{value}'."
56
+ ) from e
57
+
58
+ total_seconds = Decimal("0.0")
59
+ for val_str, unit in parts:
60
+ if unit not in UNITS:
61
+ raise ValueError(f"Unknown unit '{unit}' in string.")
62
+ total_seconds += Decimal(val_str) * UNITS[unit]
63
+
64
+ result = total_seconds / UNITS[to_unit]
65
+ return float(result)
66
+
67
+ def handle_numeric(
68
+ self, value: int | float, context: Context, *args: Any, **kwargs: Any
69
+ ) -> Any:
70
+ return self._convert(
71
+ float(value), kwargs["from_unit"], kwargs["to_unit"]
72
+ )
73
+
74
+
75
+ def convert_time(
76
+ from_unit: Literal["ns", "us", "ms", "s", "m", "h", "d", "w", "M", "y"],
77
+ to_unit: Literal["ns", "us", "ms", "s", "m", "h", "d", "w", "M", "y"],
78
+ ) -> Decorator:
79
+ """
80
+ Convert between time units.
81
+
82
+ Type: `ChildNode`
83
+
84
+ Supports: `str`, `int`, `float`
85
+
86
+ Units:
87
+ - **ns**: Nanoseconds
88
+ - **us**: Microseconds
89
+ - **ms**: Milliseconds
90
+ - **s**: Seconds
91
+ - **m**: Minutes
92
+ - **h**: Hours
93
+ - **d**: Days
94
+ - **w**: Weeks
95
+ - **M**: Months
96
+ - **y**: Years
97
+
98
+ Args:
99
+ from_unit (str):
100
+ The unit to convert from.
101
+ to_unit (str):
102
+ The unit to convert to.
103
+
104
+ Returns:
105
+ Decorator:
106
+ The decorated function.
107
+ """
108
+ return ConvertTime.as_decorator(from_unit=from_unit, to_unit=to_unit)
@@ -0,0 +1,218 @@
1
+ """Convert between various volume 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
+ "mm3": Decimal("1e-6"),
14
+ "cm3": Decimal("1e-3"),
15
+ "m3": Decimal("1000"),
16
+ "km3": Decimal("1e12"),
17
+ "mL": Decimal("1e-3"),
18
+ "cL": Decimal("1e-2"),
19
+ "dL": Decimal("1e-1"),
20
+ "L": Decimal("1"),
21
+ "kL": Decimal("1000"),
22
+ "ML": Decimal("1e6"),
23
+ "hL": Decimal("100"),
24
+ "cc": Decimal("1e-3"),
25
+ "in3": Decimal("0.016387064"),
26
+ "ft3": Decimal("28.316846592"),
27
+ "yd3": Decimal("764.554857984"),
28
+ "floz": Decimal("0.0295735295625"),
29
+ "cup": Decimal("0.2365882365"),
30
+ "pt": Decimal("0.473176473"),
31
+ "qt": Decimal("0.946352946"),
32
+ "gal": Decimal("3.785411784"),
33
+ "bbl": Decimal("119.240471196"),
34
+ "tsp": Decimal("0.00492892159375"),
35
+ "tbsp": Decimal("0.01478676478125"),
36
+ "gill": Decimal("0.11829411825"),
37
+ "drop": Decimal("0.00005"),
38
+ "dry_pt": Decimal("0.5506104713575"),
39
+ "dry_qt": Decimal("1.101220942715"),
40
+ "dry_gal": Decimal("4.40488377086"),
41
+ "pk": Decimal("8.80976754172"),
42
+ "bu": Decimal("35.23907016688"),
43
+ "imp_floz": Decimal("0.0284130625"),
44
+ "imp_pt": Decimal("0.56826125"),
45
+ "imp_qt": Decimal("1.1365225"),
46
+ "imp_gal": Decimal("4.54609"),
47
+ "imp_gill": Decimal("0.1420653125"),
48
+ "krm": Decimal("1e-3"),
49
+ "tsk": Decimal("5e-3"),
50
+ "msk": Decimal("15e-3"),
51
+ "firkin": Decimal("40.91481"),
52
+ "kilderkin": Decimal("81.82962"),
53
+ }
54
+
55
+
56
+ class ConvertVolume(ChildNode):
57
+ """Convert between various volume units."""
58
+
59
+ def handle_numeric(
60
+ self,
61
+ value: int | float,
62
+ context: Context,
63
+ *args: Any,
64
+ **kwargs: Any,
65
+ ) -> float:
66
+ from_unit = kwargs["from_unit"]
67
+ to_unit = kwargs["to_unit"]
68
+ val = Decimal(str(value))
69
+
70
+ if from_unit not in UNITS:
71
+ raise ValueError(f"Unknown unit '{from_unit}'")
72
+ if to_unit not in UNITS:
73
+ raise ValueError(f"Unknown unit '{to_unit}'")
74
+
75
+ liters = val * UNITS[from_unit]
76
+ return float(liters / UNITS[to_unit])
77
+
78
+
79
+ def convert_volume(
80
+ from_unit: Literal[
81
+ "mm3",
82
+ "cm3",
83
+ "m3",
84
+ "km3",
85
+ "mL",
86
+ "cL",
87
+ "dL",
88
+ "L",
89
+ "kL",
90
+ "ML",
91
+ "hL",
92
+ "in3",
93
+ "ft3",
94
+ "yd3",
95
+ "floz",
96
+ "cup",
97
+ "pt",
98
+ "qt",
99
+ "gal",
100
+ "imp_floz",
101
+ "imp_pt",
102
+ "imp_qt",
103
+ "imp_gal",
104
+ "bbl",
105
+ "cc",
106
+ "tsp",
107
+ "tbsp",
108
+ "gill",
109
+ "drop",
110
+ "dry_pt",
111
+ "dry_qt",
112
+ "dry_gal",
113
+ "pk",
114
+ "bu",
115
+ "imp_gill",
116
+ "firkin",
117
+ "kilderkin",
118
+ "krm",
119
+ "tsk",
120
+ "msk",
121
+ ],
122
+ to_unit: Literal[
123
+ "mm3",
124
+ "cm3",
125
+ "m3",
126
+ "km3",
127
+ "mL",
128
+ "cL",
129
+ "dL",
130
+ "L",
131
+ "kL",
132
+ "ML",
133
+ "hL",
134
+ "in3",
135
+ "ft3",
136
+ "yd3",
137
+ "floz",
138
+ "cup",
139
+ "pt",
140
+ "qt",
141
+ "gal",
142
+ "imp_floz",
143
+ "imp_pt",
144
+ "imp_qt",
145
+ "imp_gal",
146
+ "bbl",
147
+ "cc",
148
+ "tsp",
149
+ "tbsp",
150
+ "gill",
151
+ "drop",
152
+ "dry_pt",
153
+ "dry_qt",
154
+ "dry_gal",
155
+ "pk",
156
+ "bu",
157
+ "imp_gill",
158
+ "firkin",
159
+ "kilderkin",
160
+ "krm",
161
+ "tsk",
162
+ "msk",
163
+ ],
164
+ ) -> Decorator:
165
+ """
166
+ Convert between various volume units.
167
+
168
+ Type: `ChildNode`
169
+
170
+ Supports: `int`, `float`
171
+
172
+ Units:
173
+ - **mm3**: Cubic millimeter
174
+ - **cm3**: Cubic centimeter
175
+ - **m3**: Cubic meter
176
+ - **km3**: Cubic kilometer
177
+ - **mL**: Milliliter
178
+ - **cL**: Centiliter
179
+ - **dL**: Deciliter
180
+ - **L**: Liter
181
+ - **kL**: Kiloliter
182
+ - **ML**: Megaliter
183
+ - **hL**: Hectoliter
184
+ - **in3**: Cubic inch
185
+ - **ft3**: Cubic foot
186
+ - **yd3**: Cubic yard
187
+ - **floz**: US fluid ounce
188
+ - **cup**: US cup
189
+ - **pt**: US pint
190
+ - **qt**: US quart
191
+ - **gal**: US gallon
192
+ - **imp_floz**: Imperial fluid ounce
193
+ - **imp_pt**: Imperial pint
194
+ - **imp_qt**: Imperial quart
195
+ - **imp_gal**: Imperial gallon
196
+ - **bbl**: Barrel
197
+ - **cc**: Cubic centimeter
198
+ - **tsp**: Teaspoon
199
+ - **tbsp**: Tablespoon
200
+ - **gill**: US gill
201
+ - **drop**: Drop
202
+ - **dry_pt**: US dry pint
203
+ - **dry_qt**: US dry quart
204
+ - **dry_gal**: US dry gallon
205
+ - **pk**: Peck
206
+ - **bu**: Bushel
207
+ - **imp_gill**: Imperial gill
208
+ - **firkin**: Firkin (beer/brewing measure)
209
+ - **kilderkin**: Kilderkin (beer/brewing measure)
210
+ - **krm**: Kryddmått (1 ml)
211
+ - **tsk**: Tesked (5 ml)
212
+ - **msk**: Matsked (15 ml)
213
+
214
+ Returns:
215
+ Decorator:
216
+ The decorated function.
217
+ """
218
+ return ConvertVolume.as_decorator(from_unit=from_unit, to_unit=to_unit)