libarchive-c
| Language | Python 3 |
|---|---|
| License | CC0 (public domain dedication); libarchive itself is BSD 2-clause |
| Repository | github.com/Changaco/python-libarchive-c |
| Documentation | README |
| Depends on | the libarchive shared library (libarchive.so, libarchive.dylib, archive.dll) |
| Read / write | yes / yes; everything libarchive supports for 7z, including BCJ2; no encryption |
libarchive-c loads the C libarchive library with ctypes and wraps its streaming API in Python context managers: file_reader to iterate over the entries of an archive, file_writer to create one, extract_file to unpack to disk. The same code reads 7z, TAR (all compressions), ZIP, ISO, CPIO, RAR and more; libarchive detects the format.
For 7z it reads LZMA, LZMA2, PPMd, BZip2, Deflate, Zstandard and the BCJ/BCJ2 filters, and writes LZMA2 (default), LZMA1, PPMd, BZip2, Deflate, Zstandard or stored archives. It can’t open encrypted 7z archives; use py7zr for those.
Installation#
pip install libarchive-c
# plus the C library:
sudo apt install libarchive13 # Debian / Ubuntu
brew install libarchive # macOS
macOS ships an old libarchive without 7z write support; point the binding to Homebrew’s copy with the LIBARCHIVE environment variable, for example export LIBARCHIVE=$(brew --prefix libarchive)/lib/libarchive.dylib. On Windows install libarchive with vcpkg or conda and set LIBARCHIVE to the path of archive.dll.
Basic usage: list and extract a 7z archive#
import os
import libarchive
# list entries and read their data
with libarchive.file_reader("archive.7z") as archive:
for entry in archive:
print(entry.pathname, entry.size, "dir" if entry.isdir else "")
# extract to a folder (extract_file writes into the current directory)
os.makedirs("output", exist_ok=True)
os.chdir("output")
libarchive.extract_file("../archive.7z")
By default extract_file uses the secure flags (no .., no absolute paths, no writing through symlinks).
How to read a file from a 7z archive into memory#
Entries must be read in order while iterating; their data is available only for the current entry:
import libarchive
with libarchive.file_reader("archive.7z") as archive:
for entry in archive:
if entry.pathname == "docs/readme.txt":
data = b"".join(entry.get_blocks())
print(data.decode("utf-8"))
break
How to create a 7z archive#
import libarchive
with libarchive.file_writer("backup.7z", "7zip") as archive:
archive.add_files("photos", "report.pdf") # files and folders, recursively
text = b"created by my app\n"
archive.add_file_from_memory("notes/readme.txt", len(text), text)
Set compression with libarchive format options:
with libarchive.file_writer("backup.7z", "7zip", options="compression=ppmd,compression-level=9") as archive:
archive.add_files("logs")
Valid values for compression: copy, deflate, bzip2, lzma1, lzma2, ppmd, zstd (the last one needs a libarchive built with zstd).
How to open a 7z archive from bytes#
import libarchive
with open("archive.7z", "rb") as f:
buf = f.read()
with libarchive.memory_reader(buf) as archive:
for entry in archive:
print(entry.pathname)
Tips#
file_reader("archive.7z", format_name="7zip")restricts detection to 7z; the default"all"accepts any format libarchive knows.- 7z needs a seekable input:
file_reader,fd_readeron a regular file andmemory_readerwork, a pipe or socket viastream_readerdoesn’t. - libarchive writes all entries into one solid block, which is good for compression but slow for later single-file extraction.