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.
Files changed (41) hide show
  1. {python-fsutil-0.14.1 → python_fsutil-0.16.0}/MANIFEST.in +2 -1
  2. {python-fsutil-0.14.1/python_fsutil.egg-info → python_fsutil-0.16.0}/PKG-INFO +9 -9
  3. {python-fsutil-0.14.1 → python_fsutil-0.16.0}/README.md +4 -5
  4. {python-fsutil-0.14.1 → python_fsutil-0.16.0}/pyproject.toml +12 -6
  5. python_fsutil-0.16.0/src/fsutil/__init__.py +199 -0
  6. python_fsutil-0.16.0/src/fsutil/archives.py +163 -0
  7. python_fsutil-0.16.0/src/fsutil/args.py +13 -0
  8. python_fsutil-0.16.0/src/fsutil/checks.py +114 -0
  9. python_fsutil-0.16.0/src/fsutil/converters.py +34 -0
  10. python_fsutil-0.16.0/src/fsutil/deps.py +13 -0
  11. python_fsutil-0.16.0/src/fsutil/info.py +187 -0
  12. python_fsutil-0.16.0/src/fsutil/io.py +231 -0
  13. {python-fsutil-0.14.1 → python_fsutil-0.16.0/src}/fsutil/metadata.py +1 -1
  14. python_fsutil-0.16.0/src/fsutil/operations.py +538 -0
  15. python_fsutil-0.16.0/src/fsutil/paths.py +199 -0
  16. python_fsutil-0.16.0/src/fsutil/perms.py +28 -0
  17. python_fsutil-0.16.0/src/fsutil/types.py +6 -0
  18. {python-fsutil-0.14.1 → python_fsutil-0.16.0/src/python_fsutil.egg-info}/PKG-INFO +9 -9
  19. python_fsutil-0.16.0/src/python_fsutil.egg-info/SOURCES.txt +36 -0
  20. python_fsutil-0.16.0/tests/__init__.py +0 -0
  21. python_fsutil-0.16.0/tests/conftest.py +15 -0
  22. python_fsutil-0.16.0/tests/test_archives.py +133 -0
  23. python_fsutil-0.16.0/tests/test_args.py +29 -0
  24. python_fsutil-0.16.0/tests/test_checks.py +111 -0
  25. python_fsutil-0.16.0/tests/test_converters.py +86 -0
  26. python_fsutil-0.16.0/tests/test_deps.py +25 -0
  27. python_fsutil-0.16.0/tests/test_info.py +167 -0
  28. python_fsutil-0.16.0/tests/test_io.py +204 -0
  29. python_fsutil-0.16.0/tests/test_metadata.py +25 -0
  30. python_fsutil-0.16.0/tests/test_operations.py +538 -0
  31. python_fsutil-0.16.0/tests/test_paths.py +191 -0
  32. python_fsutil-0.16.0/tests/test_perms.py +26 -0
  33. python-fsutil-0.14.1/fsutil/__init__.py +0 -1495
  34. python-fsutil-0.14.1/python_fsutil.egg-info/SOURCES.txt +0 -13
  35. python-fsutil-0.14.1/tests/test.py +0 -1389
  36. {python-fsutil-0.14.1 → python_fsutil-0.16.0}/LICENSE.txt +0 -0
  37. {python-fsutil-0.14.1 → python_fsutil-0.16.0}/setup.cfg +0 -0
  38. {python-fsutil-0.14.1 → python_fsutil-0.16.0}/setup.py +0 -0
  39. {python-fsutil-0.14.1 → python_fsutil-0.16.0/src}/fsutil/py.typed +0 -0
  40. {python-fsutil-0.14.1 → python_fsutil-0.16.0/src}/python_fsutil.egg-info/dependency_links.txt +0 -0
  41. {python-fsutil-0.14.1 → python_fsutil-0.16.0/src}/python_fsutil.egg-info/top_level.txt +0 -0
@@ -1,4 +1,5 @@
1
1
  include LICENSE.txt
2
2
  include README.md
3
- recursive-include fsutil *
3
+ recursive-include src *
4
+ recursive-include tests *
4
5
  recursive-exclude * *.pyc __pycache__ .DS_Store
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: python-fsutil
3
- Version: 0.14.1
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://img.shields.io/pypi/pyversions/python-fsutil.svg?color=blue&logo=python&logoColor=white)](https://www.python.org/)
60
61
  [![](https://img.shields.io/pypi/v/python-fsutil.svg?color=blue&logo=pypi&logoColor=white)](https://pypi.org/project/python-fsutil/)
@@ -66,7 +67,6 @@ License-File: LICENSE.txt
66
67
  [![](https://img.shields.io/github/actions/workflow/status/fabiocaccamo/python-fsutil/test-package.yml?branch=main&label=build&logo=github)](https://github.com/fabiocaccamo/python-fsutil)
67
68
  [![](https://img.shields.io/codecov/c/gh/fabiocaccamo/python-fsutil?logo=codecov)](https://codecov.io/gh/fabiocaccamo/python-fsutil)
68
69
  [![](https://img.shields.io/codacy/grade/fc40788ae7d74d1fb34a38934113c4e5?logo=codacy)](https://www.codacy.com/app/fabiocaccamo/python-fsutil)
69
- [![](https://img.shields.io/codeclimate/maintainability/fabiocaccamo/python-fsutil?logo=code-climate)](https://codeclimate.com/github/fabiocaccamo/python-fsutil/)
70
70
  [![](https://img.shields.io/badge/code%20style-black-000000.svg?logo=python&logoColor=black)](https://github.com/psf/black)
71
71
  [![](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](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 ath the given directory path.
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 unittest
830
- python -m unittest
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 [Twitter](https://twitter.com/fabiocaccamo)
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://img.shields.io/github/actions/workflow/status/fabiocaccamo/python-fsutil/test-package.yml?branch=main&label=build&logo=github)](https://github.com/fabiocaccamo/python-fsutil)
9
9
  [![](https://img.shields.io/codecov/c/gh/fabiocaccamo/python-fsutil?logo=codecov)](https://codecov.io/gh/fabiocaccamo/python-fsutil)
10
10
  [![](https://img.shields.io/codacy/grade/fc40788ae7d74d1fb34a38934113c4e5?logo=codacy)](https://www.codacy.com/app/fabiocaccamo/python-fsutil)
11
- [![](https://img.shields.io/codeclimate/maintainability/fabiocaccamo/python-fsutil?logo=code-climate)](https://codeclimate.com/github/fabiocaccamo/python-fsutil/)
12
11
  [![](https://img.shields.io/badge/code%20style-black-000000.svg?logo=python&logoColor=black)](https://github.com/psf/black)
13
12
  [![](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](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 ath the given directory path.
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 unittest
772
- python -m unittest
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 [Twitter](https://twitter.com/fabiocaccamo)
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 = ["fsutil"]
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.packages.find]
107
- include = ["fsutil*"]
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,13 @@
1
+ from __future__ import annotations
2
+
3
+ import os
4
+
5
+ from fsutil.types import PathIn
6
+
7
+
8
+ def get_path(path: PathIn) -> str:
9
+ if path is None:
10
+ return None
11
+ if isinstance(path, str):
12
+ return os.path.normpath(path)
13
+ return str(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)