Skip to content

feat: replace s3fs/aiobotocore with boto3-based fsspec backend - #81

Open
acere wants to merge 3 commits into
awslabs:mainfrom
acere:feat/boto3-s3-filesystem
Open

feat: replace s3fs/aiobotocore with boto3-based fsspec backend#81
acere wants to merge 3 commits into
awslabs:mainfrom
acere:feat/boto3-s3-filesystem

Conversation

@acere

@acere acere commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Summary

Replace the s3fs/aiobotocore dependency chain with a self-contained AsyncFileSystem implementation using boto3 directly. This eliminates the version conflicts between s3fs's pinned aiobotocore and boto3 (resolves #77).

Changes

  • llmeter/s3fs.py: New Boto3S3FileSystem class (fsspec AsyncFileSystem subclass) wrapping synchronous boto3 calls in asyncio.to_thread()
  • llmeter/__init__.py: Auto-registration with fallback — defers to s3fs/obstore if installed
  • pyproject.toml: fsspec[http,s3]fsspec[http], added fsspec entry point
  • tests/test_s3fs.py: 47 unit tests using moto
  • tests/integ/test_s3fs_integ.py: 23 integration tests against real S3
  • docs/: User guide and API reference pages

Design Decisions

  • Uses asyncio.to_thread() for non-blocking I/O (boto3 clients are thread-safe, GIL released during socket I/O)
  • Idempotent rm_file (matches S3 native delete_object semantics — no pre-check)
  • Append mode uses read-modify-write (acceptable for llmeter's small result files)
  • Shared _translate_error() helper for consistent ClientError → Python exception mapping

Testing

  • 47 unit tests pass (moto mock_aws)
  • 23 integration tests pass against real S3
  • Full test suite (911 tests) passes with no regressions
  • ruff check and ruff format clean

acere added 3 commits June 3, 2026 19:46
Replace the problematic s3fs/aiobotocore dependency chain with a
self-contained AsyncFileSystem implementation using boto3 directly.
This eliminates the well-known version conflicts between s3fs's
pinned aiobotocore and boto3 (GitHub issue awslabs#77).

Key changes:
- Add llmeter/s3fs.py: Boto3S3FileSystem (AsyncFileSystem subclass)
  wrapping synchronous boto3 calls in asyncio.to_thread()
- Add registration in llmeter/__init__.py with fallback to s3fs/obstore
- Change pyproject.toml: fsspec[http,s3] -> fsspec[http], add entry point
- Add tests/test_s3fs.py: 47 unit tests using moto mock_aws
- Add tests/integ/test_s3fs_integ.py: 23 integration tests against real S3
- Add S3 fixtures to tests/integ/conftest.py
- Add docs/user_guide/s3_storage.md covering usage, credentials,
  custom config, fallback behavior, limitations, and async support
- Add docs/reference/s3fs.md for auto-generated API docs
- Update installation guide with S3 storage section
- Add both pages to mkdocs.yml navigation
- Extract _translate_error() helper to replace repeated error code checks
- Remove unnecessary boto3 ImportError guard (it's a core dependency)
- Remove head_object pre-check in _rm_file (S3 delete is idempotent)
- Let _put_file propagate boto3 errors directly (no wrapping needed)
- Remove unused `import fsspec` (only fsspec.spec is needed)
- Raise ValueError on bare bucket delete instead of silent return
- Update tests to reflect idempotent delete semantics
@acere
acere requested a review from athewsey June 3, 2026 13:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Unpinned s3fs pulls in an ancient and slow version

1 participant