absfuyu 5.0.1__py3-none-any.whl → 5.2.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 (72) hide show
  1. absfuyu/__init__.py +1 -1
  2. absfuyu/__main__.py +3 -3
  3. absfuyu/cli/__init__.py +2 -2
  4. absfuyu/cli/color.py +30 -14
  5. absfuyu/cli/config_group.py +9 -2
  6. absfuyu/cli/do_group.py +13 -6
  7. absfuyu/cli/game_group.py +9 -2
  8. absfuyu/cli/tool_group.py +15 -9
  9. absfuyu/config/__init__.py +2 -2
  10. absfuyu/core/__init__.py +2 -2
  11. absfuyu/core/baseclass.py +448 -79
  12. absfuyu/core/baseclass2.py +2 -2
  13. absfuyu/core/decorator.py +70 -4
  14. absfuyu/core/docstring.py +43 -25
  15. absfuyu/core/dummy_cli.py +2 -2
  16. absfuyu/core/dummy_func.py +15 -4
  17. absfuyu/dxt/__init__.py +2 -2
  18. absfuyu/dxt/dictext.py +5 -2
  19. absfuyu/dxt/dxt_support.py +2 -2
  20. absfuyu/dxt/intext.py +34 -3
  21. absfuyu/dxt/listext.py +300 -113
  22. absfuyu/dxt/strext.py +75 -15
  23. absfuyu/extra/__init__.py +2 -2
  24. absfuyu/extra/beautiful.py +2 -2
  25. absfuyu/extra/da/__init__.py +36 -0
  26. absfuyu/extra/da/dadf.py +1177 -0
  27. absfuyu/extra/da/dadf_base.py +186 -0
  28. absfuyu/extra/da/df_func.py +97 -0
  29. absfuyu/extra/da/mplt.py +219 -0
  30. absfuyu/extra/data_analysis.py +10 -1067
  31. absfuyu/fun/__init__.py +2 -2
  32. absfuyu/fun/tarot.py +2 -2
  33. absfuyu/game/__init__.py +2 -2
  34. absfuyu/game/game_stat.py +2 -2
  35. absfuyu/game/sudoku.py +2 -2
  36. absfuyu/game/tictactoe.py +2 -3
  37. absfuyu/game/wordle.py +2 -2
  38. absfuyu/general/__init__.py +2 -2
  39. absfuyu/general/content.py +2 -2
  40. absfuyu/general/human.py +2 -2
  41. absfuyu/general/shape.py +2 -2
  42. absfuyu/logger.py +2 -2
  43. absfuyu/pkg_data/__init__.py +2 -2
  44. absfuyu/pkg_data/deprecated.py +2 -2
  45. absfuyu/sort.py +2 -2
  46. absfuyu/tools/__init__.py +28 -2
  47. absfuyu/tools/checksum.py +27 -7
  48. absfuyu/tools/converter.py +120 -34
  49. absfuyu/tools/generator.py +251 -110
  50. absfuyu/tools/inspector.py +463 -0
  51. absfuyu/tools/keygen.py +2 -2
  52. absfuyu/tools/obfuscator.py +45 -7
  53. absfuyu/tools/passwordlib.py +88 -24
  54. absfuyu/tools/shutdownizer.py +2 -2
  55. absfuyu/tools/web.py +2 -2
  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 +2 -2
  61. absfuyu/util/path.py +190 -82
  62. absfuyu/util/performance.py +122 -7
  63. absfuyu/util/shorten_number.py +40 -10
  64. absfuyu/util/text_table.py +306 -0
  65. absfuyu/util/zipped.py +8 -7
  66. absfuyu/version.py +2 -2
  67. {absfuyu-5.0.1.dist-info → absfuyu-5.2.0.dist-info}/METADATA +9 -2
  68. absfuyu-5.2.0.dist-info/RECORD +76 -0
  69. absfuyu-5.0.1.dist-info/RECORD +0 -68
  70. {absfuyu-5.0.1.dist-info → absfuyu-5.2.0.dist-info}/WHEEL +0 -0
  71. {absfuyu-5.0.1.dist-info → absfuyu-5.2.0.dist-info}/entry_points.txt +0 -0
  72. {absfuyu-5.0.1.dist-info → absfuyu-5.2.0.dist-info}/licenses/LICENSE +0 -0
