zipp

raw JSON →
3.23.0 verified Tue May 12 auth: no python install: stale quickstart: verified

zipp is the official backport of the pathlib-compatible zipfile.Path object from the Python standard library. It provides a Traversable/pathlib-like interface for navigating and reading ZIP archives (zipp.Path), with new features introduced here first and later merged into CPython. Current version is 3.23.0, released June 2025, with a very active cadence of roughly one release per month maintained by Jason R. Coombs (jaraco).

pip install zipp
error ModuleNotFoundError: No module named 'zipp'
cause The 'zipp' package has not been installed in the current Python environment.
fix
pip install zipp
error ImportError: cannot import name 'path' from 'zipp'
cause The 'Path' class in the 'zipp' package is named with an uppercase 'P'.
fix
from zipp import Path
error zipfile.BadZipFile: File is not a zip file
cause The 'zipp.Path' object was initialized with a path to a file that is not a valid ZIP archive. 'zipp.Path' is designed to navigate *within* a ZIP file, not to represent regular filesystem paths.
fix
Ensure 'zipp.Path' is initialized with a path to a valid ZIP archive file (e.g., 'my_archive.zip') and then navigate inside it. For regular filesystem paths, use 'pathlib.Path' instead.
error IsADirectoryError: 'directory_name'
cause A method like 'read_text()' or 'read_bytes()' was called on a 'zipp.Path' object that points to a directory within the ZIP archive, not an actual file.
fix
Navigate to a specific file within the directory (e.g., 'path / 'filename.txt'') before calling read methods.
breaking zipp.Path.open() changed to text mode by default and dropped positional pwd and force_zip64 parameters.
fix Pass mode='rb' explicitly for binary reads. Remove any positional pwd argument; keyword args still pass through to io.TextIOWrapper.
breaking Path.joinpath() dropped the 'add' keyword argument; it now only accepts arbitrary positional path segments.
fix Replace joinpath(add='sub/path') with joinpath('sub/path') or the / operator (root / 'sub' / 'path').
breaking Requires Python >=3.9 as of recent releases. Older zipp releases supported Python 3.6/3.7/3.8 but those branches are no longer maintained.
fix Pin to zipp<3.20 if you must support Python 3.8. For Python 3.7, pin to zipp<3.5.
gotcha Directory entries from iterdir() always end with a trailing slash in their string representation. Comparing path names with string equality will fail unless you strip the slash.
fix Use path.name (strips the slash) rather than str(path) when comparing directory names.
gotcha Passing a ZipFile object to zipp.Path mutates the ZipFile in place (converts it to a CompleteDirs subclass). The caller's reference is affected. This is by design to prevent double-close bugs.
fix Do not rely on the original ZipFile object type after wrapping it in zipp.Path; use the zipp.Path interface exclusively for all navigation.
gotcha Path.glob('**') previously did not match directories; since 3.20 it does. Code that relied on glob returning only files may now receive unexpected directory entries.
fix Filter results with item.is_file() when you want only files: [p for p in root.glob('**/*.txt') if p.is_file()].
gotcha zipp is commonly pulled in as a transitive dependency (e.g. by importlib-metadata, pip, pytest). Pinning it too tightly in lock files can block upgrades of those tools.
fix Declare a loose lower bound (zipp>=3.9) rather than an exact pin unless you have a specific compatibility reason.
python os / libc status wheel install import disk
3.10 alpine (musl) - - - -
3.10 slim (glibc) - - - -
3.11 alpine (musl) - - - -
3.11 slim (glibc) - - - -
3.12 alpine (musl) - - - -
3.12 slim (glibc) - - - -
3.13 alpine (musl) - - - -
3.13 slim (glibc) - - - -
3.9 alpine (musl) - - - -
3.9 slim (glibc) - - - -

Create an in-memory ZIP, wrap it with zipp.Path, iterate entries, read file content, and glob for files — all using a pathlib-style API.

import io
import zipfile
import zipp

# Build an in-memory zip
data = io.BytesIO()
with zipfile.ZipFile(data, 'w') as zf:
    zf.writestr('hello.txt', 'Hello, zipp!')
    zf.writestr('sub/world.txt', 'World!')
    zf.filename = 'example.zip'

# Navigate with zipp.Path (pathlib-style)
root = zipp.Path(data)
for item in root.iterdir():
    print(item.name, item.is_file())

# Read a nested file
file_path = root / 'hello.txt'
print(file_path.read_text())  # 'Hello, zipp!'

# Glob support
for txt in root.glob('**/*.txt'):
    print(txt)