Skip to content

API hygiene: typed EditOperation constructors + public SearchResult#40

Merged
pablospe merged 3 commits into
mainfrom
task/cc8b6534-API-hygiene---typed-EditOperat
Jul 14, 2026
Merged

API hygiene: typed EditOperation constructors + public SearchResult#40
pablospe merged 3 commits into
mainfrom
task/cc8b6534-API-hygiene---typed-EditOperat

Conversation

@pablospe

@pablospe pablospe commented Jul 14, 2026

Copy link
Copy Markdown
Owner

Implements ISSUES.md items 25 (typed EditOperation constructors) and 7 (internal types exported as public API) — the repo-local backlog file, not GitHub issues.

What

Typed EditOperation constructors (#25). EditOperation.replace/.delete/.insert_after/.insert_before classmethods validate arguments at construction time with the same rules batch_edit() applies, raising ValueError with field-specific messages. Signatures mirror the corresponding Document methods 1:1, so doc.replace(...) translates mechanically to EditOperation.replace(...). The raw EditOperation(action=..., ...) form remains supported.

Public SearchResult + sealed __all__ (#7). Document.find_text() now returns a SearchResultstart/end offsets in the containing paragraph's visible text, matched text, a hash-anchored paragraph_ref directly usable as the paragraph= of follow-up edits, and a spans_revision flag. The text-map internals (TextMap, TextMapMatch, TextPosition, build_text_map, find_in_text_map) are removed from __all__; a module __getattr__ shim keeps top-level access working with a DeprecationWarning for one release, and the internals remain importable from docx_editor.xml_editor.

Breaking change

Document.find_text() no longer returns the internal TextMapMatch: code reading .spans_boundary or .positions off its result must migrate to SearchResult.spans_revision / paragraph_ref, or use the internals via docx_editor.xml_editor. All in-repo consumers (tests, docs, README, skill) are migrated in this PR.

Validation parity

Constructor checks exactly mirror apply-time _resolve_action_target semantics — including the deliberate allowances (replace_with="" and insert text="" are valid tracked edits). A dry-run drift-guard test pins the parity so future divergence fails loudly.

Testing

  • 772 passed, 8 skipped (pre-existing environment skips)
  • New: constructor validation per action, end-to-end typed batch, drift guard, SearchResult shape + ref chaining into a follow-up edit, deprecation shim warns/forwards, __all__ surface checks
  • ruff check / ruff format --check clean

…_all__

Add EditOperation.replace/.delete/.insert_after/.insert_before classmethods
that validate at construction time with the same rules batch_edit applies
(field-specific ValueError messages), mirroring the Document method
signatures 1:1. The raw EditOperation(action=..., ...) form remains
supported (#25).

Document.find_text now returns a public SearchResult (start/end offsets in
the paragraph's visible text, matched text, hash-anchored paragraph_ref
usable in follow-up edits, spans_revision flag) instead of leaking the
internal TextMapMatch. Remove TextMap, TextMapMatch, TextPosition,
build_text_map and find_in_text_map from the top-level public API; a
module __getattr__ shim keeps them importable with a DeprecationWarning
for one release, and they remain available from docx_editor.xml_editor (#7).
@coderabbitai

coderabbitai Bot commented Jul 14, 2026

Copy link
Copy Markdown

Warning

Review limit reached

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

Next review available in: 10 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: 0d2afd53-ef23-46be-afb8-a2f0555927f9

📥 Commits

Reviewing files that changed from the base of the PR and between 9c8bcc3 and 7b9e0f1.

📒 Files selected for processing (12)
  • README.md
  • docs/api.md
  • docs/quickstart.md
  • docx_editor/__init__.py
  • docx_editor/document.py
  • docx_editor/track_changes.py
  • skills/docx/SKILL.md
  • tests/test_batch_edit.py
  • tests/test_coverage_gaps.py
  • tests/test_document.py
  • tests/test_mixed_state.py
  • tests/test_multi_wt.py
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch task/cc8b6534-API-hygiene---typed-EditOperat

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 14, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 94.1%. Comparing base (9c8bcc3) to head (7b9e0f1).
⚠️ Report is 4 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##            main     #40     +/-   ##
=======================================
+ Coverage   93.5%   94.1%   +0.5%     
=======================================
  Files         11      11             
  Lines       2950    2910     -40     
  Branches     634     607     -27     
=======================================
- Hits        2761    2740     -21     
+ Misses        84      76      -8     
+ Partials     105      94     -11     

☔ 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.

pablospe added 2 commits July 14, 2026 12:27
Multi-reviewer findings: find_text's occurrence counts matches
document-wide, while edit methods count within one paragraph, so chaining
paragraph_ref into a follow-up edit could silently target a different
match when the text repeats inside the found paragraph. SearchResult now
carries paragraph_occurrence (the within-paragraph index, same text-map
counting _find_in_paragraph uses) to pass as the follow-up occurrence=.
_find_across_boundaries_located returns a _LocatedMatch dataclass instead
of a growing tuple.

Also from review: docs/quickstart.md batch example now uses the typed
constructors, and the api.md Raises summary no longer implies empty
replace/insert payloads are rejected.

Skipped deliberately: constructor error-message prefix divergence from
_resolve_action_target (field-specific messages are intentional) and a
spans_boundary compat alias (would re-introduce the deprecated name).
Review iteration 2: README's Batch Editing section and batch_edit()'s own
docstring example still showed the raw EditOperation(action=...) form the
PR steers users away from.
@pablospe
pablospe merged commit 05fee8b into main Jul 14, 2026
8 checks passed
pablospe added a commit that referenced this pull request Jul 16, 2026
…rence, refs, and batch elements (ISSUES.md #40)

Fixes the three ISSUES.md #40 input-validation bugs plus the same-class type
hardening the 5-iteration multi-reviewer loop surfaced:

- Empty/None search text: rejected up front (was: silent edit loss with
  explicit occurrence, meaningless AmbiguousTextError without)
- Non-EditOperation batch elements: BatchOperationError with op index
  (was: raw AttributeError); validate_batch returns invalid rows
- Non-string paragraph refs: ValueError from ParagraphRef.parse and the
  four Document edit methods (was: raw TypeError / silent doc-wide fallback)
- Truthy non-string search text and payloads: ValueError at every boundary
  (was: raw TypeError/AttributeError escaping the batch contracts;
  dry-run reported such ops valid)
- occurrence type-leak: shared _require_valid_occurrence guard at all six
  boundaries (was: raw TypeError for str/float, bool silently misread)
- add_comment: non-string anchor and comment rejected with CommentError
  before any mutation (was: raw TypeError; non-string comment corrupted
  document.xml with orphaned range markers)

27 new TDD-first tests; docstrings and SKILL.md error contract updated.
Known follow-up: reply_to_comment has the same non-string payload gap.
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