absfuyu/core/decorator.py CHANGED
@@ -3,20 +3,24 @@ Absfuyu: Core
3
3
  -------------
4
4
  Decorator
5
5
 
6
- Version: 5.0.0
7
- Date updated: 22/02/2025 (dd/mm/yyyy)
6
+ Version: 5.2.0
7
+ Date updated: 14/03/2025 (dd/mm/yyyy)
8
8
  """
9
9
 
10
10
  # Module Package
11
11
  # ---------------------------------------------------------------------------
12
- __all__ = ["dummy_decorator", "dummy_decorator_with_args"]
12
+ __all__ = [
13
+ "dummy_decorator",
14
+ "dummy_decorator_with_args",
15
+ "add_subclass_methods_decorator",
16
+ ]
13
17
 
14
18
 
15
19
  # Library
16
20
  # ---------------------------------------------------------------------------
17
21
  from collections.abc import Callable
18
22
  from functools import wraps
19
- from typing import ParamSpec, TypeVar, overload
23
+ from typing import ParamSpec, TypeVar, cast, overload
20
24
 
21
25
  # Type
22
26
  # ---------------------------------------------------------------------------
@@ -52,8 +56,10 @@ def dummy_decorator_with_args(*args, **kwargs):
52
56
 
53
57
  @overload
54
58
  def decorator(obj: T) -> T: ...
59
+
55
60
  @overload
56
61
  def decorator(obj: Callable[P, R]) -> Callable[P, R]: ...
62
+
57
63
  def decorator(obj: Callable[P, R] | T) -> Callable[P, R] | T:
58
64
  if isinstance(obj, type):
59
65
  return obj
@@ -65,3 +71,63 @@ def dummy_decorator_with_args(*args, **kwargs):
65
71
  return wrapper
66
72
 
67
73
  return decorator
74
+
75
+
76
+ def add_subclass_methods_decorator(cls: T) -> T:
77
+ """
78
+ Class decorator replace the ``__init_subclass__`` method.
79
+
80
+ This method populates a dictionary with subclass names as keys
81
+ and their available methods as values.
82
+
83
+ - Create 2 class attributes: ``_METHOD_INCLUDE`` (bool) and ``SUBCLASS_METHODS`` (dict[str, list[str]])
84
+ - Automatically add subclass methods to class variable: ``SUBCLASS_METHODS``
85
+ - Set class attribute ``_METHOD_INCLUDE`` to ``False`` to exclude from ``SUBCLASS_METHODS``
86
+
87
+ Example:
88
+ --------
89
+ >>> # Normal behavior
90
+ >>> @add_subclass_methods_decorator
91
+ >>> class TestParent: ...
92
+ >>> class TestChild(TestParent):
93
+ ... def method1(self): ...
94
+ >>> TestChild.SUBCLASS_METHODS
95
+ {'__main__.TestChild': ['method1']}
96
+
97
+ >>> # Hidden from ``SUBCLASS_METHODS``
98
+ >>> @add_subclass_methods_decorator
99
+ >>> class TestParent: ...
100
+ >>> class TestChildHidden(TestParent):
101
+ ... _METHOD_INCLUDE = False
102
+ ... def method1(self): ...
103
+ >>> TestChildHidden.SUBCLASS_METHODS
104
+ {}
105
+ """
106
+
107
+ # Check for class
108
+ if not isinstance(cls, type):
109
+ raise ValueError("Object is not a class")
110
+
111
+ class AutoSubclassMixin:
112
+ _METHOD_INCLUDE: bool = True # Include in SUBCLASS_METHODS
113
+ SUBCLASS_METHODS: dict[str, list[str]] = {}
114
+
115
+ def __init_subclass__(cls, *args, **kwargs) -> None:
116
+ """
117
+ This create a dictionary with:
118
+ - key (str) : Subclass
119
+ - value (list[str]): List of available methods
120
+ """
121
+ super().__init_subclass__(*args, **kwargs)
122
+
123
+ if cls._METHOD_INCLUDE and not any(
124
+ [x.endswith(cls.__name__) for x in cls.SUBCLASS_METHODS.keys()]
125
+ ):
126
+ methods_list: list[str] = [
127
+ k for k, v in cls.__dict__.items() if callable(v)
128
+ ]
129
+ if len(methods_list) > 0:
130
+ name = f"{cls.__module__}.{cls.__name__}"
131
+ cls.SUBCLASS_METHODS.update({name: sorted(methods_list)})
132
+
133
+ return cast(T, type("AutoSubclass", (AutoSubclassMixin, cls), {}))
absfuyu/core/docstring.py CHANGED
@@ -3,8 +3,8 @@ Absfuyu: Core
3
3
  -------------
4
4
  Sphinx docstring decorator
5
5
 
6
- Version: 5.0.0
7
- Date updated: 23/02/2025 (dd/mm/yyyy)
6
+ Version: 5.2.0
7
+ Date updated: 14/03/2025 (dd/mm/yyyy)
8
8
  """
9
9
 
10
10
  # Module Package
@@ -53,6 +53,24 @@ class SphinxDocstring:
53
53
  A class-based decorator to add a 'Version added',
54
54
  'Version changed', or 'Deprecated' note to a function's docstring,
55
55
  formatted for Sphinx documentation.
56
+
57
+ Parameters
58
+ ----------
59
+ version : str
60
+ The version in which the function was added, changed, or deprecated.
61
+
62
+ reason : str | None, optional
63
+ An optional reason or description for the change
64
+ or deprecation, by default ``None``
65
+
66
+ mode : SphinxDocstringMode, optional
67
+ Specifies whether the function was 'added', 'changed', or 'deprecated',
68
+ by default SphinxDocstringMode.ADDED
69
+
70
+
71
+ Usage
72
+ -----
73
+ Use this as a decorator (``@SphinxDocstring(<parameters>)``)
56
74
  """
57
75
 
58
76
  _LINEBREAK: ClassVar[str] = "\n\n" # Use ClassVar for constant
@@ -75,7 +93,7 @@ class SphinxDocstring:
75
93
  An optional reason or description for the change
76
94
  or deprecation, by default ``None``
77
95
 
78
- mode : SphinxDocstringMode | int, optional
96
+ mode : SphinxDocstringMode, optional
79
97
  Specifies whether the function was 'added', 'changed', or 'deprecated',
80
98
  by default SphinxDocstringMode.ADDED
81
99
 
@@ -87,38 +105,27 @@ class SphinxDocstring:
87
105
  self.reason = reason
88
106
  self.mode = mode
89
107
 
90
- @overload
91
- def __call__(self, obj: _T) -> _T: ... # Class overload
108
+ @overload # Class overload
109
+ def __call__(self, obj: _T) -> _T: ...
110
+
111
+ @overload # Function overload
112
+ def __call__(self, obj: Callable[_P, _R]) -> Callable[_P, _R]: ...
92
113
 
93
- @overload
94
- def __call__(
95
- self, obj: Callable[_P, _R]
96
- ) -> Callable[_P, _R]: ... # Function overload
97
114
  def __call__(self, obj: _T | Callable[_P, _R]) -> _T | Callable[_P, _R]:
115
+ """
116
+ Decorator for class and callable
117
+ """
118
+
98
119
  # Class wrapper
99
120
  if isinstance(obj, type): # if inspect.isclass(obj):
100
- obj.__doc__ = (obj.__doc__ or "") + self._generate_version_note(
101
- num_of_white_spaces=self._calculate_white_space(obj.__doc__)
102
- )
103
- return obj
121
+ return self._update_doc(obj)
104
122
 
105
123
  # Function wrapper
106
124
  @wraps(obj)
107
125
  def wrapper(*args: _P.args, **kwargs: _P.kwargs) -> _R:
108
126
  return obj(*args, **kwargs)
109
127
 
110
- # Add docstring
111
- # version_note = self._generate_version_note()
112
- # if wrapper.__doc__ is None:
113
- # wrapper.__doc__ = version_note
114
- # else:
115
- # wrapper.__doc__ += version_note
116
-
117
- wrapper.__doc__ = (wrapper.__doc__ or "") + self._generate_version_note(
118
- num_of_white_spaces=self._calculate_white_space(wrapper.__doc__)
119
- )
120
-
121
- return wrapper
128
+ return self._update_doc(wrapper)
122
129
 
123
130
  def _calculate_white_space(self, docs: str | None) -> int:
124
131
  """
@@ -158,6 +165,17 @@ class SphinxDocstring:
158
165
  reason=reason_str,
159
166
  )
160
167
 
168
+ @overload
169
+ def _update_doc(self, obj: _T) -> _T: ...
170
+ @overload
171
+ def _update_doc(self, obj: Callable[_P, _R]) -> Callable[_P, _R]: ...
172
+ def _update_doc(self, obj: _T | Callable[_P, _R]) -> _T | Callable[_P, _R]:
173
+ """Update docstring for an object"""
174
+ obj.__doc__ = (obj.__doc__ or "") + self._generate_version_note(
175
+ num_of_white_spaces=self._calculate_white_space(obj.__doc__)
176
+ )
177
+ return obj
178
+
161
179
 
162
180
  # Partial
163
181
  # ---------------------------------------------------------------------------
absfuyu/core/dummy_cli.py CHANGED
@@ -3,8 +3,8 @@ Absfuyu: Core
3
3
  -------------
4
4
  Dummy cli
5
5
 
6
- Version: 5.0.0
7
- Date updated: 25/02/2025 (dd/mm/yyyy)
6
+ Version: 5.2.0
7
+ Date updated: 10/03/2025 (dd/mm/yyyy)
8
8
  """
9
9
 
10
10
  # Module Package
@@ -3,8 +3,8 @@ Absfuyu: Core
3
3
  -------------
4
4
  Dummy functions when other libraries are unvailable
5
5
 
6
- Version: 5.0.0
7
- Date updated: 22/02/2025 (dd/mm/yyyy)
6
+ Version: 5.2.0
7
+ Date updated: 13/03/2025 (dd/mm/yyyy)
8
8
  """
9
9
 
10
10
  # Module Package
@@ -12,6 +12,7 @@ Date updated: 22/02/2025 (dd/mm/yyyy)
12
12
  __all__ = [
13
13
  "tqdm",
14
14
  "unidecode",
15
+ "dummy_function",
15
16
  ]
16
17
 
17
18
 
@@ -25,7 +26,7 @@ from importlib import import_module
25
26
  try:
26
27
  _tqdm = import_module("tqdm")
27
28
  tqdm = getattr(_tqdm, "tqdm") # noqa
28
- except (ModuleNotFoundError, AttributeError):
29
+ except (ImportError, AttributeError):
29
30
 
30
31
  def tqdm(iterable, *args, **kwargs):
31
32
  """
@@ -39,7 +40,7 @@ except (ModuleNotFoundError, AttributeError):
39
40
  try:
40
41
  _unidecode = import_module("unidecode")
41
42
  unidecode = getattr(_unidecode, "unidecode") # noqa
42
- except (ModuleNotFoundError, AttributeError):
43
+ except (ImportError, AttributeError):
43
44
 
44
45
  def unidecode(*args, **kwargs):
45
46
  """
@@ -47,3 +48,13 @@ except (ModuleNotFoundError, AttributeError):
47
48
  install package ``unidecode`` to fully use this feature
48
49
  """
49
50
  return args[0]
51
+
52
+
53
+ # dummy
54
+ def dummy_function(*args, **kwargs):
55
+ """This is a dummy function"""
56
+ if args:
57
+ return args[0]
58
+ if kwargs:
59
+ return kwargs[list(kwargs)[0]]
60
+ return None
absfuyu/dxt/__init__.py CHANGED
@@ -3,8 +3,8 @@ Absfuyu: Data Extension
3
3
  -----------------------
4
4
  Extension for data type such as ``list``, ``str``, ``dict``, ...
5
5
 
6
- Version: 5.0.0
7
- Date updated: 22/02/2025 (dd/mm/yyyy)
6
+ Version: 5.2.0
7
+ Date updated: 10/03/2025 (dd/mm/yyyy)
8
8
 
9
9
  Features:
10
10
  ---------
absfuyu/dxt/dictext.py CHANGED
@@ -3,8 +3,8 @@ Absfuyu: Data Extension
3
3
  -----------------------
4
4
  dict extension
5
5
 
6
- Version: 5.0.0
7
- Date updated: 25/02/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 Package
@@ -37,6 +37,9 @@ class DictAnalyzeResult(NamedTuple):
37
37
  class DictExt(ShowAllMethodsMixin, dict):
38
38
  """
39
39
  ``dict`` extension
40
+
41
+ >>> # For a list of new methods
42
+ >>> DictExt.show_all_methods()
40
43
  """
41
44
 
42
45
  @versionchanged("3.3.0", reason="Updated return type")
@@ -3,8 +3,8 @@ Absfuyu: Data Extension
3
3
  -----------------------
4
4
  Support classes
5
5
 
6
- Version: 5.0.0
7
- Date updated: 22/02/2025 (dd/mm/yyyy)
6
+ Version: 5.2.0
7
+ Date updated: 10/03/2025 (dd/mm/yyyy)
8
8
  """
9
9
 
10
10
  # Module Package
absfuyu/dxt/intext.py CHANGED
@@ -3,8 +3,8 @@ Absfuyu: Data Extension
3
3
  -----------------------
4
4
  int extension
5
5
 
6
- Version: 5.0.0
7
- Date updated: 25/02/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 Package
@@ -18,7 +18,7 @@ import math
18
18
  from collections import Counter
19
19
  from typing import Any, Self
20
20
 
21
- from absfuyu.core import ShowAllMethodsMixin, versionchanged
21
+ from absfuyu.core import ShowAllMethodsMixin, versionadded, versionchanged
22
22
  from absfuyu.dxt.dxt_support import DictBoolTrue
23
23
 
24
24
 
@@ -62,6 +62,9 @@ class Pow:
62
62
  class IntExt(ShowAllMethodsMixin, int):
63
63
  """
64
64
  ``int`` extension
65
+
66
+ >>> # For a list of new methods
67
+ >>> IntExt.show_all_methods()
65
68
  """
66
69
 
67
70
  # convert stuff
@@ -584,3 +587,31 @@ class IntExt(ShowAllMethodsMixin, int):
584
587
 
585
588
  output["characteristic"] = characteristic
586
589
  return output # type: ignore
590
+
591
+ @versionadded("5.1.0")
592
+ def split(self, parts: int) -> list[int]:
593
+ """
594
+ Evenly split the number into ``parts`` parts
595
+
596
+ Parameters
597
+ ----------
598
+ parts : int
599
+ Split by how many parts
600
+
601
+ Returns
602
+ -------
603
+ list[int]
604
+ List of evenly splitted numbers
605
+
606
+
607
+ Example:
608
+ --------
609
+ >>> IntExt(10).split(4)
610
+ [2, 2, 3, 3]
611
+ """
612
+ p = max(1, parts)
613
+ if p == 1:
614
+ return [int(self)]
615
+
616
+ quotient, remainder = divmod(self, p)
617
+ return [quotient + (i >= (p - remainder)) for i in range(p)]