absfuyu 5.6.1__py3-none-any.whl → 6.1.3__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 +458 -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.3.dist-info}/METADATA +37 -8
- absfuyu-6.1.3.dist-info/RECORD +105 -0
- {absfuyu-5.6.1.dist-info → absfuyu-6.1.3.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.3.dist-info}/entry_points.txt +0 -0
- {absfuyu-5.6.1.dist-info → absfuyu-6.1.3.dist-info}/licenses/LICENSE +0 -0
absfuyu/sort.py
CHANGED
|
@@ -3,8 +3,8 @@ Absfuyu: Sort
|
|
|
3
3
|
-------------
|
|
4
4
|
Sort Module
|
|
5
5
|
|
|
6
|
-
Version:
|
|
7
|
-
Date updated: 12/
|
|
6
|
+
Version: 6.1.2
|
|
7
|
+
Date updated: 30/12/2025 (dd/mm/yyyy)
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
10
|
# Module level
|
|
@@ -26,7 +26,7 @@ from typing import Any
|
|
|
26
26
|
|
|
27
27
|
# Functions
|
|
28
28
|
# ---------------------------------------------------------------------------
|
|
29
|
-
def selection_sort(iterable: list, reverse: bool = False) -> list:
|
|
29
|
+
def selection_sort[T](iterable: list[T], reverse: bool = False) -> list[T]:
|
|
30
30
|
"""
|
|
31
31
|
Sort the list with selection sort (bubble sort) algorithm
|
|
32
32
|
|
|
@@ -61,7 +61,7 @@ def selection_sort(iterable: list, reverse: bool = False) -> list:
|
|
|
61
61
|
return iterable
|
|
62
62
|
|
|
63
63
|
|
|
64
|
-
def insertion_sort(iterable: list) -> list:
|
|
64
|
+
def insertion_sort[T](iterable: list[T]) -> list[T]:
|
|
65
65
|
"""
|
|
66
66
|
Sort the list with insertion sort algorithm
|
|
67
67
|
|
absfuyu/tools/__init__.py
CHANGED
absfuyu/tools/checksum.py
CHANGED
|
@@ -3,13 +3,19 @@ Absufyu: Checksum
|
|
|
3
3
|
-----------------
|
|
4
4
|
Check MD5, SHA256, ...
|
|
5
5
|
|
|
6
|
-
Version:
|
|
7
|
-
Date updated: 12/
|
|
6
|
+
Version: 6.1.2
|
|
7
|
+
Date updated: 30/12/2025 (dd/mm/yyyy)
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
10
|
# Module level
|
|
11
11
|
# ---------------------------------------------------------------------------
|
|
12
|
-
__all__ = [
|
|
12
|
+
__all__ = [
|
|
13
|
+
# Checksum
|
|
14
|
+
"Checksum",
|
|
15
|
+
"ChecksumMode",
|
|
16
|
+
# Mixin
|
|
17
|
+
"DirectoryRemoveDuplicateMixin",
|
|
18
|
+
]
|
|
13
19
|
|
|
14
20
|
|
|
15
21
|
# Library
|
|
@@ -22,6 +28,8 @@ from typing import Literal
|
|
|
22
28
|
from absfuyu.core.baseclass import BaseClass
|
|
23
29
|
from absfuyu.core.docstring import deprecated, versionadded, versionchanged
|
|
24
30
|
from absfuyu.core.dummy_func import tqdm
|
|
31
|
+
from absfuyu.dxt import DictExt, ListExt
|
|
32
|
+
from absfuyu.util.path import DirectoryBase
|
|
25
33
|
|
|
26
34
|
|
|
27
35
|
# Function
|
|
@@ -64,6 +72,52 @@ class ChecksumMode(StrEnum):
|
|
|
64
72
|
SHA512 = "sha512"
|
|
65
73
|
|
|
66
74
|
|
|
75
|
+
class DuplicateSummary(DictExt[str, list[Path]]):
|
|
76
|
+
"""
|
|
77
|
+
Duplicate file summary
|
|
78
|
+
"""
|
|
79
|
+
|
|
80
|
+
def summary(self) -> int:
|
|
81
|
+
"""
|
|
82
|
+
Show how many duplicates (include the original)
|
|
83
|
+
|
|
84
|
+
Returns
|
|
85
|
+
-------
|
|
86
|
+
int
|
|
87
|
+
How many duplicates
|
|
88
|
+
"""
|
|
89
|
+
temp = self.__class__(self.copy())
|
|
90
|
+
try:
|
|
91
|
+
return sum(temp.apply(lambda x: len(x)).values())
|
|
92
|
+
except Exception as err:
|
|
93
|
+
print(f"Something wrong - {err}")
|
|
94
|
+
|
|
95
|
+
def remove_duplicates(self, dry_run: bool = True, keep_first: bool = True, debug: bool = False) -> None:
|
|
96
|
+
"""
|
|
97
|
+
Remove duplicates
|
|
98
|
+
|
|
99
|
+
Parameters
|
|
100
|
+
----------
|
|
101
|
+
dry_run : bool, optional
|
|
102
|
+
Simulate only (no files deleted), by default ``True``
|
|
103
|
+
|
|
104
|
+
keep_first : bool, optional
|
|
105
|
+
Keep the first duplicate file, will keep the last duplicate file when ``False``, by default ``True``
|
|
106
|
+
"""
|
|
107
|
+
temp = self.__class__(self.copy())
|
|
108
|
+
removable_files = ListExt([x[1:] if keep_first else x[:-1] for x in temp.values()]).flatten()
|
|
109
|
+
|
|
110
|
+
for x in removable_files:
|
|
111
|
+
x: Path = x
|
|
112
|
+
|
|
113
|
+
if debug or dry_run:
|
|
114
|
+
print(f"Deleting {x}")
|
|
115
|
+
if dry_run:
|
|
116
|
+
continue
|
|
117
|
+
|
|
118
|
+
x.unlink(missing_ok=True)
|
|
119
|
+
|
|
120
|
+
|
|
67
121
|
@versionchanged("4.1.1", reason="Checksum for entire folder is possible")
|
|
68
122
|
@versionadded("4.1.0")
|
|
69
123
|
class Checksum(BaseClass):
|
|
@@ -130,7 +184,8 @@ class Checksum(BaseClass):
|
|
|
130
184
|
"""This performs checksum"""
|
|
131
185
|
|
|
132
186
|
hash_engine = self._get_hash_engine().copy()
|
|
133
|
-
with open(Path(file), "rb") as f:
|
|
187
|
+
# with open(Path(file), "rb") as f:
|
|
188
|
+
with file.open("rb") as f:
|
|
134
189
|
# Read and hash the file in 4K chunks. Reading the whole
|
|
135
190
|
# file at once might consume a lot of memory if it is
|
|
136
191
|
# large.
|
|
@@ -183,3 +238,63 @@ class Checksum(BaseClass):
|
|
|
183
238
|
f.write(output)
|
|
184
239
|
|
|
185
240
|
return output
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
# Mixin
|
|
244
|
+
class DirectoryRemoveDuplicateMixin(DirectoryBase):
|
|
245
|
+
"""
|
|
246
|
+
Directory - Remove duplicate by SHA256
|
|
247
|
+
|
|
248
|
+
- remove_duplicate
|
|
249
|
+
"""
|
|
250
|
+
|
|
251
|
+
def __init__(self, source_path, create_if_not_exist=False) -> None:
|
|
252
|
+
super().__init__(source_path, create_if_not_exist)
|
|
253
|
+
|
|
254
|
+
self._duplicate_cache: DuplicateSummary | None = None
|
|
255
|
+
|
|
256
|
+
def _gather_duplicate_cache(self, recursive: bool = True) -> None:
|
|
257
|
+
engine = Checksum(self.source_path, hash_mode=ChecksumMode.SHA256, save_result_to_file=False)
|
|
258
|
+
valid = [x for x in engine.path.glob("**/*" if recursive else "*") if x.is_file()]
|
|
259
|
+
checksum_cache = {}
|
|
260
|
+
|
|
261
|
+
# Checksum
|
|
262
|
+
for x in tqdm(valid, unit_scale=True, desc="Checking..."):
|
|
263
|
+
try:
|
|
264
|
+
cs_res = engine._checksum_operation(x)
|
|
265
|
+
|
|
266
|
+
if checksum_cache.get(cs_res) is None:
|
|
267
|
+
checksum_cache[cs_res] = [x]
|
|
268
|
+
else:
|
|
269
|
+
checksum_cache[cs_res] += [x]
|
|
270
|
+
except Exception as err:
|
|
271
|
+
print(f"ERROR: {x} - {err}")
|
|
272
|
+
continue
|
|
273
|
+
|
|
274
|
+
# Save to cache
|
|
275
|
+
self._duplicate_cache = DuplicateSummary({k: v for k, v in checksum_cache.items() if len(v) > 1})
|
|
276
|
+
|
|
277
|
+
def remove_duplicate(self, dry_run: bool = True, recursive: bool = True, debug: bool = True) -> None:
|
|
278
|
+
"""
|
|
279
|
+
Remove duplicate files by SHA256 checksum
|
|
280
|
+
|
|
281
|
+
Parameters
|
|
282
|
+
----------
|
|
283
|
+
dry_run : bool, optional
|
|
284
|
+
Simulate only (no files deleted), by default ``True``
|
|
285
|
+
|
|
286
|
+
recursive : bool, optional
|
|
287
|
+
Scan every file in the folder (including child folder), by default ``True``
|
|
288
|
+
|
|
289
|
+
debug : bool, optional
|
|
290
|
+
Print delete messages, by default ``True``
|
|
291
|
+
"""
|
|
292
|
+
self._gather_duplicate_cache(recursive=recursive)
|
|
293
|
+
|
|
294
|
+
# Remove
|
|
295
|
+
try:
|
|
296
|
+
summary = self._duplicate_cache
|
|
297
|
+
print(f"Duplicate files: {summary.summary()}")
|
|
298
|
+
summary.remove_duplicates(dry_run=dry_run, keep_first=False, debug=debug)
|
|
299
|
+
except Exception as err:
|
|
300
|
+
pass
|
absfuyu/tools/converter.py
CHANGED
absfuyu/tools/generator.py
CHANGED
|
@@ -3,8 +3,8 @@ Absfuyu: Generator
|
|
|
3
3
|
------------------
|
|
4
4
|
This generate stuff (Not python's ``generator``)
|
|
5
5
|
|
|
6
|
-
Version:
|
|
7
|
-
Date updated: 12/
|
|
6
|
+
Version: 6.1.2
|
|
7
|
+
Date updated: 30/12/2025 (dd/mm/yyyy)
|
|
8
8
|
|
|
9
9
|
Features:
|
|
10
10
|
---------
|
|
@@ -25,7 +25,7 @@ import string
|
|
|
25
25
|
from collections.abc import Collection, Sequence
|
|
26
26
|
from itertools import chain, combinations
|
|
27
27
|
from random import choice
|
|
28
|
-
from typing import TypeVar, cast, overload
|
|
28
|
+
from typing import Literal, TypeVar, cast, overload
|
|
29
29
|
|
|
30
30
|
from absfuyu.core.baseclass import GetClassMembersMixin
|
|
31
31
|
from absfuyu.core.docstring import deprecated
|
|
@@ -78,6 +78,25 @@ class Generator(GetClassMembersMixin):
|
|
|
78
78
|
"""
|
|
79
79
|
|
|
80
80
|
# Generate string
|
|
81
|
+
@overload
|
|
82
|
+
@staticmethod
|
|
83
|
+
def generate_string( # type: ignore
|
|
84
|
+
charset: str = Charset.DEFAULT,
|
|
85
|
+
size: int = 8,
|
|
86
|
+
times: int = 1,
|
|
87
|
+
unique: bool = False,
|
|
88
|
+
) -> list[str]: ...
|
|
89
|
+
|
|
90
|
+
@overload
|
|
91
|
+
@staticmethod
|
|
92
|
+
def generate_string(
|
|
93
|
+
charset: str = Charset.DEFAULT,
|
|
94
|
+
size: int = 8,
|
|
95
|
+
times: int = 1,
|
|
96
|
+
unique: bool = False,
|
|
97
|
+
string_type_if_1: Literal[True] = ...,
|
|
98
|
+
) -> str: ...
|
|
99
|
+
|
|
81
100
|
@staticmethod
|
|
82
101
|
@deprecated("5.2.0", reason="Use generate_string2() instead.")
|
|
83
102
|
def generate_string(
|
|
@@ -86,7 +105,7 @@ class Generator(GetClassMembersMixin):
|
|
|
86
105
|
times: int = 1,
|
|
87
106
|
unique: bool = False,
|
|
88
107
|
string_type_if_1: bool = False,
|
|
89
|
-
):
|
|
108
|
+
) -> str | list[str]:
|
|
90
109
|
"""
|
|
91
110
|
Generate a list of random string from character set (Random string generator).
|
|
92
111
|
Deprecated
|
|
@@ -394,9 +413,7 @@ class Generator(GetClassMembersMixin):
|
|
|
394
413
|
|
|
395
414
|
@overload
|
|
396
415
|
@staticmethod
|
|
397
|
-
def combinations_range(
|
|
398
|
-
collection: Collection[T], *, min_len: int = 1, max_len: int = 0
|
|
399
|
-
) -> list[tuple[T, ...]]: ...
|
|
416
|
+
def combinations_range(collection: Collection[T], *, min_len: int = 1, max_len: int = 0) -> list[tuple[T, ...]]: ...
|
|
400
417
|
|
|
401
418
|
@staticmethod
|
|
402
419
|
def combinations_range(
|
absfuyu/tools/inspector.py
CHANGED
absfuyu/tools/keygen.py
CHANGED
absfuyu/tools/obfuscator.py
CHANGED
absfuyu/tools/passwordlib.py
CHANGED
absfuyu/tools/shutdownizer.py
CHANGED
|
@@ -3,8 +3,8 @@ Absfuyu: Shutdownizer
|
|
|
3
3
|
---------------------
|
|
4
4
|
This shutdowns
|
|
5
5
|
|
|
6
|
-
Version:
|
|
7
|
-
Date updated: 12/
|
|
6
|
+
Version: 6.1.2
|
|
7
|
+
Date updated: 30/12/2025 (dd/mm/yyyy)
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
10
|
# Module level
|
|
@@ -20,12 +20,7 @@ import sys
|
|
|
20
20
|
from abc import ABC, abstractmethod
|
|
21
21
|
from datetime import datetime, time, timedelta
|
|
22
22
|
from pathlib import Path
|
|
23
|
-
from typing import Annotated
|
|
24
|
-
|
|
25
|
-
try:
|
|
26
|
-
from typing import override # type: ignore
|
|
27
|
-
except ImportError:
|
|
28
|
-
from absfuyu.core.decorator import dummy_decorator as override
|
|
23
|
+
from typing import Annotated, override
|
|
29
24
|
|
|
30
25
|
from absfuyu.core import BaseClass, versionadded, versionchanged
|
|
31
26
|
from absfuyu.logger import logger
|
absfuyu/tools/sw.py
CHANGED
|
@@ -3,8 +3,8 @@ Absufyu: Software
|
|
|
3
3
|
-----------------
|
|
4
4
|
Software, pyinstaller related stuff
|
|
5
5
|
|
|
6
|
-
Version:
|
|
7
|
-
Date updated: 12/
|
|
6
|
+
Version: 6.1.2
|
|
7
|
+
Date updated: 30/12/2025 (dd/mm/yyyy)
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
10
|
# Module level
|
|
@@ -14,12 +14,14 @@ __all__ = [
|
|
|
14
14
|
"get_system_info",
|
|
15
15
|
"get_pyinstaller_exe_dir",
|
|
16
16
|
"get_pyinstaller_resource_path",
|
|
17
|
+
"PyinstallerHelper",
|
|
17
18
|
"HWIDgen",
|
|
18
19
|
"LicenseKeySystem",
|
|
19
20
|
"BasicSoftwareProtection",
|
|
20
21
|
# Support
|
|
21
22
|
"SystemInfo",
|
|
22
23
|
"LicenseKey",
|
|
24
|
+
"PyinstallerHiddenImportPreset",
|
|
23
25
|
]
|
|
24
26
|
|
|
25
27
|
|
|
@@ -40,13 +42,13 @@ from pathlib import Path
|
|
|
40
42
|
from typing import Literal, NamedTuple, TypedDict
|
|
41
43
|
|
|
42
44
|
from absfuyu.core.baseclass import BaseClass
|
|
43
|
-
from absfuyu.core.docstring import versionchanged
|
|
45
|
+
from absfuyu.core.docstring import versionadded, versionchanged
|
|
44
46
|
from absfuyu.dxt import Text
|
|
45
47
|
from absfuyu.tools.converter import Base64EncodeDecode
|
|
46
48
|
from absfuyu.util import stop_after_day
|
|
47
49
|
|
|
48
50
|
|
|
49
|
-
# System Info
|
|
51
|
+
# MARK: System Info
|
|
50
52
|
# ---------------------------------------------------------------------------
|
|
51
53
|
class SystemInfo(NamedTuple):
|
|
52
54
|
"""System info"""
|
|
@@ -93,7 +95,7 @@ def get_system_info() -> SystemInfo:
|
|
|
93
95
|
)
|
|
94
96
|
|
|
95
97
|
|
|
96
|
-
# Pyinstaller
|
|
98
|
+
# MARK: Pyinstaller
|
|
97
99
|
# ---------------------------------------------------------------------------
|
|
98
100
|
@versionchanged("5.6.1", "Fixed behavior")
|
|
99
101
|
def get_pyinstaller_exe_dir() -> Path:
|
|
@@ -123,7 +125,7 @@ def get_pyinstaller_resource_path(relative_path: str) -> Path:
|
|
|
123
125
|
environments and PyInstaller-packaged executables.
|
|
124
126
|
|
|
125
127
|
When running from a PyInstaller bundle, this function resolves the path relative
|
|
126
|
-
to the temporary
|
|
128
|
+
to the temporary ``_MEIPASS`` folder. During normal execution, it resolves the path
|
|
127
129
|
relative to the current script's directory.
|
|
128
130
|
|
|
129
131
|
Parameters
|
|
@@ -151,7 +153,137 @@ def get_pyinstaller_resource_path(relative_path: str) -> Path:
|
|
|
151
153
|
return base_path / relative_path
|
|
152
154
|
|
|
153
155
|
|
|
154
|
-
|
|
156
|
+
class PyinstallerHiddenImportPreset:
|
|
157
|
+
"""
|
|
158
|
+
pyinstaller hidden import preset (library preset)
|
|
159
|
+
|
|
160
|
+
Example:
|
|
161
|
+
--------
|
|
162
|
+
>>> PyinstallerHelper(...).add_hidden_import(*PyinstallerHiddenImportPreset.ABSFUYU)
|
|
163
|
+
"""
|
|
164
|
+
|
|
165
|
+
ABSFUYU = ["absfuyu"]
|
|
166
|
+
DF = ["pandas", "numpy", "openpyxl", "xlsxwriter"] # DataFrame
|
|
167
|
+
VISUAL = ["rich", "tqdm"]
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
@versionadded("5.11.0")
|
|
171
|
+
class PyinstallerHelper(BaseClass):
|
|
172
|
+
"""pyinstaller helper"""
|
|
173
|
+
|
|
174
|
+
def __init__(
|
|
175
|
+
self,
|
|
176
|
+
path_to_file: str | Path,
|
|
177
|
+
relative_to_cwd: bool = True,
|
|
178
|
+
console: bool = True,
|
|
179
|
+
onefile: bool = False,
|
|
180
|
+
noconfirm: bool = False,
|
|
181
|
+
) -> None:
|
|
182
|
+
"""
|
|
183
|
+
pyinstaller cmd helper
|
|
184
|
+
|
|
185
|
+
Parameters
|
|
186
|
+
----------
|
|
187
|
+
path_to_file : str | Path
|
|
188
|
+
Path to .py file to make .exe
|
|
189
|
+
|
|
190
|
+
relative_to_cwd : bool, optional
|
|
191
|
+
Is the file relative to cwd, by default True
|
|
192
|
+
|
|
193
|
+
console : bool, optional
|
|
194
|
+
Include console, by default True
|
|
195
|
+
|
|
196
|
+
onefile : bool, optional
|
|
197
|
+
Convert into one file, by default False
|
|
198
|
+
|
|
199
|
+
noconfirm : bool, optional
|
|
200
|
+
No confirmation, by default False
|
|
201
|
+
"""
|
|
202
|
+
self.source_path = Path(path_to_file)
|
|
203
|
+
self.relative_to_cwd = relative_to_cwd
|
|
204
|
+
self.console = console
|
|
205
|
+
self.onefile = onefile
|
|
206
|
+
self.noconfirm = noconfirm
|
|
207
|
+
|
|
208
|
+
if self.relative_to_cwd:
|
|
209
|
+
# rel = self.source_path.relative_to(Path.cwd())
|
|
210
|
+
# self._base_cmd = ["pyinstaller", f"'.\\{Path('.').joinpath(rel)}'"]
|
|
211
|
+
self._base_cmd = ["pyinstaller", f"'.\\{self.source_path.relative_to(Path.cwd())}'"]
|
|
212
|
+
else:
|
|
213
|
+
self._base_cmd = ["pyinstaller", f"'{self.source_path.resolve()}'"]
|
|
214
|
+
|
|
215
|
+
self._hidden_import = []
|
|
216
|
+
self._icon = ""
|
|
217
|
+
|
|
218
|
+
def add_hidden_import(self, *library: str) -> None:
|
|
219
|
+
"""
|
|
220
|
+
Add hidden import (library)
|
|
221
|
+
"""
|
|
222
|
+
if len(self._hidden_import) < 1:
|
|
223
|
+
self._hidden_import = list(library)
|
|
224
|
+
else:
|
|
225
|
+
self._hidden_import.extend(list(library))
|
|
226
|
+
|
|
227
|
+
def add_icon(self, path_to_icon: str | Path, *, relative_to_cwd: bool | None = None) -> None:
|
|
228
|
+
"""
|
|
229
|
+
Add icon to .exe
|
|
230
|
+
|
|
231
|
+
Parameters
|
|
232
|
+
----------
|
|
233
|
+
path_to_icon : str | Path
|
|
234
|
+
Path to icon file
|
|
235
|
+
|
|
236
|
+
relative_to_cwd : bool | None, optional
|
|
237
|
+
Use boolean value to overwrite relative_to_cwd option of the main engine, by default None
|
|
238
|
+
"""
|
|
239
|
+
p = Path(path_to_icon)
|
|
240
|
+
use_relative = self.relative_to_cwd if relative_to_cwd is None else relative_to_cwd
|
|
241
|
+
|
|
242
|
+
# --icon=favicon.ico
|
|
243
|
+
if use_relative:
|
|
244
|
+
rel = p.relative_to(Path.cwd())
|
|
245
|
+
# ensure it always shows with ./ prefix
|
|
246
|
+
# self._icon = f"'{Path('.').joinpath(rel)}'"
|
|
247
|
+
self._icon = f"--icon='.\\{rel}'"
|
|
248
|
+
else:
|
|
249
|
+
self._icon = f"--icon='{p.resolve()}'"
|
|
250
|
+
|
|
251
|
+
def export_cmd(self) -> str:
|
|
252
|
+
"""
|
|
253
|
+
Export pyinstaller cmd
|
|
254
|
+
|
|
255
|
+
Returns
|
|
256
|
+
-------
|
|
257
|
+
str
|
|
258
|
+
pyinstaller command
|
|
259
|
+
"""
|
|
260
|
+
cmd = self._base_cmd
|
|
261
|
+
|
|
262
|
+
# Hidden import
|
|
263
|
+
if len(self._hidden_import) > 0:
|
|
264
|
+
dat = (f"--hidden-import={x}" for x in list(set(self._hidden_import)))
|
|
265
|
+
cmd.append(" ".join(dat))
|
|
266
|
+
|
|
267
|
+
# Console
|
|
268
|
+
if not self.console:
|
|
269
|
+
cmd.append("--noconsole")
|
|
270
|
+
|
|
271
|
+
# One file
|
|
272
|
+
if self.onefile:
|
|
273
|
+
cmd.append("--onefile")
|
|
274
|
+
|
|
275
|
+
# No confirm
|
|
276
|
+
if self.noconfirm:
|
|
277
|
+
cmd.append("--noconfirm")
|
|
278
|
+
|
|
279
|
+
# Icon
|
|
280
|
+
if len(self._icon) > 0:
|
|
281
|
+
cmd.append(self._icon)
|
|
282
|
+
|
|
283
|
+
return " ".join(cmd)
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
# MARK: Key System
|
|
155
287
|
# ---------------------------------------------------------------------------
|
|
156
288
|
class HWIDgen(BaseClass):
|
|
157
289
|
"""
|
|
@@ -385,7 +517,7 @@ class LicenseKeySystem(BaseClass):
|
|
|
385
517
|
return False
|
|
386
518
|
|
|
387
519
|
|
|
388
|
-
# Software
|
|
520
|
+
# MARK: Software
|
|
389
521
|
# ---------------------------------------------------------------------------
|
|
390
522
|
class BasicSoftwareProtection(BaseClass):
|
|
391
523
|
"""
|
|
@@ -474,6 +606,28 @@ class BasicSoftwareProtection(BaseClass):
|
|
|
474
606
|
self._secret = secret
|
|
475
607
|
|
|
476
608
|
def check_valid_license(self, generate_helper: bool = True) -> None:
|
|
609
|
+
"""
|
|
610
|
+
Check for valid license
|
|
611
|
+
(``.zlic`` file in the same directory as the script that runs this code)
|
|
612
|
+
|
|
613
|
+
Parameters
|
|
614
|
+
----------
|
|
615
|
+
generate_helper : bool, optional
|
|
616
|
+
Generate a helper file (``license.helper``),
|
|
617
|
+
which can be used to make license key,
|
|
618
|
+
by default ``True``
|
|
619
|
+
|
|
620
|
+
Raises
|
|
621
|
+
------
|
|
622
|
+
SystemExit
|
|
623
|
+
License file not found!
|
|
624
|
+
|
|
625
|
+
SystemExit
|
|
626
|
+
Invalid license key format!
|
|
627
|
+
|
|
628
|
+
SystemExit
|
|
629
|
+
Invalid license!
|
|
630
|
+
"""
|
|
477
631
|
try:
|
|
478
632
|
# Get license file
|
|
479
633
|
license_file = list(self._cwd.glob("*.zlic"))[0]
|
|
@@ -501,14 +655,63 @@ class BasicSoftwareProtection(BaseClass):
|
|
|
501
655
|
else: # Invalid license
|
|
502
656
|
raise SystemExit("Invalid license!")
|
|
503
657
|
|
|
658
|
+
@versionadded("5.9.0")
|
|
659
|
+
def check_valid_license_gui(
|
|
660
|
+
self, title: str = "WARNING", message: str = "INVALID LICENSE!", generate_helper: bool = True
|
|
661
|
+
) -> None:
|
|
662
|
+
"""
|
|
663
|
+
Check for valid license
|
|
664
|
+
(``.zlic`` file in the same directory as the script that runs this code)
|
|
665
|
+
but will pop up a GUI when invalid license.
|
|
666
|
+
|
|
667
|
+
Parameters
|
|
668
|
+
----------
|
|
669
|
+
title : str, optional
|
|
670
|
+
Title of the GUI, by default "WARNING"
|
|
671
|
+
|
|
672
|
+
message : str, optional
|
|
673
|
+
Message in the GUI, by default "INVALID LICENSE!"
|
|
674
|
+
|
|
675
|
+
generate_helper : bool, optional
|
|
676
|
+
Generate a helper file (``license.helper``),
|
|
677
|
+
which can be used to make license key,
|
|
678
|
+
by default ``True``
|
|
679
|
+
|
|
680
|
+
Raises
|
|
681
|
+
------
|
|
682
|
+
SystemExit
|
|
683
|
+
Invalid license
|
|
684
|
+
"""
|
|
685
|
+
try:
|
|
686
|
+
self.check_valid_license(generate_helper=generate_helper)
|
|
687
|
+
except SystemExit:
|
|
688
|
+
from tkinter import messagebox
|
|
689
|
+
|
|
690
|
+
messagebox.showwarning(title, message, icon="error")
|
|
691
|
+
raise SystemExit("Invalid license")
|
|
692
|
+
|
|
504
693
|
# Make key
|
|
505
|
-
def _make_key(self, name: str, expiry: str, secret: str):
|
|
694
|
+
def _make_key(self, name: str, expiry: str, secret: str) -> None:
|
|
695
|
+
"""
|
|
696
|
+
Generate license key in the same directory as the script that runs this code.
|
|
697
|
+
|
|
698
|
+
Parameters
|
|
699
|
+
----------
|
|
700
|
+
name : str
|
|
701
|
+
Name of license's holder
|
|
702
|
+
|
|
703
|
+
expiry : str
|
|
704
|
+
Expiry date in format "yyyy-mm-dd"
|
|
705
|
+
|
|
706
|
+
secret : str
|
|
707
|
+
Secret string
|
|
708
|
+
"""
|
|
506
709
|
path = self._cwd.joinpath("license.zlic")
|
|
507
710
|
engine = LicenseKeySystem(name, expiry, secret)
|
|
508
711
|
with path.open("w", encoding="utf-8") as f:
|
|
509
712
|
f.write(engine.make_key())
|
|
510
713
|
|
|
511
|
-
def generate_license_helper(self):
|
|
714
|
+
def generate_license_helper(self) -> None:
|
|
512
715
|
"""Gather HWID and make it into a file"""
|
|
513
716
|
path = self._cwd.joinpath("license.helper")
|
|
514
717
|
with path.open("w", encoding="utf-8") as f:
|
absfuyu/tools/web.py
CHANGED
|
@@ -3,21 +3,14 @@ Absfuyu: Web
|
|
|
3
3
|
------------
|
|
4
4
|
Web, ``request``, ``BeautifulSoup`` stuff
|
|
5
5
|
|
|
6
|
-
Version:
|
|
7
|
-
Date updated: 12/
|
|
6
|
+
Version: 6.1.2
|
|
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
|