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/tools/web.py
CHANGED
|
@@ -3,21 +3,14 @@ Absfuyu: Web
|
|
|
3
3
|
------------
|
|
4
4
|
Web, ``request``, ``BeautifulSoup`` stuff
|
|
5
5
|
|
|
6
|
-
Version: 1.
|
|
7
|
-
Date updated:
|
|
6
|
+
Version: 6.1.1
|
|
7
|
+
Date updated: 30/12/2025 (dd/mm/yyyy)
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
|
-
# Library
|
|
11
|
-
# ---------------------------------------------------------------------------
|
|
12
|
-
import requests
|
|
13
|
-
from bs4 import BeautifulSoup
|
|
14
|
-
|
|
15
|
-
from absfuyu.logger import logger
|
|
16
|
-
|
|
17
10
|
|
|
18
11
|
# Function
|
|
19
12
|
# ---------------------------------------------------------------------------
|
|
20
|
-
def soup_link(link: str)
|
|
13
|
+
def soup_link(link: str):
|
|
21
14
|
"""
|
|
22
15
|
``BeautifulSoup`` the link
|
|
23
16
|
|
|
@@ -32,12 +25,17 @@ def soup_link(link: str) -> BeautifulSoup:
|
|
|
32
25
|
``BeautifulSoup`` instance
|
|
33
26
|
"""
|
|
34
27
|
try:
|
|
28
|
+
import requests
|
|
29
|
+
from bs4 import BeautifulSoup
|
|
30
|
+
|
|
35
31
|
page = requests.get(link)
|
|
36
32
|
soup = BeautifulSoup(page.content, "html.parser")
|
|
37
|
-
logger.debug("Soup completed!")
|
|
38
33
|
return soup
|
|
34
|
+
|
|
35
|
+
except ImportError:
|
|
36
|
+
raise ImportError("Please install bs4, requests package")
|
|
37
|
+
|
|
39
38
|
except Exception:
|
|
40
|
-
logger.error("Can't soup")
|
|
41
39
|
raise SystemExit("Something wrong") # noqa: B904
|
|
42
40
|
|
|
43
41
|
|
|
@@ -51,5 +49,4 @@ def gen_random_commit_msg() -> str:
|
|
|
51
49
|
Random commit message
|
|
52
50
|
"""
|
|
53
51
|
out = soup_link("https://whatthecommit.com/").get_text()[34:-20]
|
|
54
|
-
logger.debug(out)
|
|
55
52
|
return out # type: ignore
|
absfuyu/typings.py
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Absfuyu: Core
|
|
3
|
+
-------------
|
|
4
|
+
Pre-defined typing
|
|
5
|
+
|
|
6
|
+
Version: 6.1.1
|
|
7
|
+
Date updated: 30/12/2025 (dd/mm/yyyy)
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
# Module Package
|
|
11
|
+
# ---------------------------------------------------------------------------
|
|
12
|
+
__all__ = [
|
|
13
|
+
# Type
|
|
14
|
+
"T",
|
|
15
|
+
"T_co",
|
|
16
|
+
"T_contra",
|
|
17
|
+
"KT",
|
|
18
|
+
"VT",
|
|
19
|
+
"P",
|
|
20
|
+
"R",
|
|
21
|
+
"_CALLABLE",
|
|
22
|
+
"CT",
|
|
23
|
+
"N",
|
|
24
|
+
"_Number",
|
|
25
|
+
"override",
|
|
26
|
+
# Protocol
|
|
27
|
+
"SupportsShowMethods",
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
# Library
|
|
32
|
+
# ---------------------------------------------------------------------------
|
|
33
|
+
from collections.abc import Callable
|
|
34
|
+
from typing import Any, ParamSpec, Protocol, TypeVar, overload
|
|
35
|
+
|
|
36
|
+
# Type
|
|
37
|
+
# ---------------------------------------------------------------------------
|
|
38
|
+
# Types where neither is possible are invariant
|
|
39
|
+
# Type safety must be strict
|
|
40
|
+
T = TypeVar("T") # Type invariant
|
|
41
|
+
# Type variables that are covariant can be substituted
|
|
42
|
+
# with a more specific type without causing errors
|
|
43
|
+
# Safe to treat a Box[int] as a Box[object]
|
|
44
|
+
T_co = TypeVar("T_co", covariant=True) # Type covariant
|
|
45
|
+
# Type variables that are contravariant can be substituted
|
|
46
|
+
# with a more general type without causing errors
|
|
47
|
+
# Safe to use a Box[object] where a Box[int] is needed
|
|
48
|
+
T_contra = TypeVar("T_contra", contravariant=True) # Type contravariant
|
|
49
|
+
|
|
50
|
+
KT = TypeVar("KT")
|
|
51
|
+
VT = TypeVar("VT")
|
|
52
|
+
|
|
53
|
+
# Callable
|
|
54
|
+
P = ParamSpec("P") # Parameter type
|
|
55
|
+
R = TypeVar("R") # Return type - Can be anything
|
|
56
|
+
_CALLABLE = Callable[P, R]
|
|
57
|
+
|
|
58
|
+
# Class type - Can be any subtype of `type`
|
|
59
|
+
CT = TypeVar("CT", bound=type)
|
|
60
|
+
|
|
61
|
+
# Number type
|
|
62
|
+
N = TypeVar("N", int, float) # Must be int or float
|
|
63
|
+
_Number = int | float
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
# Protocol
|
|
67
|
+
# ---------------------------------------------------------------------------
|
|
68
|
+
class SupportsShowMethods(Protocol):
|
|
69
|
+
"""
|
|
70
|
+
Support class with ``show_all_methods()``
|
|
71
|
+
and ``show_all_properties()`` method
|
|
72
|
+
from ``absfuyu.core.basclass.ShowAllMethodsMixin``
|
|
73
|
+
"""
|
|
74
|
+
|
|
75
|
+
@overload
|
|
76
|
+
@classmethod
|
|
77
|
+
def show_all_methods(cls) -> dict[str, list[str]]: ...
|
|
78
|
+
|
|
79
|
+
@overload
|
|
80
|
+
@classmethod
|
|
81
|
+
def show_all_methods(
|
|
82
|
+
cls,
|
|
83
|
+
print_result: bool = False,
|
|
84
|
+
include_classmethod: bool = True,
|
|
85
|
+
classmethod_indicator: str = "<classmethod>",
|
|
86
|
+
include_staticmethod: bool = True,
|
|
87
|
+
staticmethod_indicator: str = "<staticmethod>",
|
|
88
|
+
include_private_method: bool = False,
|
|
89
|
+
) -> dict[str, list[str]]: ...
|
|
90
|
+
|
|
91
|
+
@classmethod
|
|
92
|
+
def show_all_methods(cls, *args, **kwargs) -> Any: ...
|
|
93
|
+
|
|
94
|
+
@overload
|
|
95
|
+
@classmethod
|
|
96
|
+
def show_all_properties(cls) -> dict[str, list[str]]: ...
|
|
97
|
+
|
|
98
|
+
@overload
|
|
99
|
+
@classmethod
|
|
100
|
+
def show_all_properties(
|
|
101
|
+
cls, print_result: bool = False
|
|
102
|
+
) -> dict[str, list[str]]: ...
|
|
103
|
+
|
|
104
|
+
@classmethod
|
|
105
|
+
def show_all_properties(cls, *args, **kwargs) -> Any: ...
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
# Note
|
|
109
|
+
# ---------------------------------------------------------------------------
|
|
110
|
+
# Iterable : __iter__
|
|
111
|
+
# Iterator(Iterable) : __next__, __iter__
|
|
112
|
+
# Reversible(Iterable) : __reversed__, __iter__
|
|
113
|
+
# Sized : __len__
|
|
114
|
+
# Container : __contains__
|
|
115
|
+
# Collection(Sized, Iterable, Container) : __len__, __iter__, __contains__
|
|
116
|
+
# Set(Collection) : __contains__, __iter__, __len__
|
|
117
|
+
# MutableSet(Set) : __contains__, __iter__, __len__
|
|
118
|
+
# Mapping(Collection) : __getitem__, __iter__, and __len__
|
|
119
|
+
# MutableMapping(Mapping) : __getitem__, __setitem__, __delitem__,__iter__, __len__
|
|
120
|
+
# Sequence(Reversible, Collection) : __reversed__, __len__, __iter__, __contains__
|
|
121
|
+
# MutableSequence(Sequence) : __getitem__, __setitem__, __delitem__, __reversed__, __len__, __iter__, __contains__
|
|
122
|
+
|
|
123
|
+
# Iterable : str, dict, list, tuple, set
|
|
124
|
+
# Iterator(Iterable) : Generator
|
|
125
|
+
# Reversible(Iterable) : str, dict, list, tuple
|
|
126
|
+
# Sized : str, dict, list, tuple, set
|
|
127
|
+
# Container : str, dict, list, tuple, set
|
|
128
|
+
# Collection(Sized, Iterable, Container) : str, dict, list, tuple, set
|
|
129
|
+
# Set(Collection) : set
|
|
130
|
+
# MutableSet(Set) : set
|
|
131
|
+
# Mapping(Collection) : dict
|
|
132
|
+
# MutableMapping(Mapping) : dict
|
|
133
|
+
# Sequence(Reversible, Collection) : str, list, tuple
|
|
134
|
+
# MutableSequence(Sequence) : list
|
|
135
|
+
|
|
136
|
+
# __iter__: for <...> in <...>
|
|
137
|
+
# __len__: len(<...>)
|
|
138
|
+
# __contains__: if <...> in <...>
|
absfuyu/util/__init__.py
CHANGED
|
@@ -3,16 +3,34 @@ Absufyu: Utilities
|
|
|
3
3
|
------------------
|
|
4
4
|
Some random utilities
|
|
5
5
|
|
|
6
|
-
Version: 1.
|
|
7
|
-
Date updated:
|
|
6
|
+
Version: 6.1.1
|
|
7
|
+
Date updated: 30/12/2025 (dd/mm/yyyy)
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
|
+
# Module Package
|
|
11
|
+
# ---------------------------------------------------------------------------
|
|
12
|
+
__all__ = [
|
|
13
|
+
# util
|
|
14
|
+
"get_installed_package",
|
|
15
|
+
"set_min",
|
|
16
|
+
"set_max",
|
|
17
|
+
"set_min_max",
|
|
18
|
+
"stop_after_day",
|
|
19
|
+
"convert_to_raw_unicode",
|
|
20
|
+
# shorten number
|
|
21
|
+
"Decimal",
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
|
|
10
25
|
# Library
|
|
11
26
|
# ---------------------------------------------------------------------------
|
|
12
27
|
import pkgutil
|
|
28
|
+
import subprocess
|
|
13
29
|
from datetime import datetime
|
|
30
|
+
from string import printable
|
|
14
31
|
|
|
15
|
-
from absfuyu.core import deprecated, versionadded, versionchanged
|
|
32
|
+
from absfuyu.core.docstring import deprecated, versionadded, versionchanged
|
|
33
|
+
from absfuyu.util.shorten_number import Decimal
|
|
16
34
|
|
|
17
35
|
|
|
18
36
|
# Function
|
|
@@ -137,14 +155,23 @@ def set_min_max(
|
|
|
137
155
|
>>> set_min_max(808)
|
|
138
156
|
100
|
|
139
157
|
"""
|
|
140
|
-
|
|
141
|
-
current_value =
|
|
158
|
+
# Set min
|
|
159
|
+
# current_value = set_min(current_value, min_value=min_value)
|
|
160
|
+
current_value = max(current_value, min_value)
|
|
161
|
+
# Set max
|
|
162
|
+
# current_value = set_max(current_value, max_value=max_value)
|
|
163
|
+
current_value = min(current_value, max_value)
|
|
142
164
|
return current_value
|
|
143
165
|
|
|
144
166
|
|
|
167
|
+
@versionchanged("5.6.0", reason="New `custom_msg` parameter")
|
|
145
168
|
@versionadded("3.2.0")
|
|
146
169
|
def stop_after_day(
|
|
147
|
-
year: int | None = None,
|
|
170
|
+
year: int | None = None,
|
|
171
|
+
month: int | None = None,
|
|
172
|
+
day: int | None = None,
|
|
173
|
+
*,
|
|
174
|
+
custom_msg: str | None = None,
|
|
148
175
|
) -> None:
|
|
149
176
|
"""
|
|
150
177
|
Stop working after specified day.
|
|
@@ -163,6 +190,10 @@ def stop_after_day(
|
|
|
163
190
|
day : int
|
|
164
191
|
Desired day
|
|
165
192
|
(Default: ``None`` - 1 day trial)
|
|
193
|
+
|
|
194
|
+
custom_msg : str
|
|
195
|
+
Custom exit message
|
|
196
|
+
(Default: ``None``)
|
|
166
197
|
"""
|
|
167
198
|
# None checking - By default: 1 day trial
|
|
168
199
|
now = datetime.now()
|
|
@@ -177,4 +208,81 @@ def stop_after_day(
|
|
|
177
208
|
end_date = datetime(year, month, day)
|
|
178
209
|
result = end_date - now
|
|
179
210
|
if result.days < 0:
|
|
211
|
+
if custom_msg:
|
|
212
|
+
raise SystemExit(custom_msg)
|
|
180
213
|
raise SystemExit("End of time")
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
@versionadded("5.4.0")
|
|
217
|
+
def convert_to_raw_unicode(text: str, partial: bool = True) -> str:
|
|
218
|
+
r"""
|
|
219
|
+
Convert text to raw unicode variant.
|
|
220
|
+
|
|
221
|
+
Parameters
|
|
222
|
+
----------
|
|
223
|
+
text : str
|
|
224
|
+
Text to convert
|
|
225
|
+
|
|
226
|
+
partial : bool, optional
|
|
227
|
+
Only convert characters that not in ``string.printable``,
|
|
228
|
+
by default ``True``
|
|
229
|
+
|
|
230
|
+
Returns
|
|
231
|
+
-------
|
|
232
|
+
str
|
|
233
|
+
Converted text.
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
Example:
|
|
237
|
+
--------
|
|
238
|
+
>>> convert_to_raw_unicode("résumé")
|
|
239
|
+
r\u00E9sum\u00E9
|
|
240
|
+
|
|
241
|
+
>>> convert_to_raw_unicode("résumé", partial=False)
|
|
242
|
+
\u0072\u00E9\u0073\u0075\u006D\u00E9
|
|
243
|
+
"""
|
|
244
|
+
|
|
245
|
+
character_set = printable
|
|
246
|
+
|
|
247
|
+
def _convert(character: str) -> str:
|
|
248
|
+
"""Get unicode value"""
|
|
249
|
+
# ord(c): Returns the Unicode code point for a one-character string c.
|
|
250
|
+
_ord = ord(character)
|
|
251
|
+
# f"\\u{_ord:04X}": Formats the Unicode code point as a four-digit
|
|
252
|
+
# hexadecimal number for code points less than 0x10000.
|
|
253
|
+
# f"\\U{_ord:08X}": Formats the Unicode code point as an eight-digit
|
|
254
|
+
# hexadecimal number for code points greater than or equal to 0x10000.
|
|
255
|
+
return f"\\u{_ord:04X}" if _ord < 0x10000 else f"\\U{_ord:08X}"
|
|
256
|
+
|
|
257
|
+
return "".join(
|
|
258
|
+
character if character in character_set and partial else _convert(character)
|
|
259
|
+
for character in text
|
|
260
|
+
)
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
@versionadded("5.9.0")
|
|
264
|
+
def is_command_available(cmd: list[str] | str, err_msg: str = "") -> None:
|
|
265
|
+
"""
|
|
266
|
+
Checks if the desired command available
|
|
267
|
+
|
|
268
|
+
Parameters
|
|
269
|
+
----------
|
|
270
|
+
cmd : list[str] | str
|
|
271
|
+
Command to check
|
|
272
|
+
|
|
273
|
+
err_msg : str, optional
|
|
274
|
+
Custom error message, by default ""
|
|
275
|
+
|
|
276
|
+
Raises
|
|
277
|
+
------
|
|
278
|
+
ValueError
|
|
279
|
+
When command is unvailable
|
|
280
|
+
"""
|
|
281
|
+
try:
|
|
282
|
+
subprocess.run(
|
|
283
|
+
cmd,
|
|
284
|
+
stdout=subprocess.DEVNULL,
|
|
285
|
+
stderr=subprocess.DEVNULL,
|
|
286
|
+
)
|
|
287
|
+
except Exception:
|
|
288
|
+
raise ValueError(err_msg)
|
absfuyu/util/api.py
CHANGED
|
@@ -3,8 +3,8 @@ Absufyu: API
|
|
|
3
3
|
------------
|
|
4
4
|
Fetch data stuff
|
|
5
5
|
|
|
6
|
-
Version:
|
|
7
|
-
Date updated:
|
|
6
|
+
Version: 6.1.1
|
|
7
|
+
Date updated: 30/12/2025 (dd/mm/yyyy)
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
10
|
# Module level
|
|
@@ -23,18 +23,29 @@ import subprocess
|
|
|
23
23
|
from pathlib import Path
|
|
24
24
|
from typing import NamedTuple
|
|
25
25
|
|
|
26
|
-
import
|
|
27
|
-
|
|
28
|
-
from absfuyu.core import versionadded, versionchanged
|
|
26
|
+
from absfuyu.core.baseclass import BaseClass
|
|
27
|
+
from absfuyu.core.docstring import versionadded, versionchanged
|
|
29
28
|
from absfuyu.logger import logger
|
|
30
29
|
|
|
30
|
+
try:
|
|
31
|
+
import requests
|
|
32
|
+
except ImportError:
|
|
33
|
+
raise ImportError("Please install requests package")
|
|
34
|
+
|
|
31
35
|
|
|
32
36
|
# Function
|
|
33
37
|
# ---------------------------------------------------------------------------
|
|
34
38
|
class PingResult(NamedTuple):
|
|
35
39
|
"""
|
|
36
|
-
|
|
37
|
-
|
|
40
|
+
Ping result
|
|
41
|
+
|
|
42
|
+
Parameters
|
|
43
|
+
----------
|
|
44
|
+
host : str
|
|
45
|
+
Host name/IP
|
|
46
|
+
|
|
47
|
+
result : str
|
|
48
|
+
Ping result in ms
|
|
38
49
|
"""
|
|
39
50
|
|
|
40
51
|
host: str
|
|
@@ -90,28 +101,40 @@ def ping_windows(host: list[str], ping_count: int = 3) -> list[PingResult]:
|
|
|
90
101
|
|
|
91
102
|
# Class
|
|
92
103
|
# ---------------------------------------------------------------------------
|
|
93
|
-
class APIRequest:
|
|
94
|
-
"""
|
|
104
|
+
class APIRequest(BaseClass):
|
|
105
|
+
"""
|
|
106
|
+
API data with cache feature
|
|
107
|
+
|
|
108
|
+
Parameters
|
|
109
|
+
----------
|
|
110
|
+
api_url : str
|
|
111
|
+
API link
|
|
112
|
+
|
|
113
|
+
encoding : str | None, optional
|
|
114
|
+
Data encoding, by default ``"utf-8"``
|
|
115
|
+
"""
|
|
95
116
|
|
|
96
117
|
def __init__(
|
|
97
118
|
self,
|
|
98
119
|
api_url: str,
|
|
99
|
-
*,
|
|
120
|
+
*,
|
|
100
121
|
encoding: str | None = "utf-8",
|
|
101
122
|
) -> None:
|
|
102
123
|
"""
|
|
103
|
-
|
|
104
|
-
|
|
124
|
+
Create APIRequest instance
|
|
125
|
+
|
|
126
|
+
Parameters
|
|
127
|
+
----------
|
|
128
|
+
api_url : str
|
|
129
|
+
API link
|
|
130
|
+
|
|
131
|
+
encoding : str | None, optional
|
|
132
|
+
Data encoding, by default ``"utf-8"``
|
|
105
133
|
"""
|
|
134
|
+
|
|
106
135
|
self.url = api_url
|
|
107
136
|
self.encoding = encoding
|
|
108
137
|
|
|
109
|
-
def __str__(self) -> str:
|
|
110
|
-
return f"{self.__class__.__name__}({self.url})"
|
|
111
|
-
|
|
112
|
-
def __repr__(self) -> str:
|
|
113
|
-
return self.__str__()
|
|
114
|
-
|
|
115
138
|
def fetch_data(self, *, update: bool = False, json_cache: str | Path):
|
|
116
139
|
"""
|
|
117
140
|
Fetch data from an API then cache it for later use
|
absfuyu/util/cli.py
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Absfuyu: CLI
|
|
3
|
+
------------
|
|
4
|
+
Custom Argument Parser
|
|
5
|
+
|
|
6
|
+
Version: 6.1.1
|
|
7
|
+
Date updated: 30/12/2025 (dd/mm/yyyy)
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
# Module level
|
|
11
|
+
# ---------------------------------------------------------------------------
|
|
12
|
+
__all__ = ["FuyuArgumentParser"]
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
# Library
|
|
16
|
+
# ---------------------------------------------------------------------------
|
|
17
|
+
import logging
|
|
18
|
+
import sys
|
|
19
|
+
from argparse import ArgumentDefaultsHelpFormatter, ArgumentParser
|
|
20
|
+
from collections.abc import Callable
|
|
21
|
+
from typing import Any
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
# Class
|
|
25
|
+
# ---------------------------------------------------------------------------
|
|
26
|
+
class FuyuArgumentParser(ArgumentParser):
|
|
27
|
+
"""
|
|
28
|
+
Usage:
|
|
29
|
+
------
|
|
30
|
+
>>> import sys
|
|
31
|
+
>>> args = FuyuArgumentParser()
|
|
32
|
+
>>> args.start()
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
def __init__(
|
|
36
|
+
self,
|
|
37
|
+
prog: str | None = None,
|
|
38
|
+
description: str | None = None,
|
|
39
|
+
epilog: str | None = None,
|
|
40
|
+
version: str = "0.0.1",
|
|
41
|
+
formatter_class: Any = ArgumentDefaultsHelpFormatter,
|
|
42
|
+
prefix_chars: str = "-",
|
|
43
|
+
argument_default: Any = None,
|
|
44
|
+
conflict_handler: str = "error",
|
|
45
|
+
add_help: bool = True,
|
|
46
|
+
allow_abbrev: bool = True,
|
|
47
|
+
exit_on_error: bool = True,
|
|
48
|
+
) -> None:
|
|
49
|
+
# Desc
|
|
50
|
+
if description is None:
|
|
51
|
+
description = f"Absfuyu CLI {version}"
|
|
52
|
+
|
|
53
|
+
# Default
|
|
54
|
+
self._default_args = ["--help"]
|
|
55
|
+
|
|
56
|
+
# Super
|
|
57
|
+
super().__init__(
|
|
58
|
+
prog=prog,
|
|
59
|
+
description=description,
|
|
60
|
+
epilog=epilog,
|
|
61
|
+
formatter_class=formatter_class,
|
|
62
|
+
prefix_chars=prefix_chars,
|
|
63
|
+
argument_default=argument_default,
|
|
64
|
+
conflict_handler=conflict_handler,
|
|
65
|
+
add_help=add_help,
|
|
66
|
+
allow_abbrev=allow_abbrev,
|
|
67
|
+
exit_on_error=exit_on_error,
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
# Add version
|
|
71
|
+
self.add_argument("-v", "--version", action="version", version=f"%(prog)s {version}")
|
|
72
|
+
|
|
73
|
+
# Add log level
|
|
74
|
+
_ll_val = ["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]
|
|
75
|
+
self.add_argument(
|
|
76
|
+
"-ll",
|
|
77
|
+
"--log-level",
|
|
78
|
+
metavar="LOG_LEVEL",
|
|
79
|
+
dest="log_level",
|
|
80
|
+
choices=_ll_val,
|
|
81
|
+
default="ERROR",
|
|
82
|
+
help=f"Log level: {', '.join(_ll_val)}",
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
def _fuyu_set_log_level(self, log_level: str = "ERROR") -> None:
|
|
86
|
+
log_levels = {
|
|
87
|
+
"DEBUG": logging.DEBUG,
|
|
88
|
+
"INFO": logging.INFO,
|
|
89
|
+
"WARNING": logging.WARNING,
|
|
90
|
+
"ERROR": logging.ERROR,
|
|
91
|
+
"CRITICAL": logging.CRITICAL,
|
|
92
|
+
}
|
|
93
|
+
logging.basicConfig(
|
|
94
|
+
level=log_levels[log_level],
|
|
95
|
+
format="[%(asctime)s] [%(module)s] [%(name)s] [%(funcName)s] [%(levelname)-s] %(message)s",
|
|
96
|
+
datefmt="%Y-%m-%d %H:%M:%S",
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
def start(self, default: Callable[[], Any] | None = None):
|
|
100
|
+
"""
|
|
101
|
+
Quick start for argument parser
|
|
102
|
+
|
|
103
|
+
Parameters
|
|
104
|
+
----------
|
|
105
|
+
default : Callable[[], Any] | None, optional
|
|
106
|
+
| Default callable to run, by default ``None``
|
|
107
|
+
| Shows help on default behavior (default=None)
|
|
108
|
+
|
|
109
|
+
Returns
|
|
110
|
+
-------
|
|
111
|
+
Namespace
|
|
112
|
+
Namespace
|
|
113
|
+
"""
|
|
114
|
+
if default is None:
|
|
115
|
+
args = self.parse_args(args=None if sys.argv[1:] else self._default_args)
|
|
116
|
+
else:
|
|
117
|
+
args = self.parse_args(args=None if sys.argv[1:] else default())
|
|
118
|
+
self._fuyu_set_log_level(getattr(args, "log_level", "ERROR"))
|
|
119
|
+
return args
|
absfuyu/util/gui.py
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Absfuyu: GUI
|
|
3
|
+
------------
|
|
4
|
+
Custom tkinter GUI
|
|
5
|
+
|
|
6
|
+
Version: 6.1.1
|
|
7
|
+
Date updated: 30/12/2025 (dd/mm/yyyy)
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
# Module level
|
|
11
|
+
# ---------------------------------------------------------------------------
|
|
12
|
+
__all__ = ["CustomTkinterApp"]
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
# Library
|
|
16
|
+
# ---------------------------------------------------------------------------
|
|
17
|
+
import tkinter as tk
|
|
18
|
+
|
|
19
|
+
from absfuyu.pkg_data.logo import AbsfuyuLogo
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
# Class
|
|
23
|
+
# ---------------------------------------------------------------------------
|
|
24
|
+
class CustomTkinterApp(tk.Tk):
|
|
25
|
+
|
|
26
|
+
def __init__(self, title: str | None = None, size: tuple[int, int] | None = None) -> None:
|
|
27
|
+
"""
|
|
28
|
+
Custom Tkinter GUI
|
|
29
|
+
|
|
30
|
+
Parameters
|
|
31
|
+
----------
|
|
32
|
+
title : str | None, optional
|
|
33
|
+
Title of the app, by default None
|
|
34
|
+
|
|
35
|
+
size : tuple[int, int] | None, optional
|
|
36
|
+
Size of the app (width, height), by default None
|
|
37
|
+
"""
|
|
38
|
+
super().__init__()
|
|
39
|
+
|
|
40
|
+
# Set custom icon
|
|
41
|
+
self.iconphoto(True, tk.PhotoImage(data=AbsfuyuLogo.SHORT))
|
|
42
|
+
|
|
43
|
+
# Title
|
|
44
|
+
self.title(title)
|
|
45
|
+
|
|
46
|
+
# Set size
|
|
47
|
+
self._absfuyu_set_width_height(size)
|
|
48
|
+
|
|
49
|
+
# @versionadded("5.10.0")
|
|
50
|
+
def _absfuyu_set_width_height(self, size: tuple[int, int] | None = None) -> None:
|
|
51
|
+
"""
|
|
52
|
+
Set width and height for the app.
|
|
53
|
+
|
|
54
|
+
Parameters
|
|
55
|
+
----------
|
|
56
|
+
size : tuple[int, int] | None, optional
|
|
57
|
+
Size of the app (width, height), by default ``None``
|
|
58
|
+
(420 x 250)
|
|
59
|
+
"""
|
|
60
|
+
# Set GUI appears in center
|
|
61
|
+
if size is None:
|
|
62
|
+
_width = 420
|
|
63
|
+
_height = 250
|
|
64
|
+
else:
|
|
65
|
+
_width, _height = size
|
|
66
|
+
|
|
67
|
+
_width_screen = self.winfo_screenwidth()
|
|
68
|
+
_width_screen_offset = 0.0052 # x offset mutiplier
|
|
69
|
+
_x_offset = int(_width_screen * _width_screen_offset)
|
|
70
|
+
|
|
71
|
+
_height_screen = self.winfo_screenheight()
|
|
72
|
+
_height_screen_offset = 0.0926 # y offset mutiplier
|
|
73
|
+
_y_offset = int(_height_screen * _height_screen_offset)
|
|
74
|
+
|
|
75
|
+
if _width > _width_screen:
|
|
76
|
+
_width = int(_width_screen * (1 - 0.001))
|
|
77
|
+
# _x_offset = 0
|
|
78
|
+
|
|
79
|
+
if _height > (_height_screen - _y_offset):
|
|
80
|
+
_height = int(_height_screen * (1 - 0.001))
|
|
81
|
+
_y_offset = 0
|
|
82
|
+
|
|
83
|
+
_x = (_width_screen / 2) - (_width / 2) - _x_offset
|
|
84
|
+
_y = (_height_screen / 2) - (_height / 2) - _y_offset
|
|
85
|
+
|
|
86
|
+
self.geometry(f"{_width}x{_height}+{int(_x)}+{int(_y)}")
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
if __name__ == "__main__":
|
|
90
|
+
app = CustomTkinterApp("absfuyu")
|
|
91
|
+
app.mainloop()
|