Skip to content

feat: add dry_run validation mode to batch_edit() - #28

Merged
pablospe merged 5 commits into
mainfrom
task/f8d48414-Dry-run-validation-for-batch_e
Jul 2, 2026
Merged

pablospe merged 5 commits into
mainfrom
task/f8d48414-Dry-run-validation-for-batch_e

Conversation

@pablospe

@pablospe pablospe commented Jul 1, 2026 •

Copy link
Copy Markdown
Owner

Summary

Adds a non-mutating validation mode to Document.batch_edit() (ISSUES.md #10):

results = doc.batch_edit(ops, dry_run=True)
if all(r.valid for r in results):
    doc.batch_edit(ops)

dry_run=True validates every operation without applying any edits and returns one EditValidationResult(index, paragraph, valid, error) per operation, in input order. The document is left unchanged. Validation reuses the same _resolve_paragraph / _find_in_paragraph helpers as the real apply path, so it cannot drift from execution semantics.

What it checks (per op, mirroring _apply_single_edit)

  • Paragraph ref format and presence
  • Hash freshness (stale hash → invalid, not raised)
  • Per-action argument requirements (replace needs find/replace_with, etc.)
  • Target text existence in the resolved paragraph

Details

  • @overload signatures narrow the return type by the dry_run literal (list[str] vs list[EditValidationResult]), so callers don't need an isinstance guard.
  • validate_batch never raises: malformed ops (e.g. a negative occurrence) are reported as valid=False rather than propagating an exception.
  • Not-found errors include the paragraph preview for easier debugging, matching what the live TextNotFoundError exposes.
  • Docstrings document the one limitation: each op is validated independently against the current document, so sequential effects between multiple ops on the same paragraph are not simulated. Cross-paragraph batches are unaffected (edits never change the paragraph count).

Testing

  • New TestBatchEditDryRun suite: all-valid, stale-hash, missing-text, mixed valid/invalid, empty batch, preview-in-error, and never-raises-on-bad-occurrence.
  • Full suite: 595 passed, 6 skipped.
  • make check green (ruff lint + format + ty static type check).

Summary by CodeRabbit

  • New Features

    • Added a dry_run option for batch edits to validate changes before applying them.
    • Dry-run now returns per-operation validation results (including whether each operation would apply and why when it can’t).
  • Bug Fixes

    • Validation-only runs no longer mutate document content.
    • Improved reporting for invalid, missing, stale, or malformed edit targets (with clearer error details).
  • Refactor

    • Expanded the public API for batch editing to support validation-only output and exposed validation result details.

@coderabbitai

coderabbitai Bot commented Jul 1, 2026 •

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@pablospe, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 48 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: c70c5307-39a3-44ef-9a61-de63171c844b

📥 Commits

Reviewing files that changed from the base of the PR and between 88037b4 and 4255f15.

📒 Files selected for processing (2)
  • docx_editor/track_changes.py
  • tests/test_batch_edit.py
📝 Walkthrough

Walkthrough

Adds dry-run batch edit validation that returns per-operation results without mutating the document, reuses shared action-target validation in the edit pipeline, and expands tests to cover valid and invalid dry-run cases.

Changes

Dry-run validation flow

Layer / File(s) Summary
Validation results and batch checking
docx_editor/track_changes.py
Adds EditValidationResult, shared action-target resolution, and non-mutating batch validation that reports per-operation success or error.
Dry-run batch edit tests
tests/test_batch_edit.py
Adds coverage for valid dry-run batches, stale hashes, missing text, invalid occurrences, paragraph reference errors, missing arguments, unknown actions, and empty batches.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Possibly related PRs

  • pablospe/docx-editor#10: Modifies the same batch-edit flow and shared hash-based validation logic in docx_editor/track_changes.py.
  • pablospe/docx-editor#19: Touches the same batch-edit pipeline and tests/test_batch_edit.py with related transaction-style behavior.

Poem

A rabbit hops through edits bright,
Validates by moonlit light.
No carrots moved, no text displaced,
Just dry-run checks in tidy pace.
🐇

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and accurately summarizes the main change: adding a dry-run validation mode to batch_edit().
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch task/f8d48414-Dry-run-validation-for-batch_e

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.

@codecov

codecov Bot commented Jul 1, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 94.6%. Comparing base (fb010e9) to head (4255f15).

Additional details and impacted files
@@          Coverage Diff          @@
##            main     #28   +/-   ##
=====================================
  Coverage   94.5%   94.6%           
=====================================
  Files         10      10           
  Lines       2488    2526   +38     
  Branches     560     565    +5     
=====================================
+ Hits        2353    2391   +38     
  Misses        36      36           
  Partials      99      99           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@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

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docx_editor/track_changes.py`:
- Around line 262-297: The per-action argument validation in _validate_single
duplicates the same checks already present in _apply_single_edit, so the dry-run
path can drift from the real apply path. Extract the shared
action-to-target/required-fields logic into a helper such as
_resolve_action_target(op) and have both _validate_single and _apply_single_edit
use it for replace/delete/insert_before/insert_after before proceeding with
paragraph lookup or apply semantics. Keep the existing error messages and action
handling behavior consistent by centralizing the checks in one place.
- Around line 298-304: The dry-run validation in find_in_text_map is catching
all exceptions from _find_in_paragraph, which can mask unrelated bugs as
validation failures. Narrow this logic by explicitly handling a negative
op.occurrence before calling _find_in_paragraph, or catch only the specific
error raised for that case, and leave other exceptions to surface normally. Keep
the existing preview/error message behavior for the validation path while
avoiding a broad except Exception around _find_in_paragraph.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 08399aee-f962-41fc-87ca-196c52c01135

📥 Commits

Reviewing files that changed from the base of the PR and between f2d2d2a and 1f22480.

📒 Files selected for processing (4)
  • docx_editor/__init__.py
  • docx_editor/document.py
  • docx_editor/track_changes.py
  • tests/test_batch_edit.py

Comment thread docx_editor/track_changes.py
Comment thread docx_editor/track_changes.py Outdated
pablospe added 2 commits July 2, 2026 13:27
Adds Document.batch_edit(ops, dry_run=True) which validates every
operation without mutating the document and returns one
EditValidationResult per op (in input order). Validation reuses the
same paragraph-resolution and text-search helpers as the real apply
path so it cannot drift from execution semantics.

- @overload signatures narrow the return type by the dry_run literal
- validate_batch never raises: malformed ops (e.g. negative occurrence)
  are reported as invalid rather than propagating
- not-found errors include the paragraph preview for easier debugging
- docstrings note that same-paragraph sequential effects are not
  simulated (each op is validated against the current document)
Adds dry_run tests for missing paragraph ref, malformed ref, per-action
missing-argument errors, and unknown action so codecov/patch sees the
new validation branches in _validate_single as covered.
@pablospe
pablospe force-pushed the task/f8d48414-Dry-run-validation-for-batch_e branch from 1f22480 to 75f44d6 Compare July 2, 2026 11:27
pablospe added 2 commits July 2, 2026 13:44
The per-action required-argument checks were duplicated between
_apply_single_edit and _validate_single. Extract them into a single
_resolve_action_target helper so the dry-run validation path provably
cannot drift from the apply path (addresses PR review feedback).
Replace the broad 'except Exception' in _validate_single with an
explicit op.occurrence < 0 guard. That is the only non-well-formed
input find_in_text_map chokes on, so guarding it up front gives a
clearer message ('occurrence must be >= 0') and avoids a blanket catch
that could mask unrelated bugs (addresses PR review feedback).

@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: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
tests/test_batch_edit.py (1)

480-501: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Consider adding a companion apply-path test for negative occurrence.

Once _apply_single_edit/_resolve_action_target gains the negative-occurrence guard (see track_changes.py comment), add a dry_run=False counterpart to test_bad_occurrence_reports_instead_of_raising asserting BatchOperationError is raised (not an unhandled exception), to lock in the fix and prevent regression.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/test_batch_edit.py` around lines 480 - 501, Add a companion apply-path
test for negative occurrence handling in batch editing. Extend the existing test
around test_bad_occurrence_reports_instead_of_raising to cover dry_run=False and
assert that doc.batch_edit raises BatchOperationError rather than an unhandled
exception when EditOperation uses occurrence=-1. Use the same multi_para_doc
setup and keep the assertions aligned with the
_apply_single_edit/_resolve_action_target negative-occurrence guard so the
regression stays covered.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docx_editor/track_changes.py`:
- Around line 182-230: The negative occurrence check is missing from the shared
edit path, so `_apply_single_edit` can still pass an invalid `op.occurrence`
into `_find_in_paragraph` and trigger an internal error instead of the intended
validation flow. Add the `occurrence < 0` guard to the shared
validation/resolution path used by `_resolve_action_target` and
`_apply_single_edit` (or check it directly in `_apply_single_edit`) so all
actions fail consistently before calling `_find_in_paragraph`.

---

Outside diff comments:
In `@tests/test_batch_edit.py`:
- Around line 480-501: Add a companion apply-path test for negative occurrence
handling in batch editing. Extend the existing test around
test_bad_occurrence_reports_instead_of_raising to cover dry_run=False and assert
that doc.batch_edit raises BatchOperationError rather than an unhandled
exception when EditOperation uses occurrence=-1. Use the same multi_para_doc
setup and keep the assertions aligned with the
_apply_single_edit/_resolve_action_target negative-occurrence guard so the
regression stays covered.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: fe1d49c3-67c5-4303-a9dc-6bd6c82994b7

📥 Commits

Reviewing files that changed from the base of the PR and between 75f44d6 and 88037b4.

📒 Files selected for processing (2)
  • docx_editor/track_changes.py
  • tests/test_batch_edit.py

Comment thread docx_editor/track_changes.py
The occurrence < 0 guard lived only in _validate_single, so the apply
path (_apply_single_edit) could still pass a negative occurrence into
_find_in_paragraph and raise a raw UnboundLocalError instead of a clean
BatchOperationError. Move the guard into the shared _resolve_action_target
helper both paths call, and drop the now-duplicate check in
_validate_single so the two paths stay consistent.
@pablospe
pablospe merged commit 7ada3b6 into main Jul 2, 2026
10 checks passed
pablospe added a commit that referenced this pull request Jul 14, 2026
Add a 1-based section field to ParagraphLocation, computed by walking
paragraph-level w:pPr/w:sectPr boundaries in document order. A
sectPr-carrying paragraph belongs to the section it closes; the next
paragraph starts the following one, and the body-level sectPr defines
the final section. Direct-child walks keep w:pPrChange revision records
from spoofing section boundaries.

Both accessors report it: get_paragraph_location() via a prefix scan
(same pattern as heading_path) and list_paragraph_locations() via a
single precomputed pass. Documented in docs/api.md and SKILL.md.
@pablospe
pablospe deleted the task/f8d48414-Dry-run-validation-for-batch_e branch August 30, 2026 19:29
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.

1 participant