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
absfuyu/core/baseclass.py CHANGED
@@ -3,8 +3,8 @@ Absfuyu: Core
3
3
  -------------
4
4
  Bases for other features
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 Package
@@ -13,20 +13,22 @@ __all__ = [
13
13
  # Color
14
14
  "CLITextColor",
15
15
  # Support
16
- "MethodNPropertyList",
17
- "MethodNPropertyResult",
16
+ "ClassMembers",
17
+ "ClassMembersResult",
18
18
  # Mixins
19
- "ShowAllMethodsMixin",
19
+ "GetClassMembersMixin",
20
20
  "AutoREPRMixin",
21
21
  # Class
22
22
  "BaseClass",
23
+ "BaseDataclass",
23
24
  # Metaclass
24
25
  "PositiveInitArgsMeta",
25
26
  ]
26
27
 
27
28
  # Library
28
29
  # ---------------------------------------------------------------------------
29
- from typing import ClassVar, Literal, NamedTuple, Self
30
+ from dataclasses import dataclass, field
31
+ from typing import Any, ClassVar, Literal, Self
30
32
 
31
33
 
32
34
  # Color
@@ -46,68 +48,145 @@ class CLITextColor:
46
48
  RESET = "\x1b[39m"
47
49
 
48
50
 
49
- # Mixins
51
+ # Dataclass
52
+ # ---------------------------------------------------------------------------
53
+ # @versionadded("5.5.0")
54
+ @dataclass
55
+ class BaseDataclass:
56
+ """
57
+ Base dataclass.
58
+
59
+ Contains util methods:
60
+ - Get fields
61
+ """
62
+
63
+ @classmethod
64
+ def _get_fields(cls) -> tuple[str, ...]:
65
+ """
66
+ Get dataclass's fields.
67
+
68
+ Returns
69
+ -------
70
+ tuple[str, ...]
71
+ Dataclass's fields.
72
+ """
73
+ _fields = getattr(cls, "__dataclass_fields__", ())
74
+ return tuple(_fields)
75
+
76
+
77
+ # Support
50
78
  # ---------------------------------------------------------------------------
51
- # @versionadded("5.1.0")
52
- class MethodNPropertyList(NamedTuple):
79
+ # @versionadded("5.5.0")
80
+ @dataclass
81
+ class ClassMembers(BaseDataclass):
53
82
  """
54
- Contains lists of methods, classmethods, staticmethods, and properties of a class.
83
+ Contains lists of methods, classmethods, staticmethods,
84
+ properties, and attributes of a class.
55
85
 
56
86
  Parameters
57
87
  ----------
58
- methods : list[str]
88
+ methods : list[str], optional
59
89
  List contains method names of a class.
90
+ By default inits an empty list.
60
91
 
61
- classmethods : list[str]
92
+ classmethods : list[str], optional
62
93
  List contains classmethod names of a class.
94
+ By default inits an empty list.
63
95
 
64
- staticmethods : list[str]
96
+ staticmethods : list[str], optional
65
97
  List contains staticmethod names of a class.
98
+ By default inits an empty list.
66
99
 
67
- properties : list[str]
100
+ properties : list[str], optional
68
101
  List contains property names of a class.
102
+ By default inits an empty list.
103
+
104
+ attributes : list[str], optional
105
+ List contains attributes of a class instance.
106
+ By default inits an empty list.
107
+
108
+ classattributes : list[str], optional
109
+ List contains attributes of a class.
110
+ By default inits an empty list.
111
+
112
+
113
+ Available formats
114
+ -----------------
115
+ Alternative format to use with: ``format(<obj>, <format_spec>)``
116
+ - ``s``/``short`` (This hides attributes with empty value)
69
117
  """
70
118
 
71
- methods: list[str]
72
- classmethods: list[str]
73
- staticmethods: list[str]
74
- properties: list[str]
119
+ methods: list[str] = field(default_factory=list)
120
+ classmethods: list[str] = field(default_factory=list)
121
+ staticmethods: list[str] = field(default_factory=list)
122
+ properties: list[str] = field(default_factory=list)
123
+ attributes: list[str] = field(default_factory=list)
124
+ classattributes: list[str] = field(default_factory=list)
75
125
 
76
- def __repr__(self) -> str:
126
+ def __format__(self, format_spec: str) -> str:
77
127
  """
78
- Only shows list with items in repr
128
+ Available format:
129
+ - ``s``/``short`` (This hides attributes with empty value)
130
+
131
+ Parameters
132
+ ----------
133
+ format_spec : str
134
+ Format spec
79
135
 
80
- *This overwrites ``NamedTuple.__repr__()``*
136
+ Returns
137
+ -------
138
+ str
139
+ Alternative text representation of this class.
81
140
  """
