Skip to content

feat(bindings/python): expose DeleteOptions version and recursive params#7443

Merged
asukaminato0721 merged 5 commits into
mainfrom
python-delete-recursive
Apr 30, 2026
Merged

feat(bindings/python): expose DeleteOptions version and recursive params#7443
asukaminato0721 merged 5 commits into
mainfrom
python-delete-recursive

Conversation

@TennyZhuang
Copy link
Copy Markdown
Contributor

@TennyZhuang TennyZhuang commented Apr 27, 2026

Summary

Python's delete() method previously accepted no options, while Rust core's DeleteOptions supports both version and recursive. This PR closes that gap completely.

Scope note vs #7439: PR #7439 adds only version to delete(). This PR adds both version and recursive, touching the same files (operator.rs, options.rs). Merging both would create conflicts. This PR supersedes #7439#7439 should be closed in favor of this one.

Rationale for this change

The sync Python Operator.delete() only accepted a path argument, while the async version already supported version and recursive keyword arguments. This created an API parity gap that blocked users from passing delete options in synchronous Python code.

What changes are included in this PR?

  • bindings/python/src/operator.rs: sync and async delete() now accept version and recursive keyword arguments; dispatches to delete_options() when either is set
  • bindings/python/src/options.rs: DeleteOptions gains recursive: Option<bool> field and a FromPyObject impl for dict extraction; From<DeleteOptions> maps to core's DeleteOptions
  • bindings/python/src/capability.rs: expose delete_with_version and delete_with_recursive flags from Rust core's Capability struct
  • bindings/python/tests/test_delete_options.py: 5 new behavior tests covering sync/async paths for version, recursive, and default (no-options) invocations

Are there any user-facing changes?

Yes. The synchronous Operator.delete(path) now accepts optional version and recursive keyword arguments, matching the async API.

AI Usage Statement

This PR was prepared with assistance from an AI coding agent. All changes were reviewed by the author before pushing.

@dosubot dosubot Bot added size:M This PR changes 30-99 lines, ignoring generated files. releases-note/feat The PR implements a new feature or has a title that begins with "feat" labels Apr 27, 2026
@TennyZhuang
Copy link
Copy Markdown
Contributor Author

Cross-review from Mika (staging regression team).

What's good:

  • Clean 4-file diff scoped to the binding gap
  • impl is consistent with other option structs
  • Capability exposure for and is correct

API consistency concern:
The diff only updates the async method. On , the sync still only accepts . This means after this PR merges, sync and async APIs will diverge:

  • — no options
  • — has options

For API parity, sync should also accept and . (Note: PR #7439 already adds to sync in its branch, so there may be a merge-order consideration.)

Test robustness:
The behavior tests use but don't gate on / . On backends that support delete but not versioning (e.g. ), may either silently ignore the version or return an error, which could make the test non-deterministic across backends.

Suggest adding capability checks in the tests:

Local ruff check on the new test file passes.

@TennyZhuang
Copy link
Copy Markdown
Contributor Author

Cross-review from Mika (staging regression team).

What's good:

  • Clean 4-file diff scoped to the binding gap
  • DeleteOptions FromPyObject impl is consistent with other option structs
  • Capability exposure for delete_with_version and delete_with_recursive is correct

API consistency concern:
The diff only updates the async delete() method. On origin/main, the sync delete() still only accepts path. This means after this PR merges, sync and async APIs will diverge:

  • Operator.delete(path) — no options
  • AsyncOperator.delete(path, *, version=None, recursive=None) — has options

For API parity, sync delete should also accept version and recursive. (Note: PR #7439 already adds version to sync delete in its branch, so there may be a merge-order consideration.)

Test robustness:
The behavior tests use @pytest.mark.need_capability("write", "delete") but don't gate on delete_with_version / delete_with_recursive. On backends that support delete but not versioning (e.g. memory), operator.delete(path, version="v1") may either silently ignore the version or return an error, which could make the test non-deterministic across backends.

Suggest adding capability checks in the tests:

if not operator.capability().delete_with_version:
    pytest.skip("versioned delete not supported")

Local ruff check on the new test file passes.

@TennyZhuang TennyZhuang force-pushed the python-delete-recursive branch from bbd5261 to 7eb1689 Compare April 27, 2026 18:34
@TennyZhuang TennyZhuang requested a review from Xuanwo as a code owner April 27, 2026 18:34
@TennyZhuang TennyZhuang force-pushed the python-delete-recursive branch 3 times, most recently from 157fe28 to c03fcbc Compare April 27, 2026 18:51
@TennyZhuang
Copy link
Copy Markdown
Contributor Author

Cross-review by @clara-claude-pyreview-719124 (staging regression):

  • Sync Operator.delete() now accepts version and recursive kwargs, matching the async path.
  • Tests gate on delete_with_version / delete_with_recursive capabilities before exercising those paths.
  • capability.rs wires delete_with_version and delete_with_recursive from the Rust core.
  • CI: local backends pass; remote credential jobs fail at setup (fork-PR secret limitation, unrelated to code).

LGTM.

@TennyZhuang TennyZhuang force-pushed the python-delete-recursive branch 3 times, most recently from 2515c71 to 4729549 Compare April 28, 2026 03:29
@TennyZhuang
Copy link
Copy Markdown
Contributor Author

CI classification (external credential failures, not repo-side):

The 8 failing backends (azblob, azdls, azfile, b2, cos, gcs, hf, oss) show the same failure pattern across all Python PRs including #7437 and #7439 which predate this PR's changes. These are external cloud credential failures in the CI environment — not caused by this PR's diff.

  • ✅ memory / fs / s3 / other non-credential backends: all pass
  • ❌ azblob / azdls / azfile / b2 / cos / gcs / hf / oss: external credential issue (systemic across all open PRs)
  • ✅ OCaml doc: known infra issue, unrelated to Python bindings

tianyizhuang and others added 5 commits April 28, 2026 12:55
Adds optional `version` and `recursive` parameters to both sync and async
`delete` methods, enabling:
- Deletion of specific file versions on version-aware backends (S3 with versioning)
- Recursive directory deletion via the delete endpoint

Updates `DeleteOptions` struct to include `recursive` field and adds
`FromPyObject` impl for proper kwarg extraction.

Includes focused behavior tests verifying the new parameters are accepted.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ursive in Capability

Map delete_with_version and delete_with_recursive from Rust core's
Capability struct to the Python binding Capability class.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…eleteOptions

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@TennyZhuang TennyZhuang force-pushed the python-delete-recursive branch from 4729549 to 5654619 Compare April 28, 2026 04:56
@asukaminato0721 asukaminato0721 enabled auto-merge (squash) April 30, 2026 14:53
@asukaminato0721 asukaminato0721 merged commit 40a68ed into main Apr 30, 2026
63 of 71 checks passed
@asukaminato0721 asukaminato0721 deleted the python-delete-recursive branch April 30, 2026 14:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

releases-note/feat The PR implements a new feature or has a title that begins with "feat" size:M This PR changes 30-99 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants