absfuyu 5.0.1__py3-none-any.whl → 5.1.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 +2 -2
  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 +16 -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 +69 -3
  14. absfuyu/core/docstring.py +2 -2
  15. absfuyu/core/dummy_cli.py +2 -2
  16. absfuyu/core/dummy_func.py +13 -2
  17. absfuyu/core/typings.py +40 -0
  18. absfuyu/dxt/__init__.py +2 -2
  19. absfuyu/dxt/dictext.py +2 -2
  20. absfuyu/dxt/dxt_support.py +2 -2
  21. absfuyu/dxt/intext.py +31 -3
  22. absfuyu/dxt/listext.py +28 -3
  23. absfuyu/dxt/strext.py +2 -2
  24. absfuyu/extra/__init__.py +2 -2
  25. absfuyu/extra/beautiful.py +2 -2
  26. absfuyu/extra/da/__init__.py +36 -0
  27. absfuyu/extra/da/dadf.py +1138 -0
  28. absfuyu/extra/da/dadf_base.py +186 -0
  29. absfuyu/extra/da/df_func.py +97 -0
  30. absfuyu/extra/da/mplt.py +219 -0
  31. absfuyu/extra/data_analysis.py +10 -1067
  32. absfuyu/fun/__init__.py +2 -2
  33. absfuyu/fun/tarot.py +2 -2
  34. absfuyu/game/__init__.py +2 -2
  35. absfuyu/game/game_stat.py +2 -2
  36. absfuyu/game/sudoku.py +2 -2
  37. absfuyu/game/tictactoe.py +2 -2
  38. absfuyu/game/wordle.py +2 -2
  39. absfuyu/general/__init__.py +2 -2
  40. absfuyu/general/content.py +2 -2
  41. absfuyu/general/human.py +2 -2
  42. absfuyu/general/shape.py +2 -2
  43. absfuyu/logger.py +2 -2
  44. absfuyu/pkg_data/__init__.py +2 -2
  45. absfuyu/pkg_data/deprecated.py +2 -2
  46. absfuyu/sort.py +2 -2
  47. absfuyu/tools/__init__.py +25 -2
  48. absfuyu/tools/checksum.py +27 -7
  49. absfuyu/tools/converter.py +93 -28
  50. absfuyu/tools/generator.py +2 -2
  51. absfuyu/tools/inspector.py +433 -0
  52. absfuyu/tools/keygen.py +2 -2
  53. absfuyu/tools/obfuscator.py +45 -7
  54. absfuyu/tools/passwordlib.py +87 -22
  55. absfuyu/tools/shutdownizer.py +2 -2
  56. absfuyu/tools/web.py +2 -2
  57. absfuyu/util/__init__.py +2 -2
  58. absfuyu/util/api.py +2 -2
  59. absfuyu/util/json_method.py +2 -2
  60. absfuyu/util/lunar.py +2 -2
  61. absfuyu/util/path.py +190 -82
  62. absfuyu/util/performance.py +4 -4
  63. absfuyu/util/shorten_number.py +40 -10
  64. absfuyu/util/text_table.py +272 -0
  65. absfuyu/util/zipped.py +6 -6
  66. absfuyu/version.py +2 -2
  67. {absfuyu-5.0.1.dist-info → absfuyu-5.1.0.dist-info}/METADATA +9 -2
  68. absfuyu-5.1.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.1.0.dist-info}/WHEEL +0 -0
  71. {absfuyu-5.0.1.dist-info → absfuyu-5.1.0.dist-info}/entry_points.txt +0 -0
  72. {absfuyu-5.0.1.dist-info → absfuyu-5.1.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,433 @@
1
+ """
2
+ Absfuyu: Inspector
3
+ ------------------
4
+ Inspector
5
+
6
+ Version: 5.1.0
7
+ Date updated: 10/03/2025 (dd/mm/yyyy)
8
+ """
9
+
10
+ # Module level
11
+ # ---------------------------------------------------------------------------
12
+ __all__ = ["Inspector"]
13
+
14
+ # Library
15
+ # ---------------------------------------------------------------------------
16
+ import inspect
17
+ import os
18
+ from textwrap import TextWrapper
19
+ from textwrap import shorten as text_shorten
20
+ from typing import Any
21
+
22
+ from absfuyu.core.baseclass import (
23
+ AutoREPRMixin,
24
+ MethodNPropertyResult,
25
+ ShowAllMethodsMixin,
26
+ )
27
+ from absfuyu.dxt.listext import ListExt
28
+ from absfuyu.util.text_table import OneColumnTableMaker
29
+
30
+
31
+ # Class
32
+ # ---------------------------------------------------------------------------
33
+ # TODO: rewrite with each class for docs, method, property, attr, param, title
34
+ class Inspector(AutoREPRMixin):
35
+ """
36
+ Inspect an object
37
+
38
+ Parameters
39
+ ----------
40
+ obj : Any
41
+ Object to inspect
42
+
43
+ line_length: int | None
44
+ Number of cols in inspect output (Split line every line_length).
45
+ Set to ``None`` to use ``os.get_terminal_size()``, by default ``88``
46
+
47
+ include_docs : bool, optional
48
+ Include docstring, by default ``True``
49
+
50
+ include_mro : bool, optional
51
+ Include class bases (__mro__), by default ``False``
52
+
53
+ include_method : bool, optional
54
+ Include object's methods (if any), by default ``False``
55
+
56
+ include_property : bool, optional
57
+ Include object's properties (if any), by default ``True``
58
+
59
+ include_attribute : bool, optional
60
+ Include object's attributes (if any), by default ``True``
61
+
62
+ include_private : bool, optional
63
+ Include object's private attributes, by default ``False``
64
+
65
+ include_all : bool, optional
66
+ Include all infomation availble, by default ``False``
67
+
68
+
69
+ Example:
70
+ --------
71
+ >>> print(Inspector(<object>, **kwargs))
72
+ """
73
+
74
+ def __init__(
75
+ self,
76
+ obj: Any,
77
+ *,
78
+ line_length: int | None = 88,
79
+ include_docs: bool = True,
80
+ include_mro: bool = False,
81
+ include_method: bool = False,
82
+ include_property: bool = True,
83
+ include_attribute: bool = True,
84
+ include_private: bool = False,
85
+ include_all: bool = False,
86
+ ) -> None:
87
+ """
88
+ Inspect an object
89
+
90
+ Parameters
91
+ ----------
92
+ obj : Any
93
+ Object to inspect
94
+
95
+ line_length: int | None
96
+ Number of cols in inspect output (Split line every line_length).
97
+ Set to ``None`` to use ``os.get_terminal_size()``, by default ``88``
98
+
99
+ include_docs : bool, optional
100
+ Include docstring, by default ``True``
101
+
102
+ include_mro : bool, optional
103
+ Include class bases (__mro__), by default ``False``
104
+
105
+ include_method : bool, optional
106
+ Include object's methods (if any), by default ``False``
107
+
108
+ include_property : bool, optional
109
+ Include object's properties (if any), by default ``True``
110
+
111
+ include_attribute : bool, optional
112
+ Include object's attributes (if any), by default ``True``
113
+
114
+ include_private : bool, optional
115
+ Include object's private attributes, by default ``False``
116
+
117
+ include_all : bool, optional
118
+ Include all infomation availble, by default ``False``
119
+
120
+
121
+ Example:
122
+ --------
123
+ >>> print(Inspector(<object>, **kwargs))
124
+ """
125
+ self.obj = obj
126
+ self.include_docs = include_docs
127
+ self.include_mro = include_mro
128
+ self.include_method = include_method
129
+ self.include_property = include_property
130
+ self.include_attribute = include_attribute
131
+ self.include_private = include_private
132
+
133
+ if include_all:
134
+ self.include_docs = True
135
+ self.include_mro = True
136
+ self.include_method = True
137
+ self.include_property = True
138
+ self.include_attribute = True
139
+ self.include_private = True
140
+
141
+ # Setup line length
142
+ if line_length is None:
143
+ try:
144
+ self._linelength = os.get_terminal_size().columns
145
+ except OSError:
146
+ self._linelength = 88
147
+ elif isinstance(line_length, (int, float)):
148
+ self._linelength = max(int(line_length), 9)
149
+ else:
150
+ raise ValueError("Use different line_length")
151
+
152
+ # Textwrap
153
+ self._text_wrapper = TextWrapper(
154
+ width=self._linelength - 4,
155
+ initial_indent="",
156
+ subsequent_indent="",
157
+ tabsize=4,
158
+ break_long_words=True,
159
+ max_lines=8,
160
+ )
161
+
162
+ # Output
163
+ self._inspect_output = self._make_output()
164
+
165
+ def __str__(self) -> str:
166
+ return self.detail_str()
167
+
168
+ # Support
169
+ def _long_list_terminal_size(self, long_list: list) -> list:
170
+ max_name_len = max([len(x) for x in long_list]) + 1
171
+ cols = 1
172
+
173
+ if max_name_len <= self._linelength - 4:
174
+ cols = (self._linelength - 4) // max_name_len
175
+ splitted_chunk: list[list[str]] = ListExt(long_list).split_chunk(cols)
176
+
177
+ mod_chunk = ListExt(
178
+ [[x.ljust(max_name_len, " ") for x in chunk] for chunk in splitted_chunk]
179
+ ).apply(lambda x: "".join(x))
180
+
181
+ return list(mod_chunk)
182
+
183
+ # Signature
184
+ def _make_title(self) -> str:
185
+ """
186
+ Inspector's workflow:
187
+ 01. Make title
188
+ """
189
+ title_str = (
190
+ str(self.obj)
191
+ if (
192
+ inspect.isclass(self.obj)
193
+ or callable(self.obj)
194
+ or inspect.ismodule(self.obj)
195
+ )
196
+ else str(type(self.obj))
197
+ )
198
+ return title_str
199
+
200
+ def _get_signature_prefix(self) -> str:
201
+ # signature prefix
202
+ if inspect.isclass(self.obj):
203
+ return "class"
204
+ elif inspect.iscoroutinefunction(self.obj):
205
+ return "async def"
206
+ elif inspect.isfunction(self.obj):
207
+ return "def"
208
+ return ""
209
+
210
+ def get_parameters(self) -> list[str] | None:
211
+ try:
212
+ sig = inspect.signature(self.obj)
213
+ except (ValueError, AttributeError, TypeError):
214
+ return None
215
+ return [str(x) for x in sig.parameters.values()]
216
+
217
+ def _make_signature(self) -> list[str]:
218
+ """
219
+ Inspector's workflow:
220
+ 02. Make signature
221
+ """
222
+ try:
223
+ return self._text_wrapper.wrap(
224
+ f"{self._get_signature_prefix()} {self.obj.__name__}{inspect.signature(self.obj)}"
225
+ )
226
+ # not class, func | not type | is module
227
+ except (ValueError, AttributeError, TypeError):
228
+ return self._text_wrapper.wrap(repr(self.obj))
229
+
230
+ # Method and property
231
+ def _get_method_property(self) -> MethodNPropertyResult:
232
+ # if inspect.isclass(self.obj) or inspect.ismodule(self.obj):
233
+ if inspect.isclass(self.obj):
234
+ # TODO: Support enum type
235
+ tmpcls = type(
236
+ "tmpcls",
237
+ (
238
+ self.obj,
239
+ ShowAllMethodsMixin,
240
+ ),
241
+ {},
242
+ )
243
+ else:
244
+ tmpcls = type(
245
+ "tmpcls",
246
+ (
247
+ type(self.obj),
248
+ ShowAllMethodsMixin,
249
+ ),
250
+ {},
251
+ )
252
+ med_prop = tmpcls._get_methods_and_properties( # type: ignore
253
+ include_private_method=self.include_private
254
+ )
255
+
256
+ try:
257
+ # If self.obj is a subclass of ShowAllMethodsMixin
258
+ _mro = getattr(
259
+ self.obj, "__mro__", getattr(type(self.obj), "__mro__", None)
260
+ )
261
+ if ShowAllMethodsMixin in _mro: # type: ignore
262
+ return med_prop # type: ignore
263
+ except AttributeError: # Not a class
264
+ pass
265
+ med_prop.__delitem__(ShowAllMethodsMixin.__name__)
266
+ return med_prop # type: ignore
267
+
268
+ # Docstring
269
+ def _get_docs(self) -> str:
270
+ """
271
+ Inspector's workflow:
272
+ 03. Get docstring and strip
273
+ """
274
+ docs: str | None = inspect.getdoc(self.obj)
275
+
276
+ if docs is None:
277
+ return ""
278
+
279
+ # Get docs and get first paragraph
280
+ # doc_lines: list[str] = [x.strip() for x in docs.splitlines()]
281
+ doc_lines = []
282
+ for line in docs.splitlines():
283
+ if len(line) < 1:
284
+ break
285
+ doc_lines.append(line.strip())
286
+
287
+ return text_shorten(" ".join(doc_lines), width=self._linelength - 4, tabsize=4)
288
+
289
+ # Attribute
290
+ @staticmethod
291
+ def _is_real_attribute(obj: Any) -> bool:
292
+ """
293
+ Not method, classmethod, staticmethod, property
294
+ """
295
+ if callable(obj):
296
+ return False
297
+ if isinstance(obj, staticmethod):
298
+ return False
299
+ if isinstance(obj, classmethod):
300
+ return False
301
+ if isinstance(obj, property):
302
+ return False
303
+ return True
304
+
305
+ def _get_attributes(self) -> list[tuple[str, Any]]:
306
+ # Get attributes
307
+ cls_dict = getattr(self.obj, "__dict__", None)
308
+ cls_slots = getattr(self.obj, "__slots__", None)
309
+ out = []
310
+
311
+ # Check if __dict__ exist and len(__dict__) > 0
312
+ if cls_dict is not None and len(cls_dict) > 0:
313
+ if self.include_private:
314
+ out = [
315
+ (k, v)
316
+ for k, v in self.obj.__dict__.items()
317
+ if self._is_real_attribute(v)
318
+ ]
319
+ else:
320
+ out = [
321
+ (k, v)
322
+ for k, v in self.obj.__dict__.items()
323
+ if not k.startswith("_") and self._is_real_attribute(v)
324
+ ]
325
+
326
+ # Check if __slots__ exist and len(__slots__) > 0
327
+ elif cls_slots is not None and len(cls_slots) > 0:
328
+ if self.include_private:
329
+ out = [
330
+ (x, getattr(self.obj, x))
331
+ for x in self.obj.__slots__ # type: ignore
332
+ if self._is_real_attribute(getattr(self.obj, x))
333
+ ]
334
+ else:
335
+ out = [
336
+ (x, getattr(self.obj, x))
337
+ for x in self.obj.__slots__ # type: ignore
338
+ if not x.startswith("_")
339
+ and self._is_real_attribute(getattr(self.obj, x))
340
+ ]
341
+
342
+ return out
343
+
344
+ def _handle_attributes_for_output(
345
+ self, attr_list: list[tuple[str, Any]]
346
+ ) -> list[str]:
347
+ return [
348
+ text_shorten(f"- {x[0]} = {x[1]}", self._linelength - 4) for x in attr_list
349
+ ]
350
+
351
+ # Get MRO
352
+ def _get_mro(self) -> tuple[type, ...]:
353
+ """Get MRO in reverse and subtract <class 'object'>"""
354
+ if isinstance(self.obj, type):
355
+ return self.obj.__mro__[::-1][1:]
356
+ return type(self.obj).__mro__[::-1][1:]
357
+
358
+ def _make_mro_data(self) -> list[str]:
359
+ mro = [
360
+ f"- {i:02}. {x.__module__}.{x.__name__}"
361
+ for i, x in enumerate(self._get_mro(), start=1)
362
+ ]
363
+ mod_chunk = self._long_list_terminal_size(mro)
364
+
365
+ # return [text_shorten(x, self._linelength - 4) for x in mod_chunk]
366
+ return mod_chunk
367
+
368
+ # Output
369
+ def _make_output(self) -> OneColumnTableMaker:
370
+ table = OneColumnTableMaker(self._linelength)
371
+ body: list[str] = []
372
+
373
+ # Signature
374
+ title = self._make_title()
375
+ table.add_title(title)
376
+ sig = self._make_signature()
377
+ if table._title == "": # Title too long
378
+ _title = [title]
379
+ _title.extend(sig)
380
+ table.add_paragraph(_title)
381
+ else:
382
+ table.add_paragraph(sig)
383
+
384
+ # Docstring
385
+ docs = self._get_docs()
386
+ if len(docs) > 0 and self.include_docs:
387
+ body.extend(["Docstring:", docs])
388
+
389
+ # Class bases
390
+ clsbases = self._make_mro_data()
391
+ if len(clsbases) > 0 and self.include_mro:
392
+ body.extend(["", f"Bases (Len: {len(self._get_mro())}):"])
393
+ body.extend(clsbases)
394
+
395
+ # Method & Property
396
+ try:
397
+ method_n_properties = self._get_method_property().flatten_value().pack()
398
+ if self.include_method:
399
+ ml = [
400
+ text_shorten(f"- {x}", self._linelength - 4)
401
+ for x in method_n_properties.methods
402
+ ]
403
+ if len(ml) > 0:
404
+ head = ["", f"Methods (Len: {len(ml)}):"]
405
+ head.extend(self._long_list_terminal_size(ml))
406
+ body.extend(head)
407
+ if self.include_property:
408
+ pl = [
409
+ text_shorten(
410
+ f"- {x} = {getattr(self.obj, x, None)}", self._linelength - 4
411
+ )
412
+ for x in method_n_properties.properties
413
+ ]
414
+ if len(pl) > 0:
415
+ head = ["", f"Properties (Len: {len(pl)}):"]
416
+ head.extend(pl)
417
+ body.extend(head)
418
+ except (TypeError, AttributeError):
419
+ pass
420
+
421
+ # Attribute
422
+ attrs = self._get_attributes()
423
+ if len(attrs) > 0 and self.include_attribute:
424
+ body.extend(["", f"Attributes (Len: {len(attrs)}):"])
425
+ body.extend(self._handle_attributes_for_output(attr_list=attrs))
426
+
427
+ # Add to table
428
+ table.add_paragraph(body)
429
+
430
+ return table
431
+
432
+ def detail_str(self) -> str:
433
+ return self._inspect_output.make_table()
absfuyu/tools/keygen.py CHANGED
@@ -5,8 +5,8 @@ Mod7 product key generator (90's)
5
5
 
6
6
  This is for educational and informative purposes only.
7
7
 
8
- Version: 5.0.0
9
- Date updated: 25/02/2025 (dd/mm/yyyy)
8
+ Version: 5.1.0
9
+ Date updated: 10/03/2025 (dd/mm/yyyy)
10
10
  """
11
11
 
12
12
  # Module level
@@ -3,8 +3,8 @@ Absfuyu: Obfuscator
3
3
  -------------------
4
4
  Obfuscate code
5
5
 
6
- Version: 5.0.0
7
- Date updated: 25/02/2025 (dd/mm/yyyy)
6
+ Version: 5.1.0
7
+ Date updated: 10/03/2025 (dd/mm/yyyy)
8
8
  """
9
9
 
10
10
  # Module level
@@ -22,7 +22,8 @@ from collections import deque
22
22
  from string import Template
23
23
  from typing import ClassVar
24
24
 
25
- from absfuyu.core import BaseClass, ShowAllMethodsMixin, versionadded
25
+ from absfuyu.core.baseclass import BaseClass, ShowAllMethodsMixin
26
+ from absfuyu.core.docstring import versionadded
26
27
  from absfuyu.dxt import Text
27
28
  from absfuyu.logger import logger
28
29
  from absfuyu.tools.generator import Charset, Generator
@@ -32,7 +33,17 @@ from absfuyu.tools.generator import Charset, Generator
32
33
  # ---------------------------------------------------------------------------
33
34
  @versionadded("5.0.0")
34
35
  class StrShifter(BaseClass):
35
- """Shift characters in a string by a specified number of positions."""
36
+ """
37
+ Shift characters in a string by a specified number of positions.
38
+
39
+ Parameters
40
+ ----------
41
+ str_to_shift : str
42
+ The string whose characters will be shifted.
43
+
44
+ shift_by : int, optional
45
+ The number of positions to shift the characters, by default ``5``.
46
+ """
36
47
 
37
48
  __slots__ = ("_str_to_shift", "shift_by")
38
49
 
@@ -100,7 +111,29 @@ class StrShifter(BaseClass):
100
111
 
101
112
 
102
113
  class Obfuscator(ShowAllMethodsMixin):
103
- """Obfuscate code"""
114
+ """
115
+ Obfuscate code
116
+
117
+ Parameters
118
+ ----------
119
+ code : str
120
+ Code text
121
+
122
+ base64_only : bool, optional
123
+ - ``True``: encode in base64 form only
124
+ - ``False``: base64, compress, rot13 (default)
125
+
126
+ split_every : int, optional
127
+ Split the long line of code every ``x`` character.
128
+ Minimum is ``1``, by default ``60``
129
+
130
+ variable_length : int, optional
131
+ Length of variable name (when data string split).
132
+ Minimum is ``7``, by default ``12``
133
+
134
+ fake_data : bool, optional
135
+ Generate additional meaningless data, by default ``False``
136
+ """
104
137
 
105
138
  # Var
106
139
  LIB_BASE64_ONLY: ClassVar[list[str]] = ["base64"]
@@ -219,8 +252,13 @@ class Obfuscator(ShowAllMethodsMixin):
219
252
  """
220
253
  Convert text into base64 and then return a code that decode that base64 code
221
254
 
222
- text: Code that need to convert
223
- raw: Return hex form only
255
+ Parameters
256
+ ----------
257
+ text : str
258
+ Code that need to convert
259
+
260
+ raw : bool
261
+ Return hex form only, by default ``False``
224
262
  """
225
263
  b64_encode_codec = base64.b64encode(text.encode()).decode()
226
264
  b64_decode_codec = f"base64.b64decode('{b64_encode_codec}'.encode()).decode()"