82
- # return super().__repr__()
141
+
142
+ fmt = format_spec.lower().strip()
83
143
  cls_name = self.__class__.__name__
84
- out = []
85
- sep = ", "
86
- for x in self._fields:
87
- if len(getattr(self, x)) > 0:
88
- out.append(f"{x}={repr(getattr(self, x))}")
89
- return f"{cls_name}({sep.join(out)})"
144
+
145
+ if fmt == "short" or fmt == "s":
146
+ out = []
147
+ sep = ", "
148
+ for x in self._get_fields():
149
+ if len(getattr(self, x)) > 0:
150
+ out.append(f"{x}={repr(getattr(self, x))}")
151
+ return f"{cls_name}({sep.join(out)})"
152
+
153
+ return self.__repr__()
90
154
 
91
155
  def is_empty(self) -> bool:
92
156
  """
93
- Checks if all lists (methods, classmethods, staticmethods, properties) are empty.
157
+ Checks if all fields are empty.
158
+
159
+
160
+ Example:
161
+ --------
162
+ >>> ClassMembers().is_empty()
163
+ True
164
+
165
+ >>> ClassMembers(["a"]).is_empty()
166
+ False
94
167
  """
95
- # for x in self:
96
- # if len(x) > 0:
97
- # return False
98
- # return True
99
- return all(len(getattr(self, x)) == 0 for x in self._fields)
168
+
169
+ # return all(len(getattr(self, x)) == 0 for x in self._get_fields())
170
+ for x in self._get_fields():
171
+ if len(getattr(self, x)) > 0:
172
+ return False
173
+ return True
100
174
 
101
175
  def pack(
102
176
  self,
103
177
  include_method: bool = True,
104
178
  include_classmethod: bool = True,
105
- classmethod_indicator: str = "<classmethod>",
179
+ classmethod_indicator: str = "<cls>",
106
180
  include_staticmethod: bool = True,
107
- staticmethod_indicator: str = "<staticmethod>",
181
+ staticmethod_indicator: str = "<stc>",
182
+ include_attribute: bool = True,
183
+ include_classattribute: bool = True,
184
+ classattribute_indicator: str = "<cls>",
108
185
  ) -> Self:
109
186
  """
110
- Combines methods, classmethods, and staticmethods into one list.
187
+ Combines the following into one list:
188
+ - methods, classmethods, and staticmethods
189
+ - attributes and class attributes
111
190
 
112
191
  Parameters
113
192
  ----------
@@ -120,7 +199,7 @@ class MethodNPropertyList(NamedTuple):
120
199
  classmethod_indicator : str, optional
121
200
  A string used to mark classmethod in the output. This string is appended
122
201
  to the name of each classmethod to visually differentiate it from regular
123
- instance methods, by default ``"<classmethod>"``
202
+ instance methods, by default ``"<cls>"``
124
203
 
125
204
  include_staticmethod : bool, optional
126
205
  Whether to include staticmethods in the output, by default ``True``
@@ -128,21 +207,32 @@ class MethodNPropertyList(NamedTuple):
128
207
  staticmethod_indicator : str, optional
129
208
  A string used to mark staticmethod in the output. This string is appended
130
209
  to the name of each staticmethod to visually differentiate it from regular
131
- instance methods, by default ``"<staticmethod>"``
210
+ instance methods, by default ``"<stc>"``
211
+
212
+ include_attribute : bool, optional
213
+ Whether to include attributes in the output, by default ``True``
214
+
215
+ include_classattribute : bool, optional
216
+ Whether to include class attributes in the output, by default ``True``
217
+
218
+ classattribute_indicator : str, optional
219
+ A string used to mark class attribute in the output. This string is appended
220
+ to the name of each class attribute to visually differentiate it from regular
221
+ instance attributes, by default ``"<cls>"``
132
222
 
133
223
  Returns
134
224
  -------
135
225
  Self
136
- MethodNPropertyList (combined methods lists)
226
+ ClassMembers (combined methods)
137
227
 
138
228
 
139
229
  Example:
140
230
  --------
141
- >>> test = MethodNPropertyList(["a"], ["b"], ["c"], ["d"])
142
- >>> test.pack()
143
- MethodNPropertyList(methods=['a', 'b <classmethod>', 'c <staticmethod>'], properties=['d'])
231
+ >>> ClassMembers(["a"], ["b"], ["c"], ["d"], ["e"], ["f"]).pack().__format__("short")
232
+ ClassMembers(methods=['a', 'b <cls>', 'c <stc>'], properties=['d'], attributes=['e', 'f <cls>'])
144
233
  """
145
- new_methods_list = []
234
+
235
+ new_methods_list: list[str] = []
146
236
 
