absfuyu 5.1.0__py3-none-any.whl → 5.3.0__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 (73) hide show
  1. absfuyu/__init__.py +1 -1
  2. absfuyu/__main__.py +3 -3
  3. absfuyu/cli/__init__.py +1 -1
  4. absfuyu/cli/color.py +3 -3
  5. absfuyu/cli/config_group.py +2 -2
  6. absfuyu/cli/do_group.py +12 -2
  7. absfuyu/cli/game_group.py +2 -2
  8. absfuyu/cli/tool_group.py +2 -3
  9. absfuyu/config/__init__.py +1 -1
  10. absfuyu/core/__init__.py +1 -1
  11. absfuyu/core/baseclass.py +32 -2
  12. absfuyu/core/baseclass2.py +1 -1
  13. absfuyu/core/decorator.py +4 -4
  14. absfuyu/core/docstring.py +43 -25
  15. absfuyu/core/dummy_cli.py +1 -1
  16. absfuyu/core/dummy_func.py +4 -4
  17. absfuyu/dxt/__init__.py +1 -1
  18. absfuyu/dxt/dictext.py +5 -2
  19. absfuyu/dxt/dxt_support.py +1 -1
  20. absfuyu/dxt/intext.py +5 -2
  21. absfuyu/dxt/listext.py +405 -127
  22. absfuyu/dxt/strext.py +75 -15
  23. absfuyu/extra/__init__.py +1 -1
  24. absfuyu/extra/beautiful.py +1 -1
  25. absfuyu/extra/da/__init__.py +1 -1
  26. absfuyu/extra/da/dadf.py +56 -4
  27. absfuyu/extra/da/dadf_base.py +1 -1
  28. absfuyu/extra/da/df_func.py +1 -1
  29. absfuyu/extra/da/mplt.py +1 -1
  30. absfuyu/extra/data_analysis.py +3 -3
  31. absfuyu/fun/__init__.py +1 -1
  32. absfuyu/fun/tarot.py +1 -1
  33. absfuyu/game/__init__.py +1 -1
  34. absfuyu/game/game_stat.py +1 -1
  35. absfuyu/game/sudoku.py +1 -1
  36. absfuyu/game/tictactoe.py +2 -3
  37. absfuyu/game/wordle.py +1 -1
  38. absfuyu/general/__init__.py +1 -1
  39. absfuyu/general/content.py +2 -2
  40. absfuyu/general/human.py +1 -1
  41. absfuyu/general/shape.py +1 -1
  42. absfuyu/logger.py +1 -1
  43. absfuyu/pkg_data/__init__.py +1 -1
  44. absfuyu/pkg_data/deprecated.py +1 -1
  45. absfuyu/sort.py +1 -1
  46. absfuyu/tools/__init__.py +16 -13
  47. absfuyu/tools/checksum.py +2 -2
  48. absfuyu/tools/converter.py +29 -8
  49. absfuyu/tools/generator.py +251 -110
  50. absfuyu/tools/inspector.py +84 -40
  51. absfuyu/tools/keygen.py +1 -1
  52. absfuyu/tools/obfuscator.py +2 -2
  53. absfuyu/tools/passwordlib.py +3 -4
  54. absfuyu/tools/shutdownizer.py +1 -1
  55. absfuyu/tools/web.py +1 -1
  56. absfuyu/typings.py +136 -0
  57. absfuyu/util/__init__.py +18 -4
  58. absfuyu/util/api.py +36 -16
  59. absfuyu/util/json_method.py +43 -14
  60. absfuyu/util/lunar.py +1 -1
  61. absfuyu/util/path.py +158 -4
  62. absfuyu/util/performance.py +120 -5
  63. absfuyu/util/shorten_number.py +1 -1
  64. absfuyu/util/text_table.py +235 -45
  65. absfuyu/util/zipped.py +4 -3
  66. absfuyu/version.py +2 -2
  67. {absfuyu-5.1.0.dist-info → absfuyu-5.3.0.dist-info}/METADATA +1 -1
  68. absfuyu-5.3.0.dist-info/RECORD +76 -0
  69. absfuyu/core/typings.py +0 -40
  70. absfuyu-5.1.0.dist-info/RECORD +0 -76
  71. {absfuyu-5.1.0.dist-info → absfuyu-5.3.0.dist-info}/WHEEL +0 -0
  72. {absfuyu-5.1.0.dist-info → absfuyu-5.3.0.dist-info}/entry_points.txt +0 -0
  73. {absfuyu-5.1.0.dist-info → absfuyu-5.3.0.dist-info}/licenses/LICENSE +0 -0
