Skip to content

feat: s3 module — stdlib-only S3-compatible client (Phases 1 & 2)#114

Merged
Oaklight merged 6 commits into
masterfrom
feat/s3-phase1
Jul 4, 2026
Merged

feat: s3 module — stdlib-only S3-compatible client (Phases 1 & 2)#114
Oaklight merged 6 commits into
masterfrom
feat/s3-phase1

Conversation

@elena-oaklight

@elena-oaklight elena-oaklight Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Summary

Closes #110, closes #111

Full-featured stdlib-only S3-compatible storage client with AWS SigV4 signing. Zero dependencies.

Phase 1 — Core operations

  • bucket_exists, make_bucket, get_object, put_object
  • AWS Signature V4 implementation
  • S3Response wrapper (minio-compatible interface)
  • Path-style and virtual-host URL support

Phase 2 — Object/bucket management

  • stat_object — HEAD object metadata → ObjectInfo dataclass
  • remove_object — DELETE object
  • copy_object — server-side copy via x-amz-copy-source
  • list_objects — ListObjectsV2 with pagination, prefix/delimiter support
  • list_buckets — list all accessible buckets → BucketInfo dataclass
  • remove_bucket — delete empty bucket

Tests & benchmarks

  • 58 correctness tests (unit, mocked, local mock server)
  • 40 three-way benchmarks (zerodep vs boto3 vs minio) on MinIO Docker
  • 4 cross-validation tests (interoperability between all 3 clients)

Compatibility

Tested against MinIO. Compatible with AWS S3, Cloudflare R2, Oracle OCI, and any S3-compatible backend.

Test plan

  • make test-s3 — 58 passed, 3 skipped (integration)
  • make benchmark-s3 — 40 passed
  • pre-commit run --all-files — lint clean
  • Manifest regenerated

elena-oaklight Bot and others added 2 commits July 3, 2026 05:14
Implements AWS Signature Version 4 and a minimal S3 REST client with
four operations: bucket_exists, make_bucket, get_object, put_object.

- Zero external dependencies (hashlib, hmac, http.client, urllib, xml.etree)
- Compatible with AWS S3, Cloudflare R2, Oracle OCI, MinIO
- Path-style and virtual-host-style URL support
- S3NoSuchBucket / S3NoSuchKey typed exceptions
- minio-compatible S3Response (read / close / release_conn / context manager)
- 46 unit tests: SigV4 signing, XML error parsing, URL construction,
  mocked HTTP operations, local mock server round-trips
- Integration test skeleton (skipped without S3_TEST_* env vars)

Closes #110
Part of #109
Remove unused imports (io, hmac, xml.etree.ElementTree), shorten
long comments, replace lambda with def for log_message, and apply
ruff format. Also add s3/ to ty exclude list (xml.etree.ElementTree
unresolved-import, same as xml/ module).
@Oaklight

Oaklight commented Jul 3, 2026

Copy link
Copy Markdown
Owner

Pushed a lint fix (351a3ae):

  • Removed unused imports (io in s3.py, hmac and xml.etree.ElementTree in test file)
  • Shortened long comments that exceeded the 88-char line limit
  • Replaced lambda assignment with def for log_message (E731)
  • Applied ruff format to both files
  • Added s3/ to ty exclude list in pyproject.toml (xml.etree.ElementTree triggers the same unresolved-import false positive as the xml/ module)

All 46 tests still pass.


One thing — make sure to install the pre-commit hooks locally (pre-commit install) so these get caught before pushing. That way CI lint won't be a surprise.

Oaklight added 3 commits July 3, 2026 17:04
The AWS SigV4 spec requires canonical header names to be lowercased,
but _sign_request() was using original-case keys (e.g. Content-Length
instead of content-length). This caused SignatureDoesNotMatch errors
against real S3 backends (MinIO, AWS) for any request with mixed-case
headers (PUT, POST). HEAD requests worked because they had no
additional headers beyond the already-lowercase ones.
Three-way benchmark against a real MinIO container (auto-started via
conftest.py). Covers put_object, get_object, bucket_exists, and
make_bucket at 1KB/100KB/1MB payload sizes. Includes cross-validation
tests to verify all three clients produce identical results.

Also adds s3 to testpaths, ty extra-paths, and bench-s3 extra in
pyproject.toml, plus benchmark-s3/test-s3 Makefile targets.
Add 6 new S3Client methods: stat_object, remove_object, copy_object,
list_objects, list_buckets, remove_bucket. Add ObjectInfo and BucketInfo
dataclasses. Includes mocked + local mock server correctness tests
(58 passed) and three-way benchmarks (40 passed).

Also addresses elena's review feedback: add docstring note on
bucket_exists 301 behavior, TODO for streaming PUT, comment on
canonical header case normalization, atexit vs finally explanation
in conftest, and cleaner make_bucket header construction.

Closes #111
@Oaklight Oaklight changed the title feat: s3 module Phase 1 — stdlib-only S3-compatible client (SigV4 + 4 core ops) feat: s3 module — stdlib-only S3-compatible client (Phases 1 & 2) Jul 3, 2026
Replace the single 10-object list benchmark with three tiers:
small (10), medium (100), large (1500). The large tier forces
pagination (2 pages at 1000/page). Objects use deterministic
varying sizes (64B–4KB, seeded RNG) so no files need to be stored.

Results show zerodep scales well: ~6x faster than boto3 and ~4x
faster than minio at 1500 objects, where XML parsing and pagination
overhead dominate.
@Oaklight

Oaklight commented Jul 4, 2026

Copy link
Copy Markdown
Owner

CI green, all checks pass. Merging.

Phase 3 (async client via httpclient module + multipart upload) will be a separate PR — it involves swapping the HTTP backend from http.client to the httpclient zerodep module and adding AsyncS3Client.

@Oaklight Oaklight merged commit e4be311 into master Jul 4, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant