Skip to content

Fix adding shapes and annotations inside batch_update() - #5758

Open
Belagum wants to merge 1 commit into
plotly:mainfrom
Belagum:axis-span-shapes-batch-update
Open

Belagum wants to merge 1 commit into
plotly:mainfrom
Belagum:axis-span-shapes-batch-update

Conversation

@Belagum

@Belagum Belagum commented Sep 16, 2026

Copy link
Copy Markdown
Contributor

Link to issue

Closes #4742

Description of change

Inside batch_update() property edits are deferred until the context exits, but appending to a compound array property (fig.layout.shapes += (shape,)) still swaps in the new child objects right away. Those objects have no properties to read back until the batch is applied. add_vline(), add_hline(), add_vrect() and add_hrect() read the shape they just added to append " domain" to its axis reference, which gives the TypeError: unsupported operand type(s) for +=: 'NoneType' and 'str' from the issue. The same thing makes a second add_shape()/add_annotation() inside one batch copy the first object as {}, so it silently disappears.

Adding layout objects is a structural change like adding traces, and add_traces() already applies those immediately regardless of batch mode. This does the same for layout objects: _add_annotation_like() appends the object, and _process_multiple_axis_spanning_shapes() adds and fixes up the axis-spanning shapes, with batch mode switched off (new _batch_mode_disabled() context manager next to batch_update()). Regular property edits in the batch are still deferred and sent in one update message as before.

Demo

import plotly.graph_objects as go

fig = go.Figure()
with fig.batch_update():
    fig.add_vline(1)
fig.layout.to_plotly_json()["shapes"]

before:

TypeError: unsupported operand type(s) for +=: 'NoneType' and 'str'

after (same as without batch_update()):

[{'type': 'line', 'x0': 1, 'x1': 1, 'xref': 'x', 'y0': 0, 'y1': 1, 'yref': 'y domain'}]

Two shapes in one batch:

fig = go.Figure(go.Scatter(x=[1, 2], y=[1, 2]))
with fig.batch_update():
    fig.add_shape(type="line", x0=0, x1=1, y0=0, y1=1)
    fig.add_shape(type="rect", x0=1, x1=2, y0=1, y1=2)
fig.layout.to_plotly_json()["shapes"]

before:

[{}, {'type': 'rect', 'x0': 1, 'x1': 2, 'y0': 1, 'y1': 2}]

after:

[{'type': 'line', 'x0': 0, 'x1': 1, 'y0': 0, 'y1': 1}, {'type': 'rect', 'x0': 1, 'x1': 2, 'y0': 1, 'y1': 2}]

Testing strategy

test_add_axis_spanning_shape_in_batch_update (autoshapes tests) adds each of the four shapes, some with annotations, to a single plot, to one subplot and to all subplots, inside and outside batch_update(), and checks that the resulting shapes and annotations are identical. test_add_layout_objects_in_batch_update (figure messages tests) checks that shapes and annotations added inside a batch are there right away and go out as their own relayout messages, while a regular property assignment in the same batch is still deferred and sent in the single update message. All 13 new cases fail on main. Running tests/test_core, test_autoshapes, test_px and test_figure_factory with and without the change gives the same set of other failures (Windows cp1252 decoding in test_offline, statsmodels and scikit-image not installed here).

Additional information (optional)

I kept the fix local to adding layout objects. Assigning a compound array property directly inside a batch (fig.layout.shapes = [...]) still leaves the new children without readable properties until the batch is applied; changing that would touch how FigureWidget batches its messages, so I left it alone here.

Guidelines

Inside batch_update() property edits are deferred, but appending to a
compound array property still swaps in the new child objects, so the
freshly added shapes have no properties to read back yet. add_vline()
and friends then fail with "unsupported operand type(s) for +=:
'NoneType' and 'str'" while appending " domain" to the axis reference,
and a second add_shape()/add_annotation() in the same batch copies the
first one as an empty object.

Layout objects are structural additions like traces, and add_traces()
already applies those immediately regardless of batch mode. Do the same
here: add the objects and fix up their axis references with batch mode
switched off, so only regular property edits stay batched.

Closes plotly#4742
@camdecoster

Copy link
Copy Markdown
Contributor

Thanks for the PR! We'll take a look and follow up.

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.

add_vline fails in batch mode

3 participants