absfuyu 4.0.0__py3-none-any.whl → 4.1.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.
Potentially problematic release.
This version of absfuyu might be problematic. Click here for more details.
- absfuyu/__init__.py +1 -1
- absfuyu/cli/do_group.py +62 -2
- absfuyu/core.py +14 -2
- absfuyu/game/__init__.py +1 -1
- absfuyu/game/game_stat.py +1 -1
- absfuyu/general/content.py +7 -7
- absfuyu/general/data_extension.py +3 -3
- absfuyu/general/generator.py +4 -2
- absfuyu/logger.py +2 -2
- absfuyu/tools/checksum.py +143 -0
- absfuyu/tools/converter.py +26 -2
- absfuyu/tools/obfuscator.py +1 -1
- absfuyu/util/shorten_number.py +228 -0
- {absfuyu-4.0.0.dist-info → absfuyu-4.1.1.dist-info}/METADATA +2 -1
- {absfuyu-4.0.0.dist-info → absfuyu-4.1.1.dist-info}/RECORD +18 -16
- {absfuyu-4.0.0.dist-info → absfuyu-4.1.1.dist-info}/licenses/LICENSE +21 -21
- {absfuyu-4.0.0.dist-info → absfuyu-4.1.1.dist-info}/WHEEL +0 -0
- {absfuyu-4.0.0.dist-info → absfuyu-4.1.1.dist-info}/entry_points.txt +0 -0
absfuyu/__init__.py
CHANGED
absfuyu/cli/do_group.py
CHANGED
|
@@ -3,13 +3,14 @@ ABSFUYU CLI
|
|
|
3
3
|
-----------
|
|
4
4
|
Do
|
|
5
5
|
|
|
6
|
-
Version: 1.
|
|
7
|
-
Date updated:
|
|
6
|
+
Version: 1.3.1
|
|
7
|
+
Date updated: 01/02/2025 (dd/mm/yyyy)
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
10
|
__all__ = ["do_group"]
|
|
11
11
|
|
|
12
12
|
import subprocess
|
|
13
|
+
from typing import Literal
|
|
13
14
|
|
|
14
15
|
import click
|
|
15
16
|
|
|
@@ -17,6 +18,7 @@ from absfuyu import __title__
|
|
|
17
18
|
from absfuyu.cli.color import COLOR
|
|
18
19
|
from absfuyu.core import __package_feature__
|
|
19
20
|
from absfuyu.general.human import Human2
|
|
21
|
+
from absfuyu.tools.checksum import Checksum
|
|
20
22
|
from absfuyu.util.zipped import Zipper
|
|
21
23
|
from absfuyu.version import PkgVersion
|
|
22
24
|
|
|
@@ -96,6 +98,63 @@ def unzip_files_in_dir(dir: str) -> None:
|
|
|
96
98
|
print("Done")
|
|
97
99
|
|
|
98
100
|
|
|
101
|
+
@click.command(name="checksum")
|
|
102
|
+
@click.argument("file_path", type=str)
|
|
103
|
+
@click.option(
|
|
104
|
+
"--hashmode",
|
|
105
|
+
"-m",
|
|
106
|
+
"hash_mode",
|
|
107
|
+
type=click.Choice(["md5", "sha1", "sha256", "sha512"]),
|
|
108
|
+
default="sha256",
|
|
109
|
+
show_default=True,
|
|
110
|
+
help="Hash mode",
|
|
111
|
+
)
|
|
112
|
+
@click.option(
|
|
113
|
+
"--save-result",
|
|
114
|
+
"-s",
|
|
115
|
+
"save_result",
|
|
116
|
+
type=bool,
|
|
117
|
+
default=False,
|
|
118
|
+
is_flag=True,
|
|
119
|
+
show_default=True,
|
|
120
|
+
help="Save checksum result to file",
|
|
121
|
+
)
|
|
122
|
+
@click.option(
|
|
123
|
+
"--recursive",
|
|
124
|
+
"-r",
|
|
125
|
+
"recursive_mode",
|
|
126
|
+
type=bool,
|
|
127
|
+
default=False,
|
|
128
|
+
is_flag=True,
|
|
129
|
+
show_default=True,
|
|
130
|
+
help="Do checksum for every file in the folder (including child folder)",
|
|
131
|
+
)
|
|
132
|
+
@click.option(
|
|
133
|
+
"--compare",
|
|
134
|
+
"-c",
|
|
135
|
+
"hash_to_compare",
|
|
136
|
+
type=str,
|
|
137
|
+
default=None,
|
|
138
|
+
show_default=True,
|
|
139
|
+
help="Hash to compare",
|
|
140
|
+
)
|
|
141
|
+
def file_checksum(
|
|
142
|
+
file_path: str,
|
|
143
|
+
hash_mode: str | Literal["md5", "sha1", "sha256", "sha512"],
|
|
144
|
+
save_result: bool,
|
|
145
|
+
recursive_mode: bool,
|
|
146
|
+
hash_to_compare: str,
|
|
147
|
+
) -> None:
|
|
148
|
+
"""Checksum for file"""
|
|
149
|
+
# print(hash_mode, save_result, recursive_mode)
|
|
150
|
+
instance = Checksum(file_path, hash_mode=hash_mode, save_result_to_file=save_result)
|
|
151
|
+
res = instance.checksum(recursive=recursive_mode)
|
|
152
|
+
if hash_to_compare:
|
|
153
|
+
print(res == hash_to_compare)
|
|
154
|
+
else:
|
|
155
|
+
print(res)
|
|
156
|
+
|
|
157
|
+
|
|
99
158
|
@click.group(name="do")
|
|
100
159
|
def do_group() -> None:
|
|
101
160
|
"""Perform functionalities"""
|
|
@@ -108,3 +167,4 @@ do_group.add_command(advice)
|
|
|
108
167
|
do_group.add_command(fs)
|
|
109
168
|
do_group.add_command(info)
|
|
110
169
|
do_group.add_command(unzip_files_in_dir)
|
|
170
|
+
do_group.add_command(file_checksum)
|
absfuyu/core.py
CHANGED
|
@@ -3,8 +3,8 @@ Absfuyu: Core
|
|
|
3
3
|
-------------
|
|
4
4
|
Contain type hints and other stuffs
|
|
5
5
|
|
|
6
|
-
Version: 2.
|
|
7
|
-
Date updated:
|
|
6
|
+
Version: 2.3.1
|
|
7
|
+
Date updated: 01/02/2025 (dd/mm/yyyy)
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
10
|
# Module Package
|
|
@@ -21,6 +21,9 @@ __all__ = [
|
|
|
21
21
|
__package_feature__ = ["beautiful", "extra", "res", "full", "dev"]
|
|
22
22
|
|
|
23
23
|
|
|
24
|
+
# Library
|
|
25
|
+
###########################################################################
|
|
26
|
+
from importlib import import_module
|
|
24
27
|
from importlib.resources import files
|
|
25
28
|
|
|
26
29
|
|
|
@@ -43,3 +46,12 @@ class CLITextColor:
|
|
|
43
46
|
CORE_PATH = files("absfuyu")
|
|
44
47
|
CONFIG_PATH = CORE_PATH.joinpath("config", "config.json")
|
|
45
48
|
DATA_PATH = CORE_PATH.joinpath("pkg_data")
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
# tqdm wrapper
|
|
52
|
+
try:
|
|
53
|
+
tqdm = import_module("tqdm").tqdm
|
|
54
|
+
except ModuleNotFoundError:
|
|
55
|
+
|
|
56
|
+
def tqdm(iterable, **kwargs):
|
|
57
|
+
return iterable
|
absfuyu/game/__init__.py
CHANGED
absfuyu/game/game_stat.py
CHANGED
|
@@ -26,7 +26,7 @@ class GameStats:
|
|
|
26
26
|
def _update_win_rate(self) -> None:
|
|
27
27
|
try:
|
|
28
28
|
self._win_rate = self.win / (self.win + self.draw + self.lose)
|
|
29
|
-
self.win_rate = f"{self._win_rate*100:,.2f}%"
|
|
29
|
+
self.win_rate = f"{self._win_rate * 100:,.2f}%"
|
|
30
30
|
except ZeroDivisionError:
|
|
31
31
|
self._win_rate = 0
|
|
32
32
|
self.win_rate = "N/A"
|
absfuyu/general/content.py
CHANGED
|
@@ -427,12 +427,12 @@ class ContentLoader:
|
|
|
427
427
|
self.tag_dictionary = tag_dictionary
|
|
428
428
|
|
|
429
429
|
# symbol stuff
|
|
430
|
-
assert (
|
|
431
|
-
comment_symbol
|
|
432
|
-
)
|
|
433
|
-
assert (
|
|
434
|
-
tag_separate_symbol
|
|
435
|
-
)
|
|
430
|
+
assert comment_symbol != split_symbol, (
|
|
431
|
+
"comment_symbol and split_symbol should have different values"
|
|
432
|
+
)
|
|
433
|
+
assert tag_separate_symbol != split_symbol, (
|
|
434
|
+
"tag_separate_symbol and split_symbol should have different values"
|
|
435
|
+
)
|
|
436
436
|
self.comment_symbol: str = comment_symbol
|
|
437
437
|
self.split_symbol: str = split_symbol
|
|
438
438
|
self.tag_separate_symbol: str = tag_separate_symbol
|
|
@@ -485,7 +485,7 @@ class ContentLoader:
|
|
|
485
485
|
if x.startswith(self.comment_symbol) or len(x) == 0:
|
|
486
486
|
continue # skip comment and empty lines
|
|
487
487
|
logger.debug(
|
|
488
|
-
f"### Loop {i+1} #####################################################################"
|
|
488
|
+
f"### Loop {i + 1} #####################################################################"
|
|
489
489
|
)
|
|
490
490
|
|
|
491
491
|
temp = x.split(self.split_symbol)
|
|
@@ -302,7 +302,7 @@ class Text(str):
|
|
|
302
302
|
output.append(f"{splt_name[i]}='{temp[i]}'")
|
|
303
303
|
else:
|
|
304
304
|
for i in range(splt_len):
|
|
305
|
-
output.append(f"{custom_var_name}{i+1}='{temp[i]}'")
|
|
305
|
+
output.append(f"{custom_var_name}{i + 1}='{temp[i]}'")
|
|
306
306
|
|
|
307
307
|
# joined variable
|
|
308
308
|
temp = []
|
|
@@ -319,9 +319,9 @@ class Text(str):
|
|
|
319
319
|
if i == 0:
|
|
320
320
|
temp.append(f"{custom_var_name}=")
|
|
321
321
|
if i == splt_len - 1:
|
|
322
|
-
temp.append(f"{custom_var_name}{i+1}")
|
|
322
|
+
temp.append(f"{custom_var_name}{i + 1}")
|
|
323
323
|
else:
|
|
324
|
-
temp.append(f"{custom_var_name}{i+1}+")
|
|
324
|
+
temp.append(f"{custom_var_name}{i + 1}+")
|
|
325
325
|
|
|
326
326
|
output.append("".join(temp))
|
|
327
327
|
if custom_var_name is None:
|
absfuyu/general/generator.py
CHANGED
|
@@ -156,7 +156,9 @@ class Generator:
|
|
|
156
156
|
|
|
157
157
|
while count < times:
|
|
158
158
|
s = "".join(choice(char_lst) for _ in range(size))
|
|
159
|
-
logger.debug(
|
|
159
|
+
logger.debug(
|
|
160
|
+
f"Time generated: {count + 1}. Remaining: {times - count - 1}. {s}"
|
|
161
|
+
)
|
|
160
162
|
if not unique:
|
|
161
163
|
unique_string.append(s)
|
|
162
164
|
count += 1
|
|
@@ -274,7 +276,7 @@ class Generator:
|
|
|
274
276
|
if num[i] >= 10: # type: ignore
|
|
275
277
|
num[i] -= 9 # type: ignore
|
|
276
278
|
sum += num[i] # type: ignore
|
|
277
|
-
logger.debug(f"Loop {i+1}: {num[i]}, {sum}")
|
|
279
|
+
logger.debug(f"Loop {i + 1}: {num[i]}, {sum}")
|
|
278
280
|
|
|
279
281
|
out = (10 - (sum % 10)) % 10
|
|
280
282
|
logger.debug(f"Output: {out}")
|
absfuyu/logger.py
CHANGED
|
@@ -100,7 +100,7 @@ def _compress_list_for_print(iterable: list, max_visible: Optional[int] = 5) ->
|
|
|
100
100
|
# temp = [iterable[:cut_idx_1], ["..."], iterable[len(iterable)-cut_idx_2:]]
|
|
101
101
|
# out = list(chain.from_iterable(temp))
|
|
102
102
|
# out = [*iterable[:cut_idx_1], "...", *iterable[len(iterable)-cut_idx_2:]] # Version 2
|
|
103
|
-
out = f"{str(iterable[:cut_idx_1])[:-1]}, ...,{str(iterable[len(iterable)-cut_idx_2:])[1:]}" # Version 3
|
|
103
|
+
out = f"{str(iterable[:cut_idx_1])[:-1]}, ...,{str(iterable[len(iterable) - cut_idx_2 :])[1:]}" # Version 3
|
|
104
104
|
# logger.debug(out)
|
|
105
105
|
return f"{out} [Len: {len(iterable)}]"
|
|
106
106
|
|
|
@@ -123,7 +123,7 @@ def _compress_string_for_print(text: str, max_visible: Optional[int] = 120) -> s
|
|
|
123
123
|
return str(text)
|
|
124
124
|
else:
|
|
125
125
|
cut_idx = math.floor((max_visible - 3) / 2)
|
|
126
|
-
temp = f"{text[:cut_idx]}...{text[len(text)-cut_idx:]}"
|
|
126
|
+
temp = f"{text[:cut_idx]}...{text[len(text) - cut_idx :]}"
|
|
127
127
|
return f"{temp} [Len: {len(text)}]"
|
|
128
128
|
|
|
129
129
|
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Absufyu: Checksum
|
|
3
|
+
-----------------
|
|
4
|
+
Check MD5, SHA256, ...
|
|
5
|
+
|
|
6
|
+
Version: 1.1.0
|
|
7
|
+
Date updated: 01/02/2025 (dd/mm/yyyy)
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
# Module level
|
|
11
|
+
###########################################################################
|
|
12
|
+
__all__ = ["Checksum", "checksum_operation"]
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
# Library
|
|
16
|
+
###########################################################################
|
|
17
|
+
import hashlib
|
|
18
|
+
from pathlib import Path
|
|
19
|
+
from typing import Literal
|
|
20
|
+
|
|
21
|
+
from absfuyu.core import tqdm
|
|
22
|
+
|
|
23
|
+
# Function
|
|
24
|
+
###########################################################################
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
# Deprecated
|
|
28
|
+
def checksum_operation(
|
|
29
|
+
file: Path | str,
|
|
30
|
+
hash_mode: str | Literal["md5", "sha1", "sha256", "sha512"] = "sha256",
|
|
31
|
+
) -> str:
|
|
32
|
+
"""This performs checksum"""
|
|
33
|
+
if hash_mode.lower() == "md5":
|
|
34
|
+
hash_engine = hashlib.md5()
|
|
35
|
+
elif hash_mode.lower() == "sha1":
|
|
36
|
+
hash_engine = hashlib.sha1()
|
|
37
|
+
elif hash_mode.lower() == "sha256":
|
|
38
|
+
hash_engine = hashlib.sha256()
|
|
39
|
+
elif hash_mode.lower() == "sha512":
|
|
40
|
+
hash_engine = hashlib.sha512()
|
|
41
|
+
else:
|
|
42
|
+
hash_engine = hashlib.md5()
|
|
43
|
+
|
|
44
|
+
with open(Path(file), "rb") as f:
|
|
45
|
+
while True:
|
|
46
|
+
data = f.read(4096)
|
|
47
|
+
if len(data) == 0:
|
|
48
|
+
break
|
|
49
|
+
else:
|
|
50
|
+
hash_engine.update(data)
|
|
51
|
+
return hash_engine.hexdigest()
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class Checksum:
|
|
55
|
+
def __init__(
|
|
56
|
+
self,
|
|
57
|
+
path: str | Path,
|
|
58
|
+
hash_mode: str | Literal["md5", "sha1", "sha256", "sha512"] = "sha256",
|
|
59
|
+
save_result_to_file: bool = False,
|
|
60
|
+
) -> None:
|
|
61
|
+
self.path = Path(path)
|
|
62
|
+
self.hash_mode = hash_mode
|
|
63
|
+
self.save_result_to_file = save_result_to_file
|
|
64
|
+
self.checksum_result_file_name = "checksum_results.txt"
|
|
65
|
+
|
|
66
|
+
def _get_hash_engine(self):
|
|
67
|
+
hash_mode = self.hash_mode
|
|
68
|
+
if hash_mode.lower() == "md5":
|
|
69
|
+
hash_engine = hashlib.md5()
|
|
70
|
+
elif hash_mode.lower() == "sha1":
|
|
71
|
+
hash_engine = hashlib.sha1()
|
|
72
|
+
elif hash_mode.lower() == "sha256":
|
|
73
|
+
hash_engine = hashlib.sha256()
|
|
74
|
+
elif hash_mode.lower() == "sha512":
|
|
75
|
+
hash_engine = hashlib.sha512()
|
|
76
|
+
else:
|
|
77
|
+
hash_engine = hashlib.md5()
|
|
78
|
+
return hash_engine
|
|
79
|
+
|
|
80
|
+
def _checksum_operation(
|
|
81
|
+
self,
|
|
82
|
+
file: Path | str,
|
|
83
|
+
) -> str:
|
|
84
|
+
"""This performs checksum"""
|
|
85
|
+
|
|
86
|
+
hash_engine = self._get_hash_engine().copy()
|
|
87
|
+
with open(Path(file), "rb") as f:
|
|
88
|
+
# Read and hash the file in 4K chunks. Reading the whole
|
|
89
|
+
# file at once might consume a lot of memory if it is
|
|
90
|
+
# large.
|
|
91
|
+
while True:
|
|
92
|
+
data = f.read(4096)
|
|
93
|
+
if len(data) == 0:
|
|
94
|
+
break
|
|
95
|
+
else:
|
|
96
|
+
hash_engine.update(data)
|
|
97
|
+
return hash_engine.hexdigest() # type: ignore
|
|
98
|
+
|
|
99
|
+
def checksum(self, recursive: bool = True) -> str:
|
|
100
|
+
"""Perform checksum
|
|
101
|
+
|
|
102
|
+
Parameters
|
|
103
|
+
----------
|
|
104
|
+
recursive : bool, optional
|
|
105
|
+
Do checksum for every file in the folder (including child folder), by default True
|
|
106
|
+
|
|
107
|
+
Returns
|
|
108
|
+
-------
|
|
109
|
+
str
|
|
110
|
+
Checksum hash
|
|
111
|
+
"""
|
|
112
|
+
if self.path.absolute().is_dir():
|
|
113
|
+
new_path = self.path.joinpath(self.checksum_result_file_name)
|
|
114
|
+
# List of files
|
|
115
|
+
if recursive:
|
|
116
|
+
file_list: list[Path] = [
|
|
117
|
+
x for x in self.path.glob("**/*") if x.is_file()
|
|
118
|
+
]
|
|
119
|
+
else:
|
|
120
|
+
file_list = [x for x in self.path.glob("*") if x.is_file()]
|
|
121
|
+
|
|
122
|
+
# Checksum
|
|
123
|
+
res = []
|
|
124
|
+
for x in tqdm(file_list, desc="Calculating hash", unit_scale=True):
|
|
125
|
+
name = x.relative_to(self.path)
|
|
126
|
+
res.append(f"{self._checksum_operation(x)} | {name}")
|
|
127
|
+
output = "\n".join(res)
|
|
128
|
+
else:
|
|
129
|
+
new_path = self.path.with_name(self.checksum_result_file_name)
|
|
130
|
+
output = self._checksum_operation(self.path)
|
|
131
|
+
|
|
132
|
+
# Save result
|
|
133
|
+
if self.save_result_to_file:
|
|
134
|
+
with open(new_path, "w", encoding="utf-8") as f:
|
|
135
|
+
f.write(output)
|
|
136
|
+
|
|
137
|
+
return output
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
# Run
|
|
141
|
+
###########################################################################
|
|
142
|
+
if __name__ == "__main__":
|
|
143
|
+
pass
|
absfuyu/tools/converter.py
CHANGED
|
@@ -3,8 +3,8 @@ Absufyu: Converter
|
|
|
3
3
|
------------------
|
|
4
4
|
Convert stuff
|
|
5
5
|
|
|
6
|
-
Version: 1.
|
|
7
|
-
Date updated:
|
|
6
|
+
Version: 1.3.0
|
|
7
|
+
Date updated: 01/02/2025 (dd/mm/yyyy)
|
|
8
8
|
|
|
9
9
|
Feature:
|
|
10
10
|
--------
|
|
@@ -25,6 +25,7 @@ import math
|
|
|
25
25
|
import re
|
|
26
26
|
import string
|
|
27
27
|
from itertools import chain, combinations
|
|
28
|
+
from pathlib import Path
|
|
28
29
|
|
|
29
30
|
from absfuyu.core import CLITextColor
|
|
30
31
|
from absfuyu.logger import logger
|
|
@@ -48,6 +49,29 @@ class Base64EncodeDecode:
|
|
|
48
49
|
def decode(data: str) -> str:
|
|
49
50
|
return base64.b64decode(data).decode()
|
|
50
51
|
|
|
52
|
+
@staticmethod
|
|
53
|
+
def encode_image(img_path: Path | str, data_tag: bool = False) -> str:
|
|
54
|
+
"""Encode image file into base64 string
|
|
55
|
+
|
|
56
|
+
Parameters
|
|
57
|
+
----------
|
|
58
|
+
img_path : Path | str
|
|
59
|
+
Path to image
|
|
60
|
+
data_tag : bool, optional
|
|
61
|
+
Add data tag before base64 string, by default False
|
|
62
|
+
|
|
63
|
+
Returns
|
|
64
|
+
-------
|
|
65
|
+
str
|
|
66
|
+
Encoded image
|
|
67
|
+
"""
|
|
68
|
+
img = Path(img_path)
|
|
69
|
+
with open(img, "rb") as img_file:
|
|
70
|
+
b64_data = base64.b64encode(img_file.read()).decode("utf-8")
|
|
71
|
+
if data_tag:
|
|
72
|
+
return f"data:image/{img.suffix[1:]};charset=utf-8;base64,{b64_data}"
|
|
73
|
+
return b64_data
|
|
74
|
+
|
|
51
75
|
|
|
52
76
|
class ChemistryElement:
|
|
53
77
|
"""Chemistry Element"""
|
absfuyu/tools/obfuscator.py
CHANGED
|
@@ -217,7 +217,7 @@ class Obfuscator:
|
|
|
217
217
|
)
|
|
218
218
|
for x in bait_lst:
|
|
219
219
|
output.append(
|
|
220
|
-
f"{x}='{gen.generate_string(charset=Charset.DEFAULT,size=self.split_every_length,times=1,string_type_if_1=True)}'"
|
|
220
|
+
f"{x}='{gen.generate_string(charset=Charset.DEFAULT, size=self.split_every_length, times=1, string_type_if_1=True)}'"
|
|
221
221
|
)
|
|
222
222
|
|
|
223
223
|
random_eval_text = str(random.randint(1, 100))
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Absfuyu: Shorten number
|
|
3
|
+
---------------
|
|
4
|
+
Short number base on suffixes
|
|
5
|
+
|
|
6
|
+
Version: 1.0.0
|
|
7
|
+
Last update: 01/02/2025 (dd/mm/yyyy)
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
# Module level
|
|
11
|
+
###########################################################################
|
|
12
|
+
__all__ = ["UnitSuffixFactory", "CommonUnitSuffixesFactory", "Decimal"]
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
# Library
|
|
16
|
+
###########################################################################
|
|
17
|
+
from dataclasses import dataclass, field
|
|
18
|
+
from typing import Annotated, NamedTuple, Self
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
# Class
|
|
22
|
+
###########################################################################
|
|
23
|
+
class UnitSuffixFactory(NamedTuple):
|
|
24
|
+
base: int
|
|
25
|
+
short_name: list[str]
|
|
26
|
+
full_name: list[str]
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class CommonUnitSuffixesFactory:
|
|
30
|
+
NUMBER = UnitSuffixFactory(
|
|
31
|
+
1000,
|
|
32
|
+
[
|
|
33
|
+
"",
|
|
34
|
+
"K",
|
|
35
|
+
"M",
|
|
36
|
+
"B",
|
|
37
|
+
"T",
|
|
38
|
+
"Qa",
|
|
39
|
+
"Qi",
|
|
40
|
+
"Sx",
|
|
41
|
+
"Sp",
|
|
42
|
+
"Oc",
|
|
43
|
+
"No",
|
|
44
|
+
"Dc",
|
|
45
|
+
"Ud",
|
|
46
|
+
"Dd",
|
|
47
|
+
"Td",
|
|
48
|
+
"Qad",
|
|
49
|
+
"Qid",
|
|
50
|
+
"Sxd",
|
|
51
|
+
"Spd",
|
|
52
|
+
"Ocd",
|
|
53
|
+
"Nod",
|
|
54
|
+
"Vg",
|
|
55
|
+
"Uvg",
|
|
56
|
+
"Dvg",
|
|
57
|
+
"Tvg",
|
|
58
|
+
"Qavg",
|
|
59
|
+
"Qivg",
|
|
60
|
+
"Sxvg",
|
|
61
|
+
"Spvg",
|
|
62
|
+
"Ovg",
|
|
63
|
+
"Nvg",
|
|
64
|
+
"Tg",
|
|
65
|
+
"Utg",
|
|
66
|
+
"Dtg",
|
|
67
|
+
"Ttg",
|
|
68
|
+
"Qatg",
|
|
69
|
+
"Qitg",
|
|
70
|
+
"Sxtg",
|
|
71
|
+
"Sptg",
|
|
72
|
+
"Otg",
|
|
73
|
+
"Ntg",
|
|
74
|
+
],
|
|
75
|
+
[
|
|
76
|
+
"", # < Thousand
|
|
77
|
+
"Thousand",
|
|
78
|
+
"Million",
|
|
79
|
+
"Billion", # 1e9
|
|
80
|
+
"Trillion",
|
|
81
|
+
"Quadrillion",
|
|
82
|
+
"Quintillion",
|
|
83
|
+
"Sextillion",
|
|
84
|
+
"Septillion",
|
|
85
|
+
"Octillion",
|
|
86
|
+
"Nonillion",
|
|
87
|
+
"Decillion", # 1e33
|
|
88
|
+
"Undecillion",
|
|
89
|
+
"Duodecillion",
|
|
90
|
+
"Tredecillion",
|
|
91
|
+
"Quattuordecillion",
|
|
92
|
+
"Quindecillion",
|
|
93
|
+
"Sexdecillion",
|
|
94
|
+
"Septendecillion",
|
|
95
|
+
"Octodecillion",
|
|
96
|
+
"Novemdecillion",
|
|
97
|
+
"Vigintillion", # 1e63
|
|
98
|
+
"Unvigintillion",
|
|
99
|
+
"Duovigintillion",
|
|
100
|
+
"Tresvigintillion",
|
|
101
|
+
"Quattuorvigintillion",
|
|
102
|
+
"Quinvigintillion",
|
|
103
|
+
"Sesvigintillion",
|
|
104
|
+
"Septemvigintillion",
|
|
105
|
+
"Octovigintillion",
|
|
106
|
+
"Novemvigintillion",
|
|
107
|
+
"Trigintillion", # 1e93
|
|
108
|
+
"Untrigintillion",
|
|
109
|
+
"Duotrigintillion",
|
|
110
|
+
"Trestrigintillion",
|
|
111
|
+
"Quattuortrigintillion",
|
|
112
|
+
"Quintrigintillion",
|
|
113
|
+
"Sestrigintillion",
|
|
114
|
+
"Septentrigintillion",
|
|
115
|
+
"Octotrigintillion",
|
|
116
|
+
"Noventrigintillion", # 1e120
|
|
117
|
+
],
|
|
118
|
+
)
|
|
119
|
+
DATA_SIZE = UnitSuffixFactory(
|
|
120
|
+
1024,
|
|
121
|
+
["b", "Kb", "MB", "GB", "TB", "PB", "EB", "ZB", "YB", "BB"],
|
|
122
|
+
[
|
|
123
|
+
"byte",
|
|
124
|
+
"kilobyte",
|
|
125
|
+
"megabyte",
|
|
126
|
+
"gigabyte",
|
|
127
|
+
"terabyte",
|
|
128
|
+
"petabyte",
|
|
129
|
+
"exabyte",
|
|
130
|
+
"zetabyte",
|
|
131
|
+
"yottabyte",
|
|
132
|
+
"brontobyte",
|
|
133
|
+
],
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
@dataclass
|
|
138
|
+
class Decimal:
|
|
139
|
+
"""
|
|
140
|
+
Shorten large number
|
|
141
|
+
|
|
142
|
+
:param original_value: Value to shorten
|
|
143
|
+
:param base: Short by base (must be > 0)
|
|
144
|
+
:param suffixes: List of suffixes to use (ascending order)
|
|
145
|
+
:param factory: ``UnitSuffixFactory`` to use (will overwrite ``base`` and ``suffixes``)
|
|
146
|
+
:param suffix_full_name: Use suffix full name (default: False)
|
|
147
|
+
"""
|
|
148
|
+
|
|
149
|
+
original_value: int | float = field(repr=False)
|
|
150
|
+
base: Annotated[int, "positive", "not_zero"] = field(repr=False, default=1000)
|
|
151
|
+
suffixes: list[str] = field(repr=False, default_factory=list)
|
|
152
|
+
factory: UnitSuffixFactory | None = field(repr=False, default=None)
|
|
153
|
+
suffix_full_name: bool = field(repr=False, default=False)
|
|
154
|
+
# Post init
|
|
155
|
+
value: int | float = field(init=False)
|
|
156
|
+
suffix: str = field(init=False)
|
|
157
|
+
|
|
158
|
+
def __post_init__(self) -> None:
|
|
159
|
+
self._get_factory()
|
|
160
|
+
self.value, self.suffix = self._convert_decimal()
|
|
161
|
+
|
|
162
|
+
def __str__(self) -> str:
|
|
163
|
+
return self.to_text()
|
|
164
|
+
|
|
165
|
+
@classmethod
|
|
166
|
+
def number(cls, value: int | float, suffix_full_name: bool = False) -> Self:
|
|
167
|
+
"""Decimal for normal large number"""
|
|
168
|
+
return cls(
|
|
169
|
+
value,
|
|
170
|
+
factory=CommonUnitSuffixesFactory.NUMBER,
|
|
171
|
+
suffix_full_name=suffix_full_name,
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
@classmethod
|
|
175
|
+
def data_size(cls, value: int | float, suffix_full_name: bool = False) -> Self:
|
|
176
|
+
"""Decimal for data size"""
|
|
177
|
+
return cls(
|
|
178
|
+
value,
|
|
179
|
+
factory=CommonUnitSuffixesFactory.DATA_SIZE,
|
|
180
|
+
suffix_full_name=suffix_full_name,
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
@staticmethod
|
|
184
|
+
def scientific_short(value: int | float) -> str:
|
|
185
|
+
"""Short number in scientific format"""
|
|
186
|
+
return f"{value:.2e}"
|
|
187
|
+
|
|
188
|
+
def _get_factory(self) -> None:
|
|
189
|
+
if self.factory:
|
|
190
|
+
self.base = self.factory.base
|
|
191
|
+
self.suffixes = (
|
|
192
|
+
self.factory.full_name
|
|
193
|
+
if self.suffix_full_name
|
|
194
|
+
else self.factory.short_name
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
def _convert_decimal(self) -> tuple[float, str]:
|
|
198
|
+
"""Convert to smaller number"""
|
|
199
|
+
suffix = self.suffixes[0] if len(self.suffixes) > 0 else ""
|
|
200
|
+
unit = 1
|
|
201
|
+
for i, suffix in enumerate(self.suffixes):
|
|
202
|
+
unit = self.base**i
|
|
203
|
+
if self.original_value < unit * self.base:
|
|
204
|
+
break
|
|
205
|
+
output = self.original_value / unit
|
|
206
|
+
return output, suffix
|
|
207
|
+
|
|
208
|
+
def to_text(
|
|
209
|
+
self, decimal: int = 2, *, separator: str = " ", float_only: bool = True
|
|
210
|
+
) -> str:
|
|
211
|
+
"""
|
|
212
|
+
Convert to string
|
|
213
|
+
|
|
214
|
+
:param decimal: Round up to which decimal
|
|
215
|
+
:param separator: Character between value and suffix, default: ``" "``
|
|
216
|
+
:param float_only: Returns value as <float> instead of <int> when ``decimal = 0``
|
|
217
|
+
"""
|
|
218
|
+
val = self.value.__round__(decimal)
|
|
219
|
+
formatted_value = f"{val:,}"
|
|
220
|
+
if not float_only and decimal == 0:
|
|
221
|
+
formatted_value = f"{int(val):,}"
|
|
222
|
+
return f"{formatted_value}{separator}{self.suffix}"
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
# Run
|
|
226
|
+
###########################################################################
|
|
227
|
+
if __name__ == "__main__":
|
|
228
|
+
print(str(Decimal.number(10000)))
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: absfuyu
|
|
3
|
-
Version: 4.
|
|
3
|
+
Version: 4.1.1
|
|
4
4
|
Summary: A small collection of code
|
|
5
5
|
Project-URL: Homepage, https://github.com/AbsoluteWinter/absfuyu-public
|
|
6
6
|
Project-URL: Documentation, https://absolutewinter.github.io/absfuyu-docs/
|
|
@@ -46,6 +46,7 @@ Requires-Dist: absfuyu-res; extra == 'full'
|
|
|
46
46
|
Requires-Dist: numpy; extra == 'full'
|
|
47
47
|
Requires-Dist: pandas; extra == 'full'
|
|
48
48
|
Requires-Dist: rich; extra == 'full'
|
|
49
|
+
Requires-Dist: tqdm; extra == 'full'
|
|
49
50
|
Provides-Extra: res
|
|
50
51
|
Requires-Dist: absfuyu-res; extra == 'res'
|
|
51
52
|
Description-Content-Type: text/markdown
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
absfuyu/__init__.py,sha256=
|
|
1
|
+
absfuyu/__init__.py,sha256=EGTitZIqMbg-jQFJD5nJi4papdMQsCoYnr-9EnvlUmI,638
|
|
2
2
|
absfuyu/__main__.py,sha256=OpMwc35W5VANzw6gvlqJaJOl2H89i_frFZbleUQwDss,163
|
|
3
|
-
absfuyu/core.py,sha256=
|
|
3
|
+
absfuyu/core.py,sha256=MZQH3hpUAamGx2NM-TlIWvAnRBBwa9R9fm7F2WFe60c,1240
|
|
4
4
|
absfuyu/everything.py,sha256=PGIXlqgxyADFPygohVYVIb7fZz72L_xXrlLX0WImuYg,788
|
|
5
|
-
absfuyu/logger.py,sha256=
|
|
5
|
+
absfuyu/logger.py,sha256=Pue3oWYYSF03A-fVp3E137jzlZVI71mYqddic8myauQ,13141
|
|
6
6
|
absfuyu/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
7
|
absfuyu/sort.py,sha256=Z9nK15WehmZjlUNwvAOwF4FhV-rFcqlXkCcehNYmo78,6827
|
|
8
8
|
absfuyu/version.py,sha256=KMaeXNl93L4VU2RnTySoFv23IDyTvHfy84nyatFcKaE,14128
|
|
9
9
|
absfuyu/cli/__init__.py,sha256=nKTbe4iPpkjtDDMJwVY36heInKDm1zyek9ags5an7s4,1105
|
|
10
10
|
absfuyu/cli/color.py,sha256=7M3XqFllt26tv_NR5qKgyTId6wnVADtUB74cDYy8pOQ,497
|
|
11
11
|
absfuyu/cli/config_group.py,sha256=FsyYm2apSyAA2PAD12CpHAizenRds_QlLf8j0AlLuVo,1230
|
|
12
|
-
absfuyu/cli/do_group.py,sha256=
|
|
12
|
+
absfuyu/cli/do_group.py,sha256=HS1G9nFnqtmNVwWZiOGvu4RB6URT2VnCpEtdY9e7n2Q,4132
|
|
13
13
|
absfuyu/cli/game_group.py,sha256=ySpL2hm4VCplhNY0s22kBGI5eFCdJj9fm1T58yftU74,2348
|
|
14
14
|
absfuyu/config/__init__.py,sha256=4r77tFm3mgHX8H1uWWNXC_FZ8hiSJzsYAyiStZiVj5w,8641
|
|
15
15
|
absfuyu/config/config.json,sha256=-ZQnmDuLq0aAFfsrQbSNR3tq5k9Eu9IVUQgYD9htIQM,646
|
|
@@ -25,23 +25,24 @@ absfuyu/extensions/extra/data_analysis.py,sha256=JR8smS6gVRZ4jJtboshCpC3jFabVLQ8
|
|
|
25
25
|
absfuyu/fun/WGS.py,sha256=Aug9BsLTawBDkjdCQ3oqWAfGGeY6ZSBV1kENrTVnlUA,9461
|
|
26
26
|
absfuyu/fun/__init__.py,sha256=AOaq3NdgaK7e5hk622Wspiu6Z2biVK-jpk2FChn05rA,6366
|
|
27
27
|
absfuyu/fun/tarot.py,sha256=Y0z5rNWLOmqfJV961L_9LzUORRNR89Vegy7KqgNR9xs,2539
|
|
28
|
-
absfuyu/game/__init__.py,sha256=
|
|
29
|
-
absfuyu/game/game_stat.py,sha256=
|
|
28
|
+
absfuyu/game/__init__.py,sha256=qOPsnjKXSc1LobDQmF131BrBIyOEy0oddFRCEHsg8P8,4419
|
|
29
|
+
absfuyu/game/game_stat.py,sha256=xayD7zkcR76m-5UOx8flEE5MO_LEZoCe6FnFyPg4YE4,963
|
|
30
30
|
absfuyu/game/sudoku.py,sha256=zMsQ2opVFpREZMdSmu2ZXwV4mu463rRLdwrGV9qwg2s,10130
|
|
31
31
|
absfuyu/game/tictactoe.py,sha256=OfI7WHxxiAbhuRAt8qsDG1BJuc3hlhX4N6-HORq9fbI,9603
|
|
32
32
|
absfuyu/game/wordle.py,sha256=1RpgB8fBgcL_E2TgbTFXjZHtzthJQysCAl0fbK_LG5w,101336
|
|
33
33
|
absfuyu/general/__init__.py,sha256=6AJZ7Ul6l8MKaqEwRD2ktQUIBfhTPaDUYDQB7MUQ5rc,2481
|
|
34
|
-
absfuyu/general/content.py,sha256=
|
|
35
|
-
absfuyu/general/data_extension.py,sha256=
|
|
36
|
-
absfuyu/general/generator.py,sha256=
|
|
34
|
+
absfuyu/general/content.py,sha256=IfX5SkgoD-sjYJrbRHRIS5-g6fGp_X65A07ViB0zLpk,17443
|
|
35
|
+
absfuyu/general/data_extension.py,sha256=tvZM4moHNxqOh_iT6ekFELZgfhiIoZ8SUqm9wUIaokU,48938
|
|
36
|
+
absfuyu/general/generator.py,sha256=PW_4Z-YZ30StwDuUUfHD7B0qnVKMo_ZZHHKj54BzgXk,9731
|
|
37
37
|
absfuyu/general/human.py,sha256=drZT1nI_YwGMOwZu8pbzf3frM-SUY15cOcnUpc4pzQk,12241
|
|
38
38
|
absfuyu/pkg_data/__init__.py,sha256=VbUoFnUDiw_3bKZVvIzwf_ve0y97rmhfpkbbrP_ob_w,4761
|
|
39
39
|
absfuyu/pkg_data/chemistry.pkl,sha256=kYWNa_PVffoDnzT8b9Jvimmf_GZshPe1D-SnEKERsLo,4655
|
|
40
40
|
absfuyu/pkg_data/tarot.pkl,sha256=ssXTCC_BQgslO5F-3a9HivbxFQ6BioIe2E1frPVi2m0,56195
|
|
41
41
|
absfuyu/tools/__init__.py,sha256=VDmmMLEfiRyUWPVrPYc8JUEa5TXjLguKVnyI3YavFv0,118
|
|
42
|
-
absfuyu/tools/
|
|
42
|
+
absfuyu/tools/checksum.py,sha256=cLSuL_ZsoroFYYVR1GhOfBbon_iXEm1OazbjYFPJabM,4393
|
|
43
|
+
absfuyu/tools/converter.py,sha256=8jqOSdkbzzupKJcBndV5q4OBu2kWFWpV3j2IswS4Vqc,10609
|
|
43
44
|
absfuyu/tools/keygen.py,sha256=drj8OUDCXrLvcVf9_Shd5UMShm_mBTxa9Sg_BrAT5yM,7356
|
|
44
|
-
absfuyu/tools/obfuscator.py,sha256=
|
|
45
|
+
absfuyu/tools/obfuscator.py,sha256=kiaFvMw2RwEfZvXKdbUrEYA_AuVETivoL2eVmtUWU4w,8669
|
|
45
46
|
absfuyu/tools/stats.py,sha256=zZNK59qn0xqQlyw5CP3MP5WRMSxncdKZythqvgNR1lQ,5191
|
|
46
47
|
absfuyu/tools/web.py,sha256=Mb5RYj1Fu5eB-mYMusyrE2JG6_ZPEf8WT72Z8q6Ep74,1406
|
|
47
48
|
absfuyu/util/__init__.py,sha256=1V2DwIUt-bSiSURnk6AzfKB0HRLHwoi8_6RaIvywlzU,3716
|
|
@@ -51,9 +52,10 @@ absfuyu/util/lunar.py,sha256=xW7HcgyIRjWBhQv36RimNZIT8Ed_X83moqr5h5-Ku9k,13750
|
|
|
51
52
|
absfuyu/util/path.py,sha256=p4Ac98FfW5pXLNRRuQ_QEx_G7-UOSBKABAg_xrOq7u4,16628
|
|
52
53
|
absfuyu/util/performance.py,sha256=F-aLJ-5wmwkiOdl9_wZcFRKifL9zOahw52JeL4TbCuQ,9105
|
|
53
54
|
absfuyu/util/pkl.py,sha256=ZZf6-PFC2uRGXqARNi0PGH3A0IXwMP0sYPWZJXENvak,1559
|
|
55
|
+
absfuyu/util/shorten_number.py,sha256=_R_FhzBYDvtX_B8jA0WhVyG2G_s-kyolra9Y2gH5e4w,6603
|
|
54
56
|
absfuyu/util/zipped.py,sha256=CU_le1MynFwHfpajCRRJ7_A-r1jfMjt9uu72jLooW6w,3111
|
|
55
|
-
absfuyu-4.
|
|
56
|
-
absfuyu-4.
|
|
57
|
-
absfuyu-4.
|
|
58
|
-
absfuyu-4.
|
|
59
|
-
absfuyu-4.
|
|
57
|
+
absfuyu-4.1.1.dist-info/METADATA,sha256=dJJP-975rfpiV712uDMRZXbnOhDtmLGkfAWMfriu9L0,3794
|
|
58
|
+
absfuyu-4.1.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
59
|
+
absfuyu-4.1.1.dist-info/entry_points.txt,sha256=bW5CgJRTTWJ2Pywojo07sf-YucRPcnHzMmETh5avbX0,79
|
|
60
|
+
absfuyu-4.1.1.dist-info/licenses/LICENSE,sha256=pFCHBSNSzdXwYG1AHpq7VcofI1NMQ1Fc77RzZ4Ln2O4,1097
|
|
61
|
+
absfuyu-4.1.1.dist-info/RECORD,,
|
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2022-
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2022-2025 AbsoluteWinter
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
File without changes
|
|
File without changes
|