klygo 0.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.
- klygo/__init__.py +18 -0
- klygo/archive/__init__.py +20 -0
- klygo/archive/_utils.py +7 -0
- klygo/archive/compress.py +119 -0
- klygo/archive/extract.py +183 -0
- klygo/archive/info.py +125 -0
- klygo/archive/list.py +108 -0
- klygo/archive/modify.py +166 -0
- klygo/archive/transform.py +234 -0
- klygo/datasets/__init__.py +14 -0
- klygo/datasets/_utils.py +135 -0
- klygo/datasets/info.py +68 -0
- klygo/datasets/merge.py +166 -0
- klygo/datasets/partition.py +179 -0
- klygo/datasets/remap.py +173 -0
- klygo/datasets/split.py +214 -0
- klygo/io/__init__.py +18 -0
- klygo/io/config.py +153 -0
- klygo/io/read.py +181 -0
- klygo/io/write.py +284 -0
- klygo/models/__init__.py +5 -0
- klygo/models/kernel.py +376 -0
- klygo/utils/__init__.py +0 -0
- klygo/validators/__init__.py +5 -0
- klygo/validators/archive/__init__.py +27 -0
- klygo/validators/archive/validators.py +268 -0
- klygo/validators/datasets/__init__.py +15 -0
- klygo/validators/datasets/validators.py +181 -0
- klygo/validators/io/__init__.py +8 -0
- klygo/validators/io/validators.py +113 -0
- klygo/validators/models/__init__.py +7 -0
- klygo/validators/models/validators.py +112 -0
- klygo/validators/type_validator.py +18 -0
- klygo-0.1.0.dist-info/METADATA +151 -0
- klygo-0.1.0.dist-info/RECORD +37 -0
- klygo-0.1.0.dist-info/WHEEL +5 -0
- klygo-0.1.0.dist-info/top_level.txt +1 -0
klygo/__init__.py
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from . import archive
|
|
2
|
+
from . import datasets
|
|
3
|
+
from . import io
|
|
4
|
+
from . import models
|
|
5
|
+
from . import utils
|
|
6
|
+
from . import validators
|
|
7
|
+
|
|
8
|
+
__version__ = "1.0.1"
|
|
9
|
+
__author__ = "IchigoMazone"
|
|
10
|
+
|
|
11
|
+
__all__ = [
|
|
12
|
+
"archive",
|
|
13
|
+
"datasets",
|
|
14
|
+
"io",
|
|
15
|
+
"models",
|
|
16
|
+
"utils",
|
|
17
|
+
"validators",
|
|
18
|
+
]
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
from .compress import compress
|
|
2
|
+
from .extract import extract, extract_file
|
|
3
|
+
from .list import list_files, search
|
|
4
|
+
from .info import get_info, test
|
|
5
|
+
from .modify import add, remove
|
|
6
|
+
from .transform import merge, split
|
|
7
|
+
|
|
8
|
+
__all__ = [
|
|
9
|
+
"compress",
|
|
10
|
+
"extract",
|
|
11
|
+
"extract_file",
|
|
12
|
+
"list_files",
|
|
13
|
+
"search",
|
|
14
|
+
"get_info",
|
|
15
|
+
"test",
|
|
16
|
+
"add",
|
|
17
|
+
"remove",
|
|
18
|
+
"merge",
|
|
19
|
+
"split",
|
|
20
|
+
]
|
klygo/archive/_utils.py
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
from zipfile import ZipFile, ZIP_DEFLATED
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
from tqdm import tqdm
|
|
5
|
+
|
|
6
|
+
from klygo.validators.archive import Compress
|
|
7
|
+
from klygo.archive._utils import _human_size
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def compress(
|
|
11
|
+
source: str | Path,
|
|
12
|
+
output: str | Path,
|
|
13
|
+
format: str = "zip",
|
|
14
|
+
overwrite: bool = False,
|
|
15
|
+
verbose: bool = True,
|
|
16
|
+
) -> None:
|
|
17
|
+
"""Compress a file or directory into a ZIP archive.
|
|
18
|
+
|
|
19
|
+
When ``source`` is a directory, all files are added recursively
|
|
20
|
+
while preserving the internal directory structure. Parent-level
|
|
21
|
+
directory name is kept as the root inside the archive.
|
|
22
|
+
|
|
23
|
+
Parameters
|
|
24
|
+
----------
|
|
25
|
+
source : str or Path
|
|
26
|
+
Path to the file or directory to compress.
|
|
27
|
+
output : str or Path
|
|
28
|
+
Destination path for the archive (e.g. ``"data.zip"``).
|
|
29
|
+
Parent directories are created automatically if they do not exist.
|
|
30
|
+
format : str, optional
|
|
31
|
+
Archive format. Currently only ``"zip"`` is supported.
|
|
32
|
+
Default is ``"zip"``.
|
|
33
|
+
overwrite : bool, optional
|
|
34
|
+
If *True*, silently overwrite ``output`` when it already exists.
|
|
35
|
+
If *False* (default) and ``output`` exists, raises ``FileExistsError``.
|
|
36
|
+
verbose : bool, optional
|
|
37
|
+
If *True* (default), display a coloured progress bar and print
|
|
38
|
+
the final archive size on completion.
|
|
39
|
+
|
|
40
|
+
Returns
|
|
41
|
+
-------
|
|
42
|
+
None
|
|
43
|
+
|
|
44
|
+
Raises
|
|
45
|
+
------
|
|
46
|
+
TypeError
|
|
47
|
+
If any argument has the wrong type.
|
|
48
|
+
FileNotFoundError
|
|
49
|
+
If ``source`` does not exist.
|
|
50
|
+
FileExistsError
|
|
51
|
+
If ``output`` already exists and ``overwrite`` is *False*.
|
|
52
|
+
ValueError
|
|
53
|
+
If ``format`` is not supported, or ``output`` has the wrong extension.
|
|
54
|
+
|
|
55
|
+
Examples
|
|
56
|
+
--------
|
|
57
|
+
Compress a single file:
|
|
58
|
+
|
|
59
|
+
>>> compress("report.csv", "report.zip")
|
|
60
|
+
|
|
61
|
+
Compress an entire directory, overwriting any existing archive:
|
|
62
|
+
|
|
63
|
+
>>> compress("my_dataset/", "dataset.zip", overwrite=True)
|
|
64
|
+
|
|
65
|
+
Compress silently (no progress bar):
|
|
66
|
+
|
|
67
|
+
>>> compress("images/", "images.zip", verbose=False)
|
|
68
|
+
"""
|
|
69
|
+
|
|
70
|
+
params = Compress(
|
|
71
|
+
source=source,
|
|
72
|
+
output=output,
|
|
73
|
+
format=format,
|
|
74
|
+
overwrite=overwrite,
|
|
75
|
+
verbose=verbose,
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
source_path: Path = params.source
|
|
79
|
+
output_path: Path = params.output
|
|
80
|
+
|
|
81
|
+
# Collect all files to compress
|
|
82
|
+
if source_path.is_dir():
|
|
83
|
+
all_files: list[Path] = sorted(
|
|
84
|
+
f for f in source_path.rglob("*") if f.is_file()
|
|
85
|
+
)
|
|
86
|
+
else:
|
|
87
|
+
all_files = [source_path]
|
|
88
|
+
|
|
89
|
+
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
90
|
+
|
|
91
|
+
with ZipFile(output_path, mode="w", compression=ZIP_DEFLATED) as zf:
|
|
92
|
+
bar = (
|
|
93
|
+
tqdm(
|
|
94
|
+
total=len(all_files) or 1,
|
|
95
|
+
desc="Compressing",
|
|
96
|
+
unit="file",
|
|
97
|
+
colour="green",
|
|
98
|
+
bar_format="{l_bar}{bar:30}{r_bar}",
|
|
99
|
+
)
|
|
100
|
+
if verbose
|
|
101
|
+
else None
|
|
102
|
+
)
|
|
103
|
+
for file in all_files:
|
|
104
|
+
arcname = (
|
|
105
|
+
file.relative_to(source_path.parent)
|
|
106
|
+
if source_path.is_dir()
|
|
107
|
+
else file.name
|
|
108
|
+
)
|
|
109
|
+
zf.write(file, arcname=arcname)
|
|
110
|
+
if bar is not None:
|
|
111
|
+
bar.update(1)
|
|
112
|
+
if bar is not None:
|
|
113
|
+
if not all_files:
|
|
114
|
+
bar.update(1)
|
|
115
|
+
bar.close()
|
|
116
|
+
|
|
117
|
+
if verbose:
|
|
118
|
+
total_size = output_path.stat().st_size
|
|
119
|
+
print(f"Done. Archive size: {_human_size(total_size)}")
|
klygo/archive/extract.py
ADDED
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
from zipfile import ZipFile
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
from tqdm import tqdm
|
|
5
|
+
|
|
6
|
+
from klygo.validators.archive import Extract, ExtractFile
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def extract(
|
|
10
|
+
source: str | Path,
|
|
11
|
+
output: str | Path = ".",
|
|
12
|
+
overwrite: bool = False,
|
|
13
|
+
verbose: bool = True,
|
|
14
|
+
) -> None:
|
|
15
|
+
"""Extract all contents of an archive to a directory.
|
|
16
|
+
|
|
17
|
+
The full directory structure stored inside the archive is
|
|
18
|
+
recreated under ``output``. The output directory is created
|
|
19
|
+
automatically if it does not exist.
|
|
20
|
+
|
|
21
|
+
Parameters
|
|
22
|
+
----------
|
|
23
|
+
source : str or Path
|
|
24
|
+
Path to the archive file to extract (e.g. ``"data.zip"``).
|
|
25
|
+
output : str or Path, optional
|
|
26
|
+
Directory where the archive contents will be extracted.
|
|
27
|
+
Defaults to the current working directory (``"."``).
|
|
28
|
+
overwrite : bool, optional
|
|
29
|
+
If *True*, overwrite files that already exist in ``output``.
|
|
30
|
+
If *False* (default) and conflicting files exist, raises
|
|
31
|
+
``FileExistsError`` listing the first offending names.
|
|
32
|
+
verbose : bool, optional
|
|
33
|
+
If *True* (default), display a coloured progress bar and print
|
|
34
|
+
the total number of extracted files on completion.
|
|
35
|
+
|
|
36
|
+
Returns
|
|
37
|
+
-------
|
|
38
|
+
None
|
|
39
|
+
|
|
40
|
+
Raises
|
|
41
|
+
------
|
|
42
|
+
TypeError
|
|
43
|
+
If any argument has the wrong type.
|
|
44
|
+
FileNotFoundError
|
|
45
|
+
If ``source`` does not exist.
|
|
46
|
+
ValueError
|
|
47
|
+
If ``source`` is not a supported archive format.
|
|
48
|
+
FileExistsError
|
|
49
|
+
If files in the archive already exist at the destination
|
|
50
|
+
and ``overwrite`` is *False*.
|
|
51
|
+
|
|
52
|
+
Examples
|
|
53
|
+
--------
|
|
54
|
+
Extract to a specific folder:
|
|
55
|
+
|
|
56
|
+
>>> extract("data.zip", output="data/")
|
|
57
|
+
|
|
58
|
+
Extract and overwrite any existing files:
|
|
59
|
+
|
|
60
|
+
>>> extract("data.zip", output="data/", overwrite=True)
|
|
61
|
+
|
|
62
|
+
Extract silently:
|
|
63
|
+
|
|
64
|
+
>>> extract("data.zip", output="data/", verbose=False)
|
|
65
|
+
"""
|
|
66
|
+
|
|
67
|
+
params = Extract(
|
|
68
|
+
source=source,
|
|
69
|
+
output=output,
|
|
70
|
+
overwrite=overwrite,
|
|
71
|
+
verbose=verbose,
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
source_path: Path = params.source
|
|
75
|
+
output_path: Path = params.output
|
|
76
|
+
|
|
77
|
+
output_path.mkdir(parents=True, exist_ok=True)
|
|
78
|
+
|
|
79
|
+
with ZipFile(source_path, mode="r") as zf:
|
|
80
|
+
members = zf.infolist()
|
|
81
|
+
|
|
82
|
+
if not overwrite:
|
|
83
|
+
existing = [
|
|
84
|
+
m for m in members
|
|
85
|
+
if (output_path / m.filename).exists()
|
|
86
|
+
]
|
|
87
|
+
if existing:
|
|
88
|
+
names = ", ".join(m.filename for m in existing[:5])
|
|
89
|
+
suffix = f"… (+{len(existing) - 5} more)" if len(existing) > 5 else ""
|
|
90
|
+
raise FileExistsError(
|
|
91
|
+
f"Files already exist in output directory: {names}{suffix}. "
|
|
92
|
+
"Use overwrite=True to replace them."
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
iterator = (
|
|
96
|
+
tqdm(
|
|
97
|
+
members,
|
|
98
|
+
desc="Extracting",
|
|
99
|
+
unit="file",
|
|
100
|
+
colour="cyan",
|
|
101
|
+
bar_format="{l_bar}{bar:30}{r_bar}",
|
|
102
|
+
)
|
|
103
|
+
if verbose
|
|
104
|
+
else iter(members)
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
for member in iterator:
|
|
108
|
+
zf.extract(member, path=output_path)
|
|
109
|
+
|
|
110
|
+
if verbose:
|
|
111
|
+
print(f"Done. Extracted {len(members)} file(s) to '{output_path}'")
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def extract_file(
|
|
115
|
+
source: str | Path,
|
|
116
|
+
filename: str,
|
|
117
|
+
output: str | Path = ".",
|
|
118
|
+
overwrite: bool = False,
|
|
119
|
+
) -> None:
|
|
120
|
+
"""Extract a single file from an archive without unpacking everything.
|
|
121
|
+
|
|
122
|
+
Use :func:`list_files` to discover the exact ``filename`` string
|
|
123
|
+
as it is stored inside the archive, then pass that value here.
|
|
124
|
+
|
|
125
|
+
Parameters
|
|
126
|
+
----------
|
|
127
|
+
source : str or Path
|
|
128
|
+
Path to the archive file.
|
|
129
|
+
filename : str
|
|
130
|
+
Path of the target file *inside* the archive, exactly as returned
|
|
131
|
+
by :func:`list_files` (e.g. ``"images/frame_001.jpg"``).
|
|
132
|
+
output : str or Path, optional
|
|
133
|
+
Directory where the file will be written.
|
|
134
|
+
Defaults to the current working directory (``"."``).
|
|
135
|
+
overwrite : bool, optional
|
|
136
|
+
If *True*, overwrite the file if it already exists at the destination.
|
|
137
|
+
Default is *False*.
|
|
138
|
+
|
|
139
|
+
Returns
|
|
140
|
+
-------
|
|
141
|
+
None
|
|
142
|
+
|
|
143
|
+
Raises
|
|
144
|
+
------
|
|
145
|
+
TypeError
|
|
146
|
+
If any argument has the wrong type.
|
|
147
|
+
FileNotFoundError
|
|
148
|
+
If ``source`` does not exist.
|
|
149
|
+
KeyError
|
|
150
|
+
If ``filename`` is not found inside the archive.
|
|
151
|
+
FileExistsError
|
|
152
|
+
If the destination file already exists and ``overwrite`` is *False*.
|
|
153
|
+
|
|
154
|
+
Examples
|
|
155
|
+
--------
|
|
156
|
+
>>> extract_file("data.zip", "images/frame_001.jpg", output="out/")
|
|
157
|
+
"""
|
|
158
|
+
|
|
159
|
+
params = ExtractFile(
|
|
160
|
+
source=source,
|
|
161
|
+
filename=filename,
|
|
162
|
+
output=output,
|
|
163
|
+
overwrite=overwrite,
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
output_path: Path = params.output
|
|
167
|
+
output_path.mkdir(parents=True, exist_ok=True)
|
|
168
|
+
|
|
169
|
+
with ZipFile(params.source, mode="r") as zf:
|
|
170
|
+
names = zf.namelist()
|
|
171
|
+
if params.filename not in names:
|
|
172
|
+
raise KeyError(
|
|
173
|
+
f"'{params.filename}' not found in archive. "
|
|
174
|
+
f"Use list_files() to see available files."
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
target = output_path / Path(params.filename).name
|
|
178
|
+
if target.exists() and not params.overwrite:
|
|
179
|
+
raise FileExistsError(
|
|
180
|
+
f"file already exists: {target}. Use overwrite=True."
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
zf.extract(params.filename, path=output_path)
|
klygo/archive/info.py
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
from zipfile import ZipFile
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
from klygo.validators.archive import GetInfo, Test
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def get_info(
|
|
8
|
+
source: str | Path,
|
|
9
|
+
) -> dict:
|
|
10
|
+
"""Return a metadata summary of an archive.
|
|
11
|
+
|
|
12
|
+
Reads all member headers in the ZIP central directory — no
|
|
13
|
+
decompression is performed, so this is fast even for very large archives.
|
|
14
|
+
|
|
15
|
+
Parameters
|
|
16
|
+
----------
|
|
17
|
+
source : str or Path
|
|
18
|
+
Path to the archive file.
|
|
19
|
+
|
|
20
|
+
Returns
|
|
21
|
+
-------
|
|
22
|
+
dict
|
|
23
|
+
A dictionary with the following keys:
|
|
24
|
+
|
|
25
|
+
* ``"path"`` (*str*) — absolute path to the archive.
|
|
26
|
+
* ``"format"`` (*str*) — archive format, e.g. ``"zip"``.
|
|
27
|
+
* ``"file_count"`` (*int*) — number of files inside the archive.
|
|
28
|
+
* ``"uncompressed_size"`` (*int*) — total size of all files before
|
|
29
|
+
compression, in bytes.
|
|
30
|
+
* ``"compressed_size"`` (*int*) — total size of all files after
|
|
31
|
+
compression, in bytes.
|
|
32
|
+
* ``"compress_ratio"`` (*float*) — space saved by compression,
|
|
33
|
+
expressed as a percentage (0–100). A value of ``0.0`` means
|
|
34
|
+
no compression was applied.
|
|
35
|
+
* ``"archive_size"`` (*int*) — actual size of the ``.zip`` file
|
|
36
|
+
on disk (includes ZIP metadata overhead).
|
|
37
|
+
|
|
38
|
+
Raises
|
|
39
|
+
------
|
|
40
|
+
TypeError
|
|
41
|
+
If ``source`` is not a ``str`` or ``Path``.
|
|
42
|
+
FileNotFoundError
|
|
43
|
+
If ``source`` does not exist.
|
|
44
|
+
ValueError
|
|
45
|
+
If ``source`` is not a supported archive format.
|
|
46
|
+
|
|
47
|
+
Examples
|
|
48
|
+
--------
|
|
49
|
+
>>> info = get_info("data.zip")
|
|
50
|
+
>>> print(info["file_count"], info["compress_ratio"])
|
|
51
|
+
723 0.64
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
params = GetInfo(source=source)
|
|
55
|
+
|
|
56
|
+
with ZipFile(params.source, mode="r") as zf:
|
|
57
|
+
members = zf.infolist()
|
|
58
|
+
total_uncompressed = sum(m.file_size for m in members)
|
|
59
|
+
total_compressed = sum(m.compress_size for m in members)
|
|
60
|
+
|
|
61
|
+
ratio = (
|
|
62
|
+
round((1 - total_compressed / total_uncompressed) * 100, 2)
|
|
63
|
+
if total_uncompressed > 0
|
|
64
|
+
else 0.0
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
return {
|
|
68
|
+
"path": str(params.source),
|
|
69
|
+
"format": params.source.suffix.lstrip("."),
|
|
70
|
+
"file_count": len(members),
|
|
71
|
+
"uncompressed_size": total_uncompressed,
|
|
72
|
+
"compressed_size": total_compressed,
|
|
73
|
+
"compress_ratio": ratio,
|
|
74
|
+
"archive_size": params.source.stat().st_size,
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def test(
|
|
79
|
+
source: str | Path,
|
|
80
|
+
) -> bool:
|
|
81
|
+
"""Verify the integrity of an archive using CRC checksum validation.
|
|
82
|
+
|
|
83
|
+
Each file inside the archive is read and its CRC32 checksum is compared
|
|
84
|
+
against the value stored in the ZIP header. This catches bit-rot,
|
|
85
|
+
partial downloads, and corrupted archives.
|
|
86
|
+
|
|
87
|
+
Parameters
|
|
88
|
+
----------
|
|
89
|
+
source : str or Path
|
|
90
|
+
Path to the archive file to test.
|
|
91
|
+
|
|
92
|
+
Returns
|
|
93
|
+
-------
|
|
94
|
+
bool
|
|
95
|
+
*True* if every file in the archive passes its CRC check.
|
|
96
|
+
|
|
97
|
+
Raises
|
|
98
|
+
------
|
|
99
|
+
TypeError
|
|
100
|
+
If ``source`` is not a ``str`` or ``Path``.
|
|
101
|
+
FileNotFoundError
|
|
102
|
+
If ``source`` does not exist.
|
|
103
|
+
ValueError
|
|
104
|
+
If ``source`` is not a supported archive format, or if the
|
|
105
|
+
archive is corrupted. The error message includes the name of
|
|
106
|
+
the first file that failed the checksum.
|
|
107
|
+
|
|
108
|
+
Examples
|
|
109
|
+
--------
|
|
110
|
+
>>> ok = test("data.zip")
|
|
111
|
+
>>> print(ok)
|
|
112
|
+
True
|
|
113
|
+
"""
|
|
114
|
+
|
|
115
|
+
params = Test(source=source)
|
|
116
|
+
|
|
117
|
+
with ZipFile(params.source, mode="r") as zf:
|
|
118
|
+
bad_file = zf.testzip()
|
|
119
|
+
|
|
120
|
+
if bad_file is not None:
|
|
121
|
+
raise ValueError(
|
|
122
|
+
f"archive is corrupted. First bad file: '{bad_file}'"
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
return True
|
klygo/archive/list.py
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
from zipfile import ZipFile
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
import fnmatch
|
|
4
|
+
|
|
5
|
+
from klygo.validators.archive import ListFiles, Search
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def list_files(
|
|
9
|
+
source: str | Path,
|
|
10
|
+
) -> list[str]:
|
|
11
|
+
"""Return a list of all file paths stored inside an archive.
|
|
12
|
+
|
|
13
|
+
The returned strings are the *arcnames* — the paths as they are
|
|
14
|
+
stored inside the ZIP (e.g. ``"images/frame_001.jpg"``). These
|
|
15
|
+
values can be passed directly to :func:`extract_file`, :func:`remove`,
|
|
16
|
+
and :func:`search`.
|
|
17
|
+
|
|
18
|
+
Parameters
|
|
19
|
+
----------
|
|
20
|
+
source : str or Path
|
|
21
|
+
Path to the archive file (e.g. ``"data.zip"``).
|
|
22
|
+
|
|
23
|
+
Returns
|
|
24
|
+
-------
|
|
25
|
+
list[str]
|
|
26
|
+
Ordered list of file paths as stored inside the archive.
|
|
27
|
+
|
|
28
|
+
Raises
|
|
29
|
+
------
|
|
30
|
+
TypeError
|
|
31
|
+
If ``source`` is not a ``str`` or ``Path``.
|
|
32
|
+
FileNotFoundError
|
|
33
|
+
If ``source`` does not exist.
|
|
34
|
+
ValueError
|
|
35
|
+
If ``source`` is not a supported archive format.
|
|
36
|
+
|
|
37
|
+
Examples
|
|
38
|
+
--------
|
|
39
|
+
>>> files = list_files("data.zip")
|
|
40
|
+
>>> print(files[:3])
|
|
41
|
+
['images/frame_0.jpg', 'images/frame_1.jpg', 'data.yaml']
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
params = ListFiles(source=source)
|
|
45
|
+
|
|
46
|
+
with ZipFile(params.source, mode="r") as zf:
|
|
47
|
+
names = zf.namelist()
|
|
48
|
+
|
|
49
|
+
return names
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def search(
|
|
53
|
+
source: str | Path,
|
|
54
|
+
pattern: str,
|
|
55
|
+
) -> list[str]:
|
|
56
|
+
"""Find files inside an archive whose paths match a glob pattern.
|
|
57
|
+
|
|
58
|
+
Matching is performed with :func:`fnmatch.fnmatch` against each
|
|
59
|
+
arcname stored in the archive. The search is case-sensitive on
|
|
60
|
+
case-sensitive filesystems.
|
|
61
|
+
|
|
62
|
+
Parameters
|
|
63
|
+
----------
|
|
64
|
+
source : str or Path
|
|
65
|
+
Path to the archive file.
|
|
66
|
+
pattern : str
|
|
67
|
+
Glob pattern to match against file paths inside the archive.
|
|
68
|
+
Supports the standard wildcards:
|
|
69
|
+
|
|
70
|
+
* ``*`` — matches any sequence of characters (not ``/``).
|
|
71
|
+
* ``?`` — matches any single character.
|
|
72
|
+
* ``[seq]`` — matches any character in *seq*.
|
|
73
|
+
|
|
74
|
+
Example: ``"images/*.jpg"`` matches all JPEG files under the
|
|
75
|
+
``images/`` folder.
|
|
76
|
+
|
|
77
|
+
Returns
|
|
78
|
+
-------
|
|
79
|
+
list[str]
|
|
80
|
+
Arcnames of the files that matched ``pattern``, in the order
|
|
81
|
+
they appear inside the archive. Returns an empty list if
|
|
82
|
+
nothing matches.
|
|
83
|
+
|
|
84
|
+
Raises
|
|
85
|
+
------
|
|
86
|
+
TypeError
|
|
87
|
+
If ``source`` or ``pattern`` has the wrong type.
|
|
88
|
+
FileNotFoundError
|
|
89
|
+
If ``source`` does not exist.
|
|
90
|
+
ValueError
|
|
91
|
+
If ``source`` is not a supported archive format.
|
|
92
|
+
|
|
93
|
+
Examples
|
|
94
|
+
--------
|
|
95
|
+
>>> results = search("data.zip", "images/frame_1*.jpg")
|
|
96
|
+
>>> print(len(results))
|
|
97
|
+
111
|
|
98
|
+
|
|
99
|
+
>>> search("data.zip", "*.yaml")
|
|
100
|
+
['data.yaml']
|
|
101
|
+
"""
|
|
102
|
+
|
|
103
|
+
params = Search(source=source, pattern=pattern)
|
|
104
|
+
|
|
105
|
+
with ZipFile(params.source, mode="r") as zf:
|
|
106
|
+
names = zf.namelist()
|
|
107
|
+
|
|
108
|
+
return [name for name in names if fnmatch.fnmatch(name, params.pattern)]
|