absfuyu 4.0.0__tar.gz → 4.1.0__tar.gz

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 (89) hide show
  1. {absfuyu-4.0.0 → absfuyu-4.1.0}/LICENSE +21 -21
  2. {absfuyu-4.0.0 → absfuyu-4.1.0}/PKG-INFO +2 -1
  3. {absfuyu-4.0.0 → absfuyu-4.1.0}/pyproject.toml +42 -12
  4. {absfuyu-4.0.0 → absfuyu-4.1.0}/src/absfuyu/__init__.py +1 -1
  5. {absfuyu-4.0.0 → absfuyu-4.1.0}/src/absfuyu/cli/do_group.py +39 -2
  6. {absfuyu-4.0.0 → absfuyu-4.1.0}/src/absfuyu/core.py +15 -2
  7. {absfuyu-4.0.0 → absfuyu-4.1.0}/src/absfuyu/game/__init__.py +1 -1
  8. {absfuyu-4.0.0 → absfuyu-4.1.0}/src/absfuyu/game/game_stat.py +1 -1
  9. {absfuyu-4.0.0 → absfuyu-4.1.0}/src/absfuyu/general/content.py +7 -7
  10. {absfuyu-4.0.0 → absfuyu-4.1.0}/src/absfuyu/general/data_extension.py +3 -3
  11. {absfuyu-4.0.0 → absfuyu-4.1.0}/src/absfuyu/general/generator.py +4 -2
  12. {absfuyu-4.0.0 → absfuyu-4.1.0}/src/absfuyu/logger.py +2 -2
  13. absfuyu-4.1.0/src/absfuyu/tools/checksum.py +56 -0
  14. {absfuyu-4.0.0 → absfuyu-4.1.0}/src/absfuyu/tools/converter.py +26 -2
  15. {absfuyu-4.0.0 → absfuyu-4.1.0}/src/absfuyu/tools/obfuscator.py +1 -1
  16. absfuyu-4.1.0/src/absfuyu/util/shorten_number.py +228 -0
  17. {absfuyu-4.0.0 → absfuyu-4.1.0}/tests/test_everything.py +7 -2
  18. {absfuyu-4.0.0 → absfuyu-4.1.0}/tests/test_tarot.py +3 -3
  19. {absfuyu-4.0.0 → absfuyu-4.1.0}/tests/test_util.py +34 -6
  20. {absfuyu-4.0.0 → absfuyu-4.1.0}/tests/test_version.py +13 -14
  21. {absfuyu-4.0.0 → absfuyu-4.1.0}/.gitignore +0 -0
  22. {absfuyu-4.0.0 → absfuyu-4.1.0}/README.md +0 -0
  23. {absfuyu-4.0.0 → absfuyu-4.1.0}/dev_requirements.txt +0 -0
  24. {absfuyu-4.0.0 → absfuyu-4.1.0}/docs/Makefile +0 -0
  25. {absfuyu-4.0.0 → absfuyu-4.1.0}/docs/conf.py +0 -0
  26. {absfuyu-4.0.0 → absfuyu-4.1.0}/docs/index.rst +0 -0
  27. {absfuyu-4.0.0 → absfuyu-4.1.0}/docs/info.md +0 -0
  28. {absfuyu-4.0.0 → absfuyu-4.1.0}/docs/make.bat +0 -0
  29. {absfuyu-4.0.0 → absfuyu-4.1.0}/docs/modules.rst +0 -0
  30. {absfuyu-4.0.0 → absfuyu-4.1.0}/images/repository-image-crop.png +0 -0
  31. {absfuyu-4.0.0 → absfuyu-4.1.0}/images/repository-image-white.png +0 -0
  32. {absfuyu-4.0.0 → absfuyu-4.1.0}/images/repository-image.png +0 -0
  33. {absfuyu-4.0.0 → absfuyu-4.1.0}/src/absfuyu/__main__.py +0 -0
  34. {absfuyu-4.0.0 → absfuyu-4.1.0}/src/absfuyu/cli/__init__.py +0 -0
  35. {absfuyu-4.0.0 → absfuyu-4.1.0}/src/absfuyu/cli/color.py +0 -0
  36. {absfuyu-4.0.0 → absfuyu-4.1.0}/src/absfuyu/cli/config_group.py +0 -0
  37. {absfuyu-4.0.0 → absfuyu-4.1.0}/src/absfuyu/cli/game_group.py +0 -0
  38. {absfuyu-4.0.0 → absfuyu-4.1.0}/src/absfuyu/config/__init__.py +0 -0
  39. {absfuyu-4.0.0 → absfuyu-4.1.0}/src/absfuyu/config/config.json +0 -0
  40. {absfuyu-4.0.0 → absfuyu-4.1.0}/src/absfuyu/everything.py +0 -0
  41. {absfuyu-4.0.0 → absfuyu-4.1.0}/src/absfuyu/extensions/__init__.py +0 -0
  42. {absfuyu-4.0.0 → absfuyu-4.1.0}/src/absfuyu/extensions/beautiful.py +0 -0
  43. {absfuyu-4.0.0 → absfuyu-4.1.0}/src/absfuyu/extensions/dev/__init__.py +0 -0
  44. {absfuyu-4.0.0 → absfuyu-4.1.0}/src/absfuyu/extensions/dev/password_hash.py +0 -0
  45. {absfuyu-4.0.0 → absfuyu-4.1.0}/src/absfuyu/extensions/dev/passwordlib.py +0 -0
  46. {absfuyu-4.0.0 → absfuyu-4.1.0}/src/absfuyu/extensions/dev/project_starter.py +0 -0
  47. {absfuyu-4.0.0 → absfuyu-4.1.0}/src/absfuyu/extensions/dev/shutdownizer.py +0 -0
  48. {absfuyu-4.0.0 → absfuyu-4.1.0}/src/absfuyu/extensions/extra/__init__.py +0 -0
  49. {absfuyu-4.0.0 → absfuyu-4.1.0}/src/absfuyu/extensions/extra/data_analysis.py +0 -0
  50. {absfuyu-4.0.0 → absfuyu-4.1.0}/src/absfuyu/fun/WGS.py +0 -0
  51. {absfuyu-4.0.0 → absfuyu-4.1.0}/src/absfuyu/fun/__init__.py +0 -0
  52. {absfuyu-4.0.0 → absfuyu-4.1.0}/src/absfuyu/fun/tarot.py +0 -0
  53. {absfuyu-4.0.0 → absfuyu-4.1.0}/src/absfuyu/game/sudoku.py +0 -0
  54. {absfuyu-4.0.0 → absfuyu-4.1.0}/src/absfuyu/game/tictactoe.py +0 -0
  55. {absfuyu-4.0.0 → absfuyu-4.1.0}/src/absfuyu/game/wordle.py +0 -0
  56. {absfuyu-4.0.0 → absfuyu-4.1.0}/src/absfuyu/general/__init__.py +0 -0
  57. {absfuyu-4.0.0 → absfuyu-4.1.0}/src/absfuyu/general/human.py +0 -0
  58. {absfuyu-4.0.0 → absfuyu-4.1.0}/src/absfuyu/pkg_data/__init__.py +0 -0
  59. {absfuyu-4.0.0 → absfuyu-4.1.0}/src/absfuyu/pkg_data/chemistry.pkl +0 -0
  60. {absfuyu-4.0.0 → absfuyu-4.1.0}/src/absfuyu/pkg_data/tarot.pkl +0 -0
  61. {absfuyu-4.0.0 → absfuyu-4.1.0}/src/absfuyu/py.typed +0 -0
  62. {absfuyu-4.0.0 → absfuyu-4.1.0}/src/absfuyu/sort.py +0 -0
  63. {absfuyu-4.0.0 → absfuyu-4.1.0}/src/absfuyu/tools/__init__.py +0 -0
  64. {absfuyu-4.0.0 → absfuyu-4.1.0}/src/absfuyu/tools/keygen.py +0 -0
  65. {absfuyu-4.0.0 → absfuyu-4.1.0}/src/absfuyu/tools/stats.py +0 -0
  66. {absfuyu-4.0.0 → absfuyu-4.1.0}/src/absfuyu/tools/web.py +0 -0
  67. {absfuyu-4.0.0 → absfuyu-4.1.0}/src/absfuyu/util/__init__.py +0 -0
  68. {absfuyu-4.0.0 → absfuyu-4.1.0}/src/absfuyu/util/api.py +0 -0
  69. {absfuyu-4.0.0 → absfuyu-4.1.0}/src/absfuyu/util/json_method.py +0 -0
  70. {absfuyu-4.0.0 → absfuyu-4.1.0}/src/absfuyu/util/lunar.py +0 -0
  71. {absfuyu-4.0.0 → absfuyu-4.1.0}/src/absfuyu/util/path.py +0 -0
  72. {absfuyu-4.0.0 → absfuyu-4.1.0}/src/absfuyu/util/performance.py +0 -0
  73. {absfuyu-4.0.0 → absfuyu-4.1.0}/src/absfuyu/util/pkl.py +0 -0
  74. {absfuyu-4.0.0 → absfuyu-4.1.0}/src/absfuyu/util/zipped.py +0 -0
  75. {absfuyu-4.0.0 → absfuyu-4.1.0}/src/absfuyu/version.py +0 -0
  76. {absfuyu-4.0.0 → absfuyu-4.1.0}/tests/__init__.py +0 -0
  77. {absfuyu-4.0.0 → absfuyu-4.1.0}/tests/conftest.py +0 -0
  78. {absfuyu-4.0.0 → absfuyu-4.1.0}/tests/test_beautiful.py +0 -0
  79. {absfuyu-4.0.0 → absfuyu-4.1.0}/tests/test_config.py +0 -0
  80. {absfuyu-4.0.0 → absfuyu-4.1.0}/tests/test_data_analysis.py +0 -0
  81. {absfuyu-4.0.0 → absfuyu-4.1.0}/tests/test_data_extension.py +0 -0
  82. {absfuyu-4.0.0 → absfuyu-4.1.0}/tests/test_extensions.py +0 -0
  83. {absfuyu-4.0.0 → absfuyu-4.1.0}/tests/test_fun.py +0 -0
  84. {absfuyu-4.0.0 → absfuyu-4.1.0}/tests/test_game.py +0 -0
  85. {absfuyu-4.0.0 → absfuyu-4.1.0}/tests/test_generator.py +0 -0
  86. {absfuyu-4.0.0 → absfuyu-4.1.0}/tests/test_logger.py +0 -0
  87. {absfuyu-4.0.0 → absfuyu-4.1.0}/tests/test_passwordlib.py +0 -0
  88. {absfuyu-4.0.0 → absfuyu-4.1.0}/tests/test_pkg_data.py +0 -0
  89. {absfuyu-4.0.0 → absfuyu-4.1.0}/tests/test_tools.py +0 -0
@@ -1,21 +1,21 @@
1
- MIT License
2
-
3
- Copyright (c) 2022-2024 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.
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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: absfuyu
3
- Version: 4.0.0
3
+ Version: 4.1.0
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
@@ -5,7 +5,7 @@
5
5
  # 4. Build package (hatch build)
6
6
  # 5. Generating docs (hatch run docs:build)
7
7
  # 6. Publish packge (hatch publish)
8
- ## Run entire workflow: hatch run workflow:perform
8
+ ## Run entire workflow: hatch run workflow:<major | minor | patch>
9
9
  ## `hatch env show` for more information
10
10
 
11
11
  # MARK: BUILD ====================
@@ -60,7 +60,7 @@ Issues = "https://github.com/AbsoluteWinter/absfuyu-public/issues"
60
60
 
61
61
  [project.optional-dependencies]
62
62
  res = ["absfuyu-res"]
63
- full = ["absfuyu-res", "numpy", "pandas", "rich"]
63
+ full = ["absfuyu-res", "numpy", "pandas", "rich", "tqdm"]
64
64
  beautiful = ["rich"]
65
65
  extra = ["numpy", "pandas"]
66
66
  dev = ["hatch", "pytest"]
@@ -171,12 +171,12 @@ detached = true
171
171
  skip-install = true
172
172
  description = """
173
173
  Package workflow:
174
- hatch run workflow:perform
174
+ hatch run workflow:<option>
175
175
 
176
- Note:
177
- - perform: bump version patch
178
- - perform2: bump version minor
179
- - perform3: bump version major
176
+ Options:
177
+ - patch: release version patch
178
+ - minor: release version minor
179
+ - major: release version major
180
180
  """
181
181
 
182
182
  [tool.hatch.envs.workflow.scripts]
@@ -186,25 +186,54 @@ test = [
186
186
  "hatch run all:test", # Test
187
187
  ]
188
188
  build = [
189
+ # Update license year - Convert to hex due to unable to run multiple lines
190
+ # Source code:
191
+ # import re;from datetime import datetime;from pathlib import Path
192
+ # print("Updating license year...");p=r"([12]\d{3})(-[12]\d{3})?"
193
+ # n=str(datetime.now().year);l=list(Path.cwd().glob("*license*"))[0]
194
+ # with open(l)as f:c=f.readlines()
195
+ # for i,x in enumerate(c):
196
+ # s=re.search(p,x);y=s.groups()[0]if s else n
197
+ # if y!=n:c[i]=re.sub(p,f"{y}-{n}",x)
198
+ # with open(l,"w",encoding="utf-8")as f:f.writelines(c)
199
+ # print("License year updated")
200
+ """
201
+ python -c \"exec(bytes.fromhex('696d706f72742072653b66726f6d20646174657\
202
+ 4696d6520696d706f7274206461746574696d653b66726f6d20706174686c696220696d\
203
+ 706f727420506174680a7072696e7428225570646174696e67206c6963656e736520796\
204
+ 561722e2e2e22293b703d7222285b31325d5c647b337d29282d5b31325d5c647b337d29\
205
+ 3f220a6e3d737472286461746574696d652e6e6f7728292e79656172293b6c3d6c69737\
206
+ 428506174682e63776428292e676c6f6228222a6c6963656e73652a2229295b305d0a77\
207
+ 697468206f70656e286c29617320663a633d662e726561646c696e657328290a666f722\
208
+ 0692c7820696e20656e756d65726174652863293a0a20202020733d72652e7365617263\
209
+ 6828702c78293b793d732e67726f75707328295b305d6966207320656c7365206e0a202\
210
+ 0202069662079213d6e3a635b695d3d72652e73756228702c66227b797d2d7b6e7d222c\
211
+ 78290a77697468206f70656e286c2c2277222c656e636f64696e673d227574662d38222\
212
+ 9617320663a662e77726974656c696e65732863290a7072696e7428224c6963656e7365\
213
+ 207965617220757064617465642229').decode('utf-8'))\"
214
+ """,
215
+ # "python support_scripts/update_license_year.py", # Update license year
189
216
  "hatch clean", # Clean dist/ folder
190
217
  "hatch -v build", # Build package
191
218
  "hatch run docs:build", # Build docs
192
- "hatch publish", # Publish
193
219
  ]
194
- perform = [
220
+ patch = [
195
221
  "test",
196
222
  "hatch version patch", # Bump version patch
197
223
  "build",
224
+ "hatch publish", # Publish
198
225
  ]
199
- perform2 = [
226
+ minor = [
200
227
  "test",
201
228
  "hatch version minor", # Bump version minor
202
229
  "build",
230
+ "hatch publish", # Publish
203
231
  ]
204
- perform3 = [
232
+ major = [
205
233
  "test",
206
234
  "hatch version major", # Bump version major
207
235
  "build",
236
+ "hatch publish", # Publish
208
237
  ]
209
238
 
210
239
  # MARK: TOOL: pytest
@@ -223,7 +252,7 @@ markers = [ # pytest -m <marker> -v
223
252
 
224
253
  # MARK: TOOL: mypy
225
254
  [tool.mypy]
226
- # python_version = "3.8"
255
+ python_version = "3.11"
227
256
  mypy_path = "src"
228
257
  check_untyped_defs = true
229
258
  # disallow_any_generics = true
@@ -279,6 +308,7 @@ ignore = [
279
308
  "tests/*" = [
280
309
  "F401", # imported but unused
281
310
  ]
311
+ "shorten_number.py" = ["B007"] # Loop control variable not used within loop body
282
312
 
283
313
  # MARK: TOOL: black
284
314
  [tool.black]
@@ -22,7 +22,7 @@ Using in cmd (`absfuyu[cli]` required):
22
22
  __title__ = "absfuyu"
23
23
  __author__ = "AbsoluteWinter"
24
24
  __license__ = "MIT License"
25
- __version__ = "4.0.0"
25
+ __version__ = "4.1.0"
26
26
  __all__ = [
27
27
  "core",
28
28
  "config",
@@ -3,13 +3,14 @@ ABSFUYU CLI
3
3
  -----------
4
4
  Do
5
5
 
6
- Version: 1.2.0
7
- Date updated: 07/01/2025 (dd/mm/yyyy)
6
+ Version: 1.3.0
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_operation
20
22
  from absfuyu.util.zipped import Zipper
21
23
  from absfuyu.version import PkgVersion
22
24
 
@@ -96,6 +98,40 @@ 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
+ "--compare",
114
+ "-c",
115
+ "hash_to_compare",
116
+ type=str,
117
+ default=None,
118
+ show_default=True,
119
+ help="Hash to compare",
120
+ )
121
+ def file_checksum(
122
+ file_path: str,
123
+ hash_mode: str | Literal["md5", "sha1", "sha256", "sha512"],
124
+ hash_to_compare: str,
125
+ ) -> None:
126
+ """Checksum for file"""
127
+
128
+ res = checksum_operation(file_path, hash_mode=hash_mode)
129
+ if hash_to_compare:
130
+ print(res == hash_to_compare)
131
+ else:
132
+ print(res)
133
+
134
+
99
135
  @click.group(name="do")
100
136
  def do_group() -> None:
101
137
  """Perform functionalities"""
@@ -108,3 +144,4 @@ do_group.add_command(advice)
108
144
  do_group.add_command(fs)
109
145
  do_group.add_command(info)
110
146
  do_group.add_command(unzip_files_in_dir)
147
+ do_group.add_command(file_checksum)
@@ -3,8 +3,8 @@ Absfuyu: Core
3
3
  -------------
4
4
  Contain type hints and other stuffs
5
5
 
6
- Version: 2.2.1
7
- Date updated: 14/11/2024 (dd/mm/yyyy)
6
+ Version: 2.3.0
7
+ Date updated: 01/02/2025 (dd/mm/yyyy)
8
8
  """
9
9
 
10
10
  # Module Package
@@ -21,7 +21,11 @@ __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
28
+ from typing import Iterable
25
29
 
26
30
 
27
31
  class CLITextColor:
@@ -43,3 +47,12 @@ class CLITextColor:
43
47
  CORE_PATH = files("absfuyu")
44
48
  CONFIG_PATH = CORE_PATH.joinpath("config", "config.json")
45
49
  DATA_PATH = CORE_PATH.joinpath("pkg_data")
50
+
51
+
52
+ # tqdm wrapper
53
+ try:
54
+ tqdm = import_module("tqdm").tqdm
55
+ except ModuleNotFoundError:
56
+
57
+ def tqdm(iterable: Iterable, **kwargs):
58
+ return iterable
@@ -82,7 +82,7 @@ def game_escapeLoop() -> None:
82
82
  print(x)
83
83
  stop_time = time.time()
84
84
  break
85
- print(f"= Escaped in {stop_time-start_time:,.2f}s =")
85
+ print(f"= Escaped in {stop_time - start_time:,.2f}s =")
86
86
 
87
87
 
88
88
  # Rock Paper Scissors
@@ -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"
@@ -427,12 +427,12 @@ class ContentLoader:
427
427
  self.tag_dictionary = tag_dictionary
428
428
 
429
429
  # symbol stuff
430
- assert (
431
- comment_symbol != split_symbol
432
- ), "comment_symbol and split_symbol should have different values"
433
- assert (
434
- tag_separate_symbol != split_symbol
435
- ), "tag_separate_symbol and split_symbol should have different values"
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:
@@ -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(f"Time generated: {count+1}. Remaining: {times-count-1}. {s}")
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}")
@@ -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,56 @@
1
+ """
2
+ Absufyu: Checksum
3
+ -----------------
4
+ Check MD5, SHA256, ...
5
+
6
+ Version: 1.0.0
7
+ Date updated: 01/02/2025 (dd/mm/yyyy)
8
+ """
9
+
10
+ # Module level
11
+ ###########################################################################
12
+ __all__ = ["checksum_operation"]
13
+
14
+
15
+ # Library
16
+ ###########################################################################
17
+ import hashlib
18
+ from pathlib import Path
19
+ from typing import Literal
20
+
21
+
22
+ # Function
23
+ ###########################################################################
24
+ def checksum_operation(
25
+ file: Path | str,
26
+ hash_mode: str | Literal["md5", "sha1", "sha256", "sha512"] = "sha256",
27
+ ) -> str:
28
+ """This performs checksum"""
29
+ if hash_mode.lower() == "md5":
30
+ hash_engine = hashlib.md5()
31
+ elif hash_mode.lower() == "sha1":
32
+ hash_engine = hashlib.sha1()
33
+ elif hash_mode.lower() == "sha256":
34
+ hash_engine = hashlib.sha256()
35
+ elif hash_mode.lower() == "sha512":
36
+ hash_engine = hashlib.sha512()
37
+ else:
38
+ hash_engine = hashlib.md5()
39
+
40
+ with open(Path(file), "rb") as f:
41
+ # Read and hash the file in 4K chunks. Reading the whole
42
+ # file at once might consume a lot of memory if it is
43
+ # large.
44
+ while True:
45
+ data = f.read(4096)
46
+ if len(data) == 0:
47
+ break
48
+ else:
49
+ hash_engine.update(data)
50
+ return hash_engine.hexdigest()
51
+
52
+
53
+ # Run
54
+ ###########################################################################
55
+ if __name__ == "__main__":
56
+ pass
@@ -3,8 +3,8 @@ Absufyu: Converter
3
3
  ------------------
4
4
  Convert stuff
5
5
 
6
- Version: 1.2.3
7
- Date updated: 25/11/2024 (dd/mm/yyyy)
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"""
@@ -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,8 +1,8 @@
1
1
  """
2
2
  Test: Everything
3
3
 
4
- Version: 1.1.26
5
- Date updated: 14/04/2024 (dd/mm/yyyy)
4
+ Version: 1.1.27
5
+ Date updated: 01/02/2025 (dd/mm/yyyy)
6
6
  """
7
7
 
8
8
  # Library
@@ -103,6 +103,11 @@ from absfuyu.util.lunar import LunarCalendar
103
103
  from absfuyu.util.path import Directory, SaveFileAs
104
104
  from absfuyu.util.performance import Checker, function_debug, measure_performance, retry
105
105
  from absfuyu.util.pkl import Pickler
106
+ from absfuyu.util.shorten_number import (
107
+ CommonUnitSuffixesFactory,
108
+ Decimal,
109
+ UnitSuffixFactory,
110
+ )
106
111
  from absfuyu.util.zipped import Zipper
107
112
  from absfuyu.version import (
108
113
  Bumper,
@@ -1,8 +1,8 @@
1
1
  """
2
2
  Test: Tarot
3
3
 
4
- Version: 1.0.0
5
- Date updated: 27/11/2023 (dd/mm/yyyy)
4
+ Version: 1.0.1
5
+ Date updated: 16/01/2025 (dd/mm/yyyy)
6
6
  """
7
7
 
8
8
  # Library
@@ -15,4 +15,4 @@ from absfuyu.fun.tarot import Tarot, TarotCard
15
15
  # Test
16
16
  ###########################################################################
17
17
  def test_tarot():
18
- assert type(Tarot().random_card()) == TarotCard
18
+ assert isinstance(Tarot().random_card(), TarotCard)
@@ -1,8 +1,8 @@
1
1
  """
2
2
  Test: Util
3
3
 
4
- Version: 1.1.0
5
- Date updated: 14/04/2024 (dd/mm/yyyy)
4
+ Version: 1.2.0
5
+ Date updated: 01/02/2025 (dd/mm/yyyy)
6
6
  """
7
7
 
8
8
  import sys
@@ -14,6 +14,7 @@ from absfuyu.util import set_max, set_min, set_min_max
14
14
  from absfuyu.util.api import APIRequest, PingResult, ping_windows
15
15
  from absfuyu.util.json_method import JsonFile
16
16
  from absfuyu.util.path import Directory
17
+ from absfuyu.util.shorten_number import CommonUnitSuffixesFactory, Decimal
17
18
 
18
19
 
19
20
  # MARK: util
@@ -48,11 +49,13 @@ class TestUtilAPI:
48
49
  # No internet
49
50
  assert instance
50
51
 
52
+ @pytest.mark.skipif(
53
+ sys.platform not in ["win32", "cygwin"],
54
+ reason="Not applicable on Linux and MacOS",
55
+ ) # windows only
51
56
  def test_ping_windows(self) -> None:
52
- if sys.platform in ["win32", "cygwin"]: # windows only
53
- res = ping_windows(["google.com"], 1)
54
- assert isinstance(res[0], PingResult)
55
- assert True
57
+ res = ping_windows(["google.com"], 1)
58
+ assert isinstance(res[0], PingResult)
56
59
 
57
60
 
58
61
  # MARK: json
@@ -74,3 +77,28 @@ class TestUtilPath:
74
77
  def test_list_structure(self) -> None:
75
78
  instance = Directory(source_path=CORE_PATH)
76
79
  assert instance.list_structure_pkg()
80
+
81
+
82
+ # MARK: shorten number
83
+ @pytest.mark.abs_util
84
+ class TestUtilShortenNumber:
85
+ """absfuyu.util.shorten_number"""
86
+
87
+ @pytest.mark.parametrize(["value", "output"], [(1000, 1.0), (1000000, 1.0)])
88
+ def test_number(self, value: int | float, output: float) -> None:
89
+ ins = Decimal.number(value)
90
+ assert ins.value == output
91
+
92
+ def test_number2(self) -> None:
93
+ fac = CommonUnitSuffixesFactory.NUMBER
94
+ unit = 1
95
+ for i, suffix in enumerate(fac.short_name):
96
+ unit = fac.base**i
97
+ assert Decimal.number(unit).suffix == suffix
98
+
99
+ def test_data_size(self) -> None:
100
+ fac = CommonUnitSuffixesFactory.DATA_SIZE
101
+ unit = 1
102
+ for i, suffix in enumerate(fac.short_name):
103
+ unit = fac.base**i
104
+ assert Decimal.data_size(unit).suffix == suffix
@@ -6,7 +6,6 @@ Date updated: 06/04/2024 (dd/mm/yyyy)
6
6
  """
7
7
 
8
8
  from itertools import product
9
- from typing import List, Tuple, Union
10
9
 
11
10
  import pytest
12
11
 
@@ -20,10 +19,10 @@ class TestVersion:
20
19
  )
21
20
  def test_init(
22
21
  self,
23
- major: Union[int, str],
24
- minor: Union[int, str],
25
- patch: Union[int, str],
26
- serial: Union[int, str],
22
+ major: int | str,
23
+ minor: int | str,
24
+ patch: int | str,
25
+ serial: int | str,
27
26
  ) -> None:
28
27
  try:
29
28
  _ = Version(major, minor, patch, ReleaseLevel.DEV, serial)
@@ -71,24 +70,24 @@ class TestVersion:
71
70
 
72
71
 
73
72
  # Bumper
74
- def list_of_bumper_bump_test() -> (
75
- List[Tuple[Tuple[int, int, int, str, int], str, str, str]]
76
- ):
77
- bumper_list: List[Tuple[int, int, int, str, int]] = [
73
+ def list_of_bumper_bump_test() -> list[
74
+ tuple[tuple[int, int, int, str, int], str, str, str]
75
+ ]:
76
+ bumper_list: list[tuple[int, int, int, str, int]] = [
78
77
  (1, 0, 0, ReleaseLevel.FINAL, 0),
79
78
  (1, 3, 2, ReleaseLevel.FINAL, 0),
80
79
  (2, 4, 6, ReleaseLevel.RC, 0),
81
80
  (2, 4, 6, ReleaseLevel.DEV, 0),
82
81
  ]
83
- release_options: List[str] = [
82
+ release_options: list[str] = [
84
83
  ReleaseOption.MAJOR,
85
84
  ReleaseOption.MINOR,
86
85
  ReleaseOption.PATCH,
87
86
  ]
88
- release_levels: List[str] = [ReleaseLevel.FINAL, ReleaseLevel.DEV, ReleaseLevel.RC]
87
+ release_levels: list[str] = [ReleaseLevel.FINAL, ReleaseLevel.DEV, ReleaseLevel.RC]
89
88
  merged = list(product(bumper_list, release_options, release_levels))
90
89
  # TODO: Improve this
91
- result_list: List[str] = [
90
+ result_list: list[str] = [
92
91
  # 1.0.0
93
92
  "2.0.0", # major final
94
93
  "2.0.0.dev0", # major dev
@@ -133,7 +132,7 @@ def list_of_bumper_bump_test() -> (
133
132
  if not len(result_list) == len(merged):
134
133
  # Check length
135
134
  raise ValueError("length of result_list is not in correct length")
136
- out: List[Tuple[Bumper, str, str, str]] = [
135
+ out: list[tuple[Bumper, str, str, str]] = [
137
136
  (x[0], x[1], x[2], result_list[i]) for i, x in enumerate(merged)
138
137
  ]
139
138
  return out
@@ -146,7 +145,7 @@ class TestBumper:
146
145
  )
147
146
  def test_bump(
148
147
  self,
149
- bumper: Tuple[int, int, int, str, int],
148
+ bumper: tuple[int, int, int, str, int],
150
149
  release_option: str,
151
150
  release_channel: str,
152
151
  desired_output: str,
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes