absfuyu 5.3.0__py3-none-any.whl → 5.5.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 +2 -2
  3. absfuyu/cli/__init__.py +2 -2
  4. absfuyu/cli/color.py +2 -2
  5. absfuyu/cli/config_group.py +2 -2
  6. absfuyu/cli/do_group.py +2 -2
  7. absfuyu/cli/game_group.py +2 -2
  8. absfuyu/cli/tool_group.py +2 -2
  9. absfuyu/config/__init__.py +2 -2
  10. absfuyu/core/__init__.py +4 -4
  11. absfuyu/core/baseclass.py +511 -153
  12. absfuyu/core/baseclass2.py +3 -3
  13. absfuyu/core/decorator.py +2 -2
  14. absfuyu/core/docstring.py +2 -2
  15. absfuyu/core/dummy_cli.py +2 -2
  16. absfuyu/core/dummy_func.py +2 -2
  17. absfuyu/dxt/__init__.py +2 -2
  18. absfuyu/dxt/dictext.py +17 -14
  19. absfuyu/dxt/dxt_support.py +2 -2
  20. absfuyu/dxt/intext.py +52 -34
  21. absfuyu/dxt/listext.py +81 -14
  22. absfuyu/dxt/strext.py +4 -4
  23. absfuyu/extra/__init__.py +2 -2
  24. absfuyu/extra/beautiful.py +2 -2
  25. absfuyu/extra/da/__init__.py +2 -2
  26. absfuyu/extra/da/dadf.py +59 -56
  27. absfuyu/extra/da/dadf_base.py +2 -2
  28. absfuyu/extra/da/df_func.py +2 -2
  29. absfuyu/extra/da/mplt.py +2 -2
  30. absfuyu/extra/data_analysis.py +2 -2
  31. absfuyu/fun/__init__.py +7 -2
  32. absfuyu/fun/rubik.py +442 -0
  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 +4 -4
  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 +2 -2
  48. absfuyu/tools/checksum.py +2 -2
  49. absfuyu/tools/converter.py +2 -2
  50. absfuyu/tools/generator.py +4 -4
  51. absfuyu/tools/inspector.py +346 -69
  52. absfuyu/tools/keygen.py +2 -2
  53. absfuyu/tools/obfuscator.py +4 -4
  54. absfuyu/tools/passwordlib.py +2 -2
  55. absfuyu/tools/shutdownizer.py +2 -2
  56. absfuyu/tools/web.py +2 -2
  57. absfuyu/typings.py +7 -2
  58. absfuyu/util/__init__.py +57 -3
  59. absfuyu/util/api.py +2 -2
  60. absfuyu/util/json_method.py +2 -2
  61. absfuyu/util/lunar.py +2 -2
  62. absfuyu/util/path.py +17 -4
  63. absfuyu/util/performance.py +2 -2
  64. absfuyu/util/shorten_number.py +2 -2
  65. absfuyu/util/text_table.py +33 -14
  66. absfuyu/util/zipped.py +2 -2
  67. absfuyu/version.py +3 -3
  68. {absfuyu-5.3.0.dist-info → absfuyu-5.5.0.dist-info}/METADATA +2 -2
  69. absfuyu-5.5.0.dist-info/RECORD +77 -0
  70. absfuyu-5.3.0.dist-info/RECORD +0 -76
  71. {absfuyu-5.3.0.dist-info → absfuyu-5.5.0.dist-info}/WHEEL +0 -0
  72. {absfuyu-5.3.0.dist-info → absfuyu-5.5.0.dist-info}/entry_points.txt +0 -0
  73. {absfuyu-5.3.0.dist-info → absfuyu-5.5.0.dist-info}/licenses/LICENSE +0 -0
@@ -3,35 +3,193 @@ Absfuyu: Inspector
3
3
  ------------------
4
4
  Inspector
5
5
 
6
- Version: 5.2.0
7
- Date updated: 16/03/2025 (dd/mm/yyyy)
6
+ Version: 5.5.0
7
+ Date updated: 23/04/2025 (dd/mm/yyyy)
8
8
  """
9
9
 
10
10
  # Module level
11
11
  # ---------------------------------------------------------------------------
12
12
  __all__ = ["Inspector", "inspect_all"]
13
13
 
14
+
14
15
  # Library
15
16
  # ---------------------------------------------------------------------------
16
17
  import inspect as _inspect
17
18
  import os
19
+ from collections.abc import Callable
20
+ from dataclasses import dataclass
18
21
  from functools import partial
19
22
  from textwrap import TextWrapper
20
23
  from textwrap import shorten as text_shorten
21
- from typing import Any, Literal, overload
24
+ from typing import Any, Literal, Protocol, get_overloads, overload
22
25
 
23
26
  from absfuyu.core.baseclass import (
24
27
  AutoREPRMixin,
25
- MethodNPropertyResult,
26
- ShowAllMethodsMixin,
28
+ BaseDataclass,
29
+ ClassMembers,
30
+ ClassMembersResult,
31
+ GetClassMembersMixin,
27
32
  )
28
33
  from absfuyu.dxt.listext import ListExt
29
- from absfuyu.util.text_table import OneColumnTableMaker
34
+ from absfuyu.typings import P, R
35
+ from absfuyu.util.text_table import BoxStyle, OneColumnTableMaker
36
+
37
+
38
+ # TODO: rewrite with each class for docs, method, property, attr, param, title
39
+ # Dataclass
40
+ # ---------------------------------------------------------------------------
41
+ @dataclass
42
+ class BaseDCInspect(BaseDataclass):
43
+ def _make_repr(self) -> str | None:
44
+ fields = self._get_fields()
45
+ if 0 < len(fields) < 2:
46
+ return repr(getattr(self, fields[0]))
47
+ return None
48
+
49
+ def _long_list_terminal_size(
50
+ self, long_list: list[str], width: int = 80, /
51
+ ) -> list[str]:
52
+ ll = ListExt(long_list).wrap_to_column(width, margin=4, transpose=True)
53
+ return list(ll)
54
+
55
+
56
+ @dataclass
57
+ class _TitleSignature:
58
+ """
59
+ Inspector:
60
+ Object's title and signature
61
+ """
62
+
63
+ title: str
64
+ signature: list[str]
65
+
66
+ def make_output(self):
67
+ pass
68
+
69
+
70
+ @dataclass
71
+ class _Docstring(BaseDCInspect):
72
+ """
73
+ Inspector:
74
+ Object's docstring
75
+ """
76
+
77
+ docs: str
78
+
79
+ def __repr__(self) -> str:
80
+ r = self._make_repr()
81
+ if r is not None:
82
+ return r
83
+ return super().__repr__()
84
+
85
+ def _get_first_paragraph(self) -> str:
86
+ # Get docs and get first paragraph
87
+ doc_lines = []
88
+ for line in self.docs.splitlines():
89
+ if len(line) < 1:
90
+ break
91
+ doc_lines.append(line.strip())
92
+ return " ".join(doc_lines)
93
+
94
+ def make_output(self) -> list[str]:
95
+ if len(self._get_first_paragraph()) > 0:
96
+ return ["Docstring:", self._get_first_paragraph()]
97
+ return [""]
98
+
99
+
100
+ @dataclass
101
+ class _MRO(BaseDCInspect):
102
+ """
103
+ Inspector:
104
+ Object's MRO, bases
105
+ """
106
+
107
+ mro: tuple[type, ...]
108
+
109
+ def __repr__(self) -> str:
110
+ r = self._make_repr()
111
+ if r is not None:
112
+ return r
113
+ return super().__repr__()
114
+
115
+ def _make_output(self) -> list[str]:
116
+ out = [
117
+ f"- {i:02}. {x.__module__}.{x.__name__}"
118
+ for i, x in enumerate(self.mro[1:], start=1)
119
+ ]
120
+ # ListExt.wrap_to_column
121
+ return out
122
+
123
+ def make_output(self, *, width: int = 80) -> list[str]:
124
+ out = self._make_output()
125
+ if len(out) > 0:
126
+ out_ = self._long_list_terminal_size(out, width)
127
+ return ["", f"Bases (Len: {len(out)}):", *out_]
128
+ return [""]
129
+
130
+
131
+ @dataclass
132
+ class _Member(BaseDCInspect):
133
+ """
134
+ Inspector:
135
+ Object's member
136
+ """
137
+
138
+ member: ClassMembers
139
+
140
+ def __repr__(self) -> str:
141
+ r = self._make_repr()
142
+ if r is not None:
143
+ return r
144
+ return super().__repr__()
145
+
146
+ def make_output(
147
+ self,
148
+ *,
149
+ width: int = 80,
150
+ obj,
151
+ include_method: bool = True,
152
+ include_property: bool = True,
153
+ ) -> list[str]:
154
+ mems = self.member.pack().sort()
155
+ body: list[str] = []
156
+
157
+ if include_method:
158
+ ml = [text_shorten(f"- {x}", width - 4) for x in mems.methods]
159
+ if len(ml) > 0:
160
+ head = ["", f"Methods (Len: {len(ml)}):"]
161
+ head.extend(self._long_list_terminal_size(ml, width))
162
+ body.extend(head)
163
+
164
+ if include_property:
165
+ pl = [
166
+ text_shorten(f"- {x} = {getattr(obj, x, None)}", width - 4)
167
+ for x in mems.properties
168
+ ]
169
+ if len(pl) > 0:
170
+ head = ["", f"Properties (Len: {len(pl)}):"]
171
+ head.extend(self._long_list_terminal_size(pl, width))
172
+ body.extend(head)
173
+
174
+ if len(body) > 0:
175
+ return body
176
+ return [""]
177
+
178
+
179
+ class InspectComponents(Protocol):
180
+ """Supports make_output() -> list[str]"""
181
+
182
+ @overload
183
+ def make_output(self) -> list[str]: ...
184
+ @overload
185
+ def make_output(self, *, width: int = ...) -> list[str]: ...
186
+ @overload
187
+ def make_output(self, *args, **kwargs) -> list[str]: ...
188
+ def make_output(self, *args, **kwargs) -> list[str]: ...
30
189
 
31
190
 
32
191
  # Class
33
192
  # ---------------------------------------------------------------------------
34
- # TODO: rewrite with each class for docs, method, property, attr, param, title
35
193
  class Inspector(AutoREPRMixin):
36
194
  """
37
195
  Inspect an object.
@@ -71,7 +229,7 @@ class Inspector(AutoREPRMixin):
71
229
  Maximum lines for the output's header (class, signature, repr).
72
230
  Must be >= 1, by default ``8``
73
231
 
74
- style : Literal["normal", "bold", "dashed", "double", "rounded"], optional
232
+ style : BoxStyle, optional
75
233
  Style for the table, by default ``"normal"``
76
234
 
77
235
 
@@ -99,7 +257,7 @@ class Inspector(AutoREPRMixin):
99
257
  include_attribute: bool = True,
100
258
  include_private: bool = False,
101
259
  max_textwrap_lines: int = 8,
102
- style: Literal["normal", "bold", "dashed", "double", "rounded"] = "normal",
260
+ style: BoxStyle = "normal",
103
261
  ) -> None: ...
104
262
 
105
263
  def __init__(
@@ -116,10 +274,11 @@ class Inspector(AutoREPRMixin):
116
274
  include_method: bool = False,
117
275
  include_property: bool = False,
118
276
  include_attribute: bool = True,
277
+ include_dunder: bool = False,
119
278
  include_private: bool = False,
120
279
  include_all: bool = False,
121
280
  # Style
122
- style: Literal["normal", "bold", "dashed", "double", "rounded"] = "normal",
281
+ style: BoxStyle = "normal",
123
282
  ) -> None:
124
283
  """
125
284
  Inspect an object.
@@ -159,7 +318,7 @@ class Inspector(AutoREPRMixin):
159
318
  Maximum lines for the output's header (class, signature, repr).
160
319
  Must be >= 1, by default ``8``
161
320
 
162
- style : Literal["normal", "bold", "dashed", "double", "rounded"], optional
321
+ style : BoxStyle | Literal["normal", "bold", "dashed", "double", "rounded", ...], optional
163
322
  Style for the table, by default ``"normal"``
164
323
 
165
324
 
@@ -174,6 +333,7 @@ class Inspector(AutoREPRMixin):
174
333
  self.include_property = include_property
175
334
  self.include_attribute = include_attribute
176
335
  self.include_private = include_private
336
+ self.include_dunder = include_dunder
177
337
  self._style = style
178
338
 
179
339
  if include_all:
@@ -209,21 +369,20 @@ class Inspector(AutoREPRMixin):
209
369
  self._inspect_output = self._make_output()
210
370
 
211
371
  def __str__(self) -> str:
212
- return self.detail_str()
372
+ return self._inspect_output.make_table()
213
373
 
214
- # Support
374
+ # 00. Support
375
+ # -----------------------------------------------------------
376
+ # @deprecated
215
377
  def _long_list_terminal_size(self, long_list: list) -> list:
216
378
  ll = ListExt(long_list).wrap_to_column(
217
379
  self._linelength, margin=4, transpose=True
218
380
  )
219
381
  return list(ll)
220
382
 
221
- # Signature
383
+ # 01. Signature
384
+ # -----------------------------------------------------------
222
385
  def _make_title(self) -> str:
223
- """
224
- Inspector's workflow:
225
- 01. Make title
226
- """
227
386
  title_str = (
228
387
  str(self.obj)
229
388
  if (
@@ -245,6 +404,7 @@ class Inspector(AutoREPRMixin):
245
404
  return "def"
246
405
  return ""
247
406
 
407
+ # @deprecated
248
408
  def get_parameters(self) -> list[str] | None:
249
409
  try:
250
410
  sig = _inspect.signature(self.obj)
@@ -252,12 +412,27 @@ class Inspector(AutoREPRMixin):
252
412
  return None
253
413
  return [str(x) for x in sig.parameters.values()]
254
414
 
415
+ def _get_func_signature(self, func: Callable[P, R]) -> list[str]:
416
+ overloads = list(get_overloads(func))
417
+ if len(overloads) < 1:
418
+ return [
419
+ f"{self._get_signature_prefix()} {func.__name__}{_inspect.signature(func)}"
420
+ ]
421
+ return [
422
+ f"{self._get_signature_prefix()} {x.__name__}{_inspect.signature(x)}"
423
+ for x in overloads
424
+ ]
425
+
426
+ # @deprecated
255
427
  def _make_signature(self) -> list[str]:
256
- """
257
- Inspector's workflow:
258
- 02. Make signature
259
- """
260
428
  try:
429
+ # if isinstance(self.obj, Callable):
430
+ if _inspect.isfunction(self.obj):
431
+ funcs = [
432
+ self._text_wrapper.wrap(x)
433
+ for x in self._get_func_signature(self.obj)
434
+ ]
435
+ return ListExt(funcs).flatten()
261
436
  return self._text_wrapper.wrap(
262
437
  f"{self._get_signature_prefix()} {self.obj.__name__}{_inspect.signature(self.obj)}"
263
438
  )
@@ -265,15 +440,93 @@ class Inspector(AutoREPRMixin):
265
440
  except (ValueError, AttributeError, TypeError):
266
441
  return self._text_wrapper.wrap(repr(self.obj))
267
442
 
268
- # Method and property
269
- def _get_method_property(self) -> MethodNPropertyResult:
443
+ @property
444
+ def obj_signature(self) -> _TitleSignature:
445
+ """Object's title and signature"""
446
+ title: str = self._make_title()
447
+ sig: list[str] = []
448
+ try:
449
+ if _inspect.isfunction(self.obj):
450
+ sig.extend(self._get_func_signature(self.obj))
451
+ sig.append(
452
+ f"{self._get_signature_prefix()} {self.obj.__name__}{_inspect.signature(self.obj)}"
453
+ )
454
+ # not class, func | not type | is module
455
+ except (ValueError, AttributeError, TypeError):
456
+ sig.append(repr(self.obj))
457
+
458
+ return _TitleSignature(title=title, signature=sig)
459
+
460
+ # 02. Docstring
461
+ # -----------------------------------------------------------
462
+ @property
463
+ def obj_docs(self) -> _Docstring:
464
+ """Object's docstring"""
465
+ docs: str | None = _inspect.getdoc(self.obj)
466
+
467
+ if docs is None:
468
+ return _Docstring("")
469
+ return _Docstring(docs=docs)
470
+
471
+ # @deprecated
472
+ def _get_docs(self) -> str:
473
+ docs: str | None = _inspect.getdoc(self.obj)
474
+
475
+ if docs is None:
476
+ return ""
477
+
478
+ # Get docs and get first paragraph
479
+ # doc_lines: list[str] = [x.strip() for x in docs.splitlines()]
480
+ doc_lines = []
481
+ for line in docs.splitlines():
482
+ if len(line) < 1:
483
+ break
484
+ doc_lines.append(line.strip())
485
+
486
+ return text_shorten(" ".join(doc_lines), width=self._linelength - 4, tabsize=4)
487
+
488
+ # 03. MRO/Bases
489
+ # -----------------------------------------------------------
490
+ @property
491
+ def obj_mro(self) -> _MRO:
492
+ """Object's MRO, bases"""
493
+ if isinstance(self.obj, type):
494
+ return _MRO(mro=self.obj.__mro__[::-1])
495
+ return _MRO(mro=type(self.obj).__mro__[::-1])
496
+
497
+ @property
498
+ def obj_bases(self) -> _MRO:
499
+ """Object's MRO, bases"""
500
+ return self.obj_mro
501
+
502
+ # @deprecated
503
+ def _get_mro(self) -> tuple[type, ...]:
504
+ """Get MRO in reverse and subtract <class 'object'>"""
505
+ if isinstance(self.obj, type):
506
+ return self.obj.__mro__[::-1][1:]
507
+ return type(self.obj).__mro__[::-1][1:]
508
+
509
+ # @deprecated
510
+ def _make_mro_data(self) -> list[str]:
511
+ mro = [
512
+ f"- {i:02}. {x.__module__}.{x.__name__}"
513
+ for i, x in enumerate(self._get_mro(), start=1)
514
+ ]
515
+ mod_chunk = self._long_list_terminal_size(mro)
516
+
517
+ # return [text_shorten(x, self._linelength - 4) for x in mod_chunk]
518
+ return mod_chunk
519
+
520
+ # 04. Class's members
521
+ # -----------------------------------------------------------
522
+ def _get_obj_member(self) -> ClassMembersResult:
270
523
  # if _inspect.isclass(self.obj) or inspect.ismodule(self.obj):
271
524
  if _inspect.isclass(self.obj):
272
525
  tmpcls = type(
273
526
  "tmpcls",
274
527
  (
275
528
  self.obj,
276
- ShowAllMethodsMixin,
529
+ GetClassMembersMixin,
277
530
  ),
278
531
  {},
279
532
  )
@@ -282,46 +535,38 @@ class Inspector(AutoREPRMixin):
282
535
  "tmpcls",
283
536
  (
284
537
  type(self.obj),
285
- ShowAllMethodsMixin,
538
+ GetClassMembersMixin,
286
539
  ),
287
540
  {},
288
541
  )
289
- med_prop = tmpcls._get_methods_and_properties( # type: ignore
290
- include_private_method=self.include_private
542
+ med_prop = tmpcls._get_members( # type: ignore
543
+ dunder=False, private=self.include_private
291
544
  )
292
545
 
293
546
  try:
294
- # If self.obj is a subclass of ShowAllMethodsMixin
547
+ # If self.obj is a subclass of GetClassMembersMixin
295
548
  _mro = getattr(
296
549
  self.obj, "__mro__", getattr(type(self.obj), "__mro__", None)
297
550
  )
298
- if ShowAllMethodsMixin in _mro: # type: ignore
551
+ if GetClassMembersMixin in _mro: # type: ignore
299
552
  return med_prop # type: ignore
300
553
  except AttributeError: # Not a class
301
554
  pass
302
- med_prop.__delitem__(ShowAllMethodsMixin.__name__)
555
+ med_prop.__delitem__(GetClassMembersMixin.__name__)
303
556
  return med_prop # type: ignore
304
557
 
305
- # Docstring
306
- def _get_docs(self) -> str:
307
- """
308
- Inspector's workflow:
309
- 03. Get docstring and strip
310
- """
311
- docs: str | None = _inspect.getdoc(self.obj)
312
-
313
- if docs is None:
314
- return ""
315
-
316
- # Get docs and get first paragraph
317
- # doc_lines: list[str] = [x.strip() for x in docs.splitlines()]
318
- doc_lines = []
319
- for line in docs.splitlines():
320
- if len(line) < 1:
321
- break
322
- doc_lines.append(line.strip())
558
+ @property
559
+ def obj_member(self) -> _Member:
560
+ """Object's members"""
561
+ try:
562
+ mem = self._get_obj_member()
563
+ return _Member(mem.flatten_value())
564
+ except (TypeError, AttributeError):
565
+ return _Member(ClassMembers())
323
566
 
324
- return text_shorten(" ".join(doc_lines), width=self._linelength - 4, tabsize=4)
567
+ # @deprecated
568
+ def _get_method_property(self) -> ClassMembersResult:
569
+ return self._get_obj_member()
325
570
 
326
571
  # Attribute
327
572
  @staticmethod
@@ -385,24 +630,9 @@ class Inspector(AutoREPRMixin):
385
630
  text_shorten(f"- {x[0]} = {x[1]}", self._linelength - 4) for x in attr_list
386
631
  ]
387
632
 
388
- # Get MRO
389
- def _get_mro(self) -> tuple[type, ...]:
390
- """Get MRO in reverse and subtract <class 'object'>"""
391
- if isinstance(self.obj, type):
392
- return self.obj.__mro__[::-1][1:]
393
- return type(self.obj).__mro__[::-1][1:]
394
-
395
- def _make_mro_data(self) -> list[str]:
396
- mro = [
397
- f"- {i:02}. {x.__module__}.{x.__name__}"
398
- for i, x in enumerate(self._get_mro(), start=1)
399
- ]
400
- mod_chunk = self._long_list_terminal_size(mro)
401
-
402
- # return [text_shorten(x, self._linelength - 4) for x in mod_chunk]
403
- return mod_chunk
404
-
405
633
  # Output
634
+ # -----------------------------------------------------------
635
+ # @deprecated
406
636
  def _make_output(self) -> OneColumnTableMaker:
407
637
  table = OneColumnTableMaker(self._linelength, style=self._style)
408
638
  body: list[str] = []
@@ -468,8 +698,55 @@ class Inspector(AutoREPRMixin):
468
698
 
469
699
  return table
470
700
 
471
- def detail_str(self) -> str:
472
- return self._inspect_output.make_table()
701
+ def _make_output_2(self) -> OneColumnTableMaker:
702
+ # Prep
703
+ # components: list[InspectComponents] = [
704
+ # self.obj_signature,
705
+ # self.obj_docs,
706
+ # self.obj_mro,
707
+ # self.obj_member,
708
+ # ]
709
+ table = OneColumnTableMaker(self._linelength, style=self._style)
710
+ body: list[str] = []
711
+
712
+ # Signature
713
+ title = self.obj_signature.title
714
+ table.add_title(title)
715
+ if table._title == "": # Title too long
716
+ _title = [title]
717
+ _title.extend(self.obj_signature.signature)
718
+ table.add_paragraph(_title)
719
+ else:
720
+ table.add_paragraph(self.obj_signature.signature)
721
+
722
+ # Docstring
723
+ if self.include_docs:
724
+ body.extend(self.obj_docs.make_output())
725
+
726
+ # Class bases
727
+ if self.include_mro:
728
+ body.extend(self.obj_mro.make_output(width=self._linelength))
729
+
730
+ # Method & Property
731
+ body.extend(
732
+ self.obj_member.make_output(
733
+ width=self._linelength,
734
+ obj=self.obj,
735
+ include_method=self.include_method,
736
+ include_property=self.include_property,
737
+ )
738
+ )
739
+
740
+ # Attribute
741
+ attrs = self._get_attributes()
742
+ if len(attrs) > 0 and self.include_attribute:
743
+ body.extend(["", f"Attributes (Len: {len(attrs)}):"])
744
+ body.extend(self._handle_attributes_for_output(attr_list=attrs))
745
+
746
+ # Add to table
747
+ table.add_paragraph(body)
748
+
749
+ return table
473
750
 
474
751
 
475
752
  # Partial
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.2.0
9
- Date updated: 10/03/2025 (dd/mm/yyyy)
8
+ Version: 5.5.0
9
+ Date updated: 23/04/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.2.0
7
- Date updated: 11/03/2025 (dd/mm/yyyy)
6
+ Version: 5.5.0
7
+ Date updated: 23/04/2025 (dd/mm/yyyy)
8
8
  """
9
9
 
10
10
  # Module level
@@ -22,7 +22,7 @@ from collections import deque
22
22
  from string import Template
23
23
  from typing import ClassVar
24
24
 
25
- from absfuyu.core.baseclass import BaseClass, ShowAllMethodsMixin
25
+ from absfuyu.core.baseclass import BaseClass, GetClassMembersMixin
26
26
  from absfuyu.core.docstring import versionadded
27
27
  from absfuyu.dxt import Text
28
28
  from absfuyu.logger import logger
@@ -110,7 +110,7 @@ class StrShifter(BaseClass):
110
110
  return self._use_convert_table(self._make_convert_table())
111
111
 
112
112
 
113
- class Obfuscator(ShowAllMethodsMixin):
113
+ class Obfuscator(GetClassMembersMixin):
114
114
  """
115
115
  Obfuscate code
116
116
 
@@ -3,8 +3,8 @@ Absfuyu: Passwordlib
3
3
  --------------------
4
4
  Password library
5
5
 
6
- Version: 5.2.0
7
- Date updated: 15/03/2025 (dd/mm/yyyy)
6
+ Version: 5.5.0
7
+ Date updated: 23/04/2025 (dd/mm/yyyy)
8
8
  """
9
9
 
10
10
  # Module level
@@ -3,8 +3,8 @@ Absfuyu: Shutdownizer
3
3
  ---------------------
4
4
  This shutdowns
5
5
 
6
- Version: 5.2.0
7
- Date updated: 10/03/2025 (dd/mm/yyyy)
6
+ Version: 5.5.0
7
+ Date updated: 23/04/2025 (dd/mm/yyyy)
8
8
  """
9
9
 
10
10
  # Module level
absfuyu/tools/web.py CHANGED
@@ -3,8 +3,8 @@ Absfuyu: Web
3
3
  ------------
4
4
  Web, ``request``, ``BeautifulSoup`` stuff
5
5
 
6
- Version: 5.2.0
7
- Date updated: 10/03/2025 (dd/mm/yyyy)
6
+ Version: 5.5.0
7
+ Date updated: 23/04/2025 (dd/mm/yyyy)
8
8
  """
9
9
 
10
10
  # Library
absfuyu/typings.py CHANGED
@@ -3,8 +3,8 @@ Absfuyu: Core
3
3
  -------------
4
4
  Pre-defined typing
5
5
 
6
- Version: 5.2.0
7
- Date updated: 14/03/2025 (dd/mm/yyyy)
6
+ Version: 5.5.0
7
+ Date updated: 23/04/2025 (dd/mm/yyyy)
8
8
  """
9
9
 
10
10
  # Module Package
@@ -14,6 +14,8 @@ __all__ = [
14
14
  "T",
15
15
  "T_co",
16
16
  "T_contra",
17
+ "KT",
18
+ "VT",
17
19
  "P",
18
20
  "R",
19
21
  "_CALLABLE",
@@ -48,6 +50,9 @@ T_co = TypeVar("T_co", covariant=True) # Type covariant
48
50
  # with a more general type without causing errors
49
51
  T_contra = TypeVar("T_contra", contravariant=True) # Type contravariant
50
52
 
53
+ KT = TypeVar("KT")
54
+ VT = TypeVar("VT")
55
+
51
56
  # Callable
52
57
  P = ParamSpec("P") # Parameter type
53
58
  R = TypeVar("R") # Return type - Can be anything