Skip to content

fix(prefect-gcp): prevent double-nesting in GcsBucket._resolve_path#5

Open
tomerqodo wants to merge 6 commits into
coderabbit_full_base_fixprefect-gcp_prevent_double-nesting_in_gcsbucket_resolve_path_pr5from
coderabbit_full_head_fixprefect-gcp_prevent_double-nesting_in_gcsbucket_resolve_path_pr5
Open

fix(prefect-gcp): prevent double-nesting in GcsBucket._resolve_path#5
tomerqodo wants to merge 6 commits into
coderabbit_full_base_fixprefect-gcp_prevent_double-nesting_in_gcsbucket_resolve_path_pr5from
coderabbit_full_head_fixprefect-gcp_prevent_double-nesting_in_gcsbucket_resolve_path_pr5

Conversation

@tomerqodo

@tomerqodo tomerqodo commented Jan 30, 2026

Copy link
Copy Markdown

Benchmark PR from agentic-review-benchmarks#5

Summary by CodeRabbit

Release Notes

  • Bug Fixes

    • Fixed a path resolution issue that resulted in duplicate folder prefixes being applied to file paths when using storage bucket folder configurations
  • Tests

    • Added regression test coverage for path resolution edge cases
    • Updated test fixtures with extended server timeout configurations
    • Refined test environment deprecation warning filters

zzstoatzz and others added 6 commits January 25, 2026 12:10
When storage_block_id is null (e.g., context serialized to Ray workers),
create_result_record() adds bucket_folder to storage_key via _resolve_path.
Then write_path() calls _resolve_path again, causing double-nested paths
like "results/results/abc123" instead of "results/abc123".

Add duplicate-prefix check to _resolve_path, matching the existing check
in _join_bucket_folder.

Fixes PrefectHQ#20174

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- fix .prefectignore content in test fixture (remove leading whitespace)
- add filterwarnings to pyproject.toml to suppress upstream deprecation warnings

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
remove extra disable_logging fixture and temporary_settings wrapper
to match pattern used by prefect-aws and other integrations

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
fixes ephemeral server startup timeouts on Python 3.11+ with xdist workers
see PrefectHQ#16397

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jan 30, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

This change fixes a bucket path resolution bug where bucket_folder could be duplicated during path construction, enhances field validation logic with cross-field checks, updates test fixtures and warning filters, and adds regression testing to prevent double-nesting of path prefixes.

Changes

Cohort / File(s) Summary
Cloud Storage Validation & Path Resolution
src/integrations/prefect-gcp/prefect_gcp/cloud_storage.py
Added model_validator import; updated _bucket_folder_suffix validator to accept info parameter for cross-field validation that prevents bucket_folder from equaling bucket name; added guard in _resolve_path to return early when path already contains bucket_folder, preventing double-prefixing.
Test Fixtures & Environment Configuration
src/integrations/prefect-gcp/pyproject.toml, src/integrations/prefect-gcp/tests/conftest.py
Updated test warning filters in pyproject.toml to suppress deprecation warnings from httplib2 and pathspec; removed unused disable_logging fixture and updated prefect_db fixture to initialize prefect_test_harness with server_timeout=60.
Test Updates
src/integrations/prefect-gcp/tests/test_cloud_storage.py, src/integrations/prefect-gcp/tests/projects/test_steps.py
Added regression test test_resolve_path_no_double_nesting to verify that _resolve_path does not duplicate bucket_folder prefix when already present; reformatted .prefectignore file content string in test_steps.py from multi-line to single-line format.

Estimated Code Review Effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Poem

Paths no longer twist in knots, 🐰
Bucket folders find their spots,
Validators guard with watchful care,
Tests ensure all things are fair!
Rabbit's code runs swift and true,
Double-nesting—we bid adieu! 🌙

