Skip to content

Share an operation budget across canvas preparation and writing #500

Closed

Description

@yhay81

Context

PR #477 implements #467's canvas text by splitting, positioning, encoding, and joining text in Python, then writing it through an open document transaction. DESIGN.md F6 and section 9 require one operation budget, including allocated bytes, limits, deadline, and cancellation, before input-derived collections grow. _Document::budget() and _Document::edit() currently create separate budgets, and the Python binding does not expose a reusable operation budget. The existing canvas _commit also joins its buffered content before checking the content-size limit.

Goal

Provide bounded, shared operation accounting for canvas content preparation and writing so that canvas text can use the document's limits throughout its work without creating an independent budget for each layout or encode call.

Scope

In: a private binding or equivalent mechanism for creating an operation budget from the document's DocumentLimits and passing it through the canvas preparation and core text operations; fallible, budgeted accounting for input-derived canvas content buffers and their write/commit boundary; typed refusal and cancellation/deadline propagation. Establish and document operation and transaction lifetime semantics consistently with DESIGN.md.

Out: changing DESIGN.md, unrelated drawing features, and new public Python APIs. #467 remains responsible for the actual canvas text implementation and its integration tests. The existing public Font operations from #496 keep their contracts.

Paths

  • crates/pyropdf-python/src/document.rs, crates/pyropdf-python/src/history.rs, crates/pyropdf-python/src/fonts.rs, crates/pyropdf-python/src/lib.rs, and narrowly related binding modules as needed;
  • crates/pyropdf-store/src/budget/ for budget mechanics if required;
  • python/pyropdf/_canvas.py, python/pyropdf/_core.pyi;
  • tests/test_canvas*.py, tests/test_limits*.py, and Rust binding/store tests for budget invariants.

Acceptance

  • A canvas operation obtains its budget from the actual document limits and call overrides once; its input validation, temporary buffers, layout/encoding, generated content, and writing use that same accounting, without input-sized allocations before charging.
  • Repeated calls made within one canvas operation accumulate charges; distinct operations do not leak charges. Low memory and element budgets refuse with typed LimitError, without partial writes or an undo step.
  • Deadline and cancellation are observed during long canvas preparation and writing, and tests demonstrate both.
  • Content size is checked or charged before joining input-derived buffered bytes, including at canvas commit; invalid inputs and allocation failures do not panic.
  • Regression tests cover existing path/image canvas behavior, rollback, and Python str.splitlines compatibility if line splitting is moved into Rust, including CRLF, vertical separators, trailing breaks, and empty lines.
  • Full repository checks and current-head CI pass.

Notes

A private budget handle shared across the binding and Python is one candidate; moving preparation and PDF operator generation into one bounded Rust call is another. Evaluate either against the operation/transaction boundary and existing document limits before implementation. A handle for layout/encoding alone does not solve the delayed commit or Python intermediate allocations. No upstream or DESIGN.md change is assumed.

Activity

  1. added
    area:enginecrates/pyropdf-engine: interpretation, rendering, writing, understanding
    area:pythoncrates/pyropdf-python and python/pyropdf: the binding and the Python API
    on Oct 31, 2026
  2. added
    in-progressClaimed; the latest comment names the branch
    on Oct 31, 2026
  3. yhay81 commented on Oct 31, 2026

    @yhay81
    OwnerAuthor

    Handoff: branch codex/500-canvas-operation-budget, commit 5f1add2; no PR yet. The local and pushed worktree is a draft and must not be merged.

    It shares operation budget meters across Canvas preparation, Font encode/layout and transaction writes; checks/polls before bounded path, image input and content growth; handles nested abort/rollback and writer-wait cancellation; and reads a placed page's content and resources from one source snapshot. Tests cover these paths, including concurrent source edits.

    python3 tools/check.py --locked: 6,723 Python tests passed, 5 skipped, and tests/test_canvas.py::test_pages_of_the_same_document_are_stored_once failed because the current whole-version cache key creates three Forms. Ruff, Ruff format, ty, cargo fmt, workspace clippy and workspace cargo tests passed. The earlier targeted Canvas/history/image/font/cancellation run passed apart from the same known failure.

    Next: complete blockers #501 (budgeted/fallible image conversion and compression), #502 (fallible store value copying), and #503 (dependency-aware page Form cache). Replace the temporary version-only cache key with the #503 solution, verify normal reuse and resource-change invalidation, rerun the full checks, then open a PR. Do not infer the current precharged PNG/Pixmap path or copy_for_edit clone is fully budgeted or fallible. No auto-merge is enabled.

  4. removed
    in-progressClaimed; the latest comment names the branch
    on Oct 31, 2026
  5. added
    in-progressClaimed; the latest comment names the branch
    on Nov 2, 2026
  6. yhay81 commented on Nov 2, 2026

    @yhay81
    OwnerAuthor

    Claimed on branch claude/500-canvas-operation-budget, from main (#505 and #503 merged). It builds on #505's _OperationBudget and selectively ports the rest of codex/500-canvas-operation-budget (5f1add2).

  7. yhay81 commented on Nov 2, 2026

    @yhay81
    OwnerAuthor

    Handoff: branch claude/500-canvas-operation-budget, PR #578 (not merged).

    • Changes:
      • Each canvas is one begin_canvas operation. _Document::budget() returns this thread's canvas operation, and call_budget lets max_* options tighten it.
      • Font encode, layout and shape take operation=.
      • The content buffers are checked and charged before appending or joining.
      • _line_count counts lines before splitlines; splitting stays in Python.
    • Checks: python3 tools/check.py --locked passes locally. The new tests are tests/test_canvas_operation.py (35).
    • Next: the current-head CI must pass, then the owner approves the merge.
  8. removed
    in-progressClaimed; the latest comment names the branch
    on Nov 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:enginecrates/pyropdf-engine: interpretation, rendering, writing, understandingarea:pythoncrates/pyropdf-python and python/pyropdf: the binding and the Python API

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions