Skip to content

Don't modify the annotation passed to add_vline/add_hline/add_vrect/add_hrect - #5819

Draft
adarshsm wants to merge 1 commit into
plotly:mainfrom
adarshsm:reuse-shape-annotation-3547
Draft

adarshsm wants to merge 1 commit into
plotly:mainfrom
adarshsm:reuse-shape-annotation-3547

Conversation

@adarshsm

Copy link
Copy Markdown

Link to issue

Closes #3547

Description of change

add_vline, add_hline, add_vrect and add_hrect no longer modify the dict or go.layout.Annotation passed as annotation=. Before, reusing one annotation (e.g. a shared style dict) for several shapes put every annotation at the first shape's position.

shapeannotation.axis_spanning_shape_annotation filled in the annotation it was given in place. It set text and the other annotation_* kwargs, then set x/y/xanchor/yanchor from the shape, but only where they weren't already set. After the first call, the caller's annotation already held the first shape's x/y, so later calls kept those coordinates and only the text changed (the reporter's snippet prints this). It now works on a copy.copy of the annotation. That's a shallow dict copy for dicts, and for go.layout.Annotation the copy goes through BasePlotlyType.__reduce__. The function only sets top-level keys, so the caller's object stays untouched.

Demo

The reproducer from the issue:

annotation = dict(font_color="#000000")
fig.add_vline(x=2, annotation_text="vline 1", annotation=annotation)
fig.add_vline(x=3, annotation_text="vline 2", annotation=annotation)
[a.x for a in fig.layout.annotations]

On main this gives [2, 2], and annotation ends up as {'font_color': '#000000', 'text': 'vline 2', 'xanchor': 'left', 'yanchor': 'top', 'x': 2, 'y': 1, 'showarrow': False}. With this change it gives [2, 3] and annotation is still {'font_color': '#000000'}. A go.layout.Annotation passed to add_hline twice behaved the same way (ys == [1, 1]) and is fixed too.

Testing strategy

test_reuse_annotation in tests/test_optional/test_autoshapes/test_annotated_shapes.py is parametrized over a dict and a go.layout.Annotation. It adds two vlines with the same annotation, then checks that the annotation is unchanged and that each layout annotation has its own text, its own x and the shared font color. Both cases fail on main and pass with the fix.

Local runs: tests/test_optional/test_autoshapes (35 passed), plus tests/test_core/test_update_objects, test_subplots and test_graph_objs (348 passed, 1 skipped). ruff format --check is clean on the touched files.

Guidelines

🤖 Generated with Claude Code

axis_spanning_shape_annotation filled in the annotation it was given
(text from annotation_* kwargs, and x/y/xanchor/yanchor from the shape)
in place. When the same dict or go.layout.Annotation was passed to
several add_vline/add_hline/add_vrect/add_hrect calls, the first call
left x/y in it, and later calls only set positions that weren't set
already, so every annotation ended up at the first shape.

Work on a copy of the annotation instead.

Closes plotly#3547

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
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/hline annotation text location fixed to first shape if using a pre-instantiated dict for the "annotation" input parameter

1 participant