objpath
pathlib for S3, Google Cloud Storage and Azure Blob buckets.
S3Path("reports/2026.csv", bucket="data") has the method names, signatures and
exception types of pathlib.Path, and GSPath and AZPath do the same for the
other two stores; none needs a URI. They build on
pathapi alone; with
uripath installed the same package also
provides the s3:, gs: and az: schemes. It is documented at
https://jose-pr.github.io/objpath/.
Features
| Store | Class | Extra | Directories | Server-side copy | Checksum | Append |
|---|---|---|---|---|---|---|
| Amazon S3 and S3-compatible | S3Path |
s3 |
key prefixes, mkdir() writes a marker object |
CopyObject, same client |
ETag MD5, crc32, crc32c, crc64nvme, sha1, sha256 |
no |
| Google Cloud Storage | GSPath |
gs |
key prefixes, mkdir() writes a marker object |
copy_blob, same client |
md5, crc32c |
no |
| Azure Blob Storage | AZPath |
az |
key prefixes, mkdir() writes a marker blob |
service copy, same container and client | Content-MD5 |
no |
- A
pathlibpath on a bucket:open,read_text,iterdir,glob,stat,mkdir,renameand the rest, pluswalk,copy,move,rmandPathSyncerfrompathapi. Nothing connects until the first request. - Directories without directories:
is_dir()is a prefix test, listings use a delimiter and carry the size and time of each object, and the keys a walk cannot reach (an empty segment, a name that is both an object and a prefix) are named and refused instead of copied or removed half way. - Batch deletes and server-side copies:
rm(recursive=True)deletes a thousand keys a request, and a copy within a store moves no byte through your process. - Safe writes: uploads are spooled and sent on
close(); awithblock that raised uploads nothing. A read is one version of the object, and an object replaced meanwhile is an error, not a mix. - The SDKs stay out of the way:
import objpathloads no SDK and noturipath; a store used without its extra raises anImportErrornaming it. No SDK exception crosses the API: errors are theOSErrortypespathlibraises,TimeoutErrorandConnectionError. - The
s3:,gs:andaz:schemes foruripath:URIPath("s3://data/reports")is anS3URIPaththat delegates to anS3Path.
Installation
pip install "objpath[s3]"
Requires pathapi, which pip installs with it, and Python 3.9 or newer.
| Install | Adds |
|---|---|
pip install objpath |
the paths and the backend contracts; no SDK |
pip install "objpath[s3]" |
boto3>=1.36.0,<2 for S3Path |
pip install "objpath[gs]" |
google-cloud-storage>=3.0,<4 for GSPath |
pip install "objpath[az]" |
azure-storage-blob and azure-identity for AZPath |
pip install "objpath[uri]" |
uripath and with it the s3:, gs: and az: schemes |
pip install "objpath[all]" |
all of the above |
Quick start
A path in a bucket: the bucket is a keyword, the segments are the key, and the credentials are the ones boto3 finds:
from objpath import S3Path
report = S3Path("reports/2026.csv", bucket="data")
print(report.read_text())
for child in report.parent.iterdir():
print(child.name, child.stat().st_size)
Another store, or another endpoint: client options build the path's own backend, and a backend shared between paths is one client and one connection pool:
from objpath import AZPath, GSPath, S3Backend, S3Path
blob = AZPath("logs/app.log", container="ops", account="mycompany")
gcs = GSPath("models/latest", bucket="ml", project="my-project")
with S3Backend(endpoint_url="https://minio.example.org", region_name="us-east-1") as backend:
root = S3Path(bucket="backups", backend=backend)
(root / "2026").mkdir(exist_ok=True)
(root / "2026" / "n.txt").write_text("1")
root.copy(S3Path("mirror", bucket="backups", backend=backend), recursive=True)
Mirror a tree with PathSyncer, onto a local directory:
from pathapi import LocalPath
from pathapi.sync import PathSyncer
from objpath import S3Path
PathSyncer(remove_missing=True).sync(S3Path("logs", bucket="data"), LocalPath("mirror"))
The schemes, with uripath installed:
from uripath import URIPath
print(URIPath("s3://data/reports/2026.csv").read_text())
API overview
| Module | Purpose |
|---|---|
objpath |
S3Path, GSPath, AZPath, the backends S3Backend, GSBackend, AZBackend and the contracts BaseS3Backend, BaseGSBackend, BaseAZBackend, the exceptions and __version__ |
objpath.exceptions |
ObjPathError, ObjectChangedError and UnreachableKeysError |
objpath.uri |
S3URIPath, GSURIPath and AZURIPath, the schemes of uripath (needs the uri extra) |
The full reference is on the documentation site;
an agent-oriented API summary ships inside the package as objpath/AGENTS.md.
Development
pip install -e ".[dev,all]"
python -m pytest -q -rs
The S3 tests run against moto in the test process, and the Google Cloud Storage and
Azure tests against fake servers on the loopback interface; none reaches another host. A
test for an extra that is not installed is skipped, so check the skip list (-rs).
Contributor notes (virtual environments, formatting, CI, releases) are in the repository's
AGENTS.md. To preview the
documentation: pip install -e ".[all,docs]" then mkdocs serve.
Changes are recorded in the changelog.
Releasing
This project follows Semantic Versioning and keeps a
CHANGELOG.md. Pushing a tag matching v* triggers the release
workflow: test gate → build (checking the tag names the version built) → a strict
docs build as a gate → GitHub release → publish. The release workflow never deploys
the docs site itself: for a final release its last job dispatches the docs workflow
at the tag, which owns every Pages deploy.
License
MIT — see LICENSE.
Metadata
Release files for objpath 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| objpath-0.1.0.tar.gz | 176.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| objpath-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 243.9 kB
Release files / objpath-0.1.0.tar.gz
| Download URL | objpath-0.1.0.tar.gz |
|---|---|
| Size | 176.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
d1fd7ca6e9a92d89edf726bac6b94f83795db807e529dfa0e6c34311d06bd0cd
|
|
BLAKE2b-256 checksum How to use checksums |
7e979199fb915690b91f99897cb1ecdd899c684b2ce0cd07091c2b940e66f813
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 15, 2026.
Transparency logRelease files / objpath-0.1.0-py3-none-any.whl
| Download URL | objpath-0.1.0-py3-none-any.whl |
|---|---|
| Size | 67.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
ced336a7e12f1a09d7316c98754c7fd4c1d3080a3fb6bacf7d9d74d41bdd4b07
|
|
BLAKE2b-256 checksum How to use checksums |
f487ad54c23c82793115cd6f98c74c4cf607b3626f607c2138c0370dbe1b9853
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 15, 2026.
Transparency log