plotly.py icon indicating copy to clipboard operation
plotly.py copied to clipboard

Refactor autoshapes to use shape.label for vline/hline/vrect/hrect

Open nochinxx opened this issue 2 weeks ago • 1 comments

This PR is a first step toward refactoring the autoshape helpers to use the new layout.shape.label API in Plotly.js, while keeping backward-compatible annotation behavior for now.

Scope

This PR updates:

  • Figure.add_vline
  • Figure.add_hline
  • Figure.add_vrect
  • Figure.add_hrect

Key changes:

  1. Legacy → label shim

    • Introduce _coerce_shape_label_from_legacy_annotation_kwargs(kwargs) to translate a safe subset of annotation_* kwargs into a label dict:
      • annotation_textlabel.text
      • annotation_fontlabel.font
      • annotation_textanglelabel.textangle
    • The shim is called from each of the four helpers and is non-destructive: it does not remove annotation_* from kwargs.
    • When any annotation_* field is used, a FutureWarning is emitted to encourage migrating to label={...}.
  2. Add shape.label for autoshapes

    In _process_multiple_axis_spanning_shapes, for shape_type in:

    • "vline"
    • "hline"
    • "vrect"
    • "hrect"

    we now:

    • Split kwargs into shape_kwargs and legacy_ann via shapeannotation.split_dict_by_key_prefix(..., "annotation_").
    • Build a label_dict starting from any explicit label passed by the user and then merging in legacy fields if they are not present.
    • Attach label_dict to the new shape (shape_to_add["label"] = label_dict) before calling add_shape(...).
  3. Mapping annotation_position → label.textposition

    We partially map annotation_position into label.textposition to keep intuitive placement:

    • For lines:

      • vline:
        • "top""end"
        • "bottom""start"
        • "middle"/"center""middle"
      • hline:
        • "right""end"
        • "left""start"
        • "middle"/"center""middle"
      • A helper _normalize_legacy_line_position_to_textposition is used to validate legacy strings and to keep behavior predictable.
    • For rectangles (vrect, hrect):

      • We strip "inside " / "outside " and map the remaining part to a valid rect textposition:
        • "top left", "top center", "top right", …
        • "middle left", "middle center", "middle right", …
        • "bottom left", "bottom center", "bottom right", …
      • Single-side positions like "top", "bottom", "left", "right" are mapped to sensible defaults (e.g. "top""top center").
  4. Warnings for unsupported styling

    • annotation_bgcolor / annotation_bordercolor are not supported on shape.label and are currently ignored with a FutureWarning suggesting use of label font/color or a background shape instead.
  5. Backward compatibility (important)

    • The existing annotation path is still active:
      • axis_spanning_shape_annotation(...) is still called.
      • add_annotation(...) is still called when augmented_annotation is not None.
    • This means that:
      • existing user code that relies on layout.annotations continues to work,
      • existing tests that assert on annotations continue to pass,
      • but shapes now also carry a label, which will allow us to stop creating separate annotations in a later step.

Bug / motivation

This work is motivated by the issue where using add_vline with annotation_text on a datetime x-axis can raise a TypeError due to arithmetic on mixed types when positioning the annotation (see the discussion in #3065).

By moving toward shape.label:

  • we can avoid doing arithmetic on the coordinate values just to place the label, and
  • we get a more direct and robust representation of labels attached to shapes.

For now, we keep the annotation logic in place to avoid breaking existing behavior while we gather feedback.

Tests

Locally, I ran:

python -m pytest -q tests/test_optional/test_autoshapes/test_annotated_shapes.py

## Code PR

- [ x] I have read through the [contributing notes](https://github.com/plotly/plotly.py/blob/main/CONTRIBUTING.md) and understand the structure of the package. In particular, if my PR modifies code of `plotly.graph_objects`, my modifications concern the code generator and *not* the generated files.
- [ ] I have added tests or modified existing tests.
- [ ] For a new feature, I have added documentation examples (please see the doc checklist as well).
- [ ] I have added a CHANGELOG entry if changing anything substantial.
- [ ] For a new feature or a change in behavior, I have updated the relevant docstrings in the code.


nochinxx avatar Dec 06 '25 08:12 nochinxx

Wow, fantastic PR! This is an exciting refactor and would be a great add to Plotly.py. Let us know when you need help reviewing.

ndrezn avatar Dec 10 '25 16:12 ndrezn