klygo 0.1.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 (42) hide show
  1. klygo-0.1.0/PKG-INFO +151 -0
  2. klygo-0.1.0/README.md +131 -0
  3. klygo-0.1.0/klygo/__init__.py +18 -0
  4. klygo-0.1.0/klygo/archive/__init__.py +20 -0
  5. klygo-0.1.0/klygo/archive/_utils.py +7 -0
  6. klygo-0.1.0/klygo/archive/compress.py +119 -0
  7. klygo-0.1.0/klygo/archive/extract.py +183 -0
  8. klygo-0.1.0/klygo/archive/info.py +125 -0
  9. klygo-0.1.0/klygo/archive/list.py +108 -0
  10. klygo-0.1.0/klygo/archive/modify.py +166 -0
  11. klygo-0.1.0/klygo/archive/transform.py +234 -0
  12. klygo-0.1.0/klygo/datasets/__init__.py +14 -0
  13. klygo-0.1.0/klygo/datasets/_utils.py +135 -0
  14. klygo-0.1.0/klygo/datasets/info.py +68 -0
  15. klygo-0.1.0/klygo/datasets/merge.py +166 -0
  16. klygo-0.1.0/klygo/datasets/partition.py +179 -0
  17. klygo-0.1.0/klygo/datasets/remap.py +173 -0
  18. klygo-0.1.0/klygo/datasets/split.py +214 -0
  19. klygo-0.1.0/klygo/io/__init__.py +18 -0
  20. klygo-0.1.0/klygo/io/config.py +153 -0
  21. klygo-0.1.0/klygo/io/read.py +181 -0
  22. klygo-0.1.0/klygo/io/write.py +284 -0
  23. klygo-0.1.0/klygo/models/__init__.py +5 -0
  24. klygo-0.1.0/klygo/models/kernel.py +376 -0
  25. klygo-0.1.0/klygo/utils/__init__.py +0 -0
  26. klygo-0.1.0/klygo/validators/__init__.py +5 -0
  27. klygo-0.1.0/klygo/validators/archive/__init__.py +27 -0
  28. klygo-0.1.0/klygo/validators/archive/validators.py +268 -0
  29. klygo-0.1.0/klygo/validators/datasets/__init__.py +15 -0
  30. klygo-0.1.0/klygo/validators/datasets/validators.py +181 -0
  31. klygo-0.1.0/klygo/validators/io/__init__.py +8 -0
  32. klygo-0.1.0/klygo/validators/io/validators.py +113 -0
  33. klygo-0.1.0/klygo/validators/models/__init__.py +7 -0
  34. klygo-0.1.0/klygo/validators/models/validators.py +112 -0
  35. klygo-0.1.0/klygo/validators/type_validator.py +18 -0
  36. klygo-0.1.0/klygo.egg-info/PKG-INFO +151 -0
  37. klygo-0.1.0/klygo.egg-info/SOURCES.txt +40 -0
  38. klygo-0.1.0/klygo.egg-info/dependency_links.txt +1 -0
  39. klygo-0.1.0/klygo.egg-info/requires.txt +13 -0
  40. klygo-0.1.0/klygo.egg-info/top_level.txt +1 -0
  41. klygo-0.1.0/pyproject.toml +21 -0
  42. klygo-0.1.0/setup.cfg +4 -0
klygo-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,151 @@
1
+ Metadata-Version: 2.4
2
+ Name: klygo
3
+ Version: 0.1.0
4
+ Summary: Add your description here
5
+ Requires-Python: >=3.12
6
+ Description-Content-Type: text/markdown
7
+ Requires-Dist: build>=1.5.1
8
+ Requires-Dist: numpy>=2.5.1
9
+ Requires-Dist: opencv-python>=5.0.0.93
10
+ Requires-Dist: pillow>=12.3.0
11
+ Requires-Dist: pydantic>=2.13.4
12
+ Requires-Dist: python-box>=7.4.1
13
+ Requires-Dist: pyyaml>=6.0.3
14
+ Requires-Dist: ruamel-yaml>=0.19.1
15
+ Requires-Dist: tomlkit>=0.15.0
16
+ Requires-Dist: torch>=2.13.0
17
+ Requires-Dist: tqdm>=4.68.4
18
+ Requires-Dist: transformers>=5.13.0
19
+ Requires-Dist: twine>=6.2.0
20
+
21
+ # klygo
22
+
23
+ Thư viện Python hỗ trợ nén/giải nén file, xử lý bộ dữ liệu YOLO, đọc/ghi file cấu hình và nhận diện vật thể zero-shot.
24
+
25
+ ---
26
+
27
+ ## Tính năng chính
28
+
29
+ - klygo.archive: Các thao tác với file nén ZIP (nén, giải nén, tìm kiếm, gộp, chia, thêm, xóa file) hỗ trợ hiển thị thanh tiến trình.
30
+ - klygo.datasets: Các công cụ quản lý và xử lý bộ dữ liệu YOLO (phân chia train/val/test, chia lại tỷ lệ tại chỗ, gộp nhiều dataset có ánh xạ lại class ID, và tách dataset theo class/tỷ lệ con).
31
+ - klygo.io: Đọc và ghi các file cấu hình YAML, JSON, TOML, tự động tạo thư mục, tự động phân giải đường dẫn tương đối và truy cập dạng thuộc tính qua Box.
32
+ - klygo.models: Lớp hỗ trợ nhận diện vật thể zero-shot (dựa trên Grounding DINO) trên ảnh và video, tự động xuất dữ liệu theo định dạng YOLO.
33
+
34
+ ---
35
+
36
+ ## Cấu trúc thư mục
37
+
38
+ ```
39
+ klygo/
40
+ ├── archive/ # Các tiện ích nén và giải nén
41
+ ├── datasets/ # Công cụ quản lý và phân chia bộ dữ liệu YOLO
42
+ ├── io/ # Bộ đọc/ghi cấu hình YAML, JSON, TOML
43
+ ├── models/ # Lớp nhận diện vật thể (Kernel)
44
+ └── validators/ # Bộ xác thực dữ liệu đầu vào
45
+ ```
46
+
47
+ ---
48
+
49
+ ## Hướng dẫn sử dụng nhanh
50
+
51
+ ### 1. Nén và giải nén (klygo.archive as ar)
52
+
53
+ ```python
54
+ import klygo.archive as ar
55
+
56
+ # Nén thư mục và giải nén
57
+ ar.compress("src_directory/", "archive.zip")
58
+ ar.extract("archive.zip", output="destination_dir/", overwrite=True)
59
+
60
+ # Tìm kiếm file trong archive
61
+ files = ar.search("archive.zip", "images/*.jpg")
62
+ ```
63
+
64
+ Xem tài liệu đầy đủ tại docs/archive/README.md.
65
+
66
+ ---
67
+
68
+ ### 2. Quản lý bộ dữ liệu YOLO (klygo.datasets)
69
+
70
+ ```python
71
+ from klygo.datasets import partition, repartition, merge, split, remap_classes, get_dataset_info
72
+
73
+ # 1. Phân chia tập dữ liệu thô thành train/val/test
74
+ partition(source="raw_data.zip", output="dataset/", ratios=(0.8,), overwrite=True)
75
+
76
+ # 2. Hợp nhất hai bộ dữ liệu khác class tự động đổi class ID
77
+ merge(sources=["data1/", "data2.zip"], output="merged.zip", overwrite=True)
78
+
79
+ # 3. Tách nhỏ bộ dữ liệu theo từng class đối tượng riêng biệt
80
+ split(source="merged.zip", output_dir="split_classes/", by_class=True, overwrite=True)
81
+
82
+ # 4. Ánh xạ và đổi tên lớp dữ liệu
83
+ remap_classes(source="merged.zip", output="remapped.zip", class_map={0: 1, "apple": "red_apple"}, overwrite=True)
84
+
85
+ # 5. Lấy thông tin chi tiết của bộ dữ liệu YOLO
86
+ info = get_dataset_info("merged.zip")
87
+ print(info)
88
+
89
+
90
+ ```
91
+
92
+ Xem tài liệu đầy đủ tại docs/datasets/README.md.
93
+
94
+ ---
95
+
96
+ ### 3. File cấu hình (klygo.io as io)
97
+
98
+ ```python
99
+ import klygo.io as io
100
+
101
+ # Ghi file cấu hình
102
+ config_data = {"learning_rate": 0.01, "epochs": 50}
103
+ io.write_file("configs/train_params.toml", config_data, overwrite=True)
104
+
105
+ # Đọc file cấu hình và truy cập bằng thuộc tính
106
+ cfg = io.Config("configs/train_params.toml").imread()
107
+ print(cfg.learning_rate) # 0.01
108
+ ```
109
+
110
+ Xem tài liệu đầy đủ tại docs/io/README.md.
111
+
112
+ ---
113
+
114
+ ### 4. Mô hình nhận diện (klygo.models.Kernel)
115
+
116
+ ```python
117
+ from klygo.models import Kernel
118
+
119
+ # Khởi tạo mô hình Grounding DINO
120
+ kernel = Kernel()
121
+
122
+ # Chạy nhận diện trên video và xuất ra bộ dữ liệu YOLO
123
+ kernel.detect(
124
+ input_path="input_video.mp4",
125
+ prompt="car. traffic light.",
126
+ save_yolo_dir="dataset/"
127
+ )
128
+ ```
129
+
130
+ Xem tài liệu đầy đủ tại docs/models/README.md.
131
+
132
+ ---
133
+
134
+ ## Cài đặt
135
+
136
+ ```bash
137
+ git clone https://github.com/your-username/klygo.git
138
+ cd klygo
139
+ uv sync
140
+ ```
141
+
142
+ ---
143
+
144
+ ## Tài liệu chi tiết
145
+
146
+ Tất cả hướng dẫn đầy đủ đều được lưu tại thư mục docs:
147
+ * Hướng dẫn nén & giải nén: docs/archive/README.md
148
+ * Hướng dẫn xử lý bộ dữ liệu YOLO: docs/datasets/README.md
149
+ * Hướng dẫn cấu hình & IO: docs/io/README.md
150
+ * Hướng dẫn nhận diện vật thể: docs/models/README.md
151
+ # k l y g o
klygo-0.1.0/README.md ADDED
@@ -0,0 +1,131 @@
1
+ # klygo
2
+
3
+ Thư viện Python hỗ trợ nén/giải nén file, xử lý bộ dữ liệu YOLO, đọc/ghi file cấu hình và nhận diện vật thể zero-shot.
4
+
5
+ ---
6
+
7
+ ## Tính năng chính
8
+
9
+ - klygo.archive: Các thao tác với file nén ZIP (nén, giải nén, tìm kiếm, gộp, chia, thêm, xóa file) hỗ trợ hiển thị thanh tiến trình.
10
+ - klygo.datasets: Các công cụ quản lý và xử lý bộ dữ liệu YOLO (phân chia train/val/test, chia lại tỷ lệ tại chỗ, gộp nhiều dataset có ánh xạ lại class ID, và tách dataset theo class/tỷ lệ con).
11
+ - klygo.io: Đọc và ghi các file cấu hình YAML, JSON, TOML, tự động tạo thư mục, tự động phân giải đường dẫn tương đối và truy cập dạng thuộc tính qua Box.
12
+ - klygo.models: Lớp hỗ trợ nhận diện vật thể zero-shot (dựa trên Grounding DINO) trên ảnh và video, tự động xuất dữ liệu theo định dạng YOLO.
13
+
14
+ ---
15
+
16
+ ## Cấu trúc thư mục
17
+
18
+ ```
19
+ klygo/
20
+ ├── archive/ # Các tiện ích nén và giải nén
21
+ ├── datasets/ # Công cụ quản lý và phân chia bộ dữ liệu YOLO
22
+ ├── io/ # Bộ đọc/ghi cấu hình YAML, JSON, TOML
23
+ ├── models/ # Lớp nhận diện vật thể (Kernel)
24
+ └── validators/ # Bộ xác thực dữ liệu đầu vào
25
+ ```
26
+
27
+ ---
28
+
29
+ ## Hướng dẫn sử dụng nhanh
30
+
31
+ ### 1. Nén và giải nén (klygo.archive as ar)
32
+
33
+ ```python
34
+ import klygo.archive as ar
35
+
36
+ # Nén thư mục và giải nén
37
+ ar.compress("src_directory/", "archive.zip")
38
+ ar.extract("archive.zip", output="destination_dir/", overwrite=True)
39
+
40
+ # Tìm kiếm file trong archive
41
+ files = ar.search("archive.zip", "images/*.jpg")
42
+ ```
43
+
44
+ Xem tài liệu đầy đủ tại docs/archive/README.md.
45
+
46
+ ---
47
+
48
+ ### 2. Quản lý bộ dữ liệu YOLO (klygo.datasets)
49
+
50
+ ```python
51
+ from klygo.datasets import partition, repartition, merge, split, remap_classes, get_dataset_info
52
+
53
+ # 1. Phân chia tập dữ liệu thô thành train/val/test
54
+ partition(source="raw_data.zip", output="dataset/", ratios=(0.8,), overwrite=True)
55
+
56
+ # 2. Hợp nhất hai bộ dữ liệu khác class tự động đổi class ID
57
+ merge(sources=["data1/", "data2.zip"], output="merged.zip", overwrite=True)
58
+
59
+ # 3. Tách nhỏ bộ dữ liệu theo từng class đối tượng riêng biệt
60
+ split(source="merged.zip", output_dir="split_classes/", by_class=True, overwrite=True)
61
+
62
+ # 4. Ánh xạ và đổi tên lớp dữ liệu
63
+ remap_classes(source="merged.zip", output="remapped.zip", class_map={0: 1, "apple": "red_apple"}, overwrite=True)
64
+
65
+ # 5. Lấy thông tin chi tiết của bộ dữ liệu YOLO
66
+ info = get_dataset_info("merged.zip")
67
+ print(info)
68
+
69
+
70
+ ```
71
+
72
+ Xem tài liệu đầy đủ tại docs/datasets/README.md.
73
+
74
+ ---
75
+
76
+ ### 3. File cấu hình (klygo.io as io)
77
+
78
+ ```python
79
+ import klygo.io as io
80
+
81
+ # Ghi file cấu hình
82
+ config_data = {"learning_rate": 0.01, "epochs": 50}
83
+ io.write_file("configs/train_params.toml", config_data, overwrite=True)
84
+
85
+ # Đọc file cấu hình và truy cập bằng thuộc tính
86
+ cfg = io.Config("configs/train_params.toml").imread()
87
+ print(cfg.learning_rate) # 0.01
88
+ ```
89
+
90
+ Xem tài liệu đầy đủ tại docs/io/README.md.
91
+
92
+ ---
93
+
94
+ ### 4. Mô hình nhận diện (klygo.models.Kernel)
95
+
96
+ ```python
97
+ from klygo.models import Kernel
98
+
99
+ # Khởi tạo mô hình Grounding DINO
100
+ kernel = Kernel()
101
+
102
+ # Chạy nhận diện trên video và xuất ra bộ dữ liệu YOLO
103
+ kernel.detect(
104
+ input_path="input_video.mp4",
105
+ prompt="car. traffic light.",
106
+ save_yolo_dir="dataset/"
107
+ )
108
+ ```
109
+
110
+ Xem tài liệu đầy đủ tại docs/models/README.md.
111
+
112
+ ---
113
+
114
+ ## Cài đặt
115
+
116
+ ```bash
117
+ git clone https://github.com/your-username/klygo.git
118
+ cd klygo
119
+ uv sync
120
+ ```
121
+
122
+ ---
123
+
124
+ ## Tài liệu chi tiết
125
+
126
+ Tất cả hướng dẫn đầy đủ đều được lưu tại thư mục docs:
127
+ * Hướng dẫn nén & giải nén: docs/archive/README.md
128
+ * Hướng dẫn xử lý bộ dữ liệu YOLO: docs/datasets/README.md
129
+ * Hướng dẫn cấu hình & IO: docs/io/README.md
130
+ * Hướng dẫn nhận diện vật thể: docs/models/README.md
131
+ # k l y g o
@@ -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
+ ]
@@ -0,0 +1,7 @@
1
+ def _human_size(num_bytes: int) -> str:
2
+ """Return a human-readable file size string."""
3
+ for unit in ("B", "KB", "MB", "GB", "TB"):
4
+ if num_bytes < 1024:
5
+ return f"{num_bytes:.1f} {unit}"
6
+ num_bytes //= 1024
7
+ return f"{num_bytes:.1f} PB"
@@ -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)}")
@@ -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)