python-fsutil 0.14.1__tar.gz → 0.16.0__tar.gz
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.
- {python-fsutil-0.14.1 → python_fsutil-0.16.0}/MANIFEST.in +2 -1
- {python-fsutil-0.14.1/python_fsutil.egg-info → python_fsutil-0.16.0}/PKG-INFO +9 -9
- {python-fsutil-0.14.1 → python_fsutil-0.16.0}/README.md +4 -5
- {python-fsutil-0.14.1 → python_fsutil-0.16.0}/pyproject.toml +12 -6
- python_fsutil-0.16.0/src/fsutil/__init__.py +199 -0
- python_fsutil-0.16.0/src/fsutil/archives.py +163 -0
- python_fsutil-0.16.0/src/fsutil/args.py +13 -0
- python_fsutil-0.16.0/src/fsutil/checks.py +114 -0
- python_fsutil-0.16.0/src/fsutil/converters.py +34 -0
- python_fsutil-0.16.0/src/fsutil/deps.py +13 -0
- python_fsutil-0.16.0/src/fsutil/info.py +187 -0
- python_fsutil-0.16.0/src/fsutil/io.py +231 -0
- {python-fsutil-0.14.1 → python_fsutil-0.16.0/src}/fsutil/metadata.py +1 -1
- python_fsutil-0.16.0/src/fsutil/operations.py +538 -0
- python_fsutil-0.16.0/src/fsutil/paths.py +199 -0
- python_fsutil-0.16.0/src/fsutil/perms.py +28 -0
- python_fsutil-0.16.0/src/fsutil/types.py +6 -0
- {python-fsutil-0.14.1 → python_fsutil-0.16.0/src/python_fsutil.egg-info}/PKG-INFO +9 -9
- python_fsutil-0.16.0/src/python_fsutil.egg-info/SOURCES.txt +36 -0
- python_fsutil-0.16.0/tests/__init__.py +0 -0
- python_fsutil-0.16.0/tests/conftest.py +15 -0
- python_fsutil-0.16.0/tests/test_archives.py +133 -0
- python_fsutil-0.16.0/tests/test_args.py +29 -0
- python_fsutil-0.16.0/tests/test_checks.py +111 -0
- python_fsutil-0.16.0/tests/test_converters.py +86 -0
- python_fsutil-0.16.0/tests/test_deps.py +25 -0
- python_fsutil-0.16.0/tests/test_info.py +167 -0
- python_fsutil-0.16.0/tests/test_io.py +204 -0
- python_fsutil-0.16.0/tests/test_metadata.py +25 -0
- python_fsutil-0.16.0/tests/test_operations.py +538 -0
- python_fsutil-0.16.0/tests/test_paths.py +191 -0
- python_fsutil-0.16.0/tests/test_perms.py +26 -0
- python-fsutil-0.14.1/fsutil/__init__.py +0 -1495
- python-fsutil-0.14.1/python_fsutil.egg-info/SOURCES.txt +0 -13
- python-fsutil-0.14.1/tests/test.py +0 -1389
- {python-fsutil-0.14.1 → python_fsutil-0.16.0}/LICENSE.txt +0 -0
- {python-fsutil-0.14.1 → python_fsutil-0.16.0}/setup.cfg +0 -0
- {python-fsutil-0.14.1 → python_fsutil-0.16.0}/setup.py +0 -0
- {python-fsutil-0.14.1 → python_fsutil-0.16.0/src}/fsutil/py.typed +0 -0
- {python-fsutil-0.14.1 → python_fsutil-0.16.0/src}/python_fsutil.egg-info/dependency_links.txt +0 -0
- {python-fsutil-0.14.1 → python_fsutil-0.16.0/src}/python_fsutil.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: python-fsutil
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.16.0
|
|
4
4
|
Summary: high-level file-system operations for lazy devs.
|
|
5
5
|
Author-email: Fabio Caccamo <fabio.caccamo@gmail.com>
|
|
6
6
|
Maintainer-email: Fabio Caccamo <fabio.caccamo@gmail.com>
|
|
@@ -42,11 +42,11 @@ Classifier: License :: OSI Approved :: MIT License
|
|
|
42
42
|
Classifier: Natural Language :: English
|
|
43
43
|
Classifier: Operating System :: OS Independent
|
|
44
44
|
Classifier: Programming Language :: Python :: 3
|
|
45
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
46
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
47
45
|
Classifier: Programming Language :: Python :: 3.10
|
|
48
46
|
Classifier: Programming Language :: Python :: 3.11
|
|
49
47
|
Classifier: Programming Language :: Python :: 3.12
|
|
48
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
49
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
50
50
|
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
51
51
|
Classifier: Topic :: Desktop Environment :: File Managers
|
|
52
52
|
Classifier: Topic :: Software Development :: Build Tools
|
|
@@ -55,6 +55,7 @@ Classifier: Topic :: System :: Filesystems
|
|
|
55
55
|
Classifier: Topic :: Utilities
|
|
56
56
|
Description-Content-Type: text/markdown
|
|
57
57
|
License-File: LICENSE.txt
|
|
58
|
+
Dynamic: license-file
|
|
58
59
|
|
|
59
60
|
[](https://www.python.org/)
|
|
60
61
|
[](https://pypi.org/project/python-fsutil/)
|
|
@@ -66,7 +67,6 @@ License-File: LICENSE.txt
|
|
|
66
67
|
[](https://github.com/fabiocaccamo/python-fsutil)
|
|
67
68
|
[](https://codecov.io/gh/fabiocaccamo/python-fsutil)
|
|
68
69
|
[](https://www.codacy.com/app/fabiocaccamo/python-fsutil)
|
|
69
|
-
[](https://codeclimate.com/github/fabiocaccamo/python-fsutil/)
|
|
70
70
|
[](https://github.com/psf/black)
|
|
71
71
|
[](https://github.com/astral-sh/ruff)
|
|
72
72
|
|
|
@@ -507,7 +507,7 @@ permissions = fsutil.get_permissions(path)
|
|
|
507
507
|
#### `get_unique_name`
|
|
508
508
|
|
|
509
509
|
```python
|
|
510
|
-
# Get a unique name for a directory/file
|
|
510
|
+
# Get a unique name for a directory/file at the given directory path.
|
|
511
511
|
unique_name = fsutil.get_unique_name(path, prefix="", suffix="", extension="", separator="-")
|
|
512
512
|
```
|
|
513
513
|
|
|
@@ -826,8 +826,8 @@ pre-commit install --install-hooks
|
|
|
826
826
|
# run tests using tox
|
|
827
827
|
tox
|
|
828
828
|
|
|
829
|
-
# or run tests using
|
|
830
|
-
|
|
829
|
+
# or run tests using pytest
|
|
830
|
+
pytest
|
|
831
831
|
```
|
|
832
832
|
|
|
833
833
|
## License
|
|
@@ -839,7 +839,7 @@ Released under [MIT License](LICENSE.txt).
|
|
|
839
839
|
|
|
840
840
|
- :star: Star this project on [GitHub](https://github.com/fabiocaccamo/python-fsutil)
|
|
841
841
|
- :octocat: Follow me on [GitHub](https://github.com/fabiocaccamo)
|
|
842
|
-
- :blue_heart: Follow me on [
|
|
842
|
+
- :blue_heart: Follow me on [Bluesky](https://bsky.app/profile/fabiocaccamo.bsky.social)
|
|
843
843
|
- :moneybag: Sponsor me on [Github](https://github.com/sponsors/fabiocaccamo)
|
|
844
844
|
|
|
845
845
|
## See also
|
|
@@ -8,7 +8,6 @@
|
|
|
8
8
|
[](https://github.com/fabiocaccamo/python-fsutil)
|
|
9
9
|
[](https://codecov.io/gh/fabiocaccamo/python-fsutil)
|
|
10
10
|
[](https://www.codacy.com/app/fabiocaccamo/python-fsutil)
|
|
11
|
-
[](https://codeclimate.com/github/fabiocaccamo/python-fsutil/)
|
|
12
11
|
[](https://github.com/psf/black)
|
|
13
12
|
[](https://github.com/astral-sh/ruff)
|
|
14
13
|
|
|
@@ -449,7 +448,7 @@ permissions = fsutil.get_permissions(path)
|
|
|
449
448
|
#### `get_unique_name`
|
|
450
449
|
|
|
451
450
|
```python
|
|
452
|
-
# Get a unique name for a directory/file
|
|
451
|
+
# Get a unique name for a directory/file at the given directory path.
|
|
453
452
|
unique_name = fsutil.get_unique_name(path, prefix="", suffix="", extension="", separator="-")
|
|
454
453
|
```
|
|
455
454
|
|
|
@@ -768,8 +767,8 @@ pre-commit install --install-hooks
|
|
|
768
767
|
# run tests using tox
|
|
769
768
|
tox
|
|
770
769
|
|
|
771
|
-
# or run tests using
|
|
772
|
-
|
|
770
|
+
# or run tests using pytest
|
|
771
|
+
pytest
|
|
773
772
|
```
|
|
774
773
|
|
|
775
774
|
## License
|
|
@@ -781,7 +780,7 @@ Released under [MIT License](LICENSE.txt).
|
|
|
781
780
|
|
|
782
781
|
- :star: Star this project on [GitHub](https://github.com/fabiocaccamo/python-fsutil)
|
|
783
782
|
- :octocat: Follow me on [GitHub](https://github.com/fabiocaccamo)
|
|
784
|
-
- :blue_heart: Follow me on [
|
|
783
|
+
- :blue_heart: Follow me on [Bluesky](https://bsky.app/profile/fabiocaccamo.bsky.social)
|
|
785
784
|
- :moneybag: Sponsor me on [Github](https://github.com/sponsors/fabiocaccamo)
|
|
786
785
|
|
|
787
786
|
## See also
|
|
@@ -34,11 +34,11 @@ classifiers = [
|
|
|
34
34
|
"Natural Language :: English",
|
|
35
35
|
"Operating System :: OS Independent",
|
|
36
36
|
"Programming Language :: Python :: 3",
|
|
37
|
-
"Programming Language :: Python :: 3.8",
|
|
38
|
-
"Programming Language :: Python :: 3.9",
|
|
39
37
|
"Programming Language :: Python :: 3.10",
|
|
40
38
|
"Programming Language :: Python :: 3.11",
|
|
41
39
|
"Programming Language :: Python :: 3.12",
|
|
40
|
+
"Programming Language :: Python :: 3.13",
|
|
41
|
+
"Programming Language :: Python :: 3.14",
|
|
42
42
|
"Programming Language :: Python :: Implementation :: PyPy",
|
|
43
43
|
"Topic :: Desktop Environment :: File Managers",
|
|
44
44
|
"Topic :: Software Development :: Build Tools",
|
|
@@ -86,25 +86,31 @@ exclude = '''
|
|
|
86
86
|
'''
|
|
87
87
|
|
|
88
88
|
[tool.mypy]
|
|
89
|
-
files = ["
|
|
89
|
+
files = ["src"]
|
|
90
90
|
disable_error_code = "import-untyped"
|
|
91
91
|
ignore_missing_imports = true
|
|
92
92
|
install_types = true
|
|
93
93
|
non_interactive = true
|
|
94
94
|
strict = true
|
|
95
95
|
|
|
96
|
+
[tool.pytest.ini_options]
|
|
97
|
+
pythonpath = "src"
|
|
98
|
+
addopts = "-v"
|
|
99
|
+
testpaths = ["tests"]
|
|
100
|
+
|
|
96
101
|
[tool.ruff]
|
|
97
102
|
line-length = 88
|
|
98
103
|
|
|
99
104
|
[tool.ruff.lint]
|
|
100
105
|
ignore = []
|
|
101
|
-
select = ["B", "B9", "C", "E", "F", "W"]
|
|
106
|
+
select = ["B", "B9", "C", "E", "F", "I", "W"]
|
|
102
107
|
|
|
103
108
|
[tool.ruff.lint.mccabe]
|
|
104
109
|
max-complexity = 10
|
|
105
110
|
|
|
106
|
-
[tool.setuptools
|
|
107
|
-
|
|
111
|
+
[tool.setuptools]
|
|
112
|
+
package-dir = {"" = "src"}
|
|
113
|
+
packages = ["fsutil"]
|
|
108
114
|
|
|
109
115
|
[tool.setuptools.dynamic.version]
|
|
110
116
|
attr = "fsutil.metadata.__version__"
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from fsutil.archives import (
|
|
4
|
+
create_tar_file,
|
|
5
|
+
create_zip_file,
|
|
6
|
+
extract_tar_file,
|
|
7
|
+
extract_zip_file,
|
|
8
|
+
)
|
|
9
|
+
from fsutil.checks import (
|
|
10
|
+
assert_dir,
|
|
11
|
+
assert_exists,
|
|
12
|
+
assert_file,
|
|
13
|
+
assert_not_dir,
|
|
14
|
+
assert_not_exists,
|
|
15
|
+
assert_not_file,
|
|
16
|
+
exists,
|
|
17
|
+
is_dir,
|
|
18
|
+
is_empty,
|
|
19
|
+
is_empty_dir,
|
|
20
|
+
is_empty_file,
|
|
21
|
+
is_file,
|
|
22
|
+
)
|
|
23
|
+
from fsutil.converters import convert_size_bytes_to_string, convert_size_string_to_bytes
|
|
24
|
+
from fsutil.info import (
|
|
25
|
+
get_dir_creation_date,
|
|
26
|
+
get_dir_creation_date_formatted,
|
|
27
|
+
get_dir_hash,
|
|
28
|
+
get_dir_last_modified_date,
|
|
29
|
+
get_dir_last_modified_date_formatted,
|
|
30
|
+
get_dir_size,
|
|
31
|
+
get_dir_size_formatted,
|
|
32
|
+
get_file_creation_date,
|
|
33
|
+
get_file_creation_date_formatted,
|
|
34
|
+
get_file_hash,
|
|
35
|
+
get_file_last_modified_date,
|
|
36
|
+
get_file_last_modified_date_formatted,
|
|
37
|
+
get_file_size,
|
|
38
|
+
get_file_size_formatted,
|
|
39
|
+
)
|
|
40
|
+
from fsutil.io import (
|
|
41
|
+
read_file,
|
|
42
|
+
read_file_from_url,
|
|
43
|
+
read_file_json,
|
|
44
|
+
read_file_lines,
|
|
45
|
+
read_file_lines_count,
|
|
46
|
+
write_file,
|
|
47
|
+
write_file_json,
|
|
48
|
+
)
|
|
49
|
+
from fsutil.metadata import (
|
|
50
|
+
__author__,
|
|
51
|
+
__copyright__,
|
|
52
|
+
__description__,
|
|
53
|
+
__email__,
|
|
54
|
+
__license__,
|
|
55
|
+
__title__,
|
|
56
|
+
__version__,
|
|
57
|
+
)
|
|
58
|
+
from fsutil.operations import (
|
|
59
|
+
clean_dir,
|
|
60
|
+
copy_dir,
|
|
61
|
+
copy_dir_content,
|
|
62
|
+
copy_file,
|
|
63
|
+
create_dir,
|
|
64
|
+
create_file,
|
|
65
|
+
delete_dir,
|
|
66
|
+
delete_dir_content,
|
|
67
|
+
delete_dirs,
|
|
68
|
+
delete_file,
|
|
69
|
+
delete_files,
|
|
70
|
+
download_file,
|
|
71
|
+
list_dirs,
|
|
72
|
+
list_files,
|
|
73
|
+
make_dirs,
|
|
74
|
+
make_dirs_for_file,
|
|
75
|
+
move_dir,
|
|
76
|
+
move_file,
|
|
77
|
+
remove_dir,
|
|
78
|
+
remove_dir_content,
|
|
79
|
+
remove_dirs,
|
|
80
|
+
remove_file,
|
|
81
|
+
remove_files,
|
|
82
|
+
rename_dir,
|
|
83
|
+
rename_file,
|
|
84
|
+
rename_file_basename,
|
|
85
|
+
rename_file_extension,
|
|
86
|
+
replace_dir,
|
|
87
|
+
replace_file,
|
|
88
|
+
search_dirs,
|
|
89
|
+
search_files,
|
|
90
|
+
)
|
|
91
|
+
from fsutil.paths import (
|
|
92
|
+
get_file_basename,
|
|
93
|
+
get_file_extension,
|
|
94
|
+
get_filename,
|
|
95
|
+
get_parent_dir,
|
|
96
|
+
get_unique_name,
|
|
97
|
+
join_filename,
|
|
98
|
+
join_filepath,
|
|
99
|
+
join_path,
|
|
100
|
+
split_filename,
|
|
101
|
+
split_filepath,
|
|
102
|
+
split_path,
|
|
103
|
+
transform_filepath,
|
|
104
|
+
)
|
|
105
|
+
from fsutil.perms import get_permissions, set_permissions
|
|
106
|
+
|
|
107
|
+
__all__ = [
|
|
108
|
+
"__author__",
|
|
109
|
+
"__copyright__",
|
|
110
|
+
"__description__",
|
|
111
|
+
"__email__",
|
|
112
|
+
"__license__",
|
|
113
|
+
"__title__",
|
|
114
|
+
"__version__",
|
|
115
|
+
"assert_dir",
|
|
116
|
+
"assert_exists",
|
|
117
|
+
"assert_file",
|
|
118
|
+
"assert_not_dir",
|
|
119
|
+
"assert_not_exists",
|
|
120
|
+
"assert_not_file",
|
|
121
|
+
"clean_dir",
|
|
122
|
+
"convert_size_bytes_to_string",
|
|
123
|
+
"convert_size_string_to_bytes",
|
|
124
|
+
"copy_dir",
|
|
125
|
+
"copy_dir_content",
|
|
126
|
+
"copy_file",
|
|
127
|
+
"create_dir",
|
|
128
|
+
"create_file",
|
|
129
|
+
"create_tar_file",
|
|
130
|
+
"create_zip_file",
|
|
131
|
+
"delete_dir",
|
|
132
|
+
"delete_dir_content",
|
|
133
|
+
"delete_dirs",
|
|
134
|
+
"delete_file",
|
|
135
|
+
"delete_files",
|
|
136
|
+
"download_file",
|
|
137
|
+
"exists",
|
|
138
|
+
"extract_tar_file",
|
|
139
|
+
"extract_zip_file",
|
|
140
|
+
"get_dir_creation_date",
|
|
141
|
+
"get_dir_creation_date_formatted",
|
|
142
|
+
"get_dir_hash",
|
|
143
|
+
"get_dir_last_modified_date",
|
|
144
|
+
"get_dir_last_modified_date_formatted",
|
|
145
|
+
"get_dir_size",
|
|
146
|
+
"get_dir_size_formatted",
|
|
147
|
+
"get_file_basename",
|
|
148
|
+
"get_file_creation_date",
|
|
149
|
+
"get_file_creation_date_formatted",
|
|
150
|
+
"get_file_extension",
|
|
151
|
+
"get_file_hash",
|
|
152
|
+
"get_file_last_modified_date",
|
|
153
|
+
"get_file_last_modified_date_formatted",
|
|
154
|
+
"get_file_size",
|
|
155
|
+
"get_file_size_formatted",
|
|
156
|
+
"get_filename",
|
|
157
|
+
"get_parent_dir",
|
|
158
|
+
"get_permissions",
|
|
159
|
+
"get_unique_name",
|
|
160
|
+
"is_dir",
|
|
161
|
+
"is_empty",
|
|
162
|
+
"is_empty_dir",
|
|
163
|
+
"is_empty_file",
|
|
164
|
+
"is_file",
|
|
165
|
+
"join_filename",
|
|
166
|
+
"join_filepath",
|
|
167
|
+
"join_path",
|
|
168
|
+
"list_dirs",
|
|
169
|
+
"list_files",
|
|
170
|
+
"make_dirs",
|
|
171
|
+
"make_dirs_for_file",
|
|
172
|
+
"move_dir",
|
|
173
|
+
"move_file",
|
|
174
|
+
"read_file",
|
|
175
|
+
"read_file_from_url",
|
|
176
|
+
"read_file_json",
|
|
177
|
+
"read_file_lines",
|
|
178
|
+
"read_file_lines_count",
|
|
179
|
+
"remove_dir",
|
|
180
|
+
"remove_dir_content",
|
|
181
|
+
"remove_dirs",
|
|
182
|
+
"remove_file",
|
|
183
|
+
"remove_files",
|
|
184
|
+
"rename_dir",
|
|
185
|
+
"rename_file",
|
|
186
|
+
"rename_file_basename",
|
|
187
|
+
"rename_file_extension",
|
|
188
|
+
"replace_dir",
|
|
189
|
+
"replace_file",
|
|
190
|
+
"search_dirs",
|
|
191
|
+
"search_files",
|
|
192
|
+
"set_permissions",
|
|
193
|
+
"split_filename",
|
|
194
|
+
"split_filepath",
|
|
195
|
+
"split_path",
|
|
196
|
+
"transform_filepath",
|
|
197
|
+
"write_file",
|
|
198
|
+
"write_file_json",
|
|
199
|
+
]
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import sys
|
|
5
|
+
import tarfile
|
|
6
|
+
import zipfile
|
|
7
|
+
from collections.abc import Callable, Iterable
|
|
8
|
+
from typing import Literal
|
|
9
|
+
|
|
10
|
+
from fsutil.args import get_path as _get_path
|
|
11
|
+
from fsutil.checks import (
|
|
12
|
+
assert_exists,
|
|
13
|
+
assert_file,
|
|
14
|
+
assert_not_dir,
|
|
15
|
+
assert_not_exists,
|
|
16
|
+
assert_not_file,
|
|
17
|
+
is_dir,
|
|
18
|
+
is_file,
|
|
19
|
+
)
|
|
20
|
+
from fsutil.operations import make_dirs, make_dirs_for_file, remove_file
|
|
21
|
+
from fsutil.paths import get_filename, join_path
|
|
22
|
+
from fsutil.types import PathIn
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def create_tar_file(
|
|
26
|
+
path: PathIn,
|
|
27
|
+
content_paths: list[PathIn],
|
|
28
|
+
*,
|
|
29
|
+
overwrite: bool = True,
|
|
30
|
+
compression: str = "", # literal: gz, bz2, xz
|
|
31
|
+
) -> None:
|
|
32
|
+
"""
|
|
33
|
+
Create tar file at path compressing directories/files listed in content_paths.
|
|
34
|
+
If overwrite is allowed and dest tar already exists, it will be overwritten.
|
|
35
|
+
"""
|
|
36
|
+
path = _get_path(path)
|
|
37
|
+
assert_not_dir(path)
|
|
38
|
+
if not overwrite:
|
|
39
|
+
assert_not_exists(path)
|
|
40
|
+
make_dirs_for_file(path)
|
|
41
|
+
|
|
42
|
+
def _write_content_to_tar_file(
|
|
43
|
+
file: tarfile.TarFile, content_path: PathIn, basedir: str = ""
|
|
44
|
+
) -> None:
|
|
45
|
+
path = _get_path(content_path)
|
|
46
|
+
assert_exists(path)
|
|
47
|
+
if is_file(path):
|
|
48
|
+
filename = get_filename(path)
|
|
49
|
+
filepath = join_path(basedir, filename)
|
|
50
|
+
file.add(path, filepath)
|
|
51
|
+
elif is_dir(path):
|
|
52
|
+
for item_name in os.listdir(path):
|
|
53
|
+
item_path = join_path(path, item_name)
|
|
54
|
+
item_basedir = (
|
|
55
|
+
join_path(basedir, item_name) if is_dir(item_path) else basedir
|
|
56
|
+
)
|
|
57
|
+
_write_content_to_tar_file(file, item_path, item_basedir)
|
|
58
|
+
|
|
59
|
+
mode = f"w:{compression}" if compression else "w"
|
|
60
|
+
with tarfile.open(path, mode=mode) as file: # type: ignore
|
|
61
|
+
for content_path in content_paths:
|
|
62
|
+
_write_content_to_tar_file(file, content_path)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def create_zip_file(
|
|
66
|
+
path: PathIn,
|
|
67
|
+
content_paths: list[PathIn],
|
|
68
|
+
*,
|
|
69
|
+
overwrite: bool = True,
|
|
70
|
+
compression: int = zipfile.ZIP_DEFLATED,
|
|
71
|
+
) -> None:
|
|
72
|
+
"""
|
|
73
|
+
Create zip file at path compressing directories/files listed in content_paths.
|
|
74
|
+
If overwrite is allowed and dest zip already exists, it will be overwritten.
|
|
75
|
+
"""
|
|
76
|
+
path = _get_path(path)
|
|
77
|
+
assert_not_dir(path)
|
|
78
|
+
if not overwrite:
|
|
79
|
+
assert_not_exists(path)
|
|
80
|
+
make_dirs_for_file(path)
|
|
81
|
+
|
|
82
|
+
def _write_content_to_zip_file(
|
|
83
|
+
file: zipfile.ZipFile, content_path: PathIn, basedir: str = ""
|
|
84
|
+
) -> None:
|
|
85
|
+
path = _get_path(content_path)
|
|
86
|
+
assert_exists(path)
|
|
87
|
+
if is_file(path):
|
|
88
|
+
filename = get_filename(path)
|
|
89
|
+
filepath = join_path(basedir, filename)
|
|
90
|
+
file.write(path, filepath)
|
|
91
|
+
elif is_dir(path):
|
|
92
|
+
for item_name in os.listdir(path):
|
|
93
|
+
item_path = join_path(path, item_name)
|
|
94
|
+
item_basedir = (
|
|
95
|
+
join_path(basedir, item_name) if is_dir(item_path) else basedir
|
|
96
|
+
)
|
|
97
|
+
_write_content_to_zip_file(file, item_path, item_basedir)
|
|
98
|
+
|
|
99
|
+
with zipfile.ZipFile(path, "w", compression) as file:
|
|
100
|
+
for content_path in content_paths:
|
|
101
|
+
_write_content_to_zip_file(file, content_path)
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def extract_tar_file(
|
|
105
|
+
path: PathIn,
|
|
106
|
+
dest: PathIn,
|
|
107
|
+
*,
|
|
108
|
+
autodelete: bool = False,
|
|
109
|
+
content_paths: Iterable[tarfile.TarInfo] | None = None,
|
|
110
|
+
filter: (
|
|
111
|
+
Callable[[tarfile.TarInfo, str], tarfile.TarInfo | None]
|
|
112
|
+
| Literal["fully_trusted", "tar", "data"]
|
|
113
|
+
)
|
|
114
|
+
| None = None,
|
|
115
|
+
) -> None:
|
|
116
|
+
"""
|
|
117
|
+
Extract tar file at path to dest path.
|
|
118
|
+
If autodelete, the archive will be deleted after extraction.
|
|
119
|
+
If content_paths list is defined,
|
|
120
|
+
only listed items will be extracted, otherwise all.
|
|
121
|
+
"""
|
|
122
|
+
path = _get_path(path)
|
|
123
|
+
dest = _get_path(dest)
|
|
124
|
+
assert_file(path)
|
|
125
|
+
assert_not_file(dest)
|
|
126
|
+
make_dirs(dest)
|
|
127
|
+
with tarfile.TarFile(path, "r") as file:
|
|
128
|
+
if sys.version_info < (3, 12):
|
|
129
|
+
file.extractall(dest, members=content_paths)
|
|
130
|
+
else:
|
|
131
|
+
# https://docs.python.org/3/library/tarfile.html#tarfile-extraction-filter
|
|
132
|
+
file.extractall(
|
|
133
|
+
dest,
|
|
134
|
+
members=content_paths,
|
|
135
|
+
numeric_owner=False,
|
|
136
|
+
filter=(filter or "data"),
|
|
137
|
+
)
|
|
138
|
+
if autodelete:
|
|
139
|
+
remove_file(path)
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def extract_zip_file(
|
|
143
|
+
path: PathIn,
|
|
144
|
+
dest: PathIn,
|
|
145
|
+
*,
|
|
146
|
+
autodelete: bool = False,
|
|
147
|
+
content_paths: Iterable[str | zipfile.ZipInfo] | None = None,
|
|
148
|
+
) -> None:
|
|
149
|
+
"""
|
|
150
|
+
Extract zip file at path to dest path.
|
|
151
|
+
If autodelete, the archive will be deleted after extraction.
|
|
152
|
+
If content_paths list is defined,
|
|
153
|
+
only listed items will be extracted, otherwise all.
|
|
154
|
+
"""
|
|
155
|
+
path = _get_path(path)
|
|
156
|
+
dest = _get_path(dest)
|
|
157
|
+
assert_file(path)
|
|
158
|
+
assert_not_file(dest)
|
|
159
|
+
make_dirs(dest)
|
|
160
|
+
with zipfile.ZipFile(path, "r") as file:
|
|
161
|
+
file.extractall(dest, members=content_paths)
|
|
162
|
+
if autodelete:
|
|
163
|
+
remove_file(path)
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
|
|
5
|
+
from fsutil.args import get_path as _get_path
|
|
6
|
+
from fsutil.types import PathIn
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def assert_dir(path: PathIn) -> None:
|
|
10
|
+
"""
|
|
11
|
+
Raise an OSError if the given path doesn't exist or it is not a directory.
|
|
12
|
+
"""
|
|
13
|
+
path = _get_path(path)
|
|
14
|
+
if not is_dir(path):
|
|
15
|
+
raise OSError(f"Invalid directory path: {path}")
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def assert_exists(path: PathIn) -> None:
|
|
19
|
+
"""
|
|
20
|
+
Raise an OSError if the given path doesn't exist.
|
|
21
|
+
"""
|
|
22
|
+
path = _get_path(path)
|
|
23
|
+
if not exists(path):
|
|
24
|
+
raise OSError(f"Invalid item path: {path}")
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def assert_file(path: PathIn) -> None:
|
|
28
|
+
"""
|
|
29
|
+
Raise an OSError if the given path doesn't exist or it is not a file.
|
|
30
|
+
"""
|
|
31
|
+
path = _get_path(path)
|
|
32
|
+
if not is_file(path):
|
|
33
|
+
raise OSError(f"Invalid file path: {path}")
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def assert_not_dir(path: PathIn) -> None:
|
|
37
|
+
"""
|
|
38
|
+
Raise an OSError if the given path is an existing directory.
|
|
39
|
+
"""
|
|
40
|
+
path = _get_path(path)
|
|
41
|
+
if is_dir(path):
|
|
42
|
+
raise OSError(f"Invalid path, directory already exists: {path}")
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def assert_not_exists(path: PathIn) -> None:
|
|
46
|
+
"""
|
|
47
|
+
Raise an OSError if the given path already exists.
|
|
48
|
+
"""
|
|
49
|
+
path = _get_path(path)
|
|
50
|
+
if exists(path):
|
|
51
|
+
raise OSError(f"Invalid path, item already exists: {path}")
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def assert_not_file(path: PathIn) -> None:
|
|
55
|
+
"""
|
|
56
|
+
Raise an OSError if the given path is an existing file.
|
|
57
|
+
"""
|
|
58
|
+
path = _get_path(path)
|
|
59
|
+
if is_file(path):
|
|
60
|
+
raise OSError(f"Invalid path, file already exists: {path}")
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def exists(path: PathIn) -> bool:
|
|
64
|
+
"""
|
|
65
|
+
Check if a directory of a file exists at the given path.
|
|
66
|
+
"""
|
|
67
|
+
path = _get_path(path)
|
|
68
|
+
return os.path.exists(path)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def is_dir(path: PathIn) -> bool:
|
|
72
|
+
"""
|
|
73
|
+
Determine whether the specified path represents an existing directory.
|
|
74
|
+
"""
|
|
75
|
+
path = _get_path(path)
|
|
76
|
+
return os.path.isdir(path)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def is_empty(path: PathIn) -> bool:
|
|
80
|
+
"""
|
|
81
|
+
Determine whether the specified path represents an empty directory or an empty file.
|
|
82
|
+
"""
|
|
83
|
+
path = _get_path(path)
|
|
84
|
+
assert_exists(path)
|
|
85
|
+
if is_dir(path):
|
|
86
|
+
return is_empty_dir(path)
|
|
87
|
+
return is_empty_file(path)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def is_empty_dir(path: PathIn) -> bool:
|
|
91
|
+
"""
|
|
92
|
+
Determine whether the specified path represents an empty directory.
|
|
93
|
+
"""
|
|
94
|
+
path = _get_path(path)
|
|
95
|
+
assert_dir(path)
|
|
96
|
+
return len(os.listdir(path)) == 0
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def is_empty_file(path: PathIn) -> bool:
|
|
100
|
+
"""
|
|
101
|
+
Determine whether the specified path represents an empty file.
|
|
102
|
+
"""
|
|
103
|
+
from fsutil.info import get_file_size
|
|
104
|
+
|
|
105
|
+
path = _get_path(path)
|
|
106
|
+
return get_file_size(path) == 0
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def is_file(path: PathIn) -> bool:
|
|
110
|
+
"""
|
|
111
|
+
Determine whether the specified path represents an existing file.
|
|
112
|
+
"""
|
|
113
|
+
path = _get_path(path)
|
|
114
|
+
return os.path.isfile(path)
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
SIZE_UNITS = ["bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"]
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def convert_size_bytes_to_string(size: int) -> str:
|
|
7
|
+
"""
|
|
8
|
+
Convert the given size bytes to string using the right unit suffix.
|
|
9
|
+
"""
|
|
10
|
+
size_num = float(size)
|
|
11
|
+
units = SIZE_UNITS
|
|
12
|
+
factor = 0
|
|
13
|
+
factor_limit = len(units) - 1
|
|
14
|
+
while (size_num >= 1024) and (factor <= factor_limit):
|
|
15
|
+
size_num /= 1024
|
|
16
|
+
factor += 1
|
|
17
|
+
size_units = units[factor]
|
|
18
|
+
size_str = f"{size_num:.2f}" if (factor > 1) else f"{size_num:.0f}"
|
|
19
|
+
size_str = f"{size_str} {size_units}"
|
|
20
|
+
return size_str
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def convert_size_string_to_bytes(size: str) -> float | int:
|
|
24
|
+
"""
|
|
25
|
+
Convert the given size string to bytes.
|
|
26
|
+
"""
|
|
27
|
+
units = [item.lower() for item in SIZE_UNITS]
|
|
28
|
+
parts = size.strip().replace(" ", " ").split(" ")
|
|
29
|
+
amount = float(parts[0])
|
|
30
|
+
unit = parts[1]
|
|
31
|
+
factor = units.index(unit.lower())
|
|
32
|
+
if not factor:
|
|
33
|
+
return amount
|
|
34
|
+
return int((1024**factor) * amount)
|