Summary
Chart.__init__ has a rich explicit signature, but every normal chart factory erases it behind **props: Any. IDEs and type checkers therefore cannot catch misspelled or invalid shared chart options on the entry points users normally call. Callback payloads are also generic dict, and non-callable values are accepted until an interaction event tries to invoke them.
This is a follow-up to #429, which repairs root PEP 561 exports but intentionally does not cover factory signatures or callback payloads.
Audited at 99eda6d.
Current behavior
The shared chart contract is explicit in Chart.__init__, but chart, scatter_chart, and line_chart accept only **props: Any in the main factory block, as does the wider chart family in the generated wrappers.
The docs generator manually substitutes the hidden Chart.__init__ options in docs/app/xy_docs/api_reference.py, so the rendered docs look typed while the installed callable remains opaque.
Callbacks are declared as Callable[[dict], None] and assigned without runtime validation in Chart.__init__ and its assignments. on_hover=42 therefore builds and enables hover, then fails only when the channel invokes it in python/xy/channel.py.
The core event fields are already publicly documented in docs/api-reference/events-and-callbacks.md, and the bundled Reflex adapter demonstrates a maintainable TypedDict event vocabulary in python/reflex_xy/events.py.
Proposed direction
- Define a shared
ChartProps TypedDict and use PEP 692 Unpack on every chart factory (via typing_extensions where required), or generate explicit keyword-only signatures from one manifest.
- Define/export core callback payload types that mirror the channel producers and documented fields. Dynamic row dictionaries can use a documented mapping value type; structured brush and view payloads should use TypedDicts.
- Validate every non-
None on_* argument with callable() during chart construction, and retain a defensive check at channel creation.
Acceptance criteria
Summary
Chart.__init__has a rich explicit signature, but every normal chart factory erases it behind**props: Any. IDEs and type checkers therefore cannot catch misspelled or invalid shared chart options on the entry points users normally call. Callback payloads are also genericdict, and non-callable values are accepted until an interaction event tries to invoke them.This is a follow-up to #429, which repairs root PEP 561 exports but intentionally does not cover factory signatures or callback payloads.
Audited at
99eda6d.Current behavior
The shared chart contract is explicit in
Chart.__init__, butchart,scatter_chart, andline_chartaccept only**props: Anyin the main factory block, as does the wider chart family in the generated wrappers.The docs generator manually substitutes the hidden
Chart.__init__options indocs/app/xy_docs/api_reference.py, so the rendered docs look typed while the installed callable remains opaque.Callbacks are declared as
Callable[[dict], None]and assigned without runtime validation inChart.__init__and its assignments.on_hover=42therefore builds and enables hover, then fails only when the channel invokes it inpython/xy/channel.py.The core event fields are already publicly documented in
docs/api-reference/events-and-callbacks.md, and the bundled Reflex adapter demonstrates a maintainable TypedDict event vocabulary inpython/reflex_xy/events.py.Proposed direction
ChartPropsTypedDictand use PEP 692Unpackon every chart factory (viatyping_extensionswhere required), or generate explicit keyword-only signatures from one manifest.Noneon_*argument withcallable()during chart construction, and retain a defensive check at channel creation.Acceptance criteria
widht=.Chartoption has the same externally visible type on every chart factory.on_*value fails at construction with an error naming the parameter.