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.
- absfuyu/__init__.py +1 -1
- absfuyu/__main__.py +2 -2
- absfuyu/cli/__init__.py +2 -2
- absfuyu/cli/color.py +30 -14
- absfuyu/cli/config_group.py +9 -2
- absfuyu/cli/do_group.py +13 -6
- absfuyu/cli/game_group.py +9 -2
- absfuyu/cli/tool_group.py +16 -9
- absfuyu/config/__init__.py +2 -2
- absfuyu/core/__init__.py +2 -2
- absfuyu/core/baseclass.py +448 -79
- absfuyu/core/baseclass2.py +2 -2
- absfuyu/core/decorator.py +69 -3
- absfuyu/core/docstring.py +2 -2
- absfuyu/core/dummy_cli.py +2 -2
- absfuyu/core/dummy_func.py +13 -2
- absfuyu/core/typings.py +40 -0
- absfuyu/dxt/__init__.py +2 -2
- absfuyu/dxt/dictext.py +2 -2
- absfuyu/dxt/dxt_support.py +2 -2
- absfuyu/dxt/intext.py +31 -3
- absfuyu/dxt/listext.py +28 -3
- absfuyu/dxt/strext.py +2 -2
- absfuyu/extra/__init__.py +2 -2
- absfuyu/extra/beautiful.py +2 -2
- absfuyu/extra/da/__init__.py +36 -0
- absfuyu/extra/da/dadf.py +1138 -0
- absfuyu/extra/da/dadf_base.py +186 -0
- absfuyu/extra/da/df_func.py +97 -0
- absfuyu/extra/da/mplt.py +219 -0
- absfuyu/extra/data_analysis.py +10 -1067
- absfuyu/fun/__init__.py +2 -2
- absfuyu/fun/tarot.py +2 -2
- absfuyu/game/__init__.py +2 -2
- absfuyu/game/game_stat.py +2 -2
- absfuyu/game/sudoku.py +2 -2
- absfuyu/game/tictactoe.py +2 -2
- absfuyu/game/wordle.py +2 -2
- absfuyu/general/__init__.py +2 -2
- absfuyu/general/content.py +2 -2
- absfuyu/general/human.py +2 -2
- absfuyu/general/shape.py +2 -2
- absfuyu/logger.py +2 -2
- absfuyu/pkg_data/__init__.py +2 -2
- absfuyu/pkg_data/deprecated.py +2 -2
- absfuyu/sort.py +2 -2
- absfuyu/tools/__init__.py +25 -2
- absfuyu/tools/checksum.py +27 -7
- absfuyu/tools/converter.py +93 -28
- absfuyu/tools/generator.py +2 -2
- absfuyu/tools/inspector.py +433 -0
- absfuyu/tools/keygen.py +2 -2
- absfuyu/tools/obfuscator.py +45 -7
- absfuyu/tools/passwordlib.py +87 -22
- absfuyu/tools/shutdownizer.py +2 -2
- absfuyu/tools/web.py +2 -2
- absfuyu/util/__init__.py +2 -2
- absfuyu/util/api.py +2 -2
- absfuyu/util/json_method.py +2 -2
- absfuyu/util/lunar.py +2 -2
- absfuyu/util/path.py +190 -82
- absfuyu/util/performance.py +4 -4
- absfuyu/util/shorten_number.py +40 -10
- absfuyu/util/text_table.py +272 -0
- absfuyu/util/zipped.py +6 -6
- absfuyu/version.py +2 -2
- {absfuyu-5.0.1.dist-info → absfuyu-5.1.0.dist-info}/METADATA +9 -2
- absfuyu-5.1.0.dist-info/RECORD +76 -0
- absfuyu-5.0.1.dist-info/RECORD +0 -68
- {absfuyu-5.0.1.dist-info → absfuyu-5.1.0.dist-info}/WHEEL +0 -0
- {absfuyu-5.0.1.dist-info → absfuyu-5.1.0.dist-info}/entry_points.txt +0 -0
- {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
absfuyu/tools/obfuscator.py
CHANGED
|
@@ -3,8 +3,8 @@ Absfuyu: Obfuscator
|
|
|
3
3
|
-------------------
|
|
4
4
|
Obfuscate code
|
|
5
5
|
|
|
6
|
-
Version: 5.
|
|
7
|
-
Date updated:
|
|
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
|
|
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
|
-
"""
|
|
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
|
-
"""
|
|
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
|
-
|
|
223
|
-
|
|
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()"
|