147
237
  # Method
148
238
  if include_method:
@@ -160,7 +250,27 @@ class MethodNPropertyList(NamedTuple):
160
250
  [f"{x} {staticmethod_indicator}".strip() for x in self.staticmethods]
161
251
  )
162
252
 
163
- return self.__class__(new_methods_list, [], [], self.properties)
253
+ new_attributes_list: list[str] = []
254
+
255
+ # Attribute
256
+ if include_attribute:
257
+ new_attributes_list.extend(self.attributes)
258
+
259
+ # Class attribute
260
+ if include_classattribute:
261
+ new_attributes_list.extend(
262
+ [
263
+ f"{x} {classattribute_indicator}".strip()
264
+ for x in self.classattributes
265
+ ]
266
+ )
267
+
268
+ # return self.__class__(new_methods_list, [], [], self.properties, [])
269
+ return self.__class__(
270
+ methods=new_methods_list,
271
+ properties=self.properties,
272
+ attributes=new_attributes_list,
273
+ )
164
274
 
165
275
  def sort(self, reverse: bool = False) -> Self:
166
276
  """
@@ -174,45 +284,96 @@ class MethodNPropertyList(NamedTuple):
174
284
  Returns
175
285
  -------
176
286
  Self
177
- Sorted.
287
+ Self with sorted values.
178
288
 
179
289
 
180
290
  Example:
181
291
  --------
182
- >>> test = MethodNPropertyList(["b", "a"], ["d", "c"], ["f", "e"], ["h", "g"])
183
- >>> test.sort()
184
- MethodNPropertyList(methods=['a', 'b'], classmethods=['c', 'd'], staticmethods=['e', 'f'], properties=['g', 'h'])
292
+ >>> ClassMembers(["b", "a"], ["d", "c"], ["f", "e"]).sort().__format__("short")
293
+ ClassMembers(methods=['a', 'b'], classmethods=['c', 'd'], staticmethods=['e', 'f'])
185
294
 
186
- >>> test.pack().sort()
187
- MethodNPropertyList(methods=['a', 'b', 'c <classmethod>', 'd <classmethod>', 'e <staticmethod>', 'f <staticmethod>'], properties=['g', 'h'])
295
+ >>> ClassMembers(["b", "a"], ["d", "c"], ["f", "e"]).pack().sort().__format__("short")
296
+ ClassMembers(methods=['a', 'b', 'c <cls>', 'd <cls>', 'e <stc>', 'f <stc>'])
188
297
  """
