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 |
| GZIP | Yes | No | .gz |
| BZIP2 | Yes | No | .bz2 |
| XZ | Yes | No | .xz |
| ZSTD | Yes | No | .zst, .zstd |
RAR support requires the rarfile package and an external unrar tool.
Plain compressed files expose one member named after the file without its compression extension (e.g. report.txt.gz contains report.txt). They support open() and extract(), but not writing or add(). Their infolist() entry has raw=None; calculating uncompressed size reads the compressed data.
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
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.
Passwords
with Archive("file.zip", pwd=b"secret") as arc:
...
classmethod
arc = Archive.open_archive("file.zip", pwd=b"secret")
Exceptions for passwords:
ValueError: if archive needs password but none was providedKeyError: if provided password is incorrectTypeError: if password is provided for a tarball (not supported)
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 |
kind |
Literal["file", "directory", "symlink", "junction", "hardlink", "fifo", "character-device", "block-device", "unknown"] |
Member type |
link_target |
str | None |
Symlink, junction, or hardlink target when available |
raw |
ZipInfo | TarInfo | RarInfo | None |
Original backend object (None for plain compressed files) |
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
Metadata
Release files for multiarchive 0.2.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| multiarchive-0.2.2.tar.gz | 12.3 kB | Without provenance | Details for multiarchive-0.2.2.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| multiarchive-0.2.2-py3-none-any.whl | Python 3 | none | any | Details for multiarchive-0.2.2-py3-none-any.whl |
Total release size: 24.1 kB
Release files / multiarchive-0.2.2.tar.gz
| Download URL | multiarchive-0.2.2.tar.gz |
|---|---|
| Size | 12.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
c20dd8b2a07b425040019909ecc6d47286e3cbbf121ab9edc3b4ee0fa236533c
|
|
BLAKE2b-256 checksum How to use checksums |
6682f5ec692b88de8c6d63ee782ced1c8124c1f26846062e94129d8e904ed12a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.13.7 {"installer":{"name":"uv","version":"0.13.7","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.2.2-py3-none-any.whl
| Download URL | multiarchive-0.2.2-py3-none-any.whl |
|---|---|
| Size | 11.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
249d0e57fd4f2777a0ee91e9d56449f46e8ba4ef51a488fa2f403918d0289771
|
|
BLAKE2b-256 checksum How to use checksums |
d581c60d57f34f42457ce89c3611713d838e8fc26f73ef6b3bed6fbeccfee4af
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.13.7 {"installer":{"name":"uv","version":"0.13.7","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}
|
Security for multiarchive 0.2.2
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.