absfuyu 5.6.1__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 +2 -2
- absfuyu/cli/__init__.py +13 -2
- absfuyu/cli/audio_group.py +98 -0
- absfuyu/cli/color.py +2 -2
- absfuyu/cli/config_group.py +2 -2
- absfuyu/cli/do_group.py +2 -2
- absfuyu/cli/game_group.py +20 -2
- absfuyu/cli/tool_group.py +68 -4
- absfuyu/config/__init__.py +3 -3
- absfuyu/core/__init__.py +10 -6
- absfuyu/core/baseclass.py +104 -34
- absfuyu/core/baseclass2.py +43 -2
- absfuyu/core/decorator.py +2 -2
- absfuyu/core/docstring.py +4 -2
- absfuyu/core/dummy_cli.py +3 -3
- absfuyu/core/dummy_func.py +2 -2
- absfuyu/dxt/__init__.py +2 -2
- absfuyu/dxt/base_type.py +93 -0
- absfuyu/dxt/dictext.py +188 -6
- absfuyu/dxt/dxt_support.py +2 -2
- absfuyu/dxt/intext.py +72 -4
- absfuyu/dxt/listext.py +495 -23
- absfuyu/dxt/strext.py +2 -2
- 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 +2 -2
- absfuyu/extra/da/__init__.py +39 -3
- absfuyu/extra/da/dadf.py +436 -29
- absfuyu/extra/da/dadf_base.py +2 -2
- absfuyu/extra/da/df_func.py +89 -5
- absfuyu/extra/da/mplt.py +2 -2
- 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 +4 -6
- absfuyu/extra/rclone.py +253 -0
- absfuyu/extra/xml.py +90 -0
- absfuyu/fun/__init__.py +2 -20
- absfuyu/fun/rubik.py +2 -2
- 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 -2
- absfuyu/game/wordle.py +6 -4
- absfuyu/general/__init__.py +2 -2
- absfuyu/general/content.py +2 -2
- 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 +2 -2
- absfuyu/tools/checksum.py +119 -4
- absfuyu/tools/converter.py +2 -2
- absfuyu/tools/generator.py +24 -7
- absfuyu/tools/inspector.py +2 -2
- absfuyu/tools/keygen.py +2 -2
- absfuyu/tools/obfuscator.py +2 -2
- absfuyu/tools/passwordlib.py +2 -2
- absfuyu/tools/shutdownizer.py +3 -8
- absfuyu/tools/sw.py +213 -10
- absfuyu/tools/web.py +10 -13
- absfuyu/typings.py +5 -8
- absfuyu/util/__init__.py +31 -2
- absfuyu/util/api.py +7 -4
- absfuyu/util/cli.py +119 -0
- absfuyu/util/gui.py +91 -0
- absfuyu/util/json_method.py +2 -2
- absfuyu/util/lunar.py +2 -2
- absfuyu/util/package.py +124 -0
- absfuyu/util/path.py +313 -4
- absfuyu/util/performance.py +2 -2
- absfuyu/util/shorten_number.py +206 -13
- absfuyu/util/text_table.py +2 -2
- absfuyu/util/zipped.py +2 -2
- absfuyu/version.py +22 -19
- {absfuyu-5.6.1.dist-info → absfuyu-6.1.2.dist-info}/METADATA +37 -8
- absfuyu-6.1.2.dist-info/RECORD +105 -0
- {absfuyu-5.6.1.dist-info → absfuyu-6.1.2.dist-info}/WHEEL +1 -1
- absfuyu/extra/data_analysis.py +0 -21
- absfuyu-5.6.1.dist-info/RECORD +0 -79
- {absfuyu-5.6.1.dist-info → absfuyu-6.1.2.dist-info}/entry_points.txt +0 -0
- {absfuyu-5.6.1.dist-info → absfuyu-6.1.2.dist-info}/licenses/LICENSE +0 -0
absfuyu/__init__.py
CHANGED
|
@@ -22,19 +22,21 @@ Using in cmd:
|
|
|
22
22
|
__title__ = "absfuyu"
|
|
23
23
|
__author__ = "AbsoluteWinter"
|
|
24
24
|
__license__ = "MIT License"
|
|
25
|
-
__version__ = "
|
|
25
|
+
__version__ = "6.1.2"
|
|
26
26
|
__all__ = [
|
|
27
27
|
"core",
|
|
28
28
|
"config",
|
|
29
29
|
"dxt",
|
|
30
30
|
"extra",
|
|
31
|
-
"logger",
|
|
32
31
|
"fun",
|
|
33
32
|
"game",
|
|
34
33
|
"general",
|
|
34
|
+
"numbers",
|
|
35
35
|
"pkg_data",
|
|
36
|
-
"sort",
|
|
37
36
|
"tools",
|
|
38
37
|
"util",
|
|
38
|
+
"logger",
|
|
39
|
+
"sort",
|
|
40
|
+
"typings",
|
|
39
41
|
"version",
|
|
40
42
|
]
|
absfuyu/__main__.py
CHANGED
absfuyu/cli/__init__.py
CHANGED
|
@@ -3,8 +3,8 @@ ABSFUYU
|
|
|
3
3
|
-------
|
|
4
4
|
COMMAND LINE INTERFACE
|
|
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
|
__all__ = ["cli"]
|
|
@@ -15,6 +15,7 @@ import click
|
|
|
15
15
|
import colorama
|
|
16
16
|
|
|
17
17
|
from absfuyu import __title__, __version__
|
|
18
|
+
from absfuyu.cli.audio_group import audio_group
|
|
18
19
|
from absfuyu.cli.color import COLOR
|
|
19
20
|
from absfuyu.cli.config_group import config_group
|
|
20
21
|
from absfuyu.cli.do_group import do_group
|
|
@@ -25,6 +26,15 @@ from absfuyu.cli.tool_group import tool_group
|
|
|
25
26
|
colorama.init(autoreset=True)
|
|
26
27
|
|
|
27
28
|
|
|
29
|
+
def quick_info() -> str:
|
|
30
|
+
return (
|
|
31
|
+
f"- os/type: {platform.system().lower()}\n"
|
|
32
|
+
f"- os/kernel: {platform.version()}\n"
|
|
33
|
+
f"- os/arch: {platform.machine().lower()}\n"
|
|
34
|
+
f"- python version: {platform.python_version()}\n"
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
|
|
28
38
|
@click.command()
|
|
29
39
|
def version() -> None:
|
|
30
40
|
"""Show current version"""
|
|
@@ -50,4 +60,5 @@ cli.add_command(config_group)
|
|
|
50
60
|
cli.add_command(do_group)
|
|
51
61
|
cli.add_command(game_group)
|
|
52
62
|
cli.add_command(tool_group)
|
|
63
|
+
cli.add_command(audio_group)
|
|
53
64
|
cli.add_command(version)
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"""
|
|
2
|
+
ABSFUYU CLI
|
|
3
|
+
-----------
|
|
4
|
+
Audio
|
|
5
|
+
|
|
6
|
+
Version: 6.1.1
|
|
7
|
+
Date updated: 30/12/2025 (dd/mm/yyyy)
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
# Module Package
|
|
11
|
+
# ---------------------------------------------------------------------------
|
|
12
|
+
__all__ = ["audio_group"]
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
# Library
|
|
16
|
+
# ---------------------------------------------------------------------------
|
|
17
|
+
from pathlib import Path
|
|
18
|
+
|
|
19
|
+
import click
|
|
20
|
+
|
|
21
|
+
from absfuyu.extra.audio.convert import DirectoryAudioConvertMixin
|
|
22
|
+
from absfuyu.extra.audio.lossless import DirectoryAudioLosslessCheckMixin
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
# CLI
|
|
26
|
+
# ---------------------------------------------------------------------------
|
|
27
|
+
@click.command(name="c3")
|
|
28
|
+
@click.argument("dir_path", type=str)
|
|
29
|
+
@click.option(
|
|
30
|
+
"--recursive",
|
|
31
|
+
"-r",
|
|
32
|
+
"recursive_mode",
|
|
33
|
+
type=bool,
|
|
34
|
+
default=False,
|
|
35
|
+
is_flag=True,
|
|
36
|
+
show_default=True,
|
|
37
|
+
help="Scan for every file in the folder (including child folder)",
|
|
38
|
+
)
|
|
39
|
+
@click.option(
|
|
40
|
+
"--format",
|
|
41
|
+
"-f",
|
|
42
|
+
"from_format",
|
|
43
|
+
type=str,
|
|
44
|
+
default=".flac",
|
|
45
|
+
show_default=True,
|
|
46
|
+
help="From which audio format",
|
|
47
|
+
)
|
|
48
|
+
def convert_to_mp3(dir_path: str, from_format: str, recursive_mode: bool) -> None:
|
|
49
|
+
"""Convert to .mp3 file"""
|
|
50
|
+
|
|
51
|
+
engine = DirectoryAudioConvertMixin(Path(dir_path).resolve())
|
|
52
|
+
|
|
53
|
+
try:
|
|
54
|
+
engine.convert_to_mp3(from_format=from_format, recursive=recursive_mode)
|
|
55
|
+
except Exception:
|
|
56
|
+
engine.convert_to_mp3_single_thread(from_format=from_format, recursive=recursive_mode)
|
|
57
|
+
click.echo("Done")
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
@click.command(name="lc")
|
|
61
|
+
@click.argument("dir_path", type=str)
|
|
62
|
+
@click.option(
|
|
63
|
+
"--recursive",
|
|
64
|
+
"-r",
|
|
65
|
+
"recursive_mode",
|
|
66
|
+
type=bool,
|
|
67
|
+
default=False,
|
|
68
|
+
is_flag=True,
|
|
69
|
+
show_default=True,
|
|
70
|
+
help="Scan for every file in the folder (including child folder)",
|
|
71
|
+
)
|
|
72
|
+
@click.option(
|
|
73
|
+
"--format",
|
|
74
|
+
"-f",
|
|
75
|
+
"format",
|
|
76
|
+
type=str,
|
|
77
|
+
default=".flac",
|
|
78
|
+
show_default=True,
|
|
79
|
+
help="Audio format",
|
|
80
|
+
)
|
|
81
|
+
def lossless_check(dir_path: str, format: str, recursive_mode: bool) -> None:
|
|
82
|
+
"""Check for lossless audio file"""
|
|
83
|
+
|
|
84
|
+
engine = DirectoryAudioLosslessCheckMixin(Path(dir_path).resolve())
|
|
85
|
+
try:
|
|
86
|
+
engine.lossless_check(from_format=format, recursive=recursive_mode)
|
|
87
|
+
except Exception:
|
|
88
|
+
engine.lossless_check_single_thread(from_format=format, recursive=recursive_mode)
|
|
89
|
+
click.echo("Done")
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
@click.group(name="audio")
|
|
93
|
+
def audio_group() -> None:
|
|
94
|
+
"""Audio related"""
|
|
95
|
+
pass
|
|
96
|
+
|
|
97
|
+
audio_group.add_command(lossless_check)
|
|
98
|
+
audio_group.add_command(convert_to_mp3)
|
absfuyu/cli/color.py
CHANGED
absfuyu/cli/config_group.py
CHANGED
absfuyu/cli/do_group.py
CHANGED
absfuyu/cli/game_group.py
CHANGED
|
@@ -3,8 +3,8 @@ ABSFUYU CLI
|
|
|
3
3
|
-----------
|
|
4
4
|
Game
|
|
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
|
|
@@ -17,6 +17,7 @@ __all__ = ["game_group"]
|
|
|
17
17
|
import click
|
|
18
18
|
|
|
19
19
|
from absfuyu.game import game_escapeLoop, game_RockPaperScissors
|
|
20
|
+
from absfuyu.game.schulte import SchulteTable
|
|
20
21
|
from absfuyu.game.sudoku import Sudoku
|
|
21
22
|
from absfuyu.game.tictactoe import GameMode, TicTacToe
|
|
22
23
|
from absfuyu.game.wordle import Wordle
|
|
@@ -103,6 +104,22 @@ def tictactoe(size: int, game_mode: str, bot_time: float) -> None:
|
|
|
103
104
|
instance.play(game_mode, bot_time=bot_time)
|
|
104
105
|
|
|
105
106
|
|
|
107
|
+
@click.command(name="schulte")
|
|
108
|
+
@click.option(
|
|
109
|
+
"--size",
|
|
110
|
+
"-s",
|
|
111
|
+
"size",
|
|
112
|
+
type=int,
|
|
113
|
+
default=5,
|
|
114
|
+
show_default=True,
|
|
115
|
+
help="Size of the table",
|
|
116
|
+
)
|
|
117
|
+
def schulte_table(size: int) -> None:
|
|
118
|
+
"""Schulte table"""
|
|
119
|
+
engine = SchulteTable(size=size)
|
|
120
|
+
engine.make_table()
|
|
121
|
+
|
|
122
|
+
|
|
106
123
|
@click.group(name="game")
|
|
107
124
|
def game_group() -> None:
|
|
108
125
|
"""Play game"""
|
|
@@ -114,3 +131,4 @@ game_group.add_command(escape_loop)
|
|
|
114
131
|
game_group.add_command(wordle_solver)
|
|
115
132
|
game_group.add_command(sudoku_solver)
|
|
116
133
|
game_group.add_command(tictactoe)
|
|
134
|
+
game_group.add_command(schulte_table)
|
absfuyu/cli/tool_group.py
CHANGED
|
@@ -3,8 +3,8 @@ ABSFUYU CLI
|
|
|
3
3
|
-----------
|
|
4
4
|
Tool
|
|
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
|
|
@@ -14,6 +14,7 @@ __all__ = ["tool_group"]
|
|
|
14
14
|
|
|
15
15
|
# Library
|
|
16
16
|
# ---------------------------------------------------------------------------
|
|
17
|
+
from pathlib import Path
|
|
17
18
|
from typing import Literal
|
|
18
19
|
|
|
19
20
|
import click
|
|
@@ -24,7 +25,7 @@ from absfuyu.tools.converter import Base64EncodeDecode, Text2Chemistry
|
|
|
24
25
|
|
|
25
26
|
# CLI
|
|
26
27
|
# ---------------------------------------------------------------------------
|
|
27
|
-
@click.command(name="
|
|
28
|
+
@click.command(name="cs")
|
|
28
29
|
@click.argument("file_path", type=str)
|
|
29
30
|
@click.option(
|
|
30
31
|
"--hashmode",
|
|
@@ -80,6 +81,67 @@ def file_checksum(
|
|
|
80
81
|
click.echo(res == hash_to_compare)
|
|
81
82
|
|
|
82
83
|
|
|
84
|
+
@click.command(name="cimg")
|
|
85
|
+
@click.argument("dir_path", type=str)
|
|
86
|
+
@click.argument("to_format", type=str)
|
|
87
|
+
def convert_img(dir_path: str, to_format: str) -> None:
|
|
88
|
+
"""Convert image in directory"""
|
|
89
|
+
from absfuyu.extra.img.converter import ImgConverter
|
|
90
|
+
|
|
91
|
+
engine = ImgConverter(Path(dir_path).resolve())
|
|
92
|
+
engine.img_convert(to_format)
|
|
93
|
+
click.echo("Done")
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
@click.command(name="rdimg")
|
|
97
|
+
@click.argument("dir_path", type=str)
|
|
98
|
+
@click.option(
|
|
99
|
+
"--dryrun",
|
|
100
|
+
"-d",
|
|
101
|
+
"dry_run",
|
|
102
|
+
type=bool,
|
|
103
|
+
default=False,
|
|
104
|
+
is_flag=True,
|
|
105
|
+
show_default=True,
|
|
106
|
+
help="Simulate deleting only",
|
|
107
|
+
)
|
|
108
|
+
@click.option(
|
|
109
|
+
"--recursive",
|
|
110
|
+
"-r",
|
|
111
|
+
"recursive_mode",
|
|
112
|
+
type=bool,
|
|
113
|
+
default=False,
|
|
114
|
+
is_flag=True,
|
|
115
|
+
show_default=True,
|
|
116
|
+
help="Scan for every file in the folder (including child folder)",
|
|
117
|
+
)
|
|
118
|
+
@click.option(
|
|
119
|
+
"--threshold",
|
|
120
|
+
"-t",
|
|
121
|
+
"threshold",
|
|
122
|
+
type=int,
|
|
123
|
+
default=5,
|
|
124
|
+
show_default=True,
|
|
125
|
+
help="Threshold: 0 for exact image, 5 to 10 for light edit",
|
|
126
|
+
)
|
|
127
|
+
@click.option(
|
|
128
|
+
"--keepmode",
|
|
129
|
+
"-k",
|
|
130
|
+
"keep_mode",
|
|
131
|
+
type=click.Choice(["first", "last", "best"]),
|
|
132
|
+
default="best",
|
|
133
|
+
show_default=True,
|
|
134
|
+
help="Keep mode",
|
|
135
|
+
)
|
|
136
|
+
def remove_dup_img(dir_path: str, threshold: bool, recursive_mode: bool, keep_mode: str, dry_run: bool) -> None:
|
|
137
|
+
"""Remove duplicate images in directory"""
|
|
138
|
+
from absfuyu.extra.img.dup_check import DirectoryRemoveDuplicateImageMixin as DRDI
|
|
139
|
+
|
|
140
|
+
engine = DRDI(Path(dir_path).resolve())
|
|
141
|
+
engine.remove_duplicate_images(threshold=threshold, recursive=recursive_mode, keep_mode=keep_mode, dry_run=dry_run)
|
|
142
|
+
click.echo("Done")
|
|
143
|
+
|
|
144
|
+
|
|
83
145
|
@click.command(name="t2c")
|
|
84
146
|
@click.argument("text", type=str)
|
|
85
147
|
def text2chem(text: str) -> None:
|
|
@@ -121,7 +183,7 @@ def base64convert_img(img_path: str, data_tag: bool) -> None:
|
|
|
121
183
|
|
|
122
184
|
|
|
123
185
|
@click.group(name="b64")
|
|
124
|
-
def base64_group():
|
|
186
|
+
def base64_group() -> None:
|
|
125
187
|
"""Base64 encode decode"""
|
|
126
188
|
pass
|
|
127
189
|
|
|
@@ -138,5 +200,7 @@ def tool_group() -> None:
|
|
|
138
200
|
|
|
139
201
|
|
|
140
202
|
tool_group.add_command(file_checksum)
|
|
203
|
+
tool_group.add_command(convert_img)
|
|
204
|
+
tool_group.add_command(remove_dup_img)
|
|
141
205
|
tool_group.add_command(base64_group)
|
|
142
206
|
tool_group.add_command(text2chem)
|
absfuyu/config/__init__.py
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
"""
|
|
2
2
|
Absfuyu: Configuration
|
|
3
3
|
----------------------
|
|
4
|
-
Package configuration module
|
|
4
|
+
Package configuration module - internal use
|
|
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 level
|
absfuyu/core/__init__.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
|
|
@@ -27,11 +27,15 @@ __all__ = [
|
|
|
27
27
|
]
|
|
28
28
|
|
|
29
29
|
__package_feature__ = [
|
|
30
|
-
"beautiful", # BeautifulOutput
|
|
31
|
-
"docs", # For (package) hatch's env use only
|
|
32
|
-
"extra", # DataFrame
|
|
33
30
|
"full", # All package
|
|
34
|
-
"
|
|
31
|
+
"docs", # For (package) hatch's env use only
|
|
32
|
+
"extra", # Extra features
|
|
33
|
+
"beautiful", # BeautifulOutput
|
|
34
|
+
"dadf", # DataFrame
|
|
35
|
+
"pdf", # PDF
|
|
36
|
+
"pic", # picture related
|
|
37
|
+
"xml", # XML
|
|
38
|
+
"ggapi", # Google
|
|
35
39
|
]
|
|
36
40
|
|
|
37
41
|
|
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
|
|
@@ -18,6 +18,7 @@ __all__ = [
|
|
|
18
18
|
# Mixins
|
|
19
19
|
"GetClassMembersMixin",
|
|
20
20
|
"AutoREPRMixin",
|
|
21
|
+
"AddFormatMixin",
|
|
21
22
|
# Class
|
|
22
23
|
"BaseClass",
|
|
23
24
|
"BaseDataclass",
|
|
@@ -27,6 +28,7 @@ __all__ = [
|
|
|
27
28
|
|
|
28
29
|
# Library
|
|
29
30
|
# ---------------------------------------------------------------------------
|
|
31
|
+
from collections.abc import Callable
|
|
30
32
|
from dataclasses import dataclass, field
|
|
31
33
|
from typing import Any, ClassVar, Literal, Self
|
|
32
34
|
|
|
@@ -57,7 +59,8 @@ class BaseDataclass:
|
|
|
57
59
|
Base dataclass.
|
|
58
60
|
|
|
59
61
|
Contains util methods:
|
|
60
|
-
-
|
|
62
|
+
- _get_fields
|
|
63
|
+
- to_dict
|
|
61
64
|
"""
|
|
62
65
|
|
|
63
66
|
@classmethod
|
|
@@ -73,6 +76,22 @@ class BaseDataclass:
|
|
|
73
76
|
_fields = getattr(cls, "__dataclass_fields__", ())
|
|
74
77
|
return tuple(_fields)
|
|
75
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
|
+
|
|
76
95
|
|
|
77
96
|
# Support
|
|
78
97
|
# ---------------------------------------------------------------------------
|
|
@@ -844,20 +863,6 @@ class GetClassMembersMixin:
|
|
|
844
863
|
mems[self.__class__.__name__].attributes = attrs
|
|
845
864
|
return mems
|
|
846
865
|
|
|
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
|
-
)
|
|
860
|
-
|
|
861
866
|
@classmethod
|
|
862
867
|
def show_all_methods(
|
|
863
868
|
cls,
|
|
@@ -950,13 +955,6 @@ class GetClassMembersMixin:
|
|
|
950
955
|
return result.prioritize_value("properties")
|
|
951
956
|
|
|
952
957
|
|
|
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
|
-
|
|
960
958
|
class AutoREPRMixin:
|
|
961
959
|
"""
|
|
962
960
|
Generate ``repr()`` output as ``<class(param1=any, param2=any, ...)>``
|
|
@@ -1014,19 +1012,38 @@ class AutoREPRMixin:
|
|
|
1014
1012
|
return f"{self.__class__.__name__}({sep.join(out)})"
|
|
1015
1013
|
|
|
1016
1014
|
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
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.
|
|
1021
1020
|
|
|
1022
|
-
|
|
1023
|
-
|
|
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]] = {}
|
|
1024
1031
|
|
|
1025
1032
|
def __format__(self, format_spec: str) -> str:
|
|
1026
1033
|
"""
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
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
|
+
|
|
1030
1047
|
|
|
1031
1048
|
Usage
|
|
1032
1049
|
-----
|
|
@@ -1035,7 +1052,60 @@ class BaseClass(GetClassMembersMixin, AutoREPRMixin):
|
|
|
1035
1052
|
>>> print(format(<object>, <format_spec>))
|
|
1036
1053
|
"""
|
|
1037
1054
|
|
|
1038
|
-
|
|
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)
|
|
1039
1109
|
|
|
1040
1110
|
|
|
1041
1111
|
# Metaclass
|
absfuyu/core/baseclass2.py
CHANGED
|
@@ -3,8 +3,8 @@ Absfuyu: Core
|
|
|
3
3
|
-------------
|
|
4
4
|
Bases for other features (with library)
|
|
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
|
|
@@ -62,6 +62,47 @@ class ShowAllMethodsMixinInspectVer:
|
|
|
62
62
|
return result
|
|
63
63
|
|
|
64
64
|
|
|
65
|
+
class UniversalConfigMixin:
|
|
66
|
+
"""
|
|
67
|
+
Universal config Mixin
|
|
68
|
+
|
|
69
|
+
This use these attributes:
|
|
70
|
+
- _instance_config
|
|
71
|
+
- config
|
|
72
|
+
|
|
73
|
+
Example:
|
|
74
|
+
--------
|
|
75
|
+
>>> class Test(UniversalConfigMixin):
|
|
76
|
+
>>> DEFAULT_CONFIG = {"test": True}
|
|
77
|
+
>>> test = Test(config={"new_key": True})
|
|
78
|
+
>>> print(test.config)
|
|
79
|
+
{'test': True, 'new_key': True}
|
|
80
|
+
"""
|
|
81
|
+
|
|
82
|
+
DEFAULT_CONFIG = {}
|
|
83
|
+
|
|
84
|
+
def __init__(self, **kwargs) -> None:
|
|
85
|
+
try:
|
|
86
|
+
super().__init__(**kwargs)
|
|
87
|
+
except TypeError:
|
|
88
|
+
pass
|
|
89
|
+
# instance override
|
|
90
|
+
self._instance_config = kwargs.get("config", {})
|
|
91
|
+
|
|
92
|
+
@property
|
|
93
|
+
def config(self):
|
|
94
|
+
# Priority: instance > class > default
|
|
95
|
+
merged = dict(self.DEFAULT_CONFIG)
|
|
96
|
+
|
|
97
|
+
try:
|
|
98
|
+
# merged.update(getattr(self, "CLASS_CONFIG", {}))
|
|
99
|
+
merged.update(self._instance_config)
|
|
100
|
+
except Exception:
|
|
101
|
+
pass
|
|
102
|
+
|
|
103
|
+
return merged
|
|
104
|
+
|
|
105
|
+
|
|
65
106
|
# Metaclass
|
|
66
107
|
# ---------------------------------------------------------------------------
|
|
67
108
|
class PerformanceTrackingMeta(type):
|