absfuyu 5.0.0__py3-none-any.whl → 6.1.2__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 +5 -3
- absfuyu/__main__.py +3 -3
- absfuyu/cli/__init__.py +13 -2
- absfuyu/cli/audio_group.py +98 -0
- absfuyu/cli/color.py +30 -14
- absfuyu/cli/config_group.py +9 -2
- absfuyu/cli/do_group.py +23 -6
- absfuyu/cli/game_group.py +27 -2
- absfuyu/cli/tool_group.py +81 -11
- absfuyu/config/__init__.py +3 -3
- absfuyu/core/__init__.py +12 -8
- absfuyu/core/baseclass.py +929 -96
- absfuyu/core/baseclass2.py +44 -3
- absfuyu/core/decorator.py +70 -4
- absfuyu/core/docstring.py +64 -41
- absfuyu/core/dummy_cli.py +3 -3
- absfuyu/core/dummy_func.py +19 -6
- absfuyu/dxt/__init__.py +2 -2
- absfuyu/dxt/base_type.py +93 -0
- absfuyu/dxt/dictext.py +204 -16
- absfuyu/dxt/dxt_support.py +2 -2
- absfuyu/dxt/intext.py +151 -34
- absfuyu/dxt/listext.py +969 -127
- absfuyu/dxt/strext.py +77 -17
- absfuyu/extra/__init__.py +2 -2
- absfuyu/extra/audio/__init__.py +8 -0
- absfuyu/extra/audio/_util.py +57 -0
- absfuyu/extra/audio/convert.py +192 -0
- absfuyu/extra/audio/lossless.py +281 -0
- absfuyu/extra/beautiful.py +3 -2
- absfuyu/extra/da/__init__.py +72 -0
- absfuyu/extra/da/dadf.py +1600 -0
- absfuyu/extra/da/dadf_base.py +186 -0
- absfuyu/extra/da/df_func.py +181 -0
- absfuyu/extra/da/mplt.py +219 -0
- absfuyu/extra/ggapi/__init__.py +8 -0
- absfuyu/extra/ggapi/gdrive.py +223 -0
- absfuyu/extra/ggapi/glicense.py +148 -0
- absfuyu/extra/ggapi/glicense_df.py +186 -0
- absfuyu/extra/ggapi/gsheet.py +88 -0
- absfuyu/extra/img/__init__.py +30 -0
- absfuyu/extra/img/converter.py +402 -0
- absfuyu/extra/img/dup_check.py +291 -0
- absfuyu/extra/pdf.py +87 -0
- absfuyu/extra/rclone.py +253 -0
- absfuyu/extra/xml.py +90 -0
- absfuyu/fun/__init__.py +7 -20
- absfuyu/fun/rubik.py +442 -0
- absfuyu/fun/tarot.py +2 -2
- absfuyu/game/__init__.py +2 -2
- absfuyu/game/game_stat.py +2 -2
- absfuyu/game/schulte.py +78 -0
- absfuyu/game/sudoku.py +2 -2
- absfuyu/game/tictactoe.py +2 -3
- absfuyu/game/wordle.py +6 -4
- absfuyu/general/__init__.py +4 -4
- absfuyu/general/content.py +4 -4
- absfuyu/general/human.py +2 -2
- absfuyu/general/resrel.py +213 -0
- absfuyu/general/shape.py +3 -8
- absfuyu/general/tax.py +344 -0
- absfuyu/logger.py +806 -59
- absfuyu/numbers/__init__.py +13 -0
- absfuyu/numbers/number_to_word.py +321 -0
- absfuyu/numbers/shorten_number.py +303 -0
- absfuyu/numbers/time_duration.py +217 -0
- absfuyu/pkg_data/__init__.py +2 -2
- absfuyu/pkg_data/deprecated.py +2 -2
- absfuyu/pkg_data/logo.py +1462 -0
- absfuyu/sort.py +4 -4
- absfuyu/tools/__init__.py +28 -2
- absfuyu/tools/checksum.py +144 -9
- absfuyu/tools/converter.py +120 -34
- absfuyu/tools/generator.py +461 -0
- absfuyu/tools/inspector.py +752 -0
- absfuyu/tools/keygen.py +2 -2
- absfuyu/tools/obfuscator.py +47 -9
- absfuyu/tools/passwordlib.py +89 -25
- absfuyu/tools/shutdownizer.py +3 -8
- absfuyu/tools/sw.py +718 -0
- absfuyu/tools/web.py +10 -13
- absfuyu/typings.py +138 -0
- absfuyu/util/__init__.py +114 -6
- absfuyu/util/api.py +41 -18
- absfuyu/util/cli.py +119 -0
- absfuyu/util/gui.py +91 -0
- absfuyu/util/json_method.py +43 -14
- absfuyu/util/lunar.py +2 -2
- absfuyu/util/package.py +124 -0
- absfuyu/util/path.py +702 -82
- absfuyu/util/performance.py +122 -7
- absfuyu/util/shorten_number.py +244 -21
- absfuyu/util/text_table.py +481 -0
- absfuyu/util/zipped.py +8 -7
- absfuyu/version.py +79 -59
- {absfuyu-5.0.0.dist-info → absfuyu-6.1.2.dist-info}/METADATA +52 -11
- absfuyu-6.1.2.dist-info/RECORD +105 -0
- {absfuyu-5.0.0.dist-info → absfuyu-6.1.2.dist-info}/WHEEL +1 -1
- absfuyu/extra/data_analysis.py +0 -1078
- absfuyu/general/generator.py +0 -303
- absfuyu-5.0.0.dist-info/RECORD +0 -68
- {absfuyu-5.0.0.dist-info → absfuyu-6.1.2.dist-info}/entry_points.txt +0 -0
- {absfuyu-5.0.0.dist-info → absfuyu-6.1.2.dist-info}/licenses/LICENSE +0 -0
absfuyu/core/baseclass.py
CHANGED
|
@@ -3,8 +3,8 @@ Absfuyu: Core
|
|
|
3
3
|
-------------
|
|
4
4
|
Bases for other features
|
|
5
5
|
|
|
6
|
-
Version:
|
|
7
|
-
Date updated: 12/
|
|
6
|
+
Version: 6.1.1
|
|
7
|
+
Date updated: 30/12/2025 (dd/mm/yyyy)
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
10
|
# Module Package
|
|
@@ -12,15 +12,26 @@ Date updated: 12/02/2025 (dd/mm/yyyy)
|
|
|
12
12
|
__all__ = [
|
|
13
13
|
# Color
|
|
14
14
|
"CLITextColor",
|
|
15
|
+
# Support
|
|
16
|
+
"ClassMembers",
|
|
17
|
+
"ClassMembersResult",
|
|
15
18
|
# Mixins
|
|
16
|
-
"
|
|
19
|
+
"GetClassMembersMixin",
|
|
17
20
|
"AutoREPRMixin",
|
|
21
|
+
"AddFormatMixin",
|
|
18
22
|
# Class
|
|
19
23
|
"BaseClass",
|
|
24
|
+
"BaseDataclass",
|
|
20
25
|
# Metaclass
|
|
21
26
|
"PositiveInitArgsMeta",
|
|
22
27
|
]
|
|
23
28
|
|
|
29
|
+
# Library
|
|
30
|
+
# ---------------------------------------------------------------------------
|
|
31
|
+
from collections.abc import Callable
|
|
32
|
+
from dataclasses import dataclass, field
|
|
33
|
+
from typing import Any, ClassVar, Literal, Self
|
|
34
|
+
|
|
24
35
|
|
|
25
36
|
# Color
|
|
26
37
|
# ---------------------------------------------------------------------------
|
|
@@ -39,31 +50,427 @@ class CLITextColor:
|
|
|
39
50
|
RESET = "\x1b[39m"
|
|
40
51
|
|
|
41
52
|
|
|
42
|
-
#
|
|
53
|
+
# Dataclass
|
|
43
54
|
# ---------------------------------------------------------------------------
|
|
44
|
-
|
|
55
|
+
# @versionadded("5.5.0")
|
|
56
|
+
@dataclass
|
|
57
|
+
class BaseDataclass:
|
|
45
58
|
"""
|
|
46
|
-
|
|
59
|
+
Base dataclass.
|
|
47
60
|
|
|
48
|
-
|
|
61
|
+
Contains util methods:
|
|
62
|
+
- _get_fields
|
|
63
|
+
- to_dict
|
|
49
64
|
"""
|
|
50
65
|
|
|
51
66
|
@classmethod
|
|
52
|
-
def
|
|
53
|
-
|
|
67
|
+
def _get_fields(cls) -> tuple[str, ...]:
|
|
68
|
+
"""
|
|
69
|
+
Get dataclass's fields.
|
|
70
|
+
|
|
71
|
+
Returns
|
|
72
|
+
-------
|
|
73
|
+
tuple[str, ...]
|
|
74
|
+
Dataclass's fields.
|
|
75
|
+
"""
|
|
76
|
+
_fields = getattr(cls, "__dataclass_fields__", ())
|
|
77
|
+
return tuple(_fields)
|
|
78
|
+
|
|
79
|
+
# @versionadded("5.11.0")
|
|
80
|
+
def to_dict(self):
|
|
81
|
+
"""
|
|
82
|
+
Convert dataclass into dict
|
|
83
|
+
|
|
84
|
+
Returns
|
|
85
|
+
-------
|
|
86
|
+
dict
|
|
87
|
+
Dataclass converted into dict
|
|
88
|
+
"""
|
|
89
|
+
fields = self._get_fields()
|
|
90
|
+
output = {}
|
|
91
|
+
for field in fields:
|
|
92
|
+
output.__setitem__(field, self.__getattribute__(field))
|
|
93
|
+
return output
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
# Support
|
|
97
|
+
# ---------------------------------------------------------------------------
|
|
98
|
+
# @versionadded("5.5.0")
|
|
99
|
+
@dataclass
|
|
100
|
+
class ClassMembers(BaseDataclass):
|
|
101
|
+
"""
|
|
102
|
+
Contains lists of methods, classmethods, staticmethods,
|
|
103
|
+
properties, and attributes of a class.
|
|
104
|
+
|
|
105
|
+
Parameters
|
|
106
|
+
----------
|
|
107
|
+
methods : list[str], optional
|
|
108
|
+
List contains method names of a class.
|
|
109
|
+
By default inits an empty list.
|
|
110
|
+
|
|
111
|
+
classmethods : list[str], optional
|
|
112
|
+
List contains classmethod names of a class.
|
|
113
|
+
By default inits an empty list.
|
|
114
|
+
|
|
115
|
+
staticmethods : list[str], optional
|
|
116
|
+
List contains staticmethod names of a class.
|
|
117
|
+
By default inits an empty list.
|
|
118
|
+
|
|
119
|
+
properties : list[str], optional
|
|
120
|
+
List contains property names of a class.
|
|
121
|
+
By default inits an empty list.
|
|
122
|
+
|
|
123
|
+
attributes : list[str], optional
|
|
124
|
+
List contains attributes of a class instance.
|
|
125
|
+
By default inits an empty list.
|
|
126
|
+
|
|
127
|
+
classattributes : list[str], optional
|
|
128
|
+
List contains attributes of a class.
|
|
129
|
+
By default inits an empty list.
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
Available formats
|
|
133
|
+
-----------------
|
|
134
|
+
Alternative format to use with: ``format(<obj>, <format_spec>)``
|
|
135
|
+
- ``s``/``short`` (This hides attributes with empty value)
|
|
136
|
+
"""
|
|
137
|
+
|
|
138
|
+
methods: list[str] = field(default_factory=list)
|
|
139
|
+
classmethods: list[str] = field(default_factory=list)
|
|
140
|
+
staticmethods: list[str] = field(default_factory=list)
|
|
141
|
+
properties: list[str] = field(default_factory=list)
|
|
142
|
+
attributes: list[str] = field(default_factory=list)
|
|
143
|
+
classattributes: list[str] = field(default_factory=list)
|
|
144
|
+
|
|
145
|
+
def __format__(self, format_spec: str) -> str:
|
|
146
|
+
"""
|
|
147
|
+
Available format:
|
|
148
|
+
- ``s``/``short`` (This hides attributes with empty value)
|
|
149
|
+
|
|
150
|
+
Parameters
|
|
151
|
+
----------
|
|
152
|
+
format_spec : str
|
|
153
|
+
Format spec
|
|
154
|
+
|
|
155
|
+
Returns
|
|
156
|
+
-------
|
|
157
|
+
str
|
|
158
|
+
Alternative text representation of this class.
|
|
159
|
+
"""
|
|
160
|
+
|
|
161
|
+
fmt = format_spec.lower().strip()
|
|
162
|
+
cls_name = self.__class__.__name__
|
|
163
|
+
|
|
164
|
+
if fmt == "short" or fmt == "s":
|
|
165
|
+
out = []
|
|
166
|
+
sep = ", "
|
|
167
|
+
for x in self._get_fields():
|
|
168
|
+
if len(getattr(self, x)) > 0:
|
|
169
|
+
out.append(f"{x}={repr(getattr(self, x))}")
|
|
170
|
+
return f"{cls_name}({sep.join(out)})"
|
|
171
|
+
|
|
172
|
+
return self.__repr__()
|
|
173
|
+
|
|
174
|
+
def is_empty(self) -> bool:
|
|
175
|
+
"""
|
|
176
|
+
Checks if all fields are empty.
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
Example:
|
|
180
|
+
--------
|
|
181
|
+
>>> ClassMembers().is_empty()
|
|
182
|
+
True
|
|
183
|
+
|
|
184
|
+
>>> ClassMembers(["a"]).is_empty()
|
|
185
|
+
False
|
|
186
|
+
"""
|
|
187
|
+
|
|
188
|
+
# return all(len(getattr(self, x)) == 0 for x in self._get_fields())
|
|
189
|
+
for x in self._get_fields():
|
|
190
|
+
if len(getattr(self, x)) > 0:
|
|
191
|
+
return False
|
|
192
|
+
return True
|
|
193
|
+
|
|
194
|
+
def pack(
|
|
195
|
+
self,
|
|
196
|
+
include_method: bool = True,
|
|
197
|
+
include_classmethod: bool = True,
|
|
198
|
+
classmethod_indicator: str = "<cls>",
|
|
199
|
+
include_staticmethod: bool = True,
|
|
200
|
+
staticmethod_indicator: str = "<stc>",
|
|
201
|
+
include_attribute: bool = True,
|
|
202
|
+
include_classattribute: bool = True,
|
|
203
|
+
classattribute_indicator: str = "<cls>",
|
|
204
|
+
) -> Self:
|
|
205
|
+
"""
|
|
206
|
+
Combines the following into one list:
|
|
207
|
+
- methods, classmethods, and staticmethods
|
|
208
|
+
- attributes and class attributes
|
|
209
|
+
|
|
210
|
+
Parameters
|
|
211
|
+
----------
|
|
212
|
+
include_method : bool, optional
|
|
213
|
+
Whether to include methods in the output, by default ``True``
|
|
214
|
+
|
|
215
|
+
include_classmethod : bool, optional
|
|
216
|
+
Whether to include classmethods in the output, by default ``True``
|
|
217
|
+
|
|
218
|
+
classmethod_indicator : str, optional
|
|
219
|
+
A string used to mark classmethod in the output. This string is appended
|
|
220
|
+
to the name of each classmethod to visually differentiate it from regular
|
|
221
|
+
instance methods, by default ``"<cls>"``
|
|
222
|
+
|
|
223
|
+
include_staticmethod : bool, optional
|
|
224
|
+
Whether to include staticmethods in the output, by default ``True``
|
|
225
|
+
|
|
226
|
+
staticmethod_indicator : str, optional
|
|
227
|
+
A string used to mark staticmethod in the output. This string is appended
|
|
228
|
+
to the name of each staticmethod to visually differentiate it from regular
|
|
229
|
+
instance methods, by default ``"<stc>"``
|
|
230
|
+
|
|
231
|
+
include_attribute : bool, optional
|
|
232
|
+
Whether to include attributes in the output, by default ``True``
|
|
233
|
+
|
|
234
|
+
include_classattribute : bool, optional
|
|
235
|
+
Whether to include class attributes in the output, by default ``True``
|
|
236
|
+
|
|
237
|
+
classattribute_indicator : str, optional
|
|
238
|
+
A string used to mark class attribute in the output. This string is appended
|
|
239
|
+
to the name of each class attribute to visually differentiate it from regular
|
|
240
|
+
instance attributes, by default ``"<cls>"``
|
|
241
|
+
|
|
242
|
+
Returns
|
|
243
|
+
-------
|
|
244
|
+
Self
|
|
245
|
+
ClassMembers (combined methods)
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
Example:
|
|
249
|
+
--------
|
|
250
|
+
>>> ClassMembers(["a"], ["b"], ["c"], ["d"], ["e"], ["f"]).pack().__format__("short")
|
|
251
|
+
ClassMembers(methods=['a', 'b <cls>', 'c <stc>'], properties=['d'], attributes=['e', 'f <cls>'])
|
|
252
|
+
"""
|
|
253
|
+
|
|
254
|
+
new_methods_list: list[str] = []
|
|
255
|
+
|
|
256
|
+
# Method
|
|
257
|
+
if include_method:
|
|
258
|
+
new_methods_list.extend(self.methods)
|
|
259
|
+
|
|
260
|
+
# Classmethod
|
|
261
|
+
if include_classmethod:
|
|
262
|
+
new_methods_list.extend(
|
|
263
|
+
[f"{x} {classmethod_indicator}".strip() for x in self.classmethods]
|
|
264
|
+
)
|
|
265
|
+
|
|
266
|
+
# Staticmethod
|
|
267
|
+
if include_staticmethod:
|
|
268
|
+
new_methods_list.extend(
|
|
269
|
+
[f"{x} {staticmethod_indicator}".strip() for x in self.staticmethods]
|
|
270
|
+
)
|
|
271
|
+
|
|
272
|
+
new_attributes_list: list[str] = []
|
|
273
|
+
|
|
274
|
+
# Attribute
|
|
275
|
+
if include_attribute:
|
|
276
|
+
new_attributes_list.extend(self.attributes)
|
|
277
|
+
|
|
278
|
+
# Class attribute
|
|
279
|
+
if include_classattribute:
|
|
280
|
+
new_attributes_list.extend(
|
|
281
|
+
[
|
|
282
|
+
f"{x} {classattribute_indicator}".strip()
|
|
283
|
+
for x in self.classattributes
|
|
284
|
+
]
|
|
285
|
+
)
|
|
286
|
+
|
|
287
|
+
# return self.__class__(new_methods_list, [], [], self.properties, [])
|
|
288
|
+
return self.__class__(
|
|
289
|
+
methods=new_methods_list,
|
|
290
|
+
properties=self.properties,
|
|
291
|
+
attributes=new_attributes_list,
|
|
292
|
+
)
|
|
293
|
+
|
|
294
|
+
def sort(self, reverse: bool = False) -> Self:
|
|
295
|
+
"""
|
|
296
|
+
Sorts every element in each method list.
|
|
297
|
+
|
|
298
|
+
Parameters
|
|
299
|
+
----------
|
|
300
|
+
reverse : bool, optional
|
|
301
|
+
Descending order, by default ``False``
|
|
302
|
+
|
|
303
|
+
Returns
|
|
304
|
+
-------
|
|
305
|
+
Self
|
|
306
|
+
Self with sorted values.
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
Example:
|
|
310
|
+
--------
|
|
311
|
+
>>> ClassMembers(["b", "a"], ["d", "c"], ["f", "e"]).sort().__format__("short")
|
|
312
|
+
ClassMembers(methods=['a', 'b'], classmethods=['c', 'd'], staticmethods=['e', 'f'])
|
|
313
|
+
|
|
314
|
+
>>> ClassMembers(["b", "a"], ["d", "c"], ["f", "e"]).pack().sort().__format__("short")
|
|
315
|
+
ClassMembers(methods=['a', 'b', 'c <cls>', 'd <cls>', 'e <stc>', 'f <stc>'])
|
|
316
|
+
"""
|
|
317
|
+
|
|
318
|
+
sorted_vals: list[list[str]] = [
|
|
319
|
+
sorted(getattr(self, field), reverse=reverse)
|
|
320
|
+
for field in self._get_fields()
|
|
321
|
+
]
|
|
322
|
+
|
|
323
|
+
return self.__class__(*sorted_vals)
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
# @versionadded("5.5.0")
|
|
327
|
+
class ClassMembersResult(dict[str, ClassMembers]):
|
|
328
|
+
"""
|
|
329
|
+
All members of a class and its parent classes.
|
|
330
|
+
"""
|
|
331
|
+
|
|
332
|
+
_LINELENGTH: ClassVar[int] = 88
|
|
333
|
+
|
|
334
|
+
def __format__(self, format_spec: str) -> str:
|
|
335
|
+
"""
|
|
336
|
+
Available format:
|
|
337
|
+
- ``s``/``short`` (This hides attributes with empty value)
|
|
338
|
+
|
|
339
|
+
Parameters
|
|
340
|
+
----------
|
|
341
|
+
format_spec : str
|
|
342
|
+
Format spec
|
|
343
|
+
|
|
344
|
+
Returns
|
|
345
|
+
-------
|
|
346
|
+
str
|
|
347
|
+
Alternative text representation of this class.
|
|
348
|
+
"""
|
|
349
|
+
|
|
350
|
+
fmt = format_spec.lower().strip()
|
|
351
|
+
|
|
352
|
+
if fmt == "short" or fmt == "s":
|
|
353
|
+
# out = {}
|
|
354
|
+
# for name, member in self.items():
|
|
355
|
+
# out[name] = format(member, fmt)
|
|
356
|
+
# return repr(out)
|
|
357
|
+
out = []
|
|
358
|
+
sep = ", "
|
|
359
|
+
for idx, (name, member) in enumerate(self.items()):
|
|
360
|
+
mem = format(member, fmt)
|
|
361
|
+
base = f"{repr(name)}: {mem}"
|
|
362
|
+
|
|
363
|
+
# if idx == 0:
|
|
364
|
+
# out.append("{" + base)
|
|
365
|
+
# elif idx + 1 == len(self.items()):
|
|
366
|
+
# out.append(base + "}")
|
|
367
|
+
# else:
|
|
368
|
+
# out.append(base)
|
|
369
|
+
out.append(
|
|
370
|
+
("{" if idx == 0 else "")
|
|
371
|
+
+ base
|
|
372
|
+
+ ("}" if idx + 1 == len(self.items()) else "")
|
|
373
|
+
)
|
|
374
|
+
|
|
375
|
+
return sep.join(out)
|
|
376
|
+
|
|
377
|
+
return self.__repr__()
|
|
378
|
+
|
|
379
|
+
def _merge_value(
|
|
380
|
+
self,
|
|
381
|
+
value_name: Literal[
|
|
382
|
+
"methods",
|
|
383
|
+
"classmethods",
|
|
384
|
+
"staticmethods",
|
|
385
|
+
"properties",
|
|
386
|
+
"attributes",
|
|
387
|
+
"classattributes",
|
|
388
|
+
],
|
|
389
|
+
) -> list[str]:
|
|
390
|
+
"""
|
|
391
|
+
Merge all specified values from the dictionary.
|
|
392
|
+
|
|
393
|
+
Parameters
|
|
394
|
+
----------
|
|
395
|
+
value_name : Literal["methods", "classmethods", "staticmethods", "properties", "attributes", "classattributes"]
|
|
396
|
+
The type of value to merge.
|
|
397
|
+
|
|
398
|
+
Returns
|
|
399
|
+
-------
|
|
400
|
+
list[str]
|
|
401
|
+
A list of merged values.
|
|
402
|
+
"""
|
|
403
|
+
|
|
404
|
+
merged = []
|
|
405
|
+
for _, member in self.items():
|
|
406
|
+
if value_name in member._get_fields():
|
|
407
|
+
merged.extend(getattr(member, value_name))
|
|
408
|
+
return merged
|
|
409
|
+
|
|
410
|
+
def flatten_value(self, sort: bool = True) -> ClassMembers:
|
|
411
|
+
"""
|
|
412
|
+
Merge all attributes of ``dict``'s values into one ``ClassMembers``.
|
|
413
|
+
|
|
414
|
+
Parameters
|
|
415
|
+
----------
|
|
416
|
+
sort : bool
|
|
417
|
+
Sort value in ascending order after flatten, by default ``True``
|
|
418
|
+
|
|
419
|
+
Returns
|
|
420
|
+
-------
|
|
421
|
+
ClassMembers
|
|
422
|
+
Flattened value.
|
|
423
|
+
|
|
424
|
+
|
|
425
|
+
Example:
|
|
426
|
+
--------
|
|
427
|
+
>>> test = ClassMembersResult(
|
|
428
|
+
... ABC=ClassMembers(["a"], ["b"], ["c"], ["d"], ["y"], ["x"]),
|
|
429
|
+
... DEF=ClassMembers(["e"], ["f"], ["g"], ["h"], ["w"], ["z"]),
|
|
430
|
+
... )
|
|
431
|
+
>>> test.flatten_value()
|
|
432
|
+
ClassMembers(
|
|
433
|
+
methods=["a", "e"],
|
|
434
|
+
classmethods=["b", "f"],
|
|
435
|
+
staticmethods=["c", "g"],
|
|
436
|
+
properties=["d", "h"],
|
|
437
|
+
attributes=["w", "y"],
|
|
438
|
+
classattributes=["x", "z"],
|
|
439
|
+
)
|
|
440
|
+
"""
|
|
441
|
+
|
|
442
|
+
res: list[list[str]] = []
|
|
443
|
+
# for x in [
|
|
444
|
+
# "methods",
|
|
445
|
+
# "classmethods",
|
|
446
|
+
# "staticmethods",
|
|
447
|
+
# "properties",
|
|
448
|
+
# "attributes",
|
|
449
|
+
# "classattributes",
|
|
450
|
+
# ]:
|
|
451
|
+
for x in ClassMembers._get_fields():
|
|
452
|
+
res.append(self._merge_value(x)) # type: ignore
|
|
453
|
+
return ClassMembers(*res).sort() if sort else ClassMembers(*res)
|
|
454
|
+
|
|
455
|
+
def pack_value(
|
|
456
|
+
self,
|
|
457
|
+
include_method: bool = True,
|
|
54
458
|
include_classmethod: bool = True,
|
|
55
459
|
classmethod_indicator: str = "<classmethod>",
|
|
56
460
|
include_staticmethod: bool = True,
|
|
57
461
|
staticmethod_indicator: str = "<staticmethod>",
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
462
|
+
include_attribute: bool = True,
|
|
463
|
+
include_classattribute: bool = True,
|
|
464
|
+
classattribute_indicator: str = "<cls>",
|
|
465
|
+
) -> Self:
|
|
61
466
|
"""
|
|
62
|
-
|
|
63
|
-
including the class in which they are defined in alphabetical order.
|
|
467
|
+
Join members into one list for each value.
|
|
64
468
|
|
|
65
469
|
Parameters
|
|
66
470
|
----------
|
|
471
|
+
include_method : bool, optional
|
|
472
|
+
Whether to include method in the output, by default ``True``
|
|
473
|
+
|
|
67
474
|
include_classmethod : bool, optional
|
|
68
475
|
Whether to include classmethod in the output, by default ``True``
|
|
69
476
|
|
|
@@ -80,52 +487,394 @@ class ShowAllMethodsMixin:
|
|
|
80
487
|
to the name of each staticmethod to visually differentiate it from regular
|
|
81
488
|
instance methods, by default ``"<staticmethod>"``
|
|
82
489
|
|
|
83
|
-
|
|
84
|
-
Whether to include
|
|
490
|
+
include_attribute : bool, optional
|
|
491
|
+
Whether to include attributes in the output, by default ``True``
|
|
85
492
|
|
|
86
|
-
|
|
87
|
-
|
|
493
|
+
include_classattribute : bool, optional
|
|
494
|
+
Whether to include class attributes in the output, by default ``True``
|
|
495
|
+
|
|
496
|
+
classattribute_indicator : str, optional
|
|
497
|
+
A string used to mark class attribute in the output. This string is appended
|
|
498
|
+
to the name of each class attribute to visually differentiate it from regular
|
|
499
|
+
instance attributes, by default ``"<cls>"``
|
|
500
|
+
|
|
501
|
+
Returns
|
|
502
|
+
-------
|
|
503
|
+
Self
|
|
504
|
+
ClassMembersResult with packed value.
|
|
505
|
+
|
|
506
|
+
|
|
507
|
+
Example:
|
|
508
|
+
--------
|
|
509
|
+
>>> test = ClassMembersResult(
|
|
510
|
+
... ABC=ClassMembers(["a"], ["b"], ["c"], ["d"], ["y"], ["x"]),
|
|
511
|
+
... DEF=ClassMembers(["e"], ["f"], ["g"], ["h"], ["w"], ["z"]),
|
|
512
|
+
... )
|
|
513
|
+
>>> test.pack_value()
|
|
514
|
+
{
|
|
515
|
+
"ABC": ClassMembers(
|
|
516
|
+
methods=["a", "b <classmethod>", "c <staticmethod>"],
|
|
517
|
+
classmethods=[],
|
|
518
|
+
staticmethods=[],
|
|
519
|
+
properties=["d"],
|
|
520
|
+
attributes=["y", "x <cls>"],
|
|
521
|
+
classattributes=[],
|
|
522
|
+
),
|
|
523
|
+
"DEF": ClassMembers(
|
|
524
|
+
methods=["e", "f <classmethod>", "g <staticmethod>"],
|
|
525
|
+
classmethods=[],
|
|
526
|
+
staticmethods=[],
|
|
527
|
+
properties=["h"],
|
|
528
|
+
attributes=["w", "z <cls>"],
|
|
529
|
+
classattributes=[],
|
|
530
|
+
),
|
|
531
|
+
}
|
|
532
|
+
"""
|
|
533
|
+
|
|
534
|
+
for class_name, members in self.items():
|
|
535
|
+
self[class_name] = members.pack(
|
|
536
|
+
include_method=include_method,
|
|
537
|
+
include_classmethod=include_classmethod,
|
|
538
|
+
classmethod_indicator=classmethod_indicator,
|
|
539
|
+
include_staticmethod=include_staticmethod,
|
|
540
|
+
staticmethod_indicator=staticmethod_indicator,
|
|
541
|
+
include_attribute=include_attribute,
|
|
542
|
+
include_classattribute=include_classattribute,
|
|
543
|
+
classattribute_indicator=classattribute_indicator,
|
|
544
|
+
)
|
|
545
|
+
return self
|
|
546
|
+
|
|
547
|
+
def prioritize_value(
|
|
548
|
+
self,
|
|
549
|
+
value_name: Literal[
|
|
550
|
+
"methods",
|
|
551
|
+
"classmethods",
|
|
552
|
+
"staticmethods",
|
|
553
|
+
"properties",
|
|
554
|
+
"attributes",
|
|
555
|
+
"classattributes",
|
|
556
|
+
] = "methods",
|
|
557
|
+
) -> dict[str, list[str]]:
|
|
558
|
+
"""
|
|
559
|
+
Prioritize which field of value to show.
|
|
560
|
+
|
|
561
|
+
Parameters
|
|
562
|
+
----------
|
|
563
|
+
value_name : Literal["methods", "classmethods", "staticmethods", "properties", "attributes", "classattributes"], optional
|
|
564
|
+
The type of value to prioritize, by default ``"methods"``
|
|
88
565
|
|
|
89
566
|
Returns
|
|
90
567
|
-------
|
|
91
568
|
dict[str, list[str]]
|
|
92
|
-
A dictionary
|
|
569
|
+
A dictionary with prioritized values.
|
|
570
|
+
|
|
571
|
+
|
|
572
|
+
Example:
|
|
573
|
+
--------
|
|
574
|
+
>>> test = ClassMembersResult(
|
|
575
|
+
... ABC=ClassMembers(["a"], ["b"], ["c"], ["d"]),
|
|
576
|
+
... DEF=ClassMembers(["e"], ["f"], ["g"], ["h"]),
|
|
577
|
+
... )
|
|
578
|
+
>>> test.prioritize_value("methods")
|
|
579
|
+
{'ABC': ['a'], 'DEF': ['e']}
|
|
580
|
+
>>> test.prioritize_value("classmethods")
|
|
581
|
+
{'ABC': ['b'], 'DEF': ['f']}
|
|
582
|
+
>>> test.prioritize_value("staticmethods")
|
|
583
|
+
{'ABC': ['c'], 'DEF': ['g']}
|
|
584
|
+
>>> test.prioritize_value("properties")
|
|
585
|
+
{'ABC': ['d'], 'DEF': ['h']}
|
|
586
|
+
"""
|
|
587
|
+
|
|
588
|
+
result: dict[str, list[str]] = {}
|
|
589
|
+
for name, member in self.items():
|
|
590
|
+
result[name] = getattr(member, value_name, member.methods)
|
|
591
|
+
return result
|
|
592
|
+
|
|
593
|
+
def print_output(
|
|
594
|
+
self,
|
|
595
|
+
where_to_print: Literal["methods", "properties", "attributes"] = "methods",
|
|
596
|
+
print_in_one_column: bool = False,
|
|
597
|
+
) -> None:
|
|
598
|
+
"""
|
|
599
|
+
Beautifully print the result.
|
|
600
|
+
|
|
601
|
+
*This method is deprecated.*
|
|
602
|
+
|
|
603
|
+
Parameters
|
|
604
|
+
----------
|
|
605
|
+
where_to_print : Literal["methods", "properties", "attributes"], optional
|
|
606
|
+
Whether to print ``methods`` or ``properties``, by default ``"methods"``
|
|
607
|
+
|
|
608
|
+
print_in_one_column : bool, optional
|
|
609
|
+
Whether to print in one column, by default ``False``
|
|
610
|
+
"""
|
|
611
|
+
|
|
612
|
+
print_func = print # Can be extended with function parameter
|
|
613
|
+
|
|
614
|
+
# Loop through each class base
|
|
615
|
+
for order, (class_base, member) in enumerate(self.items(), start=1):
|
|
616
|
+
methods: list[str] = getattr(member, where_to_print, member.methods)
|
|
617
|
+
mlen = len(methods) # How many methods in that class
|
|
618
|
+
if mlen == 0:
|
|
619
|
+
continue
|
|
620
|
+
print_func(f"{order:02}. <{class_base}> | len: {mlen:02}")
|
|
621
|
+
|
|
622
|
+
# Modify methods list
|
|
623
|
+
max_method_name_len = max([len(x) for x in methods])
|
|
624
|
+
if mlen % 2 == 0:
|
|
625
|
+
p1, p2 = methods[: int(mlen / 2)], methods[int(mlen / 2) :]
|
|
626
|
+
else:
|
|
627
|
+
p1, p2 = methods[: int(mlen / 2) + 1], methods[int(mlen / 2) + 1 :]
|
|
628
|
+
p2.append("")
|
|
629
|
+
new_methods = list(zip(p1, p2))
|
|
630
|
+
|
|
631
|
+
# print
|
|
632
|
+
if print_in_one_column:
|
|
633
|
+
# This print 1 method in one line
|
|
634
|
+
for name in methods:
|
|
635
|
+
print(f" - {name.ljust(max_method_name_len)}")
|
|
636
|
+
else:
|
|
637
|
+
# This print 2 methods in 1 line
|
|
638
|
+
for x1, x2 in new_methods:
|
|
639
|
+
if x2 == "":
|
|
640
|
+
print_func(f" - {x1.ljust(max_method_name_len)}")
|
|
641
|
+
else:
|
|
642
|
+
print_func(
|
|
643
|
+
f" - {x1.ljust(max_method_name_len)} - {x2.ljust(max_method_name_len)}"
|
|
644
|
+
)
|
|
645
|
+
|
|
646
|
+
print_func("".ljust(self._LINELENGTH, "-"))
|
|
647
|
+
|
|
648
|
+
|
|
649
|
+
# Mixins
|
|
650
|
+
# ---------------------------------------------------------------------------
|
|
651
|
+
class GetClassMembersMixin:
|
|
652
|
+
"""
|
|
653
|
+
Show all methods of the class and its parent class minus ``object`` class
|
|
654
|
+
|
|
655
|
+
*This class is meant to be used with other class*
|
|
656
|
+
|
|
657
|
+
|
|
658
|
+
Example:
|
|
659
|
+
--------
|
|
660
|
+
>>> class TestClass(GetClassMembersMixin):
|
|
661
|
+
... def method1(self): ...
|
|
662
|
+
>>> TestClass._get_members(dunder=False)
|
|
663
|
+
{
|
|
664
|
+
'GetClassMembersMixin': ClassMembers(
|
|
665
|
+
methods=[],
|
|
666
|
+
classmethods=['_get_members', ..., 'show_all_methods', 'show_all_properties'],
|
|
667
|
+
staticmethods=[],
|
|
668
|
+
properties=[],
|
|
669
|
+
attributes=[],
|
|
670
|
+
classattributes=[]
|
|
671
|
+
),
|
|
672
|
+
'TestClass': ClassMembers(
|
|
673
|
+
methods=['method1'],
|
|
674
|
+
classmethods=[],
|
|
675
|
+
staticmethods=[],
|
|
676
|
+
properties=[],
|
|
677
|
+
attributes=[],
|
|
678
|
+
classattributes=[]
|
|
679
|
+
)
|
|
680
|
+
}
|
|
681
|
+
"""
|
|
682
|
+
|
|
683
|
+
# @versionadded("5.5.0")
|
|
684
|
+
@classmethod
|
|
685
|
+
def _get_members(
|
|
686
|
+
cls,
|
|
687
|
+
dunder: bool = True,
|
|
688
|
+
underscore: bool = True,
|
|
689
|
+
private: bool = True,
|
|
690
|
+
) -> ClassMembersResult:
|
|
691
|
+
"""
|
|
692
|
+
Class method to get all methods, properties, and attributes
|
|
693
|
+
of the class and its parent classes.
|
|
694
|
+
|
|
695
|
+
Parameters
|
|
696
|
+
----------
|
|
697
|
+
dunder : bool, optional
|
|
698
|
+
Whether to include attribute with ``__`` (dunder)
|
|
699
|
+
in the output, by default ``True``
|
|
700
|
+
|
|
701
|
+
underscore : bool, optional
|
|
702
|
+
Whether to include attribute starts with ``_``
|
|
703
|
+
in the output (will also skip dunder), by default ``True``
|
|
704
|
+
|
|
705
|
+
private : bool, optional
|
|
706
|
+
Whether to include private attribute
|
|
707
|
+
(``_<__class__.__name__>__<attribute>``)
|
|
708
|
+
in the output, by default ``True``
|
|
709
|
+
|
|
710
|
+
Returns
|
|
711
|
+
-------
|
|
712
|
+
ClassMembersResult
|
|
713
|
+
A dictionary where keys are class names
|
|
714
|
+
and values are tuples of method names and properties.
|
|
93
715
|
"""
|
|
94
|
-
|
|
95
|
-
|
|
716
|
+
|
|
717
|
+
# MRO in reverse order
|
|
718
|
+
classes = cls.__mro__[::-1]
|
|
719
|
+
result: dict[str, ClassMembers] = {}
|
|
720
|
+
|
|
721
|
+
# For each class base in classes
|
|
96
722
|
for base in classes:
|
|
97
|
-
methods = []
|
|
723
|
+
methods: list[str] = []
|
|
724
|
+
classmethods: list[str] = []
|
|
725
|
+
staticmethods: list[str] = []
|
|
726
|
+
properties: list[str] = []
|
|
727
|
+
classattributes: list[str] = []
|
|
728
|
+
|
|
729
|
+
# Dict items of base
|
|
98
730
|
for name, attr in base.__dict__.items():
|
|
731
|
+
# Skip dunder
|
|
732
|
+
if name.startswith("__") and not dunder:
|
|
733
|
+
continue
|
|
734
|
+
|
|
99
735
|
# Skip private attribute
|
|
100
|
-
if name
|
|
736
|
+
if base.__name__ in name and not private:
|
|
101
737
|
continue
|
|
102
738
|
|
|
103
|
-
# Skip
|
|
104
|
-
if
|
|
739
|
+
# Skip underscore
|
|
740
|
+
if (
|
|
741
|
+
name.startswith("_")
|
|
742
|
+
and not underscore
|
|
743
|
+
and base.__name__ not in name
|
|
744
|
+
):
|
|
105
745
|
continue
|
|
106
746
|
|
|
107
|
-
#
|
|
747
|
+
# Methods
|
|
108
748
|
if callable(attr):
|
|
109
749
|
if isinstance(attr, staticmethod):
|
|
110
|
-
|
|
111
|
-
methods.append(f"{name} {staticmethod_indicator}")
|
|
750
|
+
staticmethods.append(name)
|
|
112
751
|
else:
|
|
113
752
|
methods.append(name)
|
|
114
|
-
|
|
115
|
-
|
|
753
|
+
elif isinstance(attr, classmethod):
|
|
754
|
+
classmethods.append(name)
|
|
116
755
|
|
|
117
|
-
|
|
118
|
-
|
|
756
|
+
# Property
|
|
757
|
+
elif isinstance(attr, property):
|
|
758
|
+
properties.append(name)
|
|
119
759
|
|
|
120
|
-
|
|
121
|
-
|
|
760
|
+
# Class attribute
|
|
761
|
+
else:
|
|
762
|
+
classattributes.append(name)
|
|
122
763
|
|
|
123
|
-
|
|
764
|
+
# Save to result
|
|
765
|
+
result[base.__name__] = ClassMembers(
|
|
766
|
+
methods=methods,
|
|
767
|
+
classmethods=classmethods,
|
|
768
|
+
staticmethods=staticmethods,
|
|
769
|
+
properties=properties,
|
|
770
|
+
classattributes=classattributes,
|
|
771
|
+
).sort()
|
|
772
|
+
|
|
773
|
+
return ClassMembersResult(result)
|
|
774
|
+
|
|
775
|
+
# @versionadded("5.5.0")
|
|
776
|
+
def _get_attributes(
|
|
777
|
+
self,
|
|
778
|
+
underscore: bool = True,
|
|
779
|
+
private: bool = True,
|
|
780
|
+
) -> list[str]:
|
|
781
|
+
"""
|
|
782
|
+
Get all attributes of the class instance.
|
|
783
|
+
|
|
784
|
+
Parameters
|
|
785
|
+
----------
|
|
786
|
+
underscore : bool, optional
|
|
787
|
+
Whether to include attribute starts with ``_``
|
|
788
|
+
in the output, by default ``True``
|
|
789
|
+
|
|
790
|
+
private : bool, optional
|
|
791
|
+
Whether to include private attribute
|
|
792
|
+
(``_<__class__.__name__>__<attribute>``)
|
|
793
|
+
in the output, by default ``True``
|
|
794
|
+
|
|
795
|
+
Returns
|
|
796
|
+
-------
|
|
797
|
+
list[str]
|
|
798
|
+
A list contains attributes.
|
|
799
|
+
"""
|
|
800
|
+
|
|
801
|
+
# Default output
|
|
802
|
+
out: list[str] = []
|
|
803
|
+
|
|
804
|
+
# Get attributes
|
|
805
|
+
cls_dict: dict[str, Any] | None = getattr(self, "__dict__", None)
|
|
806
|
+
cls_slots: tuple[str, ...] | None = getattr(self, "__slots__", None)
|
|
807
|
+
cls_name: str = self.__class__.__name__
|
|
808
|
+
|
|
809
|
+
def _is_valid(item_name: str) -> bool:
|
|
810
|
+
if not item_name.startswith("_"):
|
|
811
|
+
return True
|
|
812
|
+
if cls_name in item_name and private:
|
|
813
|
+
return True
|
|
814
|
+
if underscore and cls_name not in item_name:
|
|
815
|
+
return True
|
|
816
|
+
return False
|
|
817
|
+
|
|
818
|
+
# Check if __dict__ exist and len(__dict__) > 0
|
|
819
|
+
if cls_dict is not None and len(cls_dict) > 0:
|
|
820
|
+
# out = [x for x in self.__dict__ if _is_valid(x)]
|
|
821
|
+
out.extend(list(cls_dict))
|
|
822
|
+
|
|
823
|
+
# Check if __slots__ exist and len(__slots__) > 0
|
|
824
|
+
if cls_slots is not None and len(cls_slots) > 0:
|
|
825
|
+
# Convert __<attribute> to _<self.__class__.__name__>__<attribute>
|
|
826
|
+
_slot = [f"_{cls_name}{x}" if x.startswith("__") else x for x in cls_slots]
|
|
827
|
+
out.extend(_slot)
|
|
828
|
+
|
|
829
|
+
return [x for x in out if _is_valid(x)]
|
|
830
|
+
|
|
831
|
+
# @versionadded("5.5.0")
|
|
832
|
+
def get_members(
|
|
833
|
+
self,
|
|
834
|
+
dunder: bool = False,
|
|
835
|
+
underscore: bool = True,
|
|
836
|
+
private: bool = True,
|
|
837
|
+
) -> ClassMembersResult:
|
|
838
|
+
"""
|
|
839
|
+
Get all members of a class instance.
|
|
840
|
+
|
|
841
|
+
Parameters
|
|
842
|
+
----------
|
|
843
|
+
dunder : bool, optional
|
|
844
|
+
Whether to include attribute with ``__`` (dunder)
|
|
845
|
+
in the output, by default ``False``
|
|
846
|
+
|
|
847
|
+
underscore : bool, optional
|
|
848
|
+
Whether to include attribute starts with ``_``
|
|
849
|
+
in the output (will also skip dunder), by default ``True``
|
|
850
|
+
|
|
851
|
+
private : bool, optional
|
|
852
|
+
Whether to include private attribute
|
|
853
|
+
(``_<__class__.__name__>__<attribute>``)
|
|
854
|
+
in the output, by default ``True``
|
|
855
|
+
|
|
856
|
+
Returns
|
|
857
|
+
-------
|
|
858
|
+
ClassMembersResult
|
|
859
|
+
All member of a class instance.
|
|
860
|
+
"""
|
|
861
|
+
mems = self._get_members(dunder=dunder, underscore=underscore, private=private)
|
|
862
|
+
attrs = self._get_attributes(underscore=underscore, private=private)
|
|
863
|
+
mems[self.__class__.__name__].attributes = attrs
|
|
864
|
+
return mems
|
|
124
865
|
|
|
125
866
|
@classmethod
|
|
126
|
-
def
|
|
867
|
+
def show_all_methods(
|
|
868
|
+
cls,
|
|
869
|
+
print_result: bool = False,
|
|
870
|
+
include_classmethod: bool = True,
|
|
871
|
+
classmethod_indicator: str = "<classmethod>",
|
|
872
|
+
include_staticmethod: bool = True,
|
|
873
|
+
staticmethod_indicator: str = "<staticmethod>",
|
|
874
|
+
include_private_method: bool = False,
|
|
875
|
+
) -> dict[str, list[str]]:
|
|
127
876
|
"""
|
|
128
|
-
Class method to display all
|
|
877
|
+
Class method to display all methods of the class and its parent classes,
|
|
129
878
|
including the class in which they are defined in alphabetical order.
|
|
130
879
|
|
|
131
880
|
Parameters
|
|
@@ -133,71 +882,77 @@ class ShowAllMethodsMixin:
|
|
|
133
882
|
print_result : bool, optional
|
|
134
883
|
Beautifully print the output, by default ``False``
|
|
135
884
|
|
|
885
|
+
include_classmethod : bool, optional
|
|
886
|
+
Whether to include classmethod in the output, by default ``True``
|
|
887
|
+
|
|
888
|
+
classmethod_indicator : str, optional
|
|
889
|
+
A string used to mark classmethod in the output. This string is appended
|
|
890
|
+
to the name of each classmethod to visually differentiate it from regular
|
|
891
|
+
instance methods, by default ``"<classmethod>"``
|
|
892
|
+
|
|
893
|
+
include_staticmethod : bool, optional
|
|
894
|
+
Whether to include staticmethod in the output, by default ``True``
|
|
895
|
+
|
|
896
|
+
staticmethod_indicator : str, optional
|
|
897
|
+
A string used to mark staticmethod in the output. This string is appended
|
|
898
|
+
to the name of each staticmethod to visually differentiate it from regular
|
|
899
|
+
instance methods, by default ``"<staticmethod>"``
|
|
900
|
+
|
|
901
|
+
include_private_method : bool, optional
|
|
902
|
+
Whether to include private method in the output, by default ``False``
|
|
903
|
+
|
|
136
904
|
Returns
|
|
137
905
|
-------
|
|
138
906
|
dict[str, list[str]]
|
|
139
|
-
A dictionary where keys are class names and values are lists of
|
|
907
|
+
A dictionary where keys are class names and values are lists of method names.
|
|
140
908
|
"""
|
|
141
|
-
classes = cls.__mro__[::-1][1:] # MRO in reverse order
|
|
142
|
-
result = {}
|
|
143
|
-
for base in classes:
|
|
144
|
-
properties = []
|
|
145
|
-
for name, attr in base.__dict__.items():
|
|
146
|
-
# Skip private attribute
|
|
147
|
-
if name.startswith("__"):
|
|
148
|
-
continue
|
|
149
909
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
910
|
+
result = cls._get_members(
|
|
911
|
+
dunder=False, private=include_private_method
|
|
912
|
+
).pack_value(
|
|
913
|
+
include_classmethod=include_classmethod,
|
|
914
|
+
classmethod_indicator=classmethod_indicator,
|
|
915
|
+
include_staticmethod=include_staticmethod,
|
|
916
|
+
staticmethod_indicator=staticmethod_indicator,
|
|
917
|
+
)
|
|
155
918
|
|
|
156
919
|
if print_result:
|
|
157
|
-
|
|
920
|
+
result.print_output("methods")
|
|
158
921
|
|
|
159
|
-
return result
|
|
922
|
+
return result.prioritize_value("methods")
|
|
160
923
|
|
|
161
|
-
@
|
|
162
|
-
def
|
|
924
|
+
@classmethod
|
|
925
|
+
def show_all_properties(cls, print_result: bool = False) -> dict[str, list[str]]:
|
|
163
926
|
"""
|
|
164
|
-
|
|
927
|
+
Class method to display all properties of the class and its parent classes,
|
|
928
|
+
including the class in which they are defined in alphabetical order.
|
|
165
929
|
|
|
166
930
|
Parameters
|
|
167
931
|
----------
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
"""
|
|
171
|
-
print_func = print # Can be extended with function parameter
|
|
172
|
-
|
|
173
|
-
# Loop through each class base
|
|
174
|
-
for order, (class_base, methods) in enumerate(result.items(), start=1):
|
|
175
|
-
mlen = len(methods) # How many methods in that class
|
|
176
|
-
print_func(f"{order:02}. <{class_base}> | len: {mlen:02}")
|
|
932
|
+
print_result : bool, optional
|
|
933
|
+
Beautifully print the output, by default ``False``
|
|
177
934
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
p1, p2 = methods[: int(mlen / 2) + 1], methods[int(mlen / 2) + 1 :]
|
|
184
|
-
p2.append("")
|
|
185
|
-
new_methods = list(zip(p1, p2))
|
|
935
|
+
Returns
|
|
936
|
+
-------
|
|
937
|
+
dict[str, list[str]]
|
|
938
|
+
A dictionary where keys are class names and values are lists of property names.
|
|
939
|
+
"""
|
|
186
940
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
941
|
+
# result = cls.get_methods_and_properties().prioritize_value("properties")
|
|
942
|
+
result = ClassMembersResult(
|
|
943
|
+
{
|
|
944
|
+
cls.__name__: ClassMembers(
|
|
945
|
+
properties=cls._get_members(dunder=False)
|
|
946
|
+
.flatten_value()
|
|
947
|
+
.properties,
|
|
948
|
+
)
|
|
949
|
+
}
|
|
950
|
+
)
|
|
195
951
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
# print(f" - {name.ljust(max_method_name_len)}")
|
|
952
|
+
if print_result:
|
|
953
|
+
result.print_output("properties")
|
|
199
954
|
|
|
200
|
-
|
|
955
|
+
return result.prioritize_value("properties")
|
|
201
956
|
|
|
202
957
|
|
|
203
958
|
class AutoREPRMixin:
|
|
@@ -257,19 +1012,38 @@ class AutoREPRMixin:
|
|
|
257
1012
|
return f"{self.__class__.__name__}({sep.join(out)})"
|
|
258
1013
|
|
|
259
1014
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
1015
|
+
class AddFormatMixin:
|
|
1016
|
+
"""
|
|
1017
|
+
This mixin that allows classes to define and register custom format
|
|
1018
|
+
specifications for use with Python's built-in :func:`format` function
|
|
1019
|
+
and f-string formatting.
|
|
264
1020
|
|
|
265
|
-
|
|
266
|
-
|
|
1021
|
+
This mixin extends the standard ``__format__`` mechanism by letting you
|
|
1022
|
+
attach named formatting presets at runtime. Each format spec is simply a
|
|
1023
|
+
callable that receives the object instance and returns a formatted string.
|
|
1024
|
+
|
|
1025
|
+
Attribute ``_format_specs`` is used and must not be overwritten in any
|
|
1026
|
+
circumstances.
|
|
1027
|
+
"""
|
|
1028
|
+
|
|
1029
|
+
def __init__(self) -> None:
|
|
1030
|
+
self._format_specs: dict[str, Callable[[Self], str]] = {}
|
|
267
1031
|
|
|
268
1032
|
def __format__(self, format_spec: str) -> str:
|
|
269
1033
|
"""
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
1034
|
+
Format the object using a registered format specification.
|
|
1035
|
+
|
|
1036
|
+
Parameters
|
|
1037
|
+
----------
|
|
1038
|
+
format_spec : str
|
|
1039
|
+
The name of a previously registered format spec. If empty or not
|
|
1040
|
+
found, the object's ``__str__`` representation is returned.
|
|
1041
|
+
|
|
1042
|
+
Returns
|
|
1043
|
+
-------
|
|
1044
|
+
str
|
|
1045
|
+
The formatted string according to the given format spec.
|
|
1046
|
+
|
|
273
1047
|
|
|
274
1048
|
Usage
|
|
275
1049
|
-----
|
|
@@ -278,13 +1052,72 @@ class BaseClass(ShowAllMethodsMixin, AutoREPRMixin):
|
|
|
278
1052
|
>>> print(format(<object>, <format_spec>))
|
|
279
1053
|
"""
|
|
280
1054
|
|
|
281
|
-
|
|
1055
|
+
func = self._format_specs.get(format_spec, None)
|
|
1056
|
+
|
|
1057
|
+
if func is None:
|
|
1058
|
+
return self.__str__()
|
|
1059
|
+
else:
|
|
1060
|
+
return func(self)
|
|
1061
|
+
|
|
1062
|
+
def add_format_spec(self, name: str, format_func: Callable[[Self], str]) -> None:
|
|
1063
|
+
"""
|
|
1064
|
+
Register a custom format specification.
|
|
1065
|
+
|
|
1066
|
+
Parameters
|
|
1067
|
+
----------
|
|
1068
|
+
name : str
|
|
1069
|
+
The format specifier string to register.
|
|
1070
|
+
|
|
1071
|
+
format_func : Callable[[Self], str]
|
|
1072
|
+
A function that receives the object instance and returns a formatted string.
|
|
1073
|
+
"""
|
|
1074
|
+
if getattr(self, "_format_specs", None) is None:
|
|
1075
|
+
self._format_specs: dict[str, Callable[[Self], str]] = {}
|
|
1076
|
+
self._format_specs[name] = format_func
|
|
1077
|
+
|
|
1078
|
+
@property
|
|
1079
|
+
def available_format_spec(self) -> list[str]:
|
|
1080
|
+
"""
|
|
1081
|
+
List all registered format specification names.
|
|
1082
|
+
|
|
1083
|
+
Returns
|
|
1084
|
+
-------
|
|
1085
|
+
list[str]
|
|
1086
|
+
A list containing the names of all format specs that have been
|
|
1087
|
+
registered via :meth:`add_format_spec`. If no format specs exist,
|
|
1088
|
+
an empty list is returned.
|
|
1089
|
+
|
|
1090
|
+
|
|
1091
|
+
Notes
|
|
1092
|
+
-----
|
|
1093
|
+
- This is a convenience property to inspect which formatting presets
|
|
1094
|
+
are currently available for the object.
|
|
1095
|
+
- The list contains only the names (keys), not the formatting functions.
|
|
1096
|
+
"""
|
|
1097
|
+
if getattr(self, "_format_specs", None) is None:
|
|
1098
|
+
return []
|
|
1099
|
+
return list(self._format_specs)
|
|
1100
|
+
|
|
1101
|
+
|
|
1102
|
+
# Class
|
|
1103
|
+
# ---------------------------------------------------------------------------
|
|
1104
|
+
class BaseClass(GetClassMembersMixin, AutoREPRMixin):
|
|
1105
|
+
"""Base class"""
|
|
1106
|
+
|
|
1107
|
+
def __str__(self) -> str:
|
|
1108
|
+
return repr(self)
|
|
282
1109
|
|
|
283
1110
|
|
|
284
1111
|
# Metaclass
|
|
285
1112
|
# ---------------------------------------------------------------------------
|
|
286
1113
|
class PositiveInitArgsMeta(type):
|
|
287
|
-
"""
|
|
1114
|
+
"""
|
|
1115
|
+
Make sure that every args in a class __init__ is positive
|
|
1116
|
+
|
|
1117
|
+
Usage
|
|
1118
|
+
-----
|
|
1119
|
+
>>> class Test(metaclass=PositiveInitArgsMeta): pass
|
|
1120
|
+
"""
|
|
288
1121
|
|
|
289
1122
|
def __call__(cls, *args, **kwargs):
|
|
290
1123
|
# Check if all positional and keyword arguments are positive
|