Problem
Every element's public constructor takes an id parameter that has two small but real issues:
- Shadows the builtin
id(). id is a Python builtin; using it as a parameter name is a readability/lint smell on the public surface users actually call.
- Loses its type annotation in subclasses. The base is annotated, the subclasses are not:
src/qtviz/core/element.py:53 — def __init__(self, *, backend_hint: str | None = None, id: ElementId | None = None) ✅ annotated
- all 9 public constructors degrade it to bare
id=None: scatter.py:34, curve.py:32, bars.py:29, histogram.py:26, errorbars.py:28, heatmap.py:28, image.py:25, spread.py:28, raw_figure.py:33
For a contract-first library ("the types are the documentation"), the public-facing constructors are exactly where the annotation should be present.
Two parts, different risk
(a) Re-add the annotation — non-breaking, do anytime.
Change bare id=None to id: ElementId | None = None in the 9 constructors. Runtime-invisible; purely improves the documented contract. Each file needs ElementId imported from ..core.element.
(b) Rename id → element_id — breaking, decide before 1.0.
Removes the builtin shadow but renames a public kwarg. Under Hyrum's Law, anyone passing id= (e.g. to carry a stable identity across rebuilds, as the raster pipeline does internally via id=node.id) breaks. Weigh value vs. churn. If done, do it pre-1.0 with a CHANGELOG note.
Recommendation
Land (a) opportunistically (it's trivial and safe — could even ride along with another elements touch). Treat (b) as a genuine pre-1.0 decision: the shadow is cosmetic, so "leave id as-is, just annotate it" is a defensible close. Capture the decision here either way.
Acceptance criteria
References
Problem
Every element's public constructor takes an
idparameter that has two small but real issues:id().idis a Python builtin; using it as a parameter name is a readability/lint smell on the public surface users actually call.src/qtviz/core/element.py:53—def __init__(self, *, backend_hint: str | None = None, id: ElementId | None = None)✅ annotatedid=None:scatter.py:34,curve.py:32,bars.py:29,histogram.py:26,errorbars.py:28,heatmap.py:28,image.py:25,spread.py:28,raw_figure.py:33For a contract-first library ("the types are the documentation"), the public-facing constructors are exactly where the annotation should be present.
Two parts, different risk
(a) Re-add the annotation — non-breaking, do anytime.
Change bare
id=Nonetoid: ElementId | None = Nonein the 9 constructors. Runtime-invisible; purely improves the documented contract. Each file needsElementIdimported from..core.element.(b) Rename
id→element_id— breaking, decide before 1.0.Removes the builtin shadow but renames a public kwarg. Under Hyrum's Law, anyone passing
id=(e.g. to carry a stable identity across rebuilds, as the raster pipeline does internally viaid=node.id) breaks. Weigh value vs. churn. If done, do it pre-1.0 with a CHANGELOG note.Recommendation
Land (a) opportunistically (it's trivial and safe — could even ride along with another elements touch). Treat (b) as a genuine pre-1.0 decision: the shadow is cosmetic, so "leave
idas-is, just annotate it" is a defensible close. Capture the decision here either way.Acceptance criteria
id: ElementId | None = None).id→element_id(and done + CHANGELOG'd if yes, or closed as "won't rename" if no).id=node.idindata/pipeline.pyrasterization, any tests/examples).References