absfuyu 5.6.1__py3-none-any.whl → 6.1.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.

Potentially problematic release.


This version of absfuyu might be problematic. Click here for more details.

Files changed (102) hide show
  1. absfuyu/__init__.py +5 -3
  2. absfuyu/__main__.py +2 -2
  3. absfuyu/cli/__init__.py +13 -2
  4. absfuyu/cli/audio_group.py +98 -0
  5. absfuyu/cli/color.py +2 -2
  6. absfuyu/cli/config_group.py +2 -2
  7. absfuyu/cli/do_group.py +2 -2
  8. absfuyu/cli/game_group.py +20 -2
  9. absfuyu/cli/tool_group.py +68 -4
  10. absfuyu/config/__init__.py +3 -3
  11. absfuyu/core/__init__.py +10 -6
  12. absfuyu/core/baseclass.py +104 -34
  13. absfuyu/core/baseclass2.py +43 -2
  14. absfuyu/core/decorator.py +2 -2
  15. absfuyu/core/docstring.py +4 -2
  16. absfuyu/core/dummy_cli.py +3 -3
  17. absfuyu/core/dummy_func.py +2 -2
  18. absfuyu/dxt/__init__.py +2 -2
  19. absfuyu/dxt/base_type.py +93 -0
  20. absfuyu/dxt/dictext.py +188 -6
  21. absfuyu/dxt/dxt_support.py +2 -2
  22. absfuyu/dxt/intext.py +72 -4
  23. absfuyu/dxt/listext.py +495 -23
  24. absfuyu/dxt/strext.py +2 -2
  25. absfuyu/extra/__init__.py +2 -2
  26. absfuyu/extra/audio/__init__.py +8 -0
  27. absfuyu/extra/audio/_util.py +57 -0
  28. absfuyu/extra/audio/convert.py +192 -0
  29. absfuyu/extra/audio/lossless.py +281 -0
  30. absfuyu/extra/beautiful.py +2 -2
  31. absfuyu/extra/da/__init__.py +39 -3
  32. absfuyu/extra/da/dadf.py +436 -29
  33. absfuyu/extra/da/dadf_base.py +2 -2
  34. absfuyu/extra/da/df_func.py +89 -5
  35. absfuyu/extra/da/mplt.py +2 -2
  36. absfuyu/extra/ggapi/__init__.py +8 -0
  37. absfuyu/extra/ggapi/gdrive.py +223 -0
  38. absfuyu/extra/ggapi/glicense.py +148 -0
  39. absfuyu/extra/ggapi/glicense_df.py +186 -0
  40. absfuyu/extra/ggapi/gsheet.py +88 -0
  41. absfuyu/extra/img/__init__.py +30 -0
  42. absfuyu/extra/img/converter.py +402 -0
  43. absfuyu/extra/img/dup_check.py +291 -0
  44. absfuyu/extra/pdf.py +4 -6
  45. absfuyu/extra/rclone.py +253 -0
  46. absfuyu/extra/xml.py +90 -0
  47. absfuyu/fun/__init__.py +2 -20
  48. absfuyu/fun/rubik.py +2 -2
  49. absfuyu/fun/tarot.py +2 -2
  50. absfuyu/game/__init__.py +2 -2
  51. absfuyu/game/game_stat.py +2 -2
  52. absfuyu/game/schulte.py +78 -0
  53. absfuyu/game/sudoku.py +2 -2
  54. absfuyu/game/tictactoe.py +2 -2
  55. absfuyu/game/wordle.py +6 -4
  56. absfuyu/general/__init__.py +2 -2
  57. absfuyu/general/content.py +2 -2
  58. absfuyu/general/human.py +2 -2
  59. absfuyu/general/resrel.py +213 -0
  60. absfuyu/general/shape.py +3 -8
  61. absfuyu/general/tax.py +344 -0
  62. absfuyu/logger.py +806 -59
  63. absfuyu/numbers/__init__.py +13 -0
  64. absfuyu/numbers/number_to_word.py +321 -0
  65. absfuyu/numbers/shorten_number.py +303 -0
  66. absfuyu/numbers/time_duration.py +217 -0
  67. absfuyu/pkg_data/__init__.py +2 -2
  68. absfuyu/pkg_data/deprecated.py +2 -2
  69. absfuyu/pkg_data/logo.py +1462 -0
  70. absfuyu/sort.py +4 -4
  71. absfuyu/tools/__init__.py +2 -2
  72. absfuyu/tools/checksum.py +119 -4
  73. absfuyu/tools/converter.py +2 -2
  74. absfuyu/tools/generator.py +24 -7
  75. absfuyu/tools/inspector.py +2 -2
  76. absfuyu/tools/keygen.py +2 -2
  77. absfuyu/tools/obfuscator.py +2 -2
  78. absfuyu/tools/passwordlib.py +2 -2
  79. absfuyu/tools/shutdownizer.py +3 -8
  80. absfuyu/tools/sw.py +213 -10
  81. absfuyu/tools/web.py +10 -13
  82. absfuyu/typings.py +5 -8
  83. absfuyu/util/__init__.py +31 -2
  84. absfuyu/util/api.py +7 -4
  85. absfuyu/util/cli.py +119 -0
  86. absfuyu/util/gui.py +91 -0
  87. absfuyu/util/json_method.py +2 -2
  88. absfuyu/util/lunar.py +2 -2
  89. absfuyu/util/package.py +124 -0
  90. absfuyu/util/path.py +313 -4
  91. absfuyu/util/performance.py +2 -2
  92. absfuyu/util/shorten_number.py +206 -13
  93. absfuyu/util/text_table.py +2 -2
  94. absfuyu/util/zipped.py +2 -2
  95. absfuyu/version.py +22 -19
  96. {absfuyu-5.6.1.dist-info → absfuyu-6.1.2.dist-info}/METADATA +37 -8
  97. absfuyu-6.1.2.dist-info/RECORD +105 -0
  98. {absfuyu-5.6.1.dist-info → absfuyu-6.1.2.dist-info}/WHEEL +1 -1
  99. absfuyu/extra/data_analysis.py +0 -21
  100. absfuyu-5.6.1.dist-info/RECORD +0 -79
  101. {absfuyu-5.6.1.dist-info → absfuyu-6.1.2.dist-info}/entry_points.txt +0 -0
  102. {absfuyu-5.6.1.dist-info → absfuyu-6.1.2.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,13 @@
1
+ """
2
+ Absfuyu: Numbers
3
+ ----------------
4
+ Numbers related
5
+
6
+ Version: 6.1.1
7
+ Date updated: 30/12/2025 (dd/mm/yyyy)
8
+ """
9
+
10
+
11
+ from absfuyu.numbers.number_to_word import NumberToWords
12
+ from absfuyu.numbers.shorten_number import Decimal
13
+ from absfuyu.numbers.time_duration import Duration
@@ -0,0 +1,321 @@
1
+ """
2
+ Absfuyu: Number to word
3
+ -----------------------
4
+ Convert number to word
5
+
6
+ Version: 6.1.1
7
+ Date updated: 30/12/2025 (dd/mm/yyyy)
8
+ """
9
+
10
+ # Module level
11
+ # ---------------------------------------------------------------------------
12
+ __all__ = ["NumberToWords"]
13
+
14
+
15
+ # Library
16
+ # ---------------------------------------------------------------------------
17
+ from abc import ABC, abstractmethod
18
+ from decimal import Decimal
19
+
20
+
21
+ # Class
22
+ # ---------------------------------------------------------------------------
23
+ class NumberToWordsBase(ABC):
24
+ """
25
+ Base abstract class for Number to words
26
+ """
27
+
28
+ @abstractmethod
29
+ def convert(self, number: int) -> str:
30
+ """
31
+ Convert number to words
32
+
33
+ Parameters
34
+ ----------
35
+ number : int
36
+ Number to convert to
37
+
38
+ Returns
39
+ -------
40
+ str
41
+ Number in word form
42
+ """
43
+ pass
44
+
45
+
46
+ class NumberToWordsEN(NumberToWordsBase):
47
+ """
48
+ Number to words - English
49
+
50
+ Support up to 10^120
51
+ """
52
+
53
+ _ones = [
54
+ "zero",
55
+ "one",
56
+ "two",
57
+ "three",
58
+ "four",
59
+ "five",
60
+ "six",
61
+ "seven",
62
+ "eight",
63
+ "nine",
64
+ "ten",
65
+ "eleven",
66
+ "twelve",
67
+ "thirteen",
68
+ "fourteen",
69
+ "fifteen",
70
+ "sixteen",
71
+ "seventeen",
72
+ "eighteen",
73
+ "nineteen",
74
+ ]
75
+
76
+ _tens = ["", "", "twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety"]
77
+
78
+ _scales = [
79
+ "", # 10^0
80
+ "thousand", # 10^3
81
+ "million", # 10^6
82
+ "billion", # 10^9
83
+ "trillion", # 10^12
84
+ "quadrillion", # 10^15
85
+ "quintillion",
86
+ "sextillion",
87
+ "septillion",
88
+ "octillion",
89
+ "nonillion",
90
+ "decillion",
91
+ "undecillion",
92
+ "duodecillion",
93
+ "tredecillion",
94
+ "quattuordecillion",
95
+ "quindecillion",
96
+ "sexdecillion",
97
+ "septendecillion",
98
+ "octodecillion",
99
+ "novemdecillion",
100
+ "vigintillion",
101
+ "unvigintillion",
102
+ "duovigintillion",
103
+ "tresvigintillion",
104
+ "quattuorvigintillion",
105
+ "quinvigintillion",
106
+ "sesvigintillion",
107
+ "septemvigintillion",
108
+ "octovigintillion",
109
+ "novemvigintillion",
110
+ "trigintillion",
111
+ "untrigintillion",
112
+ "duotrigintillion",
113
+ "trestrigintillion",
114
+ "quattuortrigintillion",
115
+ "quintrigintillion",
116
+ "sestrigintillion",
117
+ "septentrigintillion",
118
+ "octotrigintillion",
119
+ "noventrigintillion",
120
+ ]
121
+
122
+ def convert(self, number) -> str:
123
+ dec = Decimal(str(number))
124
+
125
+ if dec == 0:
126
+ return "zero"
127
+
128
+ sign = ""
129
+ if dec < 0:
130
+ sign = "minus "
131
+ dec = abs(dec)
132
+
133
+ integer_part = int(dec)
134
+ fractional_part = dec - integer_part
135
+
136
+ words = sign + self._convert_integer(integer_part)
137
+
138
+ if fractional_part != 0:
139
+ frac_digits = str(fractional_part).split(".")[1]
140
+ frac_words = " ".join(self._ones[int(d)] for d in frac_digits)
141
+ words += f" point {frac_words}"
142
+
143
+ return words
144
+
145
+ def _convert_integer(self, number: int) -> str:
146
+ if number == 0:
147
+ return "zero"
148
+
149
+ parts = []
150
+ scale_index = 0
151
+
152
+ try:
153
+ while number > 0:
154
+ number, group = divmod(number, 1000)
155
+ if group > 0:
156
+ text = self._under_1000(group)
157
+ scale = self._scales[scale_index]
158
+ parts.append(f"{text} {scale}".strip())
159
+ scale_index += 1
160
+ except IndexError:
161
+ raise ValueError("Number to large")
162
+
163
+ return " ".join(reversed(parts))
164
+
165
+ def _under_1000(self, number: int) -> str:
166
+ hundreds, rest = divmod(number, 100)
167
+ parts = []
168
+
169
+ if hundreds > 0:
170
+ parts.append(f"{self._ones[hundreds]} hundred")
171
+
172
+ if rest > 0:
173
+ if hundreds > 0:
174
+ parts.append("and")
175
+ parts.append(self._under_100(rest))
176
+
177
+ return " ".join(parts)
178
+
179
+ def _under_100(self, number: int) -> str:
180
+ if number < 20:
181
+ return self._ones[number]
182
+
183
+ tens, unit = divmod(number, 10)
184
+ if unit == 0:
185
+ return self._tens[tens]
186
+
187
+ return f"{self._tens[tens]} {self._ones[unit]}"
188
+
189
+
190
+ class NumberToWordsVI(NumberToWordsBase):
191
+ """
192
+ Number to words - Vietnamese
193
+
194
+ Support:
195
+ - Scale infinitely
196
+ - Decimal number
197
+ """
198
+
199
+ _ones = ["không", "một", "hai", "ba", "bốn", "năm", "sáu", "bảy", "tám", "chín"]
200
+
201
+ def convert(self, number: int | float) -> str:
202
+ dec = Decimal(str(number))
203
+
204
+ if dec == 0:
205
+ return "không"
206
+
207
+ sign = ""
208
+ if dec < 0:
209
+ sign = "âm "
210
+ dec = abs(dec)
211
+
212
+ integer_part = int(dec)
213
+ fractional_part = dec - integer_part
214
+
215
+ words = sign + self._convert_integer(integer_part)
216
+
217
+ if fractional_part != 0:
218
+ frac_digits = str(fractional_part).split(".")[1]
219
+ frac_words = " ".join(self._ones[int(d)] for d in frac_digits)
220
+ words += f" phẩy {frac_words}"
221
+
222
+ return words
223
+
224
+ def _convert_integer(self, number: int) -> str:
225
+ parts = []
226
+ index = 0
227
+
228
+ while number > 0:
229
+ number, group = divmod(number, 1000)
230
+ if group > 0:
231
+ text = self._under_1000(group)
232
+ scale = self._scale_name(index)
233
+ parts.append(f"{text} {scale}".strip())
234
+ index += 1
235
+
236
+ return " ".join(reversed(parts))
237
+
238
+ def _scale_name(self, index: int) -> str:
239
+ if index == 0:
240
+ return ""
241
+
242
+ base = ["", "nghìn", "triệu"]
243
+ pos = index % 3
244
+ level = index // 3
245
+
246
+ scale = base[pos]
247
+ if level > 0:
248
+ scale = f"{scale} {'tỷ ' * level}".strip()
249
+
250
+ return scale.strip()
251
+
252
+ def _under_1000(self, number: int) -> str:
253
+ hundreds, rest = divmod(number, 100)
254
+ parts = []
255
+
256
+ if hundreds > 0:
257
+ parts.append(f"{self._ones[hundreds]} trăm")
258
+
259
+ if rest > 0:
260
+ if hundreds > 0 and rest < 10:
261
+ parts.append("lẻ")
262
+ parts.append(self._under_100(rest))
263
+
264
+ return " ".join(parts)
265
+
266
+ def _under_100(self, number: int) -> str:
267
+ if number < 10:
268
+ return self._ones[number]
269
+
270
+ tens, unit = divmod(number, 10)
271
+
272
+ if tens == 1:
273
+ if unit == 0:
274
+ return "mười"
275
+ if unit == 5:
276
+ return "mười lăm"
277
+ return f"mười {self._ones[unit]}"
278
+
279
+ result = f"{self._ones[tens]} mươi"
280
+
281
+ if unit == 0:
282
+ return result
283
+ if unit == 1:
284
+ return f"{result} mốt"
285
+ if unit == 5:
286
+ return f"{result} lăm"
287
+
288
+ return f"{result} {self._ones[unit]}"
289
+
290
+
291
+ # Main
292
+ class NumberToWords:
293
+ _languages: dict[str, NumberToWordsBase] = {
294
+ "en": NumberToWordsEN,
295
+ "vi": NumberToWordsVI,
296
+ }
297
+
298
+ def __init__(self, lang: str = "en"):
299
+ self.set_language(lang)
300
+
301
+ def set_language(self, lang: str) -> None:
302
+ if lang not in self._languages:
303
+ raise ValueError(f"Unsupported language: {lang}")
304
+ # self._converter = self._languages[lang]()
305
+ self._converter: NumberToWordsBase = self._languages.get(lang, NumberToWordsEN)()
306
+
307
+ def convert(self, number: int) -> str:
308
+ """
309
+ Convert number to words
310
+
311
+ Parameters
312
+ ----------
313
+ number : int
314
+ Number to convert to
315
+
316
+ Returns
317
+ -------
318
+ str
319
+ Number in word form
320
+ """
321
+ return self._converter.convert(number)
@@ -0,0 +1,303 @@
1
+ """
2
+ Absfuyu: Shorten number
3
+ -----------------------
4
+ Short number base on suffixes
5
+
6
+ Version: 6.1.1
7
+ Date updated: 30/12/2025 (dd/mm/yyyy)
8
+ """
9
+
10
+ # Module level
11
+ # ---------------------------------------------------------------------------
12
+ __all__ = [
13
+ "UnitSuffixFactory",
14
+ "CommonUnitSuffixesFactory",
15
+ "Decimal",
16
+ "shorten_number",
17
+ ]
18
+
19
+
20
+ # Library
21
+ # ---------------------------------------------------------------------------
22
+ from collections.abc import Callable
23
+ from dataclasses import dataclass, field
24
+ from functools import wraps
25
+ from typing import Annotated, NamedTuple, ParamSpec, Self, TypeVar
26
+
27
+ from absfuyu.core import versionadded
28
+
29
+ # Type
30
+ # ---------------------------------------------------------------------------
31
+ P = ParamSpec("P") # Parameter type
32
+ N = TypeVar("N", int, float) # Number type
33
+
34
+
35
+ # Class - Decimal
36
+ # ---------------------------------------------------------------------------
37
+ @versionadded("4.1.0")
38
+ class UnitSuffixFactory(NamedTuple):
39
+ base: int
40
+ short_name: list[str]
41
+ full_name: list[str]
42
+
43
+
44
+ @versionadded("4.1.0")
45
+ class CommonUnitSuffixesFactory:
46
+ NUMBER = UnitSuffixFactory(
47
+ 1000,
48
+ [
49
+ "",
50
+ "K",
51
+ "M",
52
+ "B",
53
+ "T",
54
+ "Qa",
55
+ "Qi",
56
+ "Sx",
57
+ "Sp",
58
+ "Oc",
59
+ "No",
60
+ "Dc",
61
+ "Ud",
62
+ "Dd",
63
+ "Td",
64
+ "Qad",
65
+ "Qid",
66
+ "Sxd",
67
+ "Spd",
68
+ "Ocd",
69
+ "Nod",
70
+ "Vg",
71
+ "Uvg",
72
+ "Dvg",
73
+ "Tvg",
74
+ "Qavg",
75
+ "Qivg",
76
+ "Sxvg",
77
+ "Spvg",
78
+ "Ovg",
79
+ "Nvg",
80
+ "Tg",
81
+ "Utg",
82
+ "Dtg",
83
+ "Ttg",
84
+ "Qatg",
85
+ "Qitg",
86
+ "Sxtg",
87
+ "Sptg",
88
+ "Otg",
89
+ "Ntg",
90
+ ],
91
+ [
92
+ "", # < Thousand
93
+ "Thousand",
94
+ "Million",
95
+ "Billion", # 1e9
96
+ "Trillion",
97
+ "Quadrillion",
98
+ "Quintillion",
99
+ "Sextillion",
100
+ "Septillion",
101
+ "Octillion",
102
+ "Nonillion",
103
+ "Decillion", # 1e33
104
+ "Undecillion",
105
+ "Duodecillion",
106
+ "Tredecillion",
107
+ "Quattuordecillion",
108
+ "Quindecillion",
109
+ "Sexdecillion",
110
+ "Septendecillion",
111
+ "Octodecillion",
112
+ "Novemdecillion",
113
+ "Vigintillion", # 1e63
114
+ "Unvigintillion",
115
+ "Duovigintillion",
116
+ "Tresvigintillion",
117
+ "Quattuorvigintillion",
118
+ "Quinvigintillion",
119
+ "Sesvigintillion",
120
+ "Septemvigintillion",
121
+ "Octovigintillion",
122
+ "Novemvigintillion",
123
+ "Trigintillion", # 1e93
124
+ "Untrigintillion",
125
+ "Duotrigintillion",
126
+ "Trestrigintillion",
127
+ "Quattuortrigintillion",
128
+ "Quintrigintillion",
129
+ "Sestrigintillion",
130
+ "Septentrigintillion",
131
+ "Octotrigintillion",
132
+ "Noventrigintillion", # 1e120
133
+ ],
134
+ )
135
+ DATA_SIZE = UnitSuffixFactory(
136
+ 1024,
137
+ ["b", "Kb", "MB", "GB", "TB", "PB", "EB", "ZB", "YB", "BB"],
138
+ [
139
+ "byte",
140
+ "kilobyte",
141
+ "megabyte",
142
+ "gigabyte",
143
+ "terabyte",
144
+ "petabyte",
145
+ "exabyte",
146
+ "zetabyte",
147
+ "yottabyte",
148
+ "brontobyte",
149
+ ],
150
+ )
151
+
152
+
153
+ @dataclass
154
+ @versionadded("4.1.0")
155
+ class Decimal:
156
+ """
157
+ Shorten large number
158
+
159
+ Parameters
160
+ ----------
161
+ original_value : int | float
162
+ Value to shorten
163
+
164
+ base : int
165
+ Short by base (must be > 0)
166
+
167
+ suffixes : list[str]
168
+ List of suffixes to use (ascending order)
169
+
170
+ factory : UnitSuffixFactory | None
171
+ ``UnitSuffixFactory`` to use
172
+ (will overwrite ``base`` and ``suffixes``)
173
+
174
+ suffix_full_name : bool
175
+ Use suffix full name (available with ``UnitSuffixFactory``), by default ``False``
176
+
177
+ Returns
178
+ -------
179
+ Decimal
180
+ Decimal instance
181
+ """
182
+
183
+ original_value: int | float = field(repr=False)
184
+ base: Annotated[int, "positive", "not_zero"] = field(repr=False, default=1000)
185
+ suffixes: list[str] = field(repr=False, default_factory=list)
186
+ factory: UnitSuffixFactory | None = field(repr=False, default=None)
187
+ suffix_full_name: bool = field(repr=False, default=False)
188
+ # Post init
189
+ value: int | float = field(init=False)
190
+ suffix: str = field(init=False)
191
+
192
+ def __post_init__(self) -> None:
193
+ self.base = max(1, self.base) # Make sure that base >= 1
194
+ self._get_factory()
195
+ self.value, self.suffix = self._convert_decimal()
196
+
197
+ def __str__(self) -> str:
198
+ return self.to_text().strip()
199
+
200
+ @classmethod
201
+ def number(cls, value: int | float, suffix_full_name: bool = False) -> Self:
202
+ """Decimal for normal large number"""
203
+ return cls(
204
+ value,
205
+ factory=CommonUnitSuffixesFactory.NUMBER,
206
+ suffix_full_name=suffix_full_name,
207
+ )
208
+
209
+ @classmethod
210
+ def data_size(cls, value: int | float, suffix_full_name: bool = False) -> Self:
211
+ """Decimal for data size"""
212
+ return cls(
213
+ value,
214
+ factory=CommonUnitSuffixesFactory.DATA_SIZE,
215
+ suffix_full_name=suffix_full_name,
216
+ )
217
+
218
+ @staticmethod
219
+ def scientific_short(value: int | float) -> str:
220
+ """Short number in scientific format"""
221
+ return f"{value:.2e}"
222
+
223
+ def _get_factory(self) -> None:
224
+ if self.factory is not None:
225
+ self.base = self.factory.base
226
+ self.suffixes = self.factory.full_name if self.suffix_full_name else self.factory.short_name
227
+
228
+ def _convert_decimal(self) -> tuple[float, str]:
229
+ """Convert to smaller number"""
230
+ suffix = self.suffixes[0] if len(self.suffixes) > 0 else ""
231
+ unit = 1
232
+ for i, suffix in enumerate(self.suffixes):
233
+ unit = self.base**i
234
+ if self.original_value < unit * self.base:
235
+ break
236
+ output = self.original_value / unit
237
+ return output, suffix
238
+
239
+ def to_text(self, decimal: int = 2, *, separator: str = " ", float_only: bool = True) -> str:
240
+ """
241
+ Convert to string
242
+
243
+ Parameters
244
+ ----------
245
+ decimal : int, optional
246
+ Round up to which decimal, by default ``2``
247
+
248
+ separator : str, optional
249
+ Character between value and suffix, by default ``" "``
250
+
251
+ float_only : bool, optional
252
+ Returns value as <float> instead of <int> when ``decimal = 0``, by default ``True``
253
+
254
+ Returns
255
+ -------
256
+ str
257
+ Decimal string
258
+ """
259
+ val = self.value.__round__(decimal)
260
+ formatted_value = f"{val:,}"
261
+ if not float_only and decimal == 0:
262
+ formatted_value = f"{int(val):,}"
263
+ return f"{formatted_value}{separator}{self.suffix}"
264
+
265
+
266
+ # Decorator
267
+ # ---------------------------------------------------------------------------
268
+ @versionadded("5.0.0")
269
+ def shorten_number(f: Callable[P, N]) -> Callable[P, Decimal]:
270
+ """
271
+ Shorten the number value by name
272
+
273
+ Parameters
274
+ ----------
275
+ f : Callable[P, N]
276
+ Function that return ``int`` or ``float``
277
+
278
+ Returns
279
+ -------
280
+ Callable[P, Decimal]
281
+ Function that return ``Decimal``
282
+
283
+
284
+ Usage
285
+ -----
286
+ Use this as a decorator (``@shorten_number``)
287
+
288
+ Example:
289
+ --------
290
+ >>> import random
291
+ >>> @shorten_number
292
+ >>> def big_num() -> int:
293
+ ... random.randint(100000000, 10000000000)
294
+ >>> big_num()
295
+ 4.20 B
296
+ """
297
+
298
+ @wraps(f)
299
+ def wrapper(*args: P.args, **kwargs: P.kwargs) -> Decimal:
300
+ value = Decimal.number(f(*args, **kwargs))
301
+ return value
302
+
303
+ return wrapper