-
Notifications
You must be signed in to change notification settings - Fork 116
Count S3FileSystem.find() maxdepth levels like fsspec #956
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鈥檒l 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 |
|---|---|---|
|
|
@@ -717,6 +717,24 @@ def _find( | |
| withdirs: bool | None = None, | ||
| **kwargs, | ||
| ) -> list[S3Object]: | ||
| """List the objects below a path, as described in ``find``. | ||
|
|
||
| Args: | ||
| path: S3 path to search under. | ||
| maxdepth: Maximum number of levels to descend, at least 1 | ||
| (None for unlimited). | ||
| withdirs: Whether to include directories in the result. | ||
| **kwargs: Additional arguments including ``prefix`` and | ||
| ``refresh``, as described in ``find``. | ||
|
|
||
| Returns: | ||
| The objects found, and the directories if ``withdirs`` is True. | ||
|
|
||
| Raises: | ||
| ValueError: If ``maxdepth`` is less than 1 or the path is the root. | ||
| """ | ||
| if maxdepth is not None and maxdepth < 1: | ||
| raise ValueError("maxdepth must be at least 1") | ||
| path = self._strip_protocol(path) | ||
| if path in ["", "/"]: | ||
| raise ValueError("Cannot traverse all files in S3.") | ||
|
|
@@ -742,7 +760,7 @@ def _find( | |
| result.append(item) | ||
|
|
||
| # Recursively explore subdirectory if depth allows | ||
| if maxdepth > 0: | ||
| if maxdepth > 1: | ||
| sub_path = f"s3://{bucket}/{item.key}" | ||
| sub_results = self._find( | ||
| sub_path, maxdepth=maxdepth - 1, withdirs=withdirs, **kwargs | ||
|
|
@@ -786,7 +804,9 @@ def find( | |
|
|
||
| Args: | ||
| path: S3 path to search under (e.g., "s3://bucket/prefix"). | ||
| maxdepth: Maximum depth to recurse (None for unlimited). | ||
| maxdepth: Maximum number of levels to descend, at least 1 | ||
|
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 two (claims, callers, operations): FINDINGS (PR text corrected) Base Claims checked:
Corrections to the PR body:
Caller/operator effects: callers that passed Evidence limits: live S3 coverage is the targeted
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. Round two on the repair (54660a4): FINDINGS (PR text corrected). The PR body no longer claims the object-path behavior and lists #962-#966 under "Not changed". It states the 4.0.0-only target (milestone set on #933 and this PR), and "Tested at" now names 54660a4 with the 27-test live run. Request-count claim: back to master's single listing for a no-match prefixed |
||
| (None for unlimited). With 1, only the entries directly under | ||
| the path are listed. | ||
| withdirs: Whether to include directories in results (None = default behavior). | ||
| detail: If True, return dict of {path: S3Object}; if False, return list of paths. | ||
| **kwargs: Additional arguments including: | ||
|
|
@@ -799,6 +819,9 @@ def find( | |
| Dictionary mapping paths to S3Objects (if detail=True) or | ||
| list of paths (if detail=False). | ||
|
|
||
| Raises: | ||
| ValueError: If ``maxdepth`` is less than 1 or the path is the root. | ||
|
|
||
| Example: | ||
| >>> fs = S3FileSystem() | ||
| >>> fs.find("s3://bucket/data/", maxdepth=2) # Limit depth | ||
|
|
||
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 one (implementation behavior): FINDINGS (repaired)
Base
e0e85da09435257a18c8b8832b3e10ba7840b697, head8f29bebed998ae97316a24f28a15fd6f6a1a3a89; full diff (pyathena/filesystem/s3.py,tests/pyathena/filesystem/test_s3.py,tests/pyathena/filesystem/test_s3_async.py).Covered:
walk()/find():maxdepth=1lists only the current level, and each recursion passesmaxdepth - 1 >= 1, so the new< 1check never fires inside the recursion.maxdepththroughfind(): fsspecglob()(its computed depth is at least 1 whenevermaxdepth >= 1),expand_path()(used byS3FileSystem.rm()and fsspeccopy()/get()/put()),du(), and the async_glob()/_expand_path()/_du()throughAioS3FileSystem._find(). AValueErrorfrom the sync_find()propagates throughasyncio.to_thread.info()therefore cannot make the loop recurse into the same path. A subdirectory that becomes empty between listings costs one extra HEAD request and returns[]. Withprefix, it behaves the same as the unlimited branch (see the updated docstring).test_find_refresh_bypasses_cached_listingsstill reaches the subdirectory listing withmaxdepth=2.Findings:
find()results in listing order, which comes from_ls_dirs(common prefixes before contents) and is not a contract. Repaired in c1292e0: the multi-entry assertions compare sorted lists.AioS3FileSystem._rm()(pyathena/filesystem/s3_async.py:178) callsexpand_path(p, recursive=recursive)withoutmaxdepth, somaxdepthis ignored and_rm(path, recursive=True, maxdepth=1)deletes everything under the path. This PR does not change it; to be filed separately.rm()consequence applies to the syncS3FileSystem.rm()only (see finding 2). The PR body will be corrected in round two's claim audit.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.
Round one on the repair (54660a4): CLEAN.
git range-diff e0e85da0..c1292e03 e0e85da0..54660a49shows the two reviewed commits unchanged plus one commit. Themaxdepthbranch is again master's listing loop, with only the< 1check and the> 1recursion guard changed.keyis still used by the unlimited branch. Removing the tests drops only the fallback coverage;test_find_maxdepth_counts_levels_like_fsspecand the livetest_find_maxdepthstill fail against the old numbering.