absfuyu 5.0.1__py3-none-any.whl → 5.1.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.
- absfuyu/__init__.py +1 -1
- absfuyu/__main__.py +2 -2
- absfuyu/cli/__init__.py +2 -2
- absfuyu/cli/color.py +30 -14
- absfuyu/cli/config_group.py +9 -2
- absfuyu/cli/do_group.py +13 -6
- absfuyu/cli/game_group.py +9 -2
- absfuyu/cli/tool_group.py +16 -9
- absfuyu/config/__init__.py +2 -2
- absfuyu/core/__init__.py +2 -2
- absfuyu/core/baseclass.py +448 -79
- absfuyu/core/baseclass2.py +2 -2
- absfuyu/core/decorator.py +69 -3
- absfuyu/core/docstring.py +2 -2
- absfuyu/core/dummy_cli.py +2 -2
- absfuyu/core/dummy_func.py +13 -2
- absfuyu/core/typings.py +40 -0
- absfuyu/dxt/__init__.py +2 -2
- absfuyu/dxt/dictext.py +2 -2
- absfuyu/dxt/dxt_support.py +2 -2
- absfuyu/dxt/intext.py +31 -3
- absfuyu/dxt/listext.py +28 -3
- absfuyu/dxt/strext.py +2 -2
- absfuyu/extra/__init__.py +2 -2
- absfuyu/extra/beautiful.py +2 -2
- absfuyu/extra/da/__init__.py +36 -0
- absfuyu/extra/da/dadf.py +1138 -0
- absfuyu/extra/da/dadf_base.py +186 -0
- absfuyu/extra/da/df_func.py +97 -0
- absfuyu/extra/da/mplt.py +219 -0
- absfuyu/extra/data_analysis.py +10 -1067
- absfuyu/fun/__init__.py +2 -2
- absfuyu/fun/tarot.py +2 -2
- absfuyu/game/__init__.py +2 -2
- absfuyu/game/game_stat.py +2 -2
- absfuyu/game/sudoku.py +2 -2
- absfuyu/game/tictactoe.py +2 -2
- absfuyu/game/wordle.py +2 -2
- absfuyu/general/__init__.py +2 -2
- absfuyu/general/content.py +2 -2
- absfuyu/general/human.py +2 -2
- absfuyu/general/shape.py +2 -2
- absfuyu/logger.py +2 -2
- absfuyu/pkg_data/__init__.py +2 -2
- absfuyu/pkg_data/deprecated.py +2 -2
- absfuyu/sort.py +2 -2
- absfuyu/tools/__init__.py +25 -2
- absfuyu/tools/checksum.py +27 -7
- absfuyu/tools/converter.py +93 -28
- absfuyu/tools/generator.py +2 -2
- absfuyu/tools/inspector.py +433 -0
- absfuyu/tools/keygen.py +2 -2
- absfuyu/tools/obfuscator.py +45 -7
- absfuyu/tools/passwordlib.py +87 -22
- absfuyu/tools/shutdownizer.py +2 -2
- absfuyu/tools/web.py +2 -2
- absfuyu/util/__init__.py +2 -2
- absfuyu/util/api.py +2 -2
- absfuyu/util/json_method.py +2 -2
- absfuyu/util/lunar.py +2 -2
- absfuyu/util/path.py +190 -82
- absfuyu/util/performance.py +4 -4
- absfuyu/util/shorten_number.py +40 -10
- absfuyu/util/text_table.py +272 -0
- absfuyu/util/zipped.py +6 -6
- absfuyu/version.py +2 -2
- {absfuyu-5.0.1.dist-info → absfuyu-5.1.0.dist-info}/METADATA +9 -2
- absfuyu-5.1.0.dist-info/RECORD +76 -0
- absfuyu-5.0.1.dist-info/RECORD +0 -68
- {absfuyu-5.0.1.dist-info → absfuyu-5.1.0.dist-info}/WHEEL +0 -0
- {absfuyu-5.0.1.dist-info → absfuyu-5.1.0.dist-info}/entry_points.txt +0 -0
- {absfuyu-5.0.1.dist-info → absfuyu-5.1.0.dist-info}/licenses/LICENSE +0 -0
absfuyu/tools/passwordlib.py
CHANGED
|
@@ -3,13 +3,13 @@ Absfuyu: Passwordlib
|
|
|
3
3
|
--------------------
|
|
4
4
|
Password library
|
|
5
5
|
|
|
6
|
-
Version: 5.
|
|
7
|
-
Date updated:
|
|
6
|
+
Version: 5.1.0
|
|
7
|
+
Date updated: 10/03/2025 (dd/mm/yyyy)
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
10
|
# Module level
|
|
11
11
|
# ---------------------------------------------------------------------------
|
|
12
|
-
__all__ = ["PasswordGenerator", "TOTP"]
|
|
12
|
+
__all__ = ["PasswordGenerator", "PasswordHash", "TOTP"]
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
# Library
|
|
@@ -21,7 +21,8 @@ import re
|
|
|
21
21
|
from typing import ClassVar, Literal, NamedTuple
|
|
22
22
|
from urllib.parse import quote, urlencode
|
|
23
23
|
|
|
24
|
-
from absfuyu.core import BaseClass
|
|
24
|
+
from absfuyu.core.baseclass import BaseClass
|
|
25
|
+
from absfuyu.core.docstring import deprecated, versionadded
|
|
25
26
|
from absfuyu.dxt import DictExt, Text
|
|
26
27
|
from absfuyu.logger import logger
|
|
27
28
|
from absfuyu.pkg_data import DataList, DataLoader
|
|
@@ -88,6 +89,18 @@ def _password_check(password: str) -> bool:
|
|
|
88
89
|
# Class
|
|
89
90
|
# ---------------------------------------------------------------------------
|
|
90
91
|
class PasswordHash(NamedTuple):
|
|
92
|
+
"""
|
|
93
|
+
Password hash
|
|
94
|
+
|
|
95
|
+
Parameters
|
|
96
|
+
----------
|
|
97
|
+
salt : bytes
|
|
98
|
+
Salt
|
|
99
|
+
|
|
100
|
+
key : bytes
|
|
101
|
+
Key
|
|
102
|
+
"""
|
|
103
|
+
|
|
91
104
|
salt: bytes
|
|
92
105
|
key: bytes
|
|
93
106
|
|
|
@@ -102,7 +115,17 @@ class PasswordGenerator(BaseClass):
|
|
|
102
115
|
@staticmethod
|
|
103
116
|
def password_hash(password: str) -> PasswordHash:
|
|
104
117
|
"""
|
|
105
|
-
Generate hash for password
|
|
118
|
+
Generate hash for password.
|
|
119
|
+
|
|
120
|
+
Parameters
|
|
121
|
+
----------
|
|
122
|
+
password : str
|
|
123
|
+
Password string
|
|
124
|
+
|
|
125
|
+
Returns
|
|
126
|
+
-------
|
|
127
|
+
PasswordHash
|
|
128
|
+
Password hash contains salt and key
|
|
106
129
|
"""
|
|
107
130
|
salt = os.urandom(32)
|
|
108
131
|
key = hashlib.pbkdf2_hmac(
|
|
@@ -116,7 +139,19 @@ class PasswordGenerator(BaseClass):
|
|
|
116
139
|
|
|
117
140
|
@staticmethod
|
|
118
141
|
def password_check(password: str) -> dict:
|
|
119
|
-
"""
|
|
142
|
+
"""
|
|
143
|
+
Check password's characteristic.
|
|
144
|
+
|
|
145
|
+
Parameters
|
|
146
|
+
----------
|
|
147
|
+
password : str
|
|
148
|
+
Password string
|
|
149
|
+
|
|
150
|
+
Returns
|
|
151
|
+
-------
|
|
152
|
+
dict
|
|
153
|
+
Password's characteristic.
|
|
154
|
+
"""
|
|
120
155
|
data = Text(password).analyze()
|
|
121
156
|
data = DictExt(data).apply(lambda x: True if x > 0 else False) # type: ignore
|
|
122
157
|
data.__setitem__("length", len(password))
|
|
@@ -131,7 +166,7 @@ class PasswordGenerator(BaseClass):
|
|
|
131
166
|
include_special: bool = True,
|
|
132
167
|
) -> str:
|
|
133
168
|
r"""
|
|
134
|
-
Generate a random password
|
|
169
|
+
Generate a random password.
|
|
135
170
|
|
|
136
171
|
Parameters
|
|
137
172
|
----------
|
|
@@ -141,13 +176,13 @@ class PasswordGenerator(BaseClass):
|
|
|
141
176
|
| (Default: ``8``)
|
|
142
177
|
|
|
143
178
|
include_uppercase : bool
|
|
144
|
-
Include uppercase character in the password
|
|
179
|
+
Include uppercase character in the password, by default ``True``
|
|
145
180
|
|
|
146
181
|
include_number : bool
|
|
147
|
-
Include digit character in the password
|
|
182
|
+
Include digit character in the password, by default ``True``
|
|
148
183
|
|
|
149
184
|
include_special : bool
|
|
150
|
-
Include special character in the password
|
|
185
|
+
Include special character in the password, by default ``True``
|
|
151
186
|
|
|
152
187
|
Returns
|
|
153
188
|
-------
|
|
@@ -205,16 +240,16 @@ class PasswordGenerator(BaseClass):
|
|
|
205
240
|
Parameters
|
|
206
241
|
----------
|
|
207
242
|
num_of_blocks : int
|
|
208
|
-
Number of word used
|
|
243
|
+
Number of word used, by default ``5``
|
|
209
244
|
|
|
210
245
|
block_divider : str
|
|
211
|
-
Character symbol that between each word
|
|
246
|
+
Character symbol that between each word, by default ``"-"``
|
|
212
247
|
|
|
213
248
|
first_letter_cap : bool
|
|
214
|
-
Capitalize first character of each word
|
|
249
|
+
Capitalize first character of each word, by default ``True``
|
|
215
250
|
|
|
216
251
|
include_number : bool
|
|
217
|
-
Add number to the end of each word
|
|
252
|
+
Add number to the end of each word, by default ``True``
|
|
218
253
|
|
|
219
254
|
custom_word_list : list[str] | None
|
|
220
255
|
Custom word list for passphrase generation, by default uses a list of 360K+ words
|
|
@@ -255,6 +290,33 @@ class PasswordGenerator(BaseClass):
|
|
|
255
290
|
class TOTP(BaseClass):
|
|
256
291
|
"""
|
|
257
292
|
A class to represent a Time-based One-Time Password (TOTP) generator.
|
|
293
|
+
|
|
294
|
+
Parameters
|
|
295
|
+
----------
|
|
296
|
+
secret : str
|
|
297
|
+
The shared secret key used to generate the TOTP.
|
|
298
|
+
|
|
299
|
+
name : str, optional
|
|
300
|
+
| The name associated with the TOTP.
|
|
301
|
+
| If not provided, by default ``"None"``.
|
|
302
|
+
|
|
303
|
+
issuer : str, optional
|
|
304
|
+
The issuer of the TOTP.
|
|
305
|
+
|
|
306
|
+
algorithm : Literal["SHA1", "SHA256", "SHA512"], optional
|
|
307
|
+
| The hashing algorithm used to generate the TOTP.
|
|
308
|
+
| Must be one of ``"SHA1"``, ``"SHA256"``, or ``"SHA512"``.
|
|
309
|
+
| By default ``"SHA1"``.
|
|
310
|
+
|
|
311
|
+
digit : int, optional
|
|
312
|
+
| The number of digits in the generated TOTP.
|
|
313
|
+
| Must be greater than 0.
|
|
314
|
+
| By default ``6``.
|
|
315
|
+
|
|
316
|
+
period : int, optional
|
|
317
|
+
| The time step in seconds for TOTP generation.
|
|
318
|
+
| Must be greater than 0.
|
|
319
|
+
| by default ``30``.
|
|
258
320
|
"""
|
|
259
321
|
|
|
260
322
|
URL_SCHEME: ClassVar[str] = "otpauth://totp/"
|
|
@@ -277,23 +339,26 @@ class TOTP(BaseClass):
|
|
|
277
339
|
The shared secret key used to generate the TOTP.
|
|
278
340
|
|
|
279
341
|
name : str, optional
|
|
280
|
-
The name associated with the TOTP.
|
|
342
|
+
| The name associated with the TOTP.
|
|
343
|
+
| If not provided, by default ``"None"``.
|
|
281
344
|
|
|
282
345
|
issuer : str, optional
|
|
283
346
|
The issuer of the TOTP.
|
|
284
347
|
|
|
285
348
|
algorithm : Literal["SHA1", "SHA256", "SHA512"], optional
|
|
286
|
-
The hashing algorithm used to generate the TOTP.
|
|
287
|
-
Must be one of ``"SHA1"``, ``"SHA256"``, or ``"SHA512"``.
|
|
288
|
-
|
|
349
|
+
| The hashing algorithm used to generate the TOTP.
|
|
350
|
+
| Must be one of ``"SHA1"``, ``"SHA256"``, or ``"SHA512"``.
|
|
351
|
+
| By default ``"SHA1"``.
|
|
289
352
|
|
|
290
353
|
digit : int, optional
|
|
291
|
-
The number of digits in the generated TOTP.
|
|
292
|
-
|
|
354
|
+
| The number of digits in the generated TOTP.
|
|
355
|
+
| Must be greater than 0.
|
|
356
|
+
| By default ``6``.
|
|
293
357
|
|
|
294
358
|
period : int, optional
|
|
295
|
-
The time step in seconds for TOTP generation.
|
|
296
|
-
|
|
359
|
+
| The time step in seconds for TOTP generation.
|
|
360
|
+
| Must be greater than 0.
|
|
361
|
+
| by default ``30``.
|
|
297
362
|
"""
|
|
298
363
|
self.secret = secret.upper()
|
|
299
364
|
self.name = name if name else "None"
|
absfuyu/tools/shutdownizer.py
CHANGED
absfuyu/tools/web.py
CHANGED
absfuyu/util/__init__.py
CHANGED
absfuyu/util/api.py
CHANGED
absfuyu/util/json_method.py
CHANGED
absfuyu/util/lunar.py
CHANGED
absfuyu/util/path.py
CHANGED
|
@@ -3,8 +3,8 @@ Absfuyu: Path
|
|
|
3
3
|
-------------
|
|
4
4
|
Path related
|
|
5
5
|
|
|
6
|
-
Version: 5.
|
|
7
|
-
Date updated:
|
|
6
|
+
Version: 5.1.0
|
|
7
|
+
Date updated: 10/03/2025 (dd/mm/yyyy)
|
|
8
8
|
|
|
9
9
|
Feature:
|
|
10
10
|
--------
|
|
@@ -16,8 +16,15 @@ Feature:
|
|
|
16
16
|
# ---------------------------------------------------------------------------
|
|
17
17
|
__all__ = [
|
|
18
18
|
# Main
|
|
19
|
+
"DirectoryBase",
|
|
19
20
|
"Directory",
|
|
20
21
|
"SaveFileAs",
|
|
22
|
+
# Mixin
|
|
23
|
+
"DirectoryInfoMixin",
|
|
24
|
+
"DirectoryBasicOperationMixin",
|
|
25
|
+
"DirectoryArchiverMixin",
|
|
26
|
+
"DirectoryOrganizerMixin",
|
|
27
|
+
"DirectoryTreeMixin",
|
|
21
28
|
# Support
|
|
22
29
|
"FileOrFolderWithModificationTime",
|
|
23
30
|
"DirectoryInfo",
|
|
@@ -32,9 +39,11 @@ import shutil
|
|
|
32
39
|
from datetime import datetime
|
|
33
40
|
from functools import partial
|
|
34
41
|
from pathlib import Path
|
|
35
|
-
from typing import Any, Literal, NamedTuple
|
|
42
|
+
from typing import Any, ClassVar, Literal, NamedTuple
|
|
36
43
|
|
|
37
|
-
from absfuyu.core import
|
|
44
|
+
from absfuyu.core.baseclass import BaseClass
|
|
45
|
+
from absfuyu.core.decorator import add_subclass_methods_decorator
|
|
46
|
+
from absfuyu.core.docstring import deprecated, versionadded, versionchanged
|
|
38
47
|
from absfuyu.logger import logger
|
|
39
48
|
|
|
40
49
|
|
|
@@ -53,21 +62,40 @@ class FileOrFolderWithModificationTime(NamedTuple):
|
|
|
53
62
|
modification_time: datetime
|
|
54
63
|
|
|
55
64
|
|
|
65
|
+
@deprecated(
|
|
66
|
+
"5.1.0", reason="Support for ``DirectoryInfoMixin`` which is also deprecated"
|
|
67
|
+
)
|
|
56
68
|
@versionadded("3.3.0")
|
|
57
69
|
class DirectoryInfo(NamedTuple):
|
|
58
70
|
"""
|
|
59
71
|
Information of a directory
|
|
60
72
|
"""
|
|
61
73
|
|
|
62
|
-
files: int
|
|
63
|
-
folders: int
|
|
64
74
|
creation_time: datetime
|
|
65
75
|
modification_time: datetime
|
|
66
76
|
|
|
67
77
|
|
|
68
|
-
# Class - Directory
|
|
78
|
+
# Class - Directory
|
|
69
79
|
# ---------------------------------------------------------------------------
|
|
70
|
-
|
|
80
|
+
@add_subclass_methods_decorator
|
|
81
|
+
class DirectoryBase(BaseClass):
|
|
82
|
+
"""
|
|
83
|
+
Directory - Base
|
|
84
|
+
|
|
85
|
+
Parameters
|
|
86
|
+
----------
|
|
87
|
+
source_path : str | Path
|
|
88
|
+
Source folder
|
|
89
|
+
|
|
90
|
+
create_if_not_exist : bool
|
|
91
|
+
Create directory when not exist,
|
|
92
|
+
by default ``False``
|
|
93
|
+
"""
|
|
94
|
+
|
|
95
|
+
# Custom attribute
|
|
96
|
+
_METHOD_INCLUDE: ClassVar[bool] = True # Include in DIR_METHODS
|
|
97
|
+
SUBCLASS_METHODS: ClassVar[dict[str, list[str]]] = {}
|
|
98
|
+
|
|
71
99
|
def __init__(
|
|
72
100
|
self,
|
|
73
101
|
source_path: str | Path,
|
|
@@ -80,80 +108,53 @@ class DirectoryBase:
|
|
|
80
108
|
Source folder
|
|
81
109
|
|
|
82
110
|
create_if_not_exist : bool
|
|
83
|
-
Create directory when not exist
|
|
84
|
-
|
|
111
|
+
Create directory when not exist,
|
|
112
|
+
by default ``False``
|
|
85
113
|
"""
|
|
86
114
|
self.source_path = Path(source_path)
|
|
87
|
-
if
|
|
88
|
-
if
|
|
115
|
+
if not self.source_path.exists():
|
|
116
|
+
if create_if_not_exist:
|
|
89
117
|
self.source_path.mkdir(exist_ok=True, parents=True)
|
|
118
|
+
else:
|
|
119
|
+
raise FileNotFoundError("Directory not existed")
|
|
90
120
|
|
|
91
|
-
def __str__(self) -> str:
|
|
92
|
-
return self.source_path.__str__()
|
|
93
|
-
|
|
94
|
-
def __repr__(self) -> str:
|
|
95
|
-
return f"{self.__class__.__name__}({self.source_path})"
|
|
96
|
-
|
|
97
|
-
def __format__(self, __format_spec: str) -> str:
|
|
98
|
-
"""
|
|
99
|
-
Change format of an object.
|
|
100
|
-
Avaiable option: ``info``
|
|
101
|
-
|
|
102
|
-
Usage
|
|
103
|
-
-----
|
|
104
|
-
>>> print(f"{<object>:<format_spec>}")
|
|
105
|
-
>>> print(<object>.__format__(<format_spec>))
|
|
106
|
-
>>> print(format(<object>, <format_spec>))
|
|
107
|
-
"""
|
|
108
|
-
# Show quick info
|
|
109
|
-
if __format_spec.lower().startswith("info"):
|
|
110
|
-
return self.quick_info().__repr__()
|
|
111
|
-
|
|
112
|
-
# No format spec
|
|
113
|
-
return self.__repr__()
|
|
114
121
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
def everything(self) -> list[Path]:
|
|
119
|
-
"""
|
|
120
|
-
Every folders and files in this Directory
|
|
121
|
-
"""
|
|
122
|
-
return list(x for x in self.source_path.glob("**/*"))
|
|
123
|
-
|
|
124
|
-
@versionadded("3.3.0")
|
|
125
|
-
def _every_folder(self) -> list[Path]:
|
|
126
|
-
"""
|
|
127
|
-
Every folders in this Directory
|
|
128
|
-
"""
|
|
129
|
-
return list(x for x in self.source_path.glob("**/*") if x.is_dir())
|
|
122
|
+
class DirectoryInfoMixin(DirectoryBase):
|
|
123
|
+
"""
|
|
124
|
+
Directory - Info
|
|
130
125
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
"""
|
|
134
|
-
Every folders in this Directory
|
|
135
|
-
"""
|
|
136
|
-
return list(x for x in self.source_path.glob("**/*") if x.is_file())
|
|
126
|
+
- Quick info
|
|
127
|
+
"""
|
|
137
128
|
|
|
138
|
-
|
|
129
|
+
@deprecated("5.1.0", reason="Not efficient")
|
|
139
130
|
@versionadded("3.3.0")
|
|
140
131
|
def quick_info(self) -> DirectoryInfo:
|
|
141
132
|
"""
|
|
142
133
|
Quick information about this Directory
|
|
143
134
|
|
|
144
|
-
|
|
135
|
+
Returns
|
|
136
|
+
-------
|
|
137
|
+
DirectoryInfo
|
|
138
|
+
DirectoryInfo
|
|
145
139
|
"""
|
|
146
140
|
source_stat: os.stat_result = self.source_path.stat()
|
|
147
141
|
out = DirectoryInfo(
|
|
148
|
-
files=len(self._every_file()),
|
|
149
|
-
folders=len(self._every_folder()),
|
|
150
142
|
creation_time=datetime.fromtimestamp(source_stat.st_ctime),
|
|
151
143
|
modification_time=datetime.fromtimestamp(source_stat.st_mtime),
|
|
152
144
|
)
|
|
153
145
|
return out
|
|
154
146
|
|
|
155
147
|
|
|
156
|
-
class
|
|
148
|
+
class DirectoryBasicOperationMixin(DirectoryBase):
|
|
149
|
+
"""
|
|
150
|
+
Directory - Basic operation
|
|
151
|
+
|
|
152
|
+
- Rename
|
|
153
|
+
- Copy
|
|
154
|
+
- Move
|
|
155
|
+
- Delete
|
|
156
|
+
"""
|
|
157
|
+
|
|
157
158
|
# Rename
|
|
158
159
|
def rename(self, new_name: str) -> None:
|
|
159
160
|
"""
|
|
@@ -214,7 +215,7 @@ class DirectoryBasicOperation(DirectoryBase):
|
|
|
214
215
|
shutil.move(self.source_path, Path(dst))
|
|
215
216
|
logger.debug(f"Moving to {dst}...DONE")
|
|
216
217
|
|
|
217
|
-
except
|
|
218
|
+
except OSError as e: # File already exists
|
|
218
219
|
logger.error(e)
|
|
219
220
|
logger.debug("Overwriting file...")
|
|
220
221
|
if content_only:
|
|
@@ -316,31 +317,54 @@ class DirectoryBasicOperation(DirectoryBase):
|
|
|
316
317
|
except Exception as e:
|
|
317
318
|
logger.error(f"Removing {self.source_path}...FAILED\n{e}")
|
|
318
319
|
|
|
319
|
-
|
|
320
|
+
|
|
321
|
+
class DirectoryArchiverMixin(DirectoryBase):
|
|
322
|
+
"""
|
|
323
|
+
Directory - Archiver/Compress
|
|
324
|
+
|
|
325
|
+
- Compress
|
|
326
|
+
- Decompress
|
|
327
|
+
- Register extra zip format <staticmethod>
|
|
328
|
+
"""
|
|
329
|
+
|
|
330
|
+
@versionchanged("5.1.0", reason="Update funcionality (new parameter)")
|
|
320
331
|
def compress(
|
|
321
|
-
self,
|
|
332
|
+
self,
|
|
333
|
+
format: Literal["zip", "tar", "gztar", "bztar", "xztar"] = "zip",
|
|
334
|
+
delete_after_compress: bool = False,
|
|
335
|
+
move_inside: bool = True,
|
|
322
336
|
) -> Path | None:
|
|
323
337
|
"""
|
|
324
338
|
Compress the directory (Default: Create ``.zip`` file)
|
|
325
339
|
|
|
326
340
|
Parameters
|
|
327
341
|
----------
|
|
328
|
-
format : Literal["zip", "tar", "gztar", "bztar", "xztar"]
|
|
342
|
+
format : Literal["zip", "tar", "gztar", "bztar", "xztar"], optional
|
|
343
|
+
By default ``"zip"``
|
|
329
344
|
- ``zip``: ZIP file (if the ``zlib`` module is available).
|
|
330
345
|
- ``tar``: Uncompressed tar file. Uses POSIX.1-2001 pax format for new archives.
|
|
331
346
|
- ``gztar``: gzip'ed tar-file (if the ``zlib`` module is available).
|
|
332
347
|
- ``bztar``: bzip2'ed tar-file (if the ``bz2`` module is available).
|
|
333
348
|
- ``xztar``: xz'ed tar-file (if the ``lzma`` module is available).
|
|
334
349
|
|
|
350
|
+
delete_after_compress : bool, optional
|
|
351
|
+
Delete directory after compress, by default ``False``
|
|
352
|
+
|
|
353
|
+
move_inside : bool, optional
|
|
354
|
+
Move the commpressed file inside the directory,
|
|
355
|
+
by default ``True``
|
|
356
|
+
|
|
335
357
|
Returns
|
|
336
358
|
-------
|
|
337
359
|
Path
|
|
338
360
|
Compressed path
|
|
361
|
+
|
|
339
362
|
None
|
|
340
363
|
When fail to compress
|
|
341
364
|
"""
|
|
342
365
|
logger.debug(f"Zipping {self.source_path}...")
|
|
343
366
|
try:
|
|
367
|
+
# Zip
|
|
344
368
|
# zip_name = self.source_path.parent.joinpath(self.source_path.name).__str__()
|
|
345
369
|
# shutil.make_archive(zip_name, format=format, root_dir=self.source_path)
|
|
346
370
|
zip_path = shutil.make_archive(
|
|
@@ -348,26 +372,85 @@ class DirectoryBasicOperation(DirectoryBase):
|
|
|
348
372
|
)
|
|
349
373
|
logger.debug(f"Zipping {self.source_path}...DONE")
|
|
350
374
|
logger.debug(f"Path: {zip_path}")
|
|
375
|
+
|
|
376
|
+
# Del
|
|
377
|
+
if delete_after_compress:
|
|
378
|
+
move_inside = False
|
|
379
|
+
shutil.rmtree(self.source_path)
|
|
380
|
+
|
|
381
|
+
# Move
|
|
382
|
+
if move_inside:
|
|
383
|
+
zf = Path(zip_path)
|
|
384
|
+
_move_path = self.source_path.joinpath(zf.name)
|
|
385
|
+
if _move_path.exists():
|
|
386
|
+
_move_path.unlink(missing_ok=True)
|
|
387
|
+
_move = zf.rename(_move_path)
|
|
388
|
+
return _move
|
|
389
|
+
|
|
351
390
|
return Path(zip_path)
|
|
352
|
-
except
|
|
391
|
+
except (FileExistsError, OSError) as e:
|
|
353
392
|
logger.error(f"Zipping {self.source_path}...FAILED\n{e}")
|
|
354
393
|
return None
|
|
355
394
|
|
|
395
|
+
@staticmethod
|
|
396
|
+
@versionadded("5.1.0")
|
|
397
|
+
def register_extra_zip_format() -> None:
|
|
398
|
+
"""This register extra extension for zipfile"""
|
|
399
|
+
extra_extension = [".zip", ".cbz"]
|
|
400
|
+
shutil.unregister_unpack_format("zip")
|
|
401
|
+
shutil.register_unpack_format(
|
|
402
|
+
"zip",
|
|
403
|
+
extra_extension,
|
|
404
|
+
shutil._unpack_zipfile, # type: ignore
|
|
405
|
+
description="ZIP file",
|
|
406
|
+
)
|
|
356
407
|
|
|
357
|
-
|
|
358
|
-
|
|
408
|
+
@versionadded("5.1.0")
|
|
409
|
+
def decompress(
|
|
410
|
+
self,
|
|
411
|
+
format: Literal["zip", "tar", "gztar", "bztar", "xztar"] | None = None,
|
|
412
|
+
delete_after_done: bool = False,
|
|
413
|
+
) -> None:
|
|
414
|
+
"""
|
|
415
|
+
Decompress compressed file in directory (first level only)
|
|
416
|
+
|
|
417
|
+
Parameters
|
|
418
|
+
----------
|
|
419
|
+
format : Literal["zip", "tar", "gztar", "bztar", "xztar"] | None, optional
|
|
420
|
+
By default ``None``
|
|
421
|
+
- ``zip``: ZIP file (if the ``zlib`` module is available).
|
|
422
|
+
- ``tar``: Uncompressed tar file. Uses POSIX.1-2001 pax format for new archives.
|
|
423
|
+
- ``gztar``: gzip'ed tar-file (if the ``zlib`` module is available).
|
|
424
|
+
- ``bztar``: bzip2'ed tar-file (if the ``bz2`` module is available).
|
|
425
|
+
- ``xztar``: xz'ed tar-file (if the ``lzma`` module is available).
|
|
359
426
|
|
|
427
|
+
delete_after_done : bool, optional
|
|
428
|
+
Delete compressed file when extracted, by default ``False``
|
|
429
|
+
"""
|
|
430
|
+
# Register extra extension
|
|
431
|
+
self.register_extra_zip_format()
|
|
432
|
+
|
|
433
|
+
# Decompress first level only
|
|
434
|
+
for path in self.source_path.glob("*"):
|
|
435
|
+
try:
|
|
436
|
+
shutil.unpack_archive(
|
|
437
|
+
path, path.parent.joinpath(path.stem), format=format
|
|
438
|
+
)
|
|
439
|
+
if delete_after_done and path.is_file():
|
|
440
|
+
path.unlink(missing_ok=True)
|
|
441
|
+
except OSError:
|
|
442
|
+
continue
|
|
360
443
|
|
|
361
|
-
class Directory(DirectoryBasicOperation, DirectoryTree):
|
|
362
|
-
"""
|
|
363
|
-
Some shortcuts for directory
|
|
364
444
|
|
|
365
|
-
|
|
366
|
-
- delete, rename, copy, move
|
|
367
|
-
- zip
|
|
368
|
-
- quick_info
|
|
445
|
+
class DirectoryOrganizerMixin(DirectoryBase):
|
|
369
446
|
"""
|
|
447
|
+
Directory - File organizer - SOON
|
|
448
|
+
"""
|
|
449
|
+
|
|
450
|
+
pass
|
|
370
451
|
|
|
452
|
+
|
|
453
|
+
class DirectoryTreeMixin(DirectoryBase):
|
|
371
454
|
# Directory structure
|
|
372
455
|
def _list_dir(self, *ignore: str) -> list[Path]:
|
|
373
456
|
"""
|
|
@@ -504,10 +587,41 @@ class Directory(DirectoryBasicOperation, DirectoryTree):
|
|
|
504
587
|
return self.list_structure("__pycache__", ".pyc")
|
|
505
588
|
|
|
506
589
|
|
|
590
|
+
class Directory(
|
|
591
|
+
DirectoryTreeMixin,
|
|
592
|
+
DirectoryOrganizerMixin,
|
|
593
|
+
DirectoryArchiverMixin,
|
|
594
|
+
DirectoryBasicOperationMixin,
|
|
595
|
+
DirectoryInfoMixin,
|
|
596
|
+
):
|
|
597
|
+
"""
|
|
598
|
+
Some shortcuts for directory
|
|
599
|
+
|
|
600
|
+
Parameters
|
|
601
|
+
----------
|
|
602
|
+
source_path : str | Path
|
|
603
|
+
Source folder
|
|
604
|
+
|
|
605
|
+
create_if_not_exist : bool
|
|
606
|
+
Create directory when not exist,
|
|
607
|
+
by default ``False``
|
|
608
|
+
|
|
609
|
+
|
|
610
|
+
Example:
|
|
611
|
+
--------
|
|
612
|
+
>>> # For a list of method
|
|
613
|
+
>>> Directory.SUBCLASS_METHODS
|
|
614
|
+
"""
|
|
615
|
+
|
|
616
|
+
pass
|
|
617
|
+
|
|
618
|
+
|
|
507
619
|
# Class - SaveFileAs
|
|
508
620
|
# ---------------------------------------------------------------------------
|
|
509
621
|
class SaveFileAs:
|
|
510
|
-
"""
|
|
622
|
+
"""
|
|
623
|
+
File as multiple file type
|
|
624
|
+
"""
|
|
511
625
|
|
|
512
626
|
def __init__(self, data: Any, *, encoding: str | None = "utf-8") -> None:
|
|
513
627
|
"""
|
|
@@ -552,9 +666,3 @@ class SaveFileAs:
|
|
|
552
666
|
# from absfuyu.util.json_method import JsonFile
|
|
553
667
|
# temp = JsonFile(path, sort_keys=False)
|
|
554
668
|
# temp.save_json()
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
# Dev and Test new feature before get added to the main class
|
|
558
|
-
# ---------------------------------------------------------------------------
|
|
559
|
-
class _NewDirFeature(Directory):
|
|
560
|
-
pass
|