@@ -3,8 +3,8 @@ Absfuyu: Json Method
3
3
  --------------------
4
4
  ``.json`` file handling
5
5
 
6
- Version: 5.1.0
7
- Date updated: 10/03/2025 (dd/mm/yyyy)
6
+ Version: 5.2.0
7
+ Date updated: 12/03/2025 (dd/mm/yyyy)
8
8
  """
9
9
 
10
10
  # Module level
@@ -16,7 +16,6 @@ __all__ = ["JsonFile"]
16
16
  # ---------------------------------------------------------------------------
17
17
  import json
18
18
  from pathlib import Path
19
- from typing import Any
20
19
 
21
20
  from absfuyu.core import BaseClass
22
21
 
@@ -26,6 +25,20 @@ from absfuyu.core import BaseClass
26
25
  class JsonFile(BaseClass):
27
26
  """
28
27
  ``.json`` file handling
28
+
29
+ Parameters
30
+ ----------
31
+ json_file_location : str | Path
32
+ .json file location
33
+
34
+ encoding : str | None, optional
35
+ Data encoding, by default ``"utf-8"``
36
+
37
+ indent : int | str | None, optional
38
+ Indentation when export to json file, by default ``4``
39
+
40
+ sort_keys : bool, optional
41
+ Sort the keys before export to json file, by default ``True``
29
42
  """
30
43
 
31
44
  def __init__(
@@ -37,26 +50,40 @@ class JsonFile(BaseClass):
37
50
  sort_keys: bool = True,
38
51
  ) -> None:
39
52
  """
40
- json_file_location: json file location
41
- encoding: data encoding (Default: utf-8)
42
- indent: indentation when export to json file
43
- sort_keys: sort the keys before export to json file
53
+ ``.json`` file handling
54
+
55
+ Parameters
56
+ ----------
57
+ json_file_location : str | Path
58
+ .json file location
59
+
60
+ encoding : str | None, optional
61
+ Data encoding, by default ``"utf-8"``
62
+
63
+ indent : int | str | None, optional
64
+ Indentation when export to json file, by default ``4``
65
+
66
+ sort_keys : bool, optional
67
+ Sort the keys before export to json file, by default ``True``
44
68
  """
69
+
45
70
  self.json_file_location = Path(json_file_location)
46
71
  self.encoding = encoding
47
72
  self.indent = indent
48
73
  self.sort_keys = sort_keys
49
- self.data: dict[Any, Any] = {}
74
+ self.data: dict = {}
50
75
 
51
76
  def __str__(self) -> str:
52
77
  return f"{self.__class__.__name__}({self.json_file_location.name})"
53
78
 
54
- def load_json(self) -> dict[Any, Any]:
79
+ def load_json(self) -> dict:
55
80
  """
56
81
  Load ``.json`` file
57
82
 
58
- :returns: ``.json`` data
59
- :rtype: dict
83
+ Returns
84
+ -------
85
+ dict
86
+ ``.json`` data
60
87
  """
61
88
  with open(self.json_file_location, "r", encoding=self.encoding) as file:
62
89
  self.data = json.load(file)
@@ -68,11 +95,13 @@ class JsonFile(BaseClass):
68
95
  with open(self.json_file_location, "w", encoding=self.encoding) as file:
69
96
  file.writelines(json_data)
70
97
 
71
- def update_data(self, data: dict[Any, Any]) -> None:
98
+ def update_data(self, data: dict) -> None:
72
99
  """
73
100
  Update ``.json`` data without save
74
101
 
75
- :param data: ``.json`` data
76
- :type data: dict
102
+ Parameters
103
+ ----------
104
+ data : dict
105
+ ``.json`` data
77
106
  """
78
107
  self.data = data
absfuyu/util/lunar.py CHANGED
@@ -4,7 +4,7 @@ Absfuyu: Lunar calendar
4
4
  -----------------------
5
5
  Convert to lunar calendar
6
6
 
7
- Version: 5.1.0
7
+ Version: 5.2.0
8
8
  Date updated: 10/03/2025 (dd/mm/yyyy)
9
9
 
10
10
  Source:
absfuyu/util/path.py CHANGED
@@ -3,8 +3,8 @@ Absfuyu: Path
3
3
  -------------
4
4
  Path related
5
5
 
6
- Version: 5.1.0
7
- Date updated: 10/03/2025 (dd/mm/yyyy)
6
+ Version: 5.2.0
7
+ Date updated: 14/03/2025 (dd/mm/yyyy)
8
8
 
9
9
  Feature:
10
10
  --------
@@ -46,6 +46,128 @@ from absfuyu.core.decorator import add_subclass_methods_decorator
46
46
  from absfuyu.core.docstring import deprecated, versionadded, versionchanged
47
47
  from absfuyu.logger import logger
48
48
 
49
+ # Template
50
+ # ---------------------------------------------------------------------------
51
+ ORGANIZE_TEMPLATE: dict[str, list[str]] = {
52
+ "Code": [".ps1", ".py", ".rs", ".js", ".c", ".h", ".cpp", ".r", ".cmd", ".bat"],
53
+ "Comic": [".cbz", ".cbr", ".cb7", ".cbt", ".cba"],
54
+ "Compressed": [
55
+ ".7z",
56
+ ".zip",
57
+ ".rar",
58
+ ".apk",
59
+ ".cab",
60
+ ".tar",
61
+ ".tgz",
62
+ ".txz",
63
+ ".bz2",
64
+ ".gz",
65
+ ".lz",
66
+ ".lz4",
67
+ ".lzma",
68
+ ".xz",
69
+ ".zipx",
70
+ ".zst",
71
+ ],
72
+ "Documents": [".docx", ".doc", ".xls", ".xlsx", ".ppt", ".pptx", ".pdf", ".txt"],
73
+ "Ebook": [
74
+ ".epub",
75
+ ".mobi",
76
+ ".prc",
77
+ ".lrf",
78
+ ".lrx",
79
+ ".pdb",
80
+ ".azw",
81
+ ".azw3",
82
+ ".kf8",
83
+ ".kfx",
84
+ ".opf",
85
+ ],
86
+ "Music": [".mp3", ".flac", ".wav", ".m4a", ".wma", ".aac", ".alac", ".aiff"],
87
+ "OS": [".iso", ".dmg", ".wim"],
88
+ "Pictures": [
89
+ ".jpg",
90
+ ".jpeg",
91
+ ".png",
92
+ ".apng",
93
+ ".avif",
94
+ ".bmp",
95
+ ".gif",
96
+ ".jfif",
97
+ ".pjpeg",
98
+ ".pjp",
99
+ ".svg",
100
+ ".ico",
101
+ ".cur",
102
+ ".tif",
103
+ ".tiff",
104
+ ".webp",
105
+ ],
106
+ "Programs": [".exe", ".msi"],
107
+ "Video": [
108
+ ".3g2",
109
+ ".3gp",
110
+ ".avi",
111
+ ".flv",
112
+ ".m2ts",
113
+ ".m4v",
114
+ ".mkv",
115
+ ".mov",
116
+ ".mp4",
117
+ ".mpeg",
118
+ ".mpv",
119
+ ".mts",
120
+ ".ts",
121
+ ".vob",
122
+ ".webm",
123
+ ],
124
+ "Raw pictures": [
125
+ ".3fr",
126
+ ".ari",
127
+ ".arw",
128
+ ".bay",
129
+ ".braw",
130
+ ".crw",
131
+ ".cr2",
132
+ ".cr3",
133
+ ".cap",
134
+ ".data",
135
+ ".dcs",
136
+ ".dcr",
137
+ ".dng",
138
+ ".drf",
139
+ ".eip",
140
+ ".erf",
141
+ ".fff",
142
+ ".gpr",
143
+ ".iiq",
144
+ ".k25",
145
+ ".kdc",
146
+ ".mdc",
147
+ ".mef",
148
+ ".mos",
149
+ ".mrw",
150
+ ".nef",
151
+ ".nrw",
152
+ ".obm",
153
+ ".orf",
154
+ ".pef",
155
+ ".ptx",
156
+ ".pxn",
157
+ ".r3d",
158
+ ".raf",
159
+ ".raw",
160
+ ".rwl",
161
+ ".rw2",
162
+ ".rwz",
163
+ ".sr2",
164
+ ".srf",
165
+ ".srw",
166
+ ".tif",
167
+ ".x3f",
168
+ ],
169
+ }
170
+
49
171
 
50
172
  # Support Class
51
173
  # ---------------------------------------------------------------------------
@@ -444,10 +566,42 @@ class DirectoryArchiverMixin(DirectoryBase):
444
566
 
445
567
  class DirectoryOrganizerMixin(DirectoryBase):
446
568
  """