189
- sorted_vals = [
190
- sorted(getattr(self, field), reverse=reverse) for field in self._fields
298
+
299
+ sorted_vals: list[list[str]] = [
300
+ sorted(getattr(self, field), reverse=reverse)
301
+ for field in self._get_fields()
191
302
  ]
192
- # return self._make(sorted_vals)
303
+
193
304
  return self.__class__(*sorted_vals)
194
305
 
195
306
 
196
- # @versionadded("5.1.0")
197
- class MethodNPropertyResult(dict[str, MethodNPropertyList]):
307
+ # @versionadded("5.5.0")
308
+ class ClassMembersResult(dict[str, ClassMembers]):
198
309
  """
199
- All methods and properties of a class and its parent classes.
200
-
201
- Sorted in ascending order.
310
+ All members of a class and its parent classes.
202
311
  """
203
312
 
204
313
  _LINELENGTH: ClassVar[int] = 88
205
314
 
315
+ def __format__(self, format_spec: str) -> str:
316
+ """
317
+ Available format:
318
+ - ``s``/``short`` (This hides attributes with empty value)
319
+
320
+ Parameters
321
+ ----------
322
+ format_spec : str
323
+ Format spec
324
+
325
+ Returns
326
+ -------
327
+ str
328
+ Alternative text representation of this class.
329
+ """
330
+
331
+ fmt = format_spec.lower().strip()
332
+
333
+ if fmt == "short" or fmt == "s":
334
+ # out = {}
335
+ # for name, member in self.items():
336
+ # out[name] = format(member, fmt)
337
+ # return repr(out)
338
+ out = []
339
+ sep = ", "
340
+ for idx, (name, member) in enumerate(self.items()):
341
+ mem = format(member, fmt)
342
+ base = f"{repr(name)}: {mem}"
343
+
344
+ # if idx == 0:
345
+ # out.append("{" + base)
346
+ # elif idx + 1 == len(self.items()):
347
+ # out.append(base + "}")
348
+ # else:
349
+ # out.append(base)
350
+ out.append(
351
+ ("{" if idx == 0 else "")
352
+ + base
353
+ + ("}" if idx + 1 == len(self.items()) else "")
354
+ )
355
+
356
+ return sep.join(out)
357
+
358
+ return self.__repr__()
359
+
206
360
  def _merge_value(
207
361
  self,
208
- value_name: Literal["methods", "classmethods", "staticmethods", "properties"],
362
+ value_name: Literal[
363
+ "methods",
364
+ "classmethods",
365
+ "staticmethods",
366
+ "properties",
367
+ "attributes",
368
+ "classattributes",
369
+ ],
209
370
  ) -> list[str]:
210
371
  """
211
372
  Merge all specified values from the dictionary.
212
373
 
213
374
  Parameters
214
375
  ----------
215
- value_name : Literal["methods", "classmethods", "staticmethods", "properties"]
376
+ value_name : Literal["methods", "classmethods", "staticmethods", "properties", "attributes", "classattributes"]
216
377
  The type of value to merge.
217
378
 
218
379
  Returns
@@ -220,35 +381,57 @@ class MethodNPropertyResult(dict[str, MethodNPropertyList]):
220
381
  list[str]
221
382
  A list of merged values.
222
383
  """
384
+
223
385
  merged = []
224
- for _, methods_n_properties in self.items():
225
- if value_name in methods_n_properties._fields:
226
- merged.extend(getattr(methods_n_properties, value_name))
386
+ for _, member in self.items():
387
+ if value_name in member._get_fields():
388
+ merged.extend(getattr(member, value_name))
227
389
  return merged
228
390
 
229
- def flatten_value(self) -> MethodNPropertyList:
391
+ def flatten_value(self, sort: bool = True) -> ClassMembers:
230
392
  """
231
- Merge all attributes of ``dict``'s values into one ``MethodNPropertyList``.
393
+ Merge all attributes of ``dict``'s values into one ``ClassMembers``.
394
+
395
+ Parameters
396
+ ----------
397
+ sort : bool
398
+ Sort value in ascending order after flatten, by default ``True``
232
399
 
233
400
  Returns
234
401
  -------
235
- MethodNPropertyList
236
- Flattened value
402
+ ClassMembers
403
+ Flattened value.
237
404
 
238
405
 
239
406
  Example:
240
407
  --------
241
- >>> test = MethodNPropertyResult(
242
- ... ABC=MethodNPropertyList(["a"], ["b"], ["c"], ["d"]),
243
- ... DEF=MethodNPropertyList(["e"], ["f"], ["g"], ["h"]),
408
+ >>> test = ClassMembersResult(
409
+ ... ABC=ClassMembers(["a"], ["b"], ["c"], ["d"], ["y"], ["x"]),
410
+ ... DEF=ClassMembers(["e"], ["f"], ["g"], ["h"], ["w"], ["z"]),
244
411
  ... )
245
412
  >>> test.flatten_value()
246
- MethodNPropertyList(methods=["a", "e"], classmethods=["b", "f"], staticmethods=["c", "g"], properties=["d", "h"])
413
+ ClassMembers(
414
+ methods=["a", "e"],
415
+ classmethods=["b", "f"],
416
+ staticmethods=["c", "g"],
417
+ properties=["d", "h"],
418
+ attributes=["w", "y"],
419
+ classattributes=["x", "z"],
420
+ )
247
421
  """
248
- res = []
249
- for x in ["methods", "classmethods", "staticmethods", "properties"]:
422
+
423
+ res: list[list[str]] = []
424
+ # for x in [
425
+ # "methods",
426
+ # "classmethods",
427
+ # "staticmethods",
428
+ # "properties",
429
+ # "attributes",
430
+ # "classattributes",
431
+ # ]:
432
+ for x in ClassMembers._get_fields():
250
433
  res.append(self._merge_value(x)) # type: ignore
251
- return MethodNPropertyList._make(res)
434
+ return ClassMembers(*res).sort() if sort else ClassMembers(*res)
252
435
 
253
436
  def pack_value(
254
437
  self,
@@ -257,9 +440,12 @@ class MethodNPropertyResult(dict[str, MethodNPropertyList]):
257
440
  classmethod_indicator: str = "<classmethod>",
258
441
  include_staticmethod: bool = True,
259
442
  staticmethod_indicator: str = "<staticmethod>",
443
+ include_attribute: bool = True,
444
+ include_classattribute: bool = True,
445
+ classattribute_indicator: str = "<cls>",
260
446
  ) -> Self:
261
447
  """
262
- Join method, classmethod, staticmethod into one list for each value.
448
+ Join members into one list for each value.
263
449
 
264
450
  Parameters
265
451
  ----------
@@ -282,42 +468,72 @@ class MethodNPropertyResult(dict[str, MethodNPropertyList]):
282
468
  to the name of each staticmethod to visually differentiate it from regular
283
469
  instance methods, by default ``"<staticmethod>"``
284
470
 
471
+ include_attribute : bool, optional
472
+ Whether to include attributes in the output, by default ``True``
473
+
474
+ include_classattribute : bool, optional
475
+ Whether to include class attributes in the output, by default ``True``
476
+
477
+ classattribute_indicator : str, optional
478
+ A string used to mark class attribute in the output. This string is appended
479
+ to the name of each class attribute to visually differentiate it from regular
480
+ instance attributes, by default ``"<cls>"``
481
+
285
482
  Returns
286
483
  -------
287
484
  Self
288
- MethodNPropertyResult with packed value.
485
+ ClassMembersResult with packed value.
289
486
 
290
487
 
291
488
  Example:
292
489
  --------
293
- >>> test = MethodNPropertyResult(
294
- ... ABC=MethodNPropertyList(["a"], ["b"], ["c"], ["d"]),
295
- ... DEF=MethodNPropertyList(["e"], ["f"], ["g"], ["h"]),
490
+ >>> test = ClassMembersResult(
491
+ ... ABC=ClassMembers(["a"], ["b"], ["c"], ["d"], ["y"], ["x"]),
492
+ ... DEF=ClassMembers(["e"], ["f"], ["g"], ["h"], ["w"], ["z"]),
296
493
  ... )
297
494
  >>> test.pack_value()
298
495
  {
299
- "ABC": MethodNPropertyList(
300
- methods=["a", "b <classmethod>", "c <staticmethod>"], properties=["d"]
496
+ "ABC": ClassMembers(
497
+ methods=["a", "b <classmethod>", "c <staticmethod>"],
498
+ classmethods=[],
499
+ staticmethods=[],
500
+ properties=["d"],
501
+ attributes=["y", "x <cls>"],
502
+ classattributes=[],
301
503
  ),
302
- "DEF": MethodNPropertyList(
303
- methods=["e", "f <classmethod>", "g <staticmethod>"], properties=["h"]
504
+ "DEF": ClassMembers(
505
+ methods=["e", "f <classmethod>", "g <staticmethod>"],
506
+ classmethods=[],
507
+ staticmethods=[],
508
+ properties=["h"],
509
+ attributes=["w", "z <cls>"],
510
+ classattributes=[],
304
511
  ),
305
512
  }
306
513
  """
307
- for class_name, method_prop_list in self.items():
308
- self[class_name] = method_prop_list.pack(
514
+
515
+ for class_name, members in self.items():
516
+ self[class_name] = members.pack(
309
517
  include_method=include_method,
310
518
  include_classmethod=include_classmethod,
311
519
  classmethod_indicator=classmethod_indicator,
312
520
  include_staticmethod=include_staticmethod,
313
521
  staticmethod_indicator=staticmethod_indicator,
522
+ include_attribute=include_attribute,
523
+ include_classattribute=include_classattribute,
524
+ classattribute_indicator=classattribute_indicator,
314
525
  )
315
526
  return self
316
527
 
317
528
  def prioritize_value(
318
529
  self,
319
530
  value_name: Literal[
320
- "methods", "classmethods", "staticmethods", "properties"
531
+ "methods",
532
+ "classmethods",
533
+ "staticmethods",
534
+ "properties",
535
+ "attributes",
536
+ "classattributes",
321
537
  ] = "methods",
322
538
  ) -> dict[str, list[str]]:
323
539
  """
@@ -325,7 +541,7 @@ class MethodNPropertyResult(dict[str, MethodNPropertyList]):
325
541
 
326
542
  Parameters
327
543
  ----------
328
- value_name : Literal["methods", "classmethods", "staticmethods", "properties"], optional
544
+ value_name : Literal["methods", "classmethods", "staticmethods", "properties", "attributes", "classattributes"], optional
329
545
  The type of value to prioritize, by default ``"methods"``
330
546
 
331
547
  Returns
@@ -336,9 +552,9 @@ class MethodNPropertyResult(dict[str, MethodNPropertyList]):
336
552
 
337
553
  Example:
338
554
  --------
339
- >>> test = MethodNPropertyResult(
340
- ... ABC=MethodNPropertyList(["a"], ["b"], ["c"], ["d"]),
341
- ... DEF=MethodNPropertyList(["e"], ["f"], ["g"], ["h"]),
555
+ >>> test = ClassMembersResult(
556
+ ... ABC=ClassMembers(["a"], ["b"], ["c"], ["d"]),
557
+ ... DEF=ClassMembers(["e"], ["f"], ["g"], ["h"]),
342
558
  ... )
343
559
  >>> test.prioritize_value("methods")
344
560
  {'ABC': ['a'], 'DEF': ['e']}
@@ -349,23 +565,26 @@ class MethodNPropertyResult(dict[str, MethodNPropertyList]):
349
565
  >>> test.prioritize_value("properties")
350
566
  {'ABC': ['d'], 'DEF': ['h']}
351
567
  """
352
- result = {}
353
- for k, v in self.items():
354
- result[k] = getattr(v, value_name, v.methods)
568
+
569
+ result: dict[str, list[str]] = {}
570
+ for name, member in self.items():
571
+ result[name] = getattr(member, value_name, member.methods)
355
572
  return result
356
573
 
357
574
  def print_output(
358
575
  self,
359
- where_to_print: Literal["methods", "properties"] = "methods",
576
+ where_to_print: Literal["methods", "properties", "attributes"] = "methods",
360
577
  print_in_one_column: bool = False,
361
578
  ) -> None:
362
579
  """
363
580
  Beautifully print the result.
364
581
 
582
+ *This method is deprecated.*
583
+
365
584
  Parameters
366
585
  ----------
367
- where_to_print : Literal["methods", "properties"], optional
368
- Whether to print ``self.methods`` or ``self.properties``, by default ``"methods"``
586
+ where_to_print : Literal["methods", "properties", "attributes"], optional
587
+ Whether to print ``methods`` or ``properties``, by default ``"methods"``
369
588
 
370
589
  print_in_one_column : bool, optional
371
590
  Whether to print in one column, by default ``False``
@@ -374,12 +593,8 @@ class MethodNPropertyResult(dict[str, MethodNPropertyList]):
374
593
  print_func = print # Can be extended with function parameter
375
594
 
376
595
  # Loop through each class base
377
- for order, (class_base, methods_n_properties) in enumerate(
378
- self.items(), start=1
379
- ):
380
- methods: list[str] = getattr(
381
- methods_n_properties, where_to_print, methods_n_properties.methods
382
- )
596
+ for order, (class_base, member) in enumerate(self.items(), start=1):
597
+ methods: list[str] = getattr(member, where_to_print, member.methods)
383
598
  mlen = len(methods) # How many methods in that class
384
599
  if mlen == 0:
385
600
  continue
@@ -412,7 +627,9 @@ class MethodNPropertyResult(dict[str, MethodNPropertyList]):
412
627
  print_func("".ljust(self._LINELENGTH, "-"))
413
628
 
414
629
 
415
- class ShowAllMethodsMixin:
630
+ # Mixins
631
+ # ---------------------------------------------------------------------------
632
+ class GetClassMembersMixin:
416
633
  """
417
634
  Show all methods of the class and its parent class minus ``object`` class
418
635
 
@@ -421,66 +638,91 @@ class ShowAllMethodsMixin:
421
638
 
422
639
  Example:
423
640
  --------
424
- >>> class TestClass(ShowAllMethodsMixin):
641
+ >>> class TestClass(GetClassMembersMixin):
425
642
  ... def method1(self): ...
426
- >>> TestClass._get_methods_and_properties()
643
+ >>> TestClass._get_members(dunder=False)
427
644
  {
428
- "ShowAllMethodsMixin": MethodNPropertyList(
429
- classmethods=[
430
- "_get_methods_and_properties",
431
- "show_all_methods",
432
- "show_all_properties",
433
- ]
434
- ),
435
- "TestClass": MethodNPropertyList(
436
- methods=["method1"]
645
+ 'GetClassMembersMixin': ClassMembers(
646
+ methods=[],
647
+ classmethods=['_get_members', ..., 'show_all_methods', 'show_all_properties'],
648
+ staticmethods=[],
649
+ properties=[],
650
+ attributes=[],
651
+ classattributes=[]
437
652
  ),
653
+ 'TestClass': ClassMembers(
654
+ methods=['method1'],
655
+ classmethods=[],
656
+ staticmethods=[],
657
+ properties=[],
658
+ attributes=[],
659
+ classattributes=[]
660
+ )
438
661
  }
439
662
  """
440
663
 
441
- # @versionadded("5.1.0")
664
+ # @versionadded("5.5.0")
442
665
  @classmethod
443
- def _get_methods_and_properties(
666
+ def _get_members(
444
667
  cls,
445
- skip_private_attribute: bool = True,
446
- include_private_method: bool = False,
447
- ) -> MethodNPropertyResult:
668
+ dunder: bool = True,
669
+ underscore: bool = True,
670
+ private: bool = True,
671
+ ) -> ClassMembersResult:
448
672
  """
449
- Class method to get all methods and properties of the class and its parent classes
673
+ Class method to get all methods, properties, and attributes
674
+ of the class and its parent classes.
450
675
 
451
676
  Parameters
452
677
  ----------
453
- skip_private_attribute : bool, optional
454
- Whether to include attribute with ``__`` (dunder) in the output, by default ``True``
678
+ dunder : bool, optional
679
+ Whether to include attribute with ``__`` (dunder)
680
+ in the output, by default ``True``
455
681
 
456
- include_private_method : bool, optional
457
- Whether to include private method in the output, by default ``False``
682
+ underscore : bool, optional
683
+ Whether to include attribute starts with ``_``
684
+ in the output (will also skip dunder), by default ``True``
685
+
686
+ private : bool, optional
687
+ Whether to include private attribute
688
+ (``_<__class__.__name__>__<attribute>``)
689
+ in the output, by default ``True``
458
690
 
459
691
  Returns
460
692
  -------
461
- MethodNPropertyResult
462
- A dictionary where keys are class names and values are tuples of method names and properties.
693
+ ClassMembersResult
694
+ A dictionary where keys are class names
695
+ and values are tuples of method names and properties.
463
696
  """
464
697
 
465
698
  # MRO in reverse order
466
699
  classes = cls.__mro__[::-1]
467
- result = {}
700
+ result: dict[str, ClassMembers] = {}
468
701
 
469
702
  # For each class base in classes
470
703
  for base in classes:
471
- methods = []
472
- classmethods = []
473
- staticmethods = []
474
- properties = []
704
+ methods: list[str] = []
705
+ classmethods: list[str] = []
706
+ staticmethods: list[str] = []
707
+ properties: list[str] = []
708
+ classattributes: list[str] = []
475
709
 
476
710
  # Dict items of base
477
711
  for name, attr in base.__dict__.items():
712
+ # Skip dunder
713
+ if name.startswith("__") and not dunder:
714
+ continue
715
+
478
716
  # Skip private attribute
479
- if name.startswith("__") and skip_private_attribute:
717
+ if base.__name__ in name and not private:
480
718
  continue
481
719
 
482
- # Skip private Callable
483
- if base.__name__ in name and not include_private_method:
720
+ # Skip underscore
721
+ if (
722
+ name.startswith("_")
723
+ and not underscore
724
+ and base.__name__ not in name
725
+ ):
484
726
  continue
485
727
 
486
728
  # Methods
@@ -489,22 +731,132 @@ class ShowAllMethodsMixin:
489
731
  staticmethods.append(name)
490
732
  else:
491
733
  methods.append(name)
492
- if isinstance(attr, classmethod):
734
+ elif isinstance(attr, classmethod):
493
735
  classmethods.append(name)
494
736
 
495
737
  # Property
496
- if isinstance(attr, property):
738
+ elif isinstance(attr, property):
497
739
  properties.append(name)
498
740
 
741
+ # Class attribute
742
+ else:
743
+ classattributes.append(name)
744
+
499
745
  # Save to result
500
- result[base.__name__] = MethodNPropertyList(
501
- methods=sorted(methods),
502
- classmethods=sorted(classmethods),
503
- staticmethods=sorted(staticmethods),
504
- properties=sorted(properties),
505
- )
746
+ result[base.__name__] = ClassMembers(
747
+ methods=methods,
748
+ classmethods=classmethods,
749
+ staticmethods=staticmethods,
750
+ properties=properties,
751
+ classattributes=classattributes,
752
+ ).sort()
753
+
754
+ return ClassMembersResult(result)
755
+
756
+ # @versionadded("5.5.0")
757
+ def _get_attributes(
758
+ self,
759
+ underscore: bool = True,
760
+ private: bool = True,
761
+ ) -> list[str]:
762
+ """
763
+ Get all attributes of the class instance.
764
+
765
+ Parameters
766
+ ----------
767
+ underscore : bool, optional
768
+ Whether to include attribute starts with ``_``
769
+ in the output, by default ``True``
770
+
771
+ private : bool, optional
772
+ Whether to include private attribute
773
+ (``_<__class__.__name__>__<attribute>``)
774
+ in the output, by default ``True``
775
+
776
+ Returns
777
+ -------
778
+ list[str]
779
+ A list contains attributes.
780
+ """
781
+
782
+ # Default output
783
+ out: list[str] = []
784
+
785
+ # Get attributes
786
+ cls_dict: dict[str, Any] | None = getattr(self, "__dict__", None)
787
+ cls_slots: tuple[str, ...] | None = getattr(self, "__slots__", None)
788
+ cls_name: str = self.__class__.__name__
789
+
790
+ def _is_valid(item_name: str) -> bool:
791
+ if not item_name.startswith("_"):
792
+ return True
793
+ if cls_name in item_name and private:
794
+ return True
795
+ if underscore and cls_name not in item_name:
796
+ return True
797
+ return False
798
+
799
+ # Check if __dict__ exist and len(__dict__) > 0
800
+ if cls_dict is not None and len(cls_dict) > 0:
801
+ # out = [x for x in self.__dict__ if _is_valid(x)]
802
+ out.extend(list(cls_dict))
506
803
 
507
- return MethodNPropertyResult(result)
804
+ # Check if __slots__ exist and len(__slots__) > 0
805
+ if cls_slots is not None and len(cls_slots) > 0:
806
+ # Convert __<attribute> to _<self.__class__.__name__>__<attribute>
807
+ _slot = [f"_{cls_name}{x}" if x.startswith("__") else x for x in cls_slots]
808
+ out.extend(_slot)
809
+
810
+ return [x for x in out if _is_valid(x)]
811
+
812
+ # @versionadded("5.5.0")
813
+ def get_members(
814
+ self,
815
+ dunder: bool = False,
816
+ underscore: bool = True,
817
+ private: bool = True,
818
+ ) -> ClassMembersResult:
819
+ """
820
+ Get all members of a class instance.
821
+
822
+ Parameters
823
+ ----------
824
+ dunder : bool, optional
825
+ Whether to include attribute with ``__`` (dunder)
826
+ in the output, by default ``False``
827
+
828
+ underscore : bool, optional
829
+ Whether to include attribute starts with ``_``
830
+ in the output (will also skip dunder), by default ``True``
831
+
832
+ private : bool, optional
833
+ Whether to include private attribute
834
+ (``_<__class__.__name__>__<attribute>``)
835
+ in the output, by default ``True``
836
+
837
+ Returns
838
+ -------
839
+ ClassMembersResult
840
+ All member of a class instance.
841
+ """
842
+ mems = self._get_members(dunder=dunder, underscore=underscore, private=private)
843
+ attrs = self._get_attributes(underscore=underscore, private=private)
844
+ mems[self.__class__.__name__].attributes = attrs
845
+ return mems
846
+
847
+ # @remove("6.0.0")
848
+ # @deprecated("5.5.0")
849
+ # @versionadded("5.1.0")
850
+ @classmethod
851
+ def _get_methods_and_properties(
852
+ cls,
853
+ skip_private_attribute: bool = True,
854
+ include_private_method: bool = False,
855
+ ) -> ClassMembersResult:
856
+ """Wrapper of ``cls._get_members``, deprecated"""
857
+ return cls._get_members(
858
+ dunder=skip_private_attribute, private=include_private_method
859
+ )
508
860
 
509
861
  @classmethod
510
862
  def show_all_methods(
@@ -550,8 +902,8 @@ class ShowAllMethodsMixin:
550
902
  A dictionary where keys are class names and values are lists of method names.
551
903
  """
552
904
 
553
- result = cls._get_methods_and_properties(
554
- include_private_method=include_private_method
905
+ result = cls._get_members(
906
+ dunder=False, private=include_private_method
555
907
  ).pack_value(
556
908
  include_classmethod=include_classmethod,
557
909
  classmethod_indicator=classmethod_indicator,
@@ -582,13 +934,12 @@ class ShowAllMethodsMixin:
582
934
  """
583
935
 
584
936
  # result = cls.get_methods_and_properties().prioritize_value("properties")
585
- result = MethodNPropertyResult(
937
+ result = ClassMembersResult(
586
938
  {
587
- cls.__name__: MethodNPropertyList(
588
- [],
589
- [],
590
- [],
591
- cls._get_methods_and_properties().flatten_value().properties,
939
+ cls.__name__: ClassMembers(
940
+ properties=cls._get_members(dunder=False)
941
+ .flatten_value()
942
+ .properties,
592
943
  )
593
944
  }
594
945
  )
@@ -599,6 +950,13 @@ class ShowAllMethodsMixin:
599
950
  return result.prioritize_value("properties")
600
951
 
601
952
 
953
+ # Temp name for backward compability
954
+ # Will be removed at version 6.0.0
955
+ MethodNPropertyList = ClassMembers
956
+ MethodNPropertyResult = ClassMembersResult
957
+ ShowAllMethodsMixin = GetClassMembersMixin
958
+
959
+
602
960
  class AutoREPRMixin:
603
961
  """
604
962
  Generate ``repr()`` output as ``<class(param1=any, param2=any, ...)>``
@@ -658,7 +1016,7 @@ class AutoREPRMixin:
658
1016
 
659
1017
  # Class
660
1018
  # ---------------------------------------------------------------------------
661
- class BaseClass(ShowAllMethodsMixin, AutoREPRMixin):
1019
+ class BaseClass(GetClassMembersMixin, AutoREPRMixin):
662
1020
  """Base class"""
663
1021
 
664
1022
  def __str__(self) -> str: