Hello world!
Как‑то раз мне понадобилось работать с архивами в одном своем проекте, и я знатно прифигел от стандартных инструментов. Возник такой конфуз: для обычного ZIP нужен zipfile, для какого нибудь TAR tarfile, а если понадобится добавить 7z или RAR, то вообще нужны py7zr и rarfile, из за этого код превращается в странный и «не очень красивый» забор из импортов и кусков логики под каждый чих. Меня это ооочень сильно выбесило, и буквально 2–3 дня назад я сел писать свой «комбайн» для архивов, который назвал huntZip.
Какие вообще архивы поддерживаются?
ZIP (так же включая Zip64): Поддерживается распаковка и запаковка
TAR (а так же его версии GZip, Bzip2, XZ): Полноценная поддержка
RAR: В разработке (Попробую добавить запаковку, по поводу распаковки пока не знаю)
Тесты скорости (бенчмарки)
Я решил проверить скорость моей библиотеки по сравнению с zipfile, и пришел к выводу что huntZip быстрее zipfile в 1.26 раз. Все тесты я проводил на Ryzen 5 (Но скорее всего будет так же быстро работать и на других ПК)
huntZip: 0.334 сек.
zipfile: 0.421 сек.
Почему так быстро? Дело в том, что я использовал метод
f.relative_to(source)и подключил Zip64 через allowZip64=True.И вместо старого и уже многим известного метода
os.walk, который выдает кучу лишних строк и усложняет код, я использую методsource.rglob("*")из библиотекиpathlib. Он работает как ленивый генератор. Это значит, что Python не загружает весь список файлов папки в память сразу, а перебирает их по одному на лету.По поводу
f.relative_to(source)—f.relative_to(source)находит относительный путь файла внутри папки. Из‑за этого структура архива получается чистой, без дублирования лишних родительских путей в самом ZIP‑файле.
Как скачать библиотеку?
Скачать вы можете в PyPI по простецкой команде:
pip install huntzip
Либо в разделе Releases на Codeberg
А как вообще работать с библиотекой?
Возьмем в пример ZIP:
from huntzip import pack, unpack pack("my_project", "backup.zip")
(Для других форматов просто меняем расширение.)
А если захотели распаковать:
unpack("backup.7z", "extracted_ready")
(либо вместо 7z поставьте ваш формат)
Как это устроено внутри?
Код получился очень простым и прозрачным. Я собираю расширения через pathlib и вызываю нужный контекстный менеджер под капотом:
import os import tarfile import zipfile from pathlib import Path import py7zr def pack(source_path: str, archive_name: str) -> str: source = Path(source_path) if not source.exists(): raise FileNotFoundError(f"Path not found: {source_path}") archive = Path(archive_name) ext = "".join(archive.suffixes).lower() if ext == ".zip": with zipfile.ZipFile(archive, "w", zipfile.ZIP_DEFLATED, allowZip64=True) as zipf: if source.is_file(): zipf.write(source, source.name) else: for f in source.rglob("*"): if f.is_file(): zipf.write(f, f.relative_to(source)) elif ext == ".7z": with py7zr.SevenZipFile(archive, "w") as sz: if source.is_file(): sz.write(source, source.name) else: sz.writeall(source, source.name) elif ".tar" in ext or ext.endswith((".gz", ".bz2", ".xz")): mode = "w" if ext.endswith(".gz"): mode = "w:gz" elif ext.endswith(".bz2"): mode = "w:bz2" elif ext.endswith(".xz"): mode = "w:xz" with tarfile.open(archive, mode) as tar: tar.add(source, arcname=source.name) else: raise ValueError(f"Unsupported format: {ext}") return str(archive.resolve()) def unpack(archive_path: str, extract_to: str = ".") -> str: archive = Path(archive_path) if not archive.exists(): raise FileNotFoundError(f"Archive not found: {archive_path}") target_dir = Path(extract_to) target_dir.mkdir(parents=True, exist_ok=True) ext = "".join(archive.suffixes).lower() if ext == ".zip": with zipfile.ZipFile(archive, "r", allowZip64=True) as zipf: zipf.extractall(target_dir) elif ext == ".7z": with py7zr.SevenZipFile(archive, "r") as sz: sz.extractall(path=target_dir) elif ".tar" in ext or ext.endswith((".gz", ".bz2", ".xz")): with tarfile.open(archive, "r:*") as tar: try: tar.extractall(target_dir, filter='fully_trusted') except TypeError: tar.extractall(target_dir) else: raise ValueError(f"Unknown archive format: {ext}") return str(target_dir.resolve())
Что дальше, и проблема с RAR
Проект развивается, но я есть юридический тупик с RAR.
Изначально хотел сделать и чтение, и запись. Но формат проприетарный, алгоритм сжатия принадлежит WinRAR. Бесплатно и легально можно только распаковать файлы. Напишешь свою запаковку...И прилетит DMCA, а тем самым репозиторий удалят.
Поэтому планы такие:
RAR только на распаковку: Скоро добавлю распаковку через
rarfile. При попытке запаковать в.rarбиблиотека просто выдаст ошибку.Индикатор прогресса: Сделаю коллбеки, чтобы при сборке больших архивов в консоли бежала полоса загрузки.
Как помочь проекту?
Код открытый, и я буду рад любой помощи. Если хотите докинуть фичу:
Форкайте репозиторий на Codeberg.
Делайте ветку с вашей фичей.
Пишите тесты.
Присылайте Pull Request.