447
- Directory - File organizer - SOON
569
+ Directory - File organizer
570
+
571
+ - Organize
448
572
  """
449
573
 
450
- pass
574
+ @versionadded("5.3.0")
575
+ def organize(self, dirtemplate: dict[str, list[str]] | None = None) -> None:
576
+ """
577
+ Organize a directory.
578
+
579
+ Parameters
580
+ ----------
581
+ dirtemplate : dict[str, Collection[str]] | None, optional
582
+ | Template to move file to, by default ``None``.
583
+ | Example: {"Documents": [".txt", ".pdf", ...]}
584
+ """
585
+ if dirtemplate is None:
586
+ template = ORGANIZE_TEMPLATE
587
+ else:
588
+ template = dirtemplate
589
+
590
+ other_dir = self.source_path.joinpath("Others")
591
+ other_dir.mkdir(parents=True, exist_ok=True)
592
+
593
+ for path in self.source_path.iterdir():
594
+ if path.is_dir():
595
+ continue
596
+
597
+ for dir_name, suffixes in template.items():
598
+ if path.suffix.lower() in suffixes:
599
+ move_path = self.source_path.joinpath(dir_name)
600
+ move_path.mkdir(parents=True, exist_ok=True)
601
+ path.rename(move_path.joinpath(path.name))
602
+ break
603
+ else:
604
+ path.rename(other_dir.joinpath(path.name))
451
605
 
452
606
 
453
607
  class DirectoryTreeMixin(DirectoryBase):
@@ -3,8 +3,8 @@ Absfuyu: Performance
3
3
  --------------------
4
4
  Performance Check
5
5
 
6
- Version: 5.1.0
7
- Date updated: 10/03/2025 (dd/mm/yyyy)
6
+ Version: 5.2.0
7
+ Date updated: 15/03/2025 (dd/mm/yyyy)
8
8
 
9
9
  Feature:
10
10
  --------
@@ -20,6 +20,7 @@ Feature:
20
20
  __all__ = [
21
21
  # Wrapper
22
22
  "function_debug",
23
+ "function_benchmark",
23
24
  "measure_performance",
24
25
  "retry",
25
26
  # Class
@@ -32,12 +33,12 @@ __all__ = [
32
33
  import time
33
34
  import tracemalloc
34
35
  from collections.abc import Callable
36
+ from dataclasses import dataclass
35
37
  from functools import wraps
36
38
  from inspect import getsource
37
- from typing import Any, ParamSpec, TypeVar
39
+ from typing import Any, Literal, ParamSpec, TypeVar, overload
38
40
 
39
41
  from absfuyu.core import deprecated, versionadded, versionchanged
40
- from absfuyu.dxt import ListNoDunder
41
42
 
42
43
  # Type
43
44
  # ---------------------------------------------------------------------------
@@ -45,6 +46,30 @@ P = ParamSpec("P") # Parameter type
45
46
  R = TypeVar("R") # Return type
46
47
 
47
48
 
49
+ # Support
50
+ # ---------------------------------------------------------------------------
51
+ @dataclass
52
+ class BenchmarkResult:
53
+ """
54
+ Use ``format(BenchmarkResult(...), "seconds")`` to view result in seconds.
55
+ """
56
+
57
+ min_: float
58
+ max_: float
59
+ avg: float
60
+
61
+ def __format__(self, format_spec: str) -> str:
62
+ clsname = self.__class__.__name__
63
+ if format_spec.lower().strip().startswith("seconds"):
64
+ fields = [f"{x}={getattr(self, x):,.6f}s" for x in self._get_fields()]
65
+ return f"{clsname}({', '.join(fields)})"
66
+ return repr(self)
67
+
68
+ @classmethod
69
+ def _get_fields(cls) -> tuple[str, ...]:
70
+ return tuple(cls.__dataclass_fields__)
71
+
72
+
48
73
  # Function
49
74
  # ---------------------------------------------------------------------------
50
75
  @versionchanged("3.2.0", reason="Updated functionality")
@@ -108,6 +133,96 @@ def measure_performance(f: Callable[P, R]) -> Callable[P, R]:
108
133
  return wrapper
109
134
 
110
135
 
136
+ @overload
137
+ def function_benchmark(func: Callable[P, R], /) -> Callable[P, R]: ...
138
+ @overload
139
+ def function_benchmark(*, n: int = 1) -> Callable[[Callable[P, R]], Callable[P, R]]: ...
140
+ @overload
141
+ def function_benchmark(
142
+ *, n: int = 1, result_only: Literal[False] = False
143
+ ) -> Callable[[Callable[P, R]], Callable[P, R]]: ...
144
+ @overload
145
+ def function_benchmark(
146
+ *, n: int = 1, result_only: Literal[True] = ...
147
+ ) -> Callable[[Callable[P, R]], Callable[P, BenchmarkResult]]: ...
148
+
149
+
150
+ @versionadded("5.2.0")
151
+ def function_benchmark(
152
+ func: Callable[P, R] | None = None, /, *, n: int = 1, result_only: bool = False
153
+ ):
154
+ """
155
+ This run function for ``n`` times and calculate min, max, average runtime.
156
+
157
+ Parameters
158
+ ----------
159
+ func : Callable[P, R] | None, optional
160
+ Callable with parameter **P and returns R, by default ``None``
161
+
162
+ n : int, optional
163
+ Run how many times, by default ``1``
164
+
165
+ result_only : bool, optional
166
+ Returns BenchmarkResult instead of ``func`` result, by default ``False``
167
+
168
+
169
+ Usage
170
+ -----
171
+ Use this as a decorator (``@function_benchmark``)
172
+
173
+ Example:
174
+ --------
175
+ >>> @function_benchmark
176
+ >>> def test():
177
+ ... return 1 + 1
178
+ >>> test()
179
+ BenchmarkResult(min_=0.000000s, max_=0.000000s, avg=0.000000s)
180
+ 2
181
+
182
+ >>> @function_benchmark(n=1)
183
+ >>> def test():
184
+ ... return 1 + 1
185
+ >>> test()
186
+ BenchmarkResult(min_=0.000000s, max_=0.000000s, avg=0.000000s)
187
+ 2
188
+ """
189
+
190
+ times = max(n, 1)
191
+
192
+ def decorator(f: Callable[P, R]) -> Callable[P, R | BenchmarkResult]:
193
+ @wraps(f)
194
+ def wrapper(*args: P.args, **kwargs: P.kwargs) -> R | BenchmarkResult:
195
+ output = f(*args, **kwargs) # Run function and save result into a variable
196
+
197
+ def _run() -> float:
198
+ # Performance check
199
+ start_time = time.perf_counter() # Start time measure
200
+ f(*args, **kwargs)
201
+ finish_time = time.perf_counter() # Get finished time
202
+ return finish_time - start_time
203
+
204
+ # run = (_run() for _ in range(times))
205
+ run = [_run() for _ in range(times)]
206
+ try:
207
+ avg_runtime = sum(run) / len(run)
208
+ except ZeroDivisionError:
209
+ avg_runtime = min(run)
210
+ result = BenchmarkResult(min(run), max(run), avg_runtime)
211
+
212
+ if result_only:
213
+ return result
214
+
215
+ print(format(result, "seconds"))
216
+
217
+ return output
218
+
219
+ return wrapper
220
+
221
+ if func is None:
222
+ return decorator
223
+ return decorator(func)
224
+
225
+
111
226
  @versionadded("3.2.0")
112
227
  def function_debug(f: Callable[P, R]) -> Callable[P, R]:
113
228
  """
@@ -284,7 +399,7 @@ class Checker:
284
399
  def dir_(self) -> list[str]:
285
400
  """``dir()`` of variable"""
286
401
  # return self.item_to_check.__dir__()
287
- return ListNoDunder(self.item_to_check.__dir__())
402
+ return [x for x in dir(self.item_to_check) if not x.startswith("__")]
288
403
 
289
404
  @property
290
405
  def source(self) -> str | None:
@@ -3,7 +3,7 @@ Absfuyu: Shorten number
3
3
  -----------------------
4
4
  Short number base on suffixes
5
5
 
6
- Version: 5.1.0
6
+ Version: 5.2.0
7
7
  Date updated: 10/03/2025 (dd/mm/yyyy)
8
8
  """
9
9