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
@@ -0,0 +1,463 @@
1
+ """
2
+ Absfuyu: Inspector
3
+ ------------------
4
+ Inspector
5
+
6
+ Version: 5.2.0
7
+ Date updated: 16/03/2025 (dd/mm/yyyy)
8
+ """
9
+
10
+ # Module level
11
+ # ---------------------------------------------------------------------------
12
+ __all__ = ["Inspector", "inspect_all"]
13
+
14
+ # Library
15
+ # ---------------------------------------------------------------------------
16
+ import inspect as _inspect
17
+ import os
18
+ from functools import partial
19
+ from textwrap import TextWrapper
20
+ from textwrap import shorten as text_shorten
21
+ from typing import Any, Literal, overload
22
+
23
+ from absfuyu.core.baseclass import (
24
+ AutoREPRMixin,
25
+ MethodNPropertyResult,
26
+ ShowAllMethodsMixin,
27
+ )
28
+ from absfuyu.dxt.listext import ListExt
29
+ from absfuyu.util.text_table import OneColumnTableMaker
30
+
31
+
32
+ # Class
33
+ # ---------------------------------------------------------------------------
34
+ # TODO: rewrite with each class for docs, method, property, attr, param, title
35
+ class Inspector(AutoREPRMixin):
36
+ """
37
+ Inspect an object.
38
+ By default shows object's docstring and attribute (if any).
39
+
40
+ Parameters
41
+ ----------
42
+ obj : Any
43
+ Object to inspect
44
+
45
+ line_length: int | None
46
+ Number of cols in inspect output (Split line every line_length).
47
+ Set to ``None`` to use ``os.get_terminal_size()``, by default ``None``
48
+
49
+ include_docs : bool, optional
50
+ Include docstring, by default ``True``
51
+
52
+ include_mro : bool, optional
53
+ Include class bases (__mro__), by default ``False``
54
+
55
+ include_method : bool, optional
56
+ Include object's methods (if any), by default ``False``
57
+
58
+ include_property : bool, optional
59
+ Include object's properties (if any), by default ``False``
60
+
61
+ include_attribute : bool, optional
62
+ Include object's attributes (if any), by default ``True``
63
+
64
+ include_private : bool, optional
65
+ Include object's private attributes, by default ``False``
66
+
67
+ include_all : bool, optional
68
+ Include all infomation availble, by default ``False``
69
+
70
+ max_textwrap_lines : int, optional
71
+ Maximum lines for the output's header (class, signature, repr).
72
+ Must be >= 1, by default ``8``
73
+
74
+
75
+ Example:
76
+ --------
77
+ >>> print(Inspector(<object>, **kwargs))
78
+ """
79
+
80
+ @overload
81
+ def __init__(self, obj: Any) -> None: ...
82
+
83
+ @overload
84
+ def __init__(self, obj: Any, *, include_all: Literal[True] = ...) -> None: ...
85
+
86
+ @overload
87
+ def __init__(
88
+ self,
89
+ obj: Any,
90
+ *,
91
+ line_length: int | None = None,
92
+ include_docs: bool = True,
93
+ include_mro: bool = False,
94
+ include_method: bool = False,
95
+ include_property: bool = False,
96
+ include_attribute: bool = True,
97
+ include_private: bool = False,
98
+ max_textwrap_lines: int = 8,
99
+ ) -> None: ...
100
+
101
+ def __init__(
102
+ self,
103
+ obj: Any,
104
+ *,
105
+ # Line length
106
+ line_length: int | None = None,
107
+ line_length_offset: int = 0, # line_length += line_length_offset (when line_length=None)
108
+ max_textwrap_lines: int = 8,
109
+ # Include
110
+ include_docs: bool = True,
111
+ include_mro: bool = False,
112
+ include_method: bool = False,
113
+ include_property: bool = False,
114
+ include_attribute: bool = True,
115
+ include_private: bool = False,
116
+ include_all: bool = False,
117
+ ) -> None:
118
+ """
119
+ Inspect an object.
120
+ By default shows object's docstring and attribute (if any).
121
+
122
+ Parameters
123
+ ----------
124
+ obj : Any
125
+ Object to inspect
126
+
127
+ line_length: int | None
128
+ Number of cols in inspect output (Split line every line_length).
129
+ Set to ``None`` to use ``os.get_terminal_size()``, by default ``None``
130
+
131
+ include_docs : bool, optional
132
+ Include docstring, by default ``True``
133
+
134
+ include_mro : bool, optional
135
+ Include class bases (__mro__), by default ``False``
136
+
137
+ include_method : bool, optional
138
+ Include object's methods (if any), by default ``False``
139
+
140
+ include_property : bool, optional
141
+ Include object's properties (if any), by default ``False``
142
+
143
+ include_attribute : bool, optional
144
+ Include object's attributes (if any), by default ``True``
145
+
146
+ include_private : bool, optional
147
+ Include object's private attributes, by default ``False``
148
+
149
+ include_all : bool, optional
150
+ Include all infomation availble, by default ``False``
151
+
152
+ max_textwrap_lines : int, optional
153
+ Maximum lines for the output's header (class, signature, repr).
154
+ Must be >= 1, by default ``8``
155
+
156
+
157
+ Example:
158
+ --------
159
+ >>> print(Inspector(<object>, **kwargs))
160
+ """
161
+ self.obj = obj
162
+ self.include_docs = include_docs
163
+ self.include_mro = include_mro
164
+ self.include_method = include_method
165
+ self.include_property = include_property
166
+ self.include_attribute = include_attribute
167
+ self.include_private = include_private
168
+
169
+ if include_all:
170
+ self.include_docs = True
171
+ self.include_mro = True
172
+ self.include_method = True
173
+ self.include_property = True
174
+ self.include_attribute = True
175
+ self.include_private = True
176
+
177
+ # Setup line length
178
+ if line_length is None:
179
+ try:
180
+ self._linelength = os.get_terminal_size().columns + line_length_offset
181
+ except OSError:
182
+ self._linelength = 88
183
+ elif isinstance(line_length, (int, float)):
184
+ self._linelength = max(int(line_length), 9)
185
+ else:
186
+ raise ValueError("Use different line_length")
187
+
188
+ # Textwrap
189
+ self._text_wrapper = TextWrapper(
190
+ width=self._linelength - 4,
191
+ initial_indent="",
192
+ subsequent_indent="",
193
+ tabsize=4,
194
+ break_long_words=True,
195
+ max_lines=max(max_textwrap_lines, 1),
196
+ )
197
+
198
+ # Output
199
+ self._inspect_output = self._make_output()
200
+
201
+ def __str__(self) -> str:
202
+ return self.detail_str()
203
+
204
+ # Support
205
+ def _long_list_terminal_size(self, long_list: list) -> list:
206
+ ll = ListExt(long_list).wrap_to_column(self._linelength, margin=4)
207
+ return list(ll)
208
+
209
+ # Signature
210
+ def _make_title(self) -> str:
211
+ """
212
+ Inspector's workflow:
213
+ 01. Make title
214
+ """
215
+ title_str = (
216
+ str(self.obj)
217
+ if (
218
+ _inspect.isclass(self.obj)
219
+ or callable(self.obj)
220
+ or _inspect.ismodule(self.obj)
221
+ )
222
+ else str(type(self.obj))
223
+ )
224
+ return title_str
225
+
226
+ def _get_signature_prefix(self) -> str:
227
+ # signature prefix
228
+ if _inspect.isclass(self.obj):
229
+ return "class"
230
+ elif _inspect.iscoroutinefunction(self.obj):
231
+ return "async def"
232
+ elif _inspect.isfunction(self.obj):
233
+ return "def"
234
+ return ""
235
+
236
+ def get_parameters(self) -> list[str] | None:
237
+ try:
238
+ sig = _inspect.signature(self.obj)
239
+ except (ValueError, AttributeError, TypeError):
240
+ return None
241
+ return [str(x) for x in sig.parameters.values()]
242
+
243
+ def _make_signature(self) -> list[str]:
244
+ """
245
+ Inspector's workflow:
246
+ 02. Make signature
247
+ """
248
+ try:
249
+ return self._text_wrapper.wrap(
250
+ f"{self._get_signature_prefix()} {self.obj.__name__}{_inspect.signature(self.obj)}"
251
+ )
252
+ # not class, func | not type | is module
253
+ except (ValueError, AttributeError, TypeError):
254
+ return self._text_wrapper.wrap(repr(self.obj))
255
+
256
+ # Method and property
257
+ def _get_method_property(self) -> MethodNPropertyResult:
258
+ # if _inspect.isclass(self.obj) or inspect.ismodule(self.obj):
259
+ if _inspect.isclass(self.obj):
260
+ tmpcls = type(
261
+ "tmpcls",
262
+ (
263
+ self.obj,
264
+ ShowAllMethodsMixin,
265
+ ),
266
+ {},
267
+ )
268
+ else:
269
+ tmpcls = type(
270
+ "tmpcls",
271
+ (
272
+ type(self.obj),
273
+ ShowAllMethodsMixin,
274
+ ),
275
+ {},
276
+ )
277
+ med_prop = tmpcls._get_methods_and_properties( # type: ignore
278
+ include_private_method=self.include_private
279
+ )
280
+
281
+ try:
282
+ # If self.obj is a subclass of ShowAllMethodsMixin
283
+ _mro = getattr(
284
+ self.obj, "__mro__", getattr(type(self.obj), "__mro__", None)
285
+ )
286
+ if ShowAllMethodsMixin in _mro: # type: ignore
287
+ return med_prop # type: ignore
288
+ except AttributeError: # Not a class
289
+ pass
290
+ med_prop.__delitem__(ShowAllMethodsMixin.__name__)
291
+ return med_prop # type: ignore
292
+
293
+ # Docstring
294
+ def _get_docs(self) -> str:
295
+ """
296
+ Inspector's workflow:
297
+ 03. Get docstring and strip
298
+ """
299
+ docs: str | None = _inspect.getdoc(self.obj)
300
+
301
+ if docs is None:
302
+ return ""
303
+
304
+ # Get docs and get first paragraph
305
+ # doc_lines: list[str] = [x.strip() for x in docs.splitlines()]
306
+ doc_lines = []
307
+ for line in docs.splitlines():
308
+ if len(line) < 1:
309
+ break
310
+ doc_lines.append(line.strip())
311
+
312
+ return text_shorten(" ".join(doc_lines), width=self._linelength - 4, tabsize=4)
313
+
314
+ # Attribute
315
+ @staticmethod
316
+ def _is_real_attribute(obj: Any) -> bool:
317
+ """
318
+ Not method, classmethod, staticmethod, property
319
+ """
320
+ if callable(obj):
321
+ return False
322
+ if isinstance(obj, staticmethod):
323
+ return False
324
+ if isinstance(obj, classmethod):
325
+ return False
326
+ if isinstance(obj, property):
327
+ return False
328
+ return True
329
+
330
+ def _get_attributes(self) -> list[tuple[str, Any]]:
331
+ # Get attributes
332
+ cls_dict = getattr(self.obj, "__dict__", None)
333
+ cls_slots = getattr(self.obj, "__slots__", None)
334
+ out = []
335
+
336
+ # Check if __dict__ exist and len(__dict__) > 0
337
+ if cls_dict is not None and len(cls_dict) > 0:
338
+ if self.include_private:
339
+ out = [
340
+ (k, v)
341
+ for k, v in self.obj.__dict__.items()
342
+ if self._is_real_attribute(v)
343
+ ]
344
+ else:
345
+ out = [
346
+ (k, v)
347
+ for k, v in self.obj.__dict__.items()
348
+ if not k.startswith("_") and self._is_real_attribute(v)
349
+ ]
350
+
351
+ # Check if __slots__ exist and len(__slots__) > 0
352
+ elif cls_slots is not None and len(cls_slots) > 0:
353
+ if self.include_private:
354
+ out = [
355
+ (x, getattr(self.obj, x))
356
+ for x in self.obj.__slots__ # type: ignore
357
+ if self._is_real_attribute(getattr(self.obj, x))
358
+ ]
359
+ else:
360
+ out = [
361
+ (x, getattr(self.obj, x))
362
+ for x in self.obj.__slots__ # type: ignore
363
+ if not x.startswith("_")
364
+ and self._is_real_attribute(getattr(self.obj, x))
365
+ ]
366
+
367
+ return out
368
+
369
+ def _handle_attributes_for_output(
370
+ self, attr_list: list[tuple[str, Any]]
371
+ ) -> list[str]:
372
+ return [
373
+ text_shorten(f"- {x[0]} = {x[1]}", self._linelength - 4) for x in attr_list
374
+ ]
375
+
376
+ # Get MRO
377
+ def _get_mro(self) -> tuple[type, ...]:
378
+ """Get MRO in reverse and subtract <class 'object'>"""
379
+ if isinstance(self.obj, type):
380
+ return self.obj.__mro__[::-1][1:]
381
+ return type(self.obj).__mro__[::-1][1:]
382
+
383
+ def _make_mro_data(self) -> list[str]:
384
+ mro = [
385
+ f"- {i:02}. {x.__module__}.{x.__name__}"
386
+ for i, x in enumerate(self._get_mro(), start=1)
387
+ ]
388
+ mod_chunk = self._long_list_terminal_size(mro)
389
+
390
+ # return [text_shorten(x, self._linelength - 4) for x in mod_chunk]
391
+ return mod_chunk
392
+
393
+ # Output
394
+ def _make_output(self) -> OneColumnTableMaker:
395
+ table = OneColumnTableMaker(self._linelength)
396
+ body: list[str] = []
397
+
398
+ # Signature
399
+ title = self._make_title()
400
+ table.add_title(title)
401
+ sig = self._make_signature()
402
+ if table._title == "": # Title too long
403
+ _title = [title]
404
+ _title.extend(sig)
405
+ table.add_paragraph(_title)
406
+ else:
407
+ table.add_paragraph(sig)
408
+
409
+ # Docstring
410
+ docs = self._get_docs()
411
+ if len(docs) > 0 and self.include_docs:
412
+ body.extend(["Docstring:", docs])
413
+
414
+ # Class bases
415
+ clsbases = self._make_mro_data()
416
+ if len(clsbases) > 0 and self.include_mro:
417
+ body.extend(["", f"Bases (Len: {len(self._get_mro())}):"])
418
+ body.extend(clsbases)
419
+
420
+ # Method & Property
421
+ try:
422
+ method_n_properties = self._get_method_property().flatten_value().pack()
423
+ if self.include_method:
424
+ ml = [
425
+ text_shorten(f"- {x}", self._linelength - 4)
426
+ for x in method_n_properties.methods
427
+ ]
428
+ if len(ml) > 0:
429
+ head = ["", f"Methods (Len: {len(ml)}):"]
430
+ head.extend(self._long_list_terminal_size(ml))
431
+ body.extend(head)
432
+ if self.include_property:
433
+ pl = [
434
+ text_shorten(
435
+ f"- {x} = {getattr(self.obj, x, None)}", self._linelength - 4
436
+ )
437
+ for x in method_n_properties.properties
438
+ ]
439
+ if len(pl) > 0:
440
+ head = ["", f"Properties (Len: {len(pl)}):"]
441
+ head.extend(pl)
442
+ body.extend(head)
443
+ except (TypeError, AttributeError):
444
+ pass
445
+
446
+ # Attribute
447
+ attrs = self._get_attributes()
448
+ if len(attrs) > 0 and self.include_attribute:
449
+ body.extend(["", f"Attributes (Len: {len(attrs)}):"])
450
+ body.extend(self._handle_attributes_for_output(attr_list=attrs))
451
+
452
+ # Add to table
453
+ table.add_paragraph(body)
454
+
455
+ return table
456
+
457
+ def detail_str(self) -> str:
458
+ return self._inspect_output.make_table()
459
+
460
+
461
+ # Partial
462
+ # ---------------------------------------------------------------------------
463
+ inspect_all = partial(Inspector, line_length=None, include_all=True)
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.2.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.2.0
7
+ Date updated: 11/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()"