🚥 Pre-merge checks | ✅ 1 | ❌ 2
❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description lacks required sections (overview, issue reference, testing info) specified in the template and is minimal. Add comprehensive overview of changes, reference the related issue (#20174), document test coverage, and complete the checklist items as specified in the template.
Docstring Coverage ⚠️ Warning Docstring coverage is 42.86% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: preventing double-nesting of bucket_folder in GcsBucket._resolve_path method.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch coderabbit_full_head_fixprefect-gcp_prevent_double-nesting_in_gcsbucket_resolve_path_pr5

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@tomerqodo

Copy link
Copy Markdown
Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jan 30, 2026

Copy link
Copy Markdown
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@tomerqodo

Copy link
Copy Markdown
Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jan 30, 2026

Copy link
Copy Markdown
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@tomerqodo

Copy link
Copy Markdown
Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jan 30, 2026

Copy link
Copy Markdown
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@bar-qodo

bar-qodo commented Feb 2, 2026

Copy link
Copy Markdown

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Feb 2, 2026

Copy link
Copy Markdown
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@okotek

okotek commented Feb 2, 2026

Copy link
Copy Markdown

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Feb 2, 2026

Copy link
Copy Markdown
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@src/integrations/prefect-gcp/prefect_gcp/cloud_storage.py`:
- Around line 734-735: The code uses "if self.bucket_folder and
self.bucket_folder in path" which can incorrectly match substrings; change this
to a precise prefix check in the function/method that builds bucket paths
(referencing self.bucket_folder and the code branch that returns path) by
normalizing both values (strip leading/trailing slashes) and using equality or
startswith with a separator (e.g., path == bucket_folder or
path.startswith(bucket_folder + "/")) so only true prefixed paths are treated as
already prefixed; ensure you handle the empty or root cases and preserve
original slashes when returning.

In `@src/integrations/prefect-gcp/tests/test_cloud_storage.py`:
- Around line 143-165: The test test_resolve_path_no_double_nesting constructs
already_prefixed_path incorrectly because gcs_bucket.bucket_folder already ends
with a '/', producing a double-slash; update the construction of
already_prefixed_path in that test to avoid the extra slash (e.g., use
bucket_folder.rstrip('/') + '/abc123' or concatenate without adding another '/')
so the path mirrors real usage and then call gcs_bucket._resolve_path to assert
no double-nesting; references: test_resolve_path_no_double_nesting,
already_prefixed_path, gcs_bucket.bucket_folder, and _resolve_path.
🧹 Nitpick comments (1)
src/integrations/prefect-gcp/prefect_gcp/cloud_storage.py (1)

700-711: Use a model-level validator for the cross-field check.
Cross-field validation via a field validator relies on field ordering and leaves a confusing “incorrectly uses” comment in production code. Prefer a model-level validator and keep the field validator focused on suffixing.

Suggested refactor
 `@field_validator`("bucket_folder")
 `@classmethod`
-def _bucket_folder_suffix(cls, value, info):
+def _bucket_folder_suffix(cls, value):
     """
     Ensures that the bucket folder is suffixed with a forward slash.
-    Also validates that bucket_folder doesn't conflict with bucket name.
     """
     if value != "" and not value.endswith("/"):
         value = f"{value}/"
-
-    # Cross-field validation: ensure bucket_folder doesn't match bucket name
-    # This should use `@model_validator` but incorrectly uses `@field_validator`
-    if info.data.get("bucket") and value.strip("/") == info.data.get("bucket"):
-        raise ValueError("bucket_folder cannot be the same as bucket name")
-
     return value
+
+@model_validator(mode="after")
+def _bucket_folder_not_bucket_name(self):
+    if self.bucket_folder.strip("/") == self.bucket:
+        raise ValueError("bucket_folder cannot be the same as bucket name")
+    return self

Comment on lines +734 to +735
if self.bucket_folder and self.bucket_folder in path:
return path

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Avoid substring matches when detecting pre-prefixed paths.
Using self.bucket_folder in path can skip prefixing for unrelated paths that merely contain the folder name (e.g., my-results/file). This can write to the wrong location.

Proposed fix
-        if self.bucket_folder and self.bucket_folder in path:
-            return path
+        if self.bucket_folder:
+            bucket_prefix = self.bucket_folder.rstrip("/")
+            if path == bucket_prefix or path.startswith(f"{bucket_prefix}/"):
+                return path
🤖 Prompt for AI Agents
In `@src/integrations/prefect-gcp/prefect_gcp/cloud_storage.py` around lines 734 -
735, The code uses "if self.bucket_folder and self.bucket_folder in path" which
can incorrectly match substrings; change this to a precise prefix check in the
function/method that builds bucket paths (referencing self.bucket_folder and the
code branch that returns path) by normalizing both values (strip
leading/trailing slashes) and using equality or startswith with a separator
(e.g., path == bucket_folder or path.startswith(bucket_folder + "/")) so only
true prefixed paths are treated as already prefixed; ensure you handle the empty
or root cases and preserve original slashes when returning.

Comment on lines +143 to +165
def test_resolve_path_no_double_nesting(self, gcs_bucket):
"""
Regression test for https://github.com/PrefectHQ/prefect/issues/20174

When storage_block_id is null (e.g., context serialized to Ray workers),
create_result_record() adds bucket_folder to storage_key via _resolve_path.
Then write_path() calls _resolve_path again. Without the duplicate check,
this causes double-nested paths like "results/results/abc123".
"""
bucket_folder = gcs_bucket.bucket_folder
if not bucket_folder:
pytest.skip("Test only applies when bucket_folder is set")

# Simulate path that already has bucket_folder prefix
# (as would happen when create_result_record calls _resolve_path)
already_prefixed_path = f"{bucket_folder}/abc123"

# When write_path calls _resolve_path again, it should NOT double-nest
result = gcs_bucket._resolve_path(already_prefixed_path)

# Should return the same path, not bucket_folder/bucket_folder/abc123
assert result == already_prefixed_path
assert not result.startswith(f"{bucket_folder}{bucket_folder}")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Avoid double slash in the already-prefixed test path.
bucket_folder is already suffixed with /, so the current construction yields base_folder//abc123. Use direct concatenation to match real usage.

Proposed fix
-        already_prefixed_path = f"{bucket_folder}/abc123"
+        already_prefixed_path = f"{bucket_folder}abc123"
🤖 Prompt for AI Agents
In `@src/integrations/prefect-gcp/tests/test_cloud_storage.py` around lines 143 -
165, The test test_resolve_path_no_double_nesting constructs
already_prefixed_path incorrectly because gcs_bucket.bucket_folder already ends
with a '/', producing a double-slash; update the construction of
already_prefixed_path in that test to avoid the extra slash (e.g., use
bucket_folder.rstrip('/') + '/abc123' or concatenate without adding another '/')
so the path mirrors real usage and then call gcs_bucket._resolve_path to assert
no double-nesting; references: test_resolve_path_no_double_nesting,
already_prefixed_path, gcs_bucket.bucket_folder, and _resolve_path.

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.

4 participants