-
Notifications
You must be signed in to change notification settings - Fork 116
Make S3Object follow the mapping contract #991
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -33,6 +33,19 @@ | |
| "Metadata": "metadata", | ||
| "LastModified": "last_modified", | ||
| } | ||
| # Fields read as None through attribute access when the object does not have them. | ||
| _S3_OBJECT_FIELDS = frozenset( | ||
| [ | ||
| *_API_FIELD_TO_S3_OBJECT_PROPERTY.values(), | ||
| "name", | ||
| "type", | ||
| "bucket", | ||
| "key", | ||
| "size", | ||
| "version_id", | ||
| "is_latest", | ||
| ] | ||
| ) | ||
|
|
||
|
|
||
| class S3ObjectType: | ||
|
|
@@ -95,7 +108,10 @@ class S3Object(MutableMapping[str, Any]): | |
|
|
||
| The object supports both dictionary-style access and property-style | ||
| access to metadata fields like content type, storage class, encryption | ||
| settings, and object lock configurations. | ||
| settings, and object lock configurations. Dictionary-style access | ||
| behaves like a dictionary, so a missing key raises KeyError. Property-style | ||
| access returns None for a known field that the object does not have, | ||
| and raises AttributeError for any other missing name. | ||
|
|
||
| Example: | ||
| >>> s3_obj = S3Object({"ContentType": "text/csv", "ContentLength": 1024}) | ||
|
|
@@ -163,10 +179,26 @@ def get(self, key: str, default: Any = None) -> Any: | |
|
|
||
| @override | ||
| def __getitem__(self, item: str) -> Any: | ||
| return self.__dict__.get(item) | ||
| return self.__dict__[item] | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Self-review round one (implementation behavior): CLEAN Base Covered:
Note:
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Rebase onto master (no code change)
|
||
|
|
||
| def __getattr__(self, item: str): | ||
| return self.get(item) | ||
| def __getattr__(self, item: str) -> Any: | ||
| """Return None for a known field that the object does not have. | ||
|
|
||
| Called only when normal attribute lookup fails, so fields that the | ||
| object has are returned without reaching this method. | ||
|
|
||
| Args: | ||
| item: The attribute name. | ||
|
|
||
| Returns: | ||
| None, if ``item`` is a known S3 object field. | ||
|
|
||
| Raises: | ||
| AttributeError: If ``item`` is not a known S3 object field. | ||
| """ | ||
| if item in _S3_OBJECT_FIELDS: | ||
| return None | ||
| raise AttributeError(f"{type(self).__name__!r} object has no attribute {item!r}") | ||
|
|
||
| @override | ||
| def __setitem__(self, key: str, value: Any) -> None: | ||
|
|
@@ -192,6 +224,14 @@ def __len__(self) -> int: | |
| def __str__(self): | ||
| return str(self.__dict__) | ||
|
|
||
| def copy(self) -> S3Object: | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Independent review (relayed): CLEAN
This is a static review: the reviewer executed nothing. Reviewer output (verbatim): Covered:
CLEAN No actionable defects introduced by this diff found. No caller requires an omitted optional attribute field. The new tests exercise the previous failures and check that renaming copied entries preserves cached names. Static review only; no tests, builds, or network access. Checkout remains clean at |
||
| """Return a shallow copy of the object. | ||
|
|
||
| Returns: | ||
| A new S3Object with the same fields. | ||
| """ | ||
| return copy.copy(self) | ||
|
|
||
| def to_dict(self) -> dict[str, Any]: | ||
| """Convert S3Object to dictionary representation. | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Self-review round two (claims and operational behavior): FINDINGS (PR description only, corrected)
Base
aa0fc9146f4e683d6cdf96b894f963f9dd8f7abe, head2b45c152535c0eb36fef3802adda9e341bdafa89. This is a full claim pass over the PR body, the commit message, and the changed docstrings and comments.Claims checked:
_S3_OBJECT_FIELDS(s3_object.py:37-48). These are the only kwargs thats3.pypasses toS3Object(type,bucket,key,version_id,is_latest), plus thenameandsizethat__init__sets.hasattr(o, "__setstate__")is nowFalse(asserted intest_attribute).TypeError: 'NoneType' object is not callable, which matches the__setstate__call incopy._reconstructand the unpickler.implementations/dirfs.py:105, 307, 313, 322, 334andgeneric.py:202, 216, 231.name,typeandkeyonly.s3_async.pyconstructs noS3Object;_info/_lsdelegate to_sync_fs.f.is_latestonls(versions=True)entries is "as documented indocs/filesystem.md". Theversion.is_latestloop atdocs/filesystem.md:155-157iteratesobject_version_info()(S3ObjectVersion), notls()entries. The reader that actually exists istest_ls_versions(tests/pyathena/filesystem/test_s3.py:810), and the PR body now cites that test.AioS3FileSystem/GenericFileSystemunder fsspec wrappers; copy/pickle on 3.11 and 3.14) from automated tests, and states that PR CI runs the newest Python only.Existing caller: code that relied on
entry["missing"] is None, on"x" in entrybeing alwaysTrue, or onentry.unknown is Nonenow getsKeyError,FalseorAttributeError. The PR body lists these as release-note behavior changes.getattr(entry, name, default)andentry.get(name)keep working.Documentation reader:
docs/api/filesystem.rst:49autodocsS3Object, so the updated class docstring andcopy()are rendered there. No prose indocs/describes the oldNonebehavior.AWS operator: no request changes.
test_dir_filesystemis offline (2 mocked calls).