Repository navigation
Share an operation budget across canvas preparation and writing (#500) - #578
Merged
Merged
Conversation
A canvas runs as one operation from its document's limits. Reads and writes on the owner thread, image encoding, and font encode, layout and shape charge it; call options can only tighten it. The canvas checks its content size and charges its buffers before appending or joining them, counts text lines before splitting, and aborts without a write or undo step on refusal, cancellation, or deadline. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
6 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #500
Builds on #505 (
_OperationBudget,SharedBudget,begin_canvas/abort_canvas) and #503. Of thecodex/500-canvas-operation-budgetdraft (5f1add2), this ports the fontoperation=keyword and the idea of_Document::budget()returning the canvas operation. The rest was already merged through #505 and #503, or is written anew here.What changed
Canvas.__enter__opens it withbegin_canvas, which takes its limits from the document'sDocumentLimitsonce. A discarded or refused canvas isabort_canvased: nothing is written and no undo step is recorded.end_transactionrefuses to commit a cancelled or expired operation._Document::budget()now returns this thread's innermost canvas operation when one is open, or a new one otherwise. SoObjectsreads, image encoding (pixmap_png),Pagereads and writes, all on the owner thread, accumulate against the canvas.max_*options go through_Document::call_budget. Inside a canvas an option can only tighten the operation (SharedBudget::tightened), andNonekeeps the operation's limit. Outside a canvas the behaviour is unchanged: the option replaces the limit, andNoneremoves it._Font.encode,_Font.layoutand_Font.shapetakeoperation=. They charge the operation and observe its deadline and cancellation, and when the call ends they return the memory they charged.text_input_sizeandtext_line_countto each call'smax_text_size, as before.shapecharges its index buffer before allocating it.Canvas.textcounts lines without splitting (_line_count, which matchesstr.splitlinesexactly), checks the line limit, and charges the lines beforesplitlines().Canvas.textboxcharges the normalized copies before making them. Line splitting stays in Python._append. It checks the canvas's running content size againstcontent_input_sizeand chargesmemory_size(which polls) before appending._commitchecks the framed size and charges the joined copies before joining.Acceptance
test_a_canvas_charges_one_operation_from_the_document_limits,test_font_calls_observe_the_canvas_operation,test_text_layout_observes_the_operation_memory,test_the_line_limit_counts_before_splitting.LimitError, with no write and no undo step. Tests:test_charges_accumulate_*,test_draws_that_fit_alone_are_refused_together,test_a_low_memory_limit_refuses_*(path, text, textbox),test_the_element_limit_refuses_without_a_write,test_a_call_inside_a_canvas_tightens_the_operation.test_cancellation_stops_preparation,test_cancellation_stops_writing,test_the_deadline_stops_preparation_and_writing.test_content_size_is_checked_before_buffering,test_content_size_is_checked_before_joining_at_commit,test_an_opacity_group_returns_its_buffer.str.splitlinescompatibility (CRLF,\v,\f,\x1c–\x1e,\x85,,, trailing breaks, empty lines) is tested, although splitting stays in Python.python3 tools/check.py --lockedpasses locally (pytest, ruff, ruff format, ty, cargo fmt, clippy, cargo test). Current-head CI: see checks.🤖 Generated with Claude Code