Luigi S3 module now uses boto3 by default#29
Open
andresvelasquez2-affirm wants to merge 2 commits intorafay/BATCH-3679-luigi-py-312-upgradefrom
Open
Conversation
Flip `S3Client = S3ClientBoto3` (was boto1) and revert the `client=` parameter on `S3PathTask` / `S3EmrTask` / `S3FlagTask` (now redundant). Drop the boto1/boto3 dual-mode shim from `s3_test.py`; add boto3 ports of the SSE / credential / STS tests, boto1-gated test classes, and a `MOTO_LT_2` skip for boto3 round-trips moto<2 corrupts via raw chunked Transfer-Encoding. Document the new default and compatibility matrix. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR makes boto3 the default backend for Luigi's S3 module, finishing the boto3 migration that PR #28 started. The legacy
S3ClientBoto1class stays defined and importable for callers that still need boto1 explicitly, but unparameterizedS3Target(...),S3PathTask(...), etc. now resolve to boto3.This PR is built on top of
rafay/BATCH-3679-luigi-py-312-upgrade(PR #28).JIRA Ticket
BATCH-3679
Changes
S3 Client Default Flipped to boto3
luigi/contrib/s3.py:S3Client = S3ClientBoto3(wasS3ClientBoto1); same forReadableS3File = ReadableS3FileBoto3.S3ClientBoto1andReadableS3FileBoto1remain defined and importable — opt-in only.client=constructor parameter onS3PathTask,S3EmrTask, andS3FlagTask(added in05c71137to let callers opt into boto3 while the default was still boto1) — the injection plumbing is redundant now that the default itself is boto3.Test Refactor —
test/contrib/s3_test.pytry/except ImportErrorHAS_BOTOshim that, post-flip, would have routed boto1-style calls (e.g.client.s3.create_bucket('mybucket')) into what is now actually a boto3 client whenever boto1 happened to be installed.ServerSideEncryption='AES256'instead ofencrypt_key=True.s3.meta.client._request_signer._credentials.ASIA-prefixed key + token populated) rather than canonical example values that newer moto no longer returns.test_put_encrypt_key_raises,test_put_string_encrypt_key_raises,test_put_multipart_encrypt_key_raises— verifyingS3ClientBoto3rejects the boto1encrypt_key=parameter withDeprecatedBotoClientException.TestS3TargetBoto1andTestS3ClientBoto1test classes that exerciseS3ClientBoto1/ReadableS3FileBoto1directly (basic put/get round-trip, the originalboto.s3.key.Key.BufferSizeline-buffering test, boto1 credential introspection, all fourencrypt_key=Truemultipart variants). Gated onBOTO1_AVAILABLE = HAS_BOTO_1 and HAS_BOTO_1_MOTO— bothboto<3and moto'smock_s3_deprecated/mock_sts_deprecateddecorators must be importable; skip cleanly otherwise.MOTO_LT_2skip gate —moto<2doesn't decode boto3'sTransfer-Encoding: chunkedupload bodies, so any round-trip throughput_multipart/upload_fileobj/copystores chunk-size markers (b"4f\n...real-data...\n0\n") instead of real content. Class-level@unittest.skipIf(MOTO_LT_2, ...)onTestS3Target,self.skipTest(...)guards inside_run_multipart_test/_run_copy_test/_run_multipart_copy_test, plus@unittest.skipIfontest_getandtest_get_as_string.Dependency Updates
setup.py: version bump2.7.5+affirm.1.4.9.rc8→2.7.5+affirm.1.4.9.rc9.Documentation
CLAUDE.md: New "S3 Module: boto3-Only Default + Boto1 Legacy Shim" section withuv run --no-projectinvocations for running the S3 suite, the moto/boto compatibility matrix, theMOTO_LT_2andBOTO1_AVAILABLEflag explanations, and the macOS arm64 +arch -x86_64note for Python 3.9.PLAN_Py39_P312_DUAL_COMPATIBILITY.md: Added a "May 6 2026 — Boto3-Only Default + Test Refactor" section. Annotated the original April 13 checklist step 5 as reverted (preserving the rationale verbatim as a quoted block). Updated Migration Guide patterns 3 and 4 + the Migration Impact Summary to reflect thatS3PathTask/S3EmrTask/S3FlagTaskno longer acceptclient=.Testing
The S3 test file was validated under
uv run --no-projectacross five Python + moto + boto combinations:ssl.wrap_socketremoved; boto1 vendoredsix.movesfails to import). Documented; not addressable from test code.MOTO_LT_2)moto==1.3.16declaresbotoas a hard runtime dep, transitively pulled in.Full-suite results via
./run_tests.sh:1382 passed, 39 failed, 36 skipped, 18 errors— 39 failures match the documented baseline (no regressions). 18 errors are pre-existing SQLAlchemy'Engine' object has no attribute …issues.arch -x86_64 ./run_tests.sh --py39):1355 passed, 39 failed, 63 skipped, 18 errors— same matching baseline.Verification