multiarchive
A high level abstraction of multiple archive formats
Installation
uv add multiarchive
Optional dependency for RAR support:
uv add rarfile
ZStandard support requires the backports-zstd package on Python <3.14:
uv add "backports-zstd; python_version < '3.14'"
Quick Start
from multiarchive import Archive
# Open and inspect
with Archive("archive.zip") as arc:
print(arc.namelist()) # list of member names
print(len(arc)) # number of members
print(arc.size) # total uncompressed size in bytes
# Extract a single file
with Archive("archive.tar.gz") as arc:
arc.extract("docs/readme.md", path="./output")
# Read file contents without extracting
with Archive("archive.zip") as arc:
f = arc.open("config.json")
content = f.read()
f.close()
# Iterate over members
with Archive("archive.zip") as arc:
for name in arc:
if arc.is_file(name):
print(f" {name}")
Supported Formats
| Format | Read | Write | Extensions |
|---|---|---|---|
| ZIP | Yes | Yes | .zip |
| TAR | Yes | Yes | .tar |
| TAR.GZ | Yes | Yes | .tgz, .tar.gz |
| TAR.BZ2 | Yes | Yes | .tbz, .tbz2, .tar.bz2 |
| TAR.XZ | Yes | Yes | .tar.xz, .tar.lzma |
| TAR.ZST | Yes | Yes | .tzst, .tar.zst |
| RAR | Yes | No | .rar |
RAR support requires the rarfile package and an external unrar tool.
Reading Archives
Opening
Use the context manager (recommended):
with Archive("file.zip") as arc:
...
Or the classmethod:
arc = Archive.open_archive("file.zip")
try:
arc.namelist()
finally:
arc.close()
Reading is supported on all formats, but password-protected archives are not supported and will raise a ValueError.
If algo isnt explicitly provided, it will attempt to open it as ZIP first, then TAR, then RAR (if rarfile is installed). If all fails, it raises a ValueError.
Listing Members
with Archive("file.zip") as arc:
names = arc.namelist() # Just names
names = arc.members # alias to namelist
# extra info (ArchiveMemberInfo objects)
for info in arc.infolist():
print(f"{info.name}: {info.uncompressed_size} bytes, mtime={info.mtime}")
Checking Membership
with Archive("file.zip") as arc:
if "config.json" in arc:
print("Found!")
for name in arc:
if arc.is_file(name):
...
if arc.is_dir(name):
...
Extracting
with Archive("file.tar.gz") as arc:
# Single file
arc.extract("src/main.py", path="./extracted")
Reading Member Contents
with Archive("file.zip") as arc:
f = arc.open("data.json")
if f is not None:
content = f.read()
f.close()
Writing Archives
Creating a New Archive
from multiarchive import Archive
# ZIP
with Archive("output.zip", mode="w") as arc:
...
# TAR.GZ with compression level
with Archive("output.tar.gz", mode="w", compression_level=9) as arc:
...
Add members with an optional name to use inside the archive:
with Archive("output.zip", mode="w") as arc:
arc.add("src/main.py", arcname="main.py")
ZIP timestamps are limited to the years 1980 through 2107. To clamp timestamps outside that range instead of raising an error:
with Archive("output.zip", mode="w", strict_timestamps=False) as arc:
arc.add("old-file.txt")
The format is determined by file extension. Use the algo parameter to override:
with Archive("my_archive", mode="w", algo="tar.xz") as arc:
...
Compression Levels
| Format | Range | Default |
|---|---|---|
| ZIP | 0–9 | 6 |
| TAR.GZ | 0–9 | 6 |
| TAR.BZ2 | 1–9 | 9 |
| TAR.XZ | 0–9 | 6 |
| TAR.ZST | 1–22 | 3 |
File Information Object
The infolist() method returns ArchiveMemberInfo objects with a unified interface across all formats:
| Field | Type | Description |
|---|---|---|
name |
str |
Member filename |
uncompressed_size |
int |
Original file size in bytes |
compressed_size |
int | None |
Compressed size (None for TAR) |
mtime |
float |
Modification time as epoch seconds |
mode |
int | None |
Unix permission bits |
is_dir |
bool |
Whether the member is a directory |
raw |
ZipInfo | TarInfo | RarInfo |
Original backend object (escape hatch) |
Factory methods for direct conversion (you shouldn't need these in normal usage):
info = ArchiveMemberInfo.from_zipinfo(zip_info)
info = ArchiveMemberInfo.from_tarinfo(tar_info)
info = ArchiveMemberInfo.from_rarinfo(rar_info)
Properties
| Method/Property | Description |
|---|---|
members |
Alias for namelist() |
size |
Total uncompressed size of all members |
comment / comment= |
Get/set archive comment (ZIP only, raises ValueError otherwise) |
Error Handling
from multiarchive import Archive, BadArchiveError
try:
with Archive("corrupted.zip") as arc:
arc.namelist()
except BadArchiveError as e:
print(f"Archive error: {e}")
except NotImplementedError as e:
# password-protected (not supported)
print(f"Not implemented: {e}")
except ValueError as e:
# Unknown format
print(f"Value error: {e}")
except FileNotFoundError:
print("File not found")
API Reference
Archive
Archive(
filename: str | Path,
mode: str = "r",
compression_level: int | None = None,
)
| Parameter | Description |
|---|---|
filename |
Path to the archive file |
mode |
'r' for read, 'w' for write, 'a' for append |
compression_level |
Compression level (format-dependent) |
Methods
| Method | Description |
|---|---|
open_archive(cls, filename, mode, compression_level) |
Factory method, returns opened archive |
namelist() |
List of member names |
infolist() |
List of ArchiveMemberInfo objects |
extract(member, path) |
Extract single member |
open(member, mode) |
Open member as file-like object |
is_dir(member) |
Check if member is a directory |
is_file(member) |
Check if member is a file |
close() |
Explicitly close the archive |
BadArchiveError
Raised when an archive file is corrupt or in an unsupported format.
ArchiveExtensions
Frozen dataclass mapping format names to their common file extensions:
ArchiveExtensions.zip # (".zip",)
ArchiveExtensions.gz # (".tgz", ".tar.gz")
ArchiveExtensions.xz # (".tar.xz", ".tar.lzma")
# ... etc
Release files for multiarchive 0.1.4
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| multiarchive-0.1.4.tar.gz | 9.4 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| multiarchive-0.1.4-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 18.8 kB
Release files / multiarchive-0.1.4.tar.gz
| Download URL | multiarchive-0.1.4.tar.gz |
|---|---|
| Size | 9.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
ade1906042be744c01d63ce629cd1611ae9b73b459c0faf516f6e1045ce08fe8
|
|
BLAKE2b-256 checksum How to use checksums |
de7bc7ec2e97183e9931a2752f1d8068c02612283f8189dc0fc7886196446bf2
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.12 {"installer":{"name":"uv","version":"0.12.12","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / multiarchive-0.1.4-py3-none-any.whl
| Download URL | multiarchive-0.1.4-py3-none-any.whl |
|---|---|
| Size | 9.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
baff6aa99921eb24f663f4dbc72f6389517bf49cbe9f55b9f1cb92e68dc26023
|
|
BLAKE2b-256 checksum How to use checksums |
19395563ed02b32b47c73a39aa947e159c78c4a96b21565f555d3428af6f3397
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.12 {"installer":{"name":"uv","version":"0.12.12","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|