Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions news/+pyi-union-prop-optional.bugfix.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Generated `.pyi` stubs now type a prop declared as a union — `content: Var[str] | Component`, say — as optional, matching the `None` default that `create()` gives every prop. Type checkers previously reported the generated signature itself as an error.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Generated `.pyi` stubs now type a prop declared as a union — `content: Var[str] | Component`, say — as optional, matching the `None` default that `create()` gives every prop. Type checkers previously reported the generated signature itself as an error.
8 changes: 5 additions & 3 deletions packages/reflex-base/src/reflex_base/utils/pyi_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,8 @@ def _get_type_hint(
for arg in value.__args__
]
res_args.sort()
return f"{' | '.join(res_args)}"
res = f"{' | '.join(res_args)}"
return f"{res} | None" if is_optional else res

if args:
inner_container_type_args = (
Expand Down Expand Up @@ -257,11 +258,12 @@ def _get_type_hint(
return _get_type_hint(ev, type_hint_globals, is_optional=False)

if _is_union(ev):
res = [
res_args = [
_get_type_hint(arg, type_hint_globals, _is_optional(arg))
for arg in ev.__args__
]
return f"{' | '.join(res)}"
res = f"{' | '.join(res_args)}"
return f"{res} | None" if is_optional else res
res = (
_get_type_hint(ev, type_hint_globals, is_optional=False)
if ev.__name__ == "Var"
Expand Down
2 changes: 1 addition & 1 deletion pyi_hashes.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
"packages/reflex-components-dataeditor/src/reflex_components_dataeditor/dataeditor.pyi": "8aedc1629118cd3c0075340a5a0c3356",
"packages/reflex-components-gridjs/src/reflex_components_gridjs/datatable.pyi": "2ce1c076ecf5c2fa4945b4abdbf2f91d",
"packages/reflex-components-lucide/src/reflex_components_lucide/icon.pyi": "1e331a3d6420b97e5b1ce7f63ad53de8",
"packages/reflex-components-markdown/src/reflex_components_markdown/markdown.pyi": "79d0a59b1ba12a2f2c4a09fa6b5c776f",
"packages/reflex-components-markdown/src/reflex_components_markdown/markdown.pyi": "6759daf6e242e209d9298045c7b4456a",
"packages/reflex-components-moment/src/reflex_components_moment/moment.pyi": "13e39f8d2b46bfd76c50cb04062da8ff",
"packages/reflex-components-plotly/src/reflex_components_plotly/plotly.pyi": "beb057e382e527224597c320dbb72385",
"packages/reflex-components-radix/src/reflex_components_radix/__init__.pyi": "a77352f60fb6f4135b5d08a6e56efa6d",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@
- Callable prop: Var[Callable[[], bool]] (should NOT expand inner type)
- Component with no custom props (just inherited defaults)
- Component with only event handlers (no data props)
- Union props without None: still optional in create()
"""

from collections.abc import Callable
from typing import Any
from typing import Any, Literal

from reflex_base.components.component import Component, field
from reflex_base.event import EventHandler, passthrough_event_spec
Expand Down Expand Up @@ -48,3 +49,13 @@ class VarTypesComponent(Component):

# Callable prop — the inner Callable type should not be expanded.
on_check: Var[Callable[[], bool]] = field(doc="A callable that returns bool.")


class UnionPropsComponent(Component):
"""A component with props annotated as unions that do not include None."""

# Union of a Var and another type; create() still defaults it to None.
content: Var[str] | Component = field(doc="The content to render.")

# Union of a Literal and a Var.
color_scheme: Literal["red", "blue"] | Var[str] = field(doc="The color scheme.")
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
# This file was generated by `reflex/utils/pyi_generator.py`!
# ------------------------------------------------------
from collections.abc import Callable, Mapping, Sequence
from typing import Any
from typing import Any, Literal

from reflex_base.components.component import Component
from reflex_base.event import EventType, PointerEventInfo
Expand Down Expand Up @@ -225,3 +225,72 @@ class VarTypesComponent(Component):
Returns:
The component.
"""

class UnionPropsComponent(Component):
@classmethod
def create(
cls,
*children,
content: Component | Var[str] | str | None = None,
color_scheme: Literal["blue", "red"] | Var[str] | str | None = None,
style: Sequence[Mapping[str, Any]]
| Mapping[str, Any]
| Var[Mapping[str, Any]]
| Breakpoints
| None = None,
key: Any | None = None,
id: Any | None = None,
ref: Var | None = None,
class_name: Any | None = None,
custom_attrs: dict[str, Any | Var] | None = None,
on_blur: EventType[()] | None = None,
on_click: EventType[()] | EventType[PointerEventInfo] | None = None,
on_context_menu: EventType[()] | EventType[PointerEventInfo] | None = None,
on_double_click: EventType[()] | EventType[PointerEventInfo] | None = None,
on_focus: EventType[()] | None = None,
on_mount: EventType[()] | None = None,
on_mouse_down: EventType[()] | None = None,
on_mouse_enter: EventType[()] | None = None,
on_mouse_leave: EventType[()] | None = None,
on_mouse_move: EventType[()] | None = None,
on_mouse_out: EventType[()] | None = None,
on_mouse_over: EventType[()] | None = None,
on_mouse_up: EventType[()] | None = None,
on_scroll: EventType[()] | None = None,
on_scroll_end: EventType[()] | None = None,
on_unmount: EventType[()] | None = None,
**props,
) -> UnionPropsComponent:
"""Create the component.

Args:
*children: The children of the component.
content: The content to render.
color_scheme: The color scheme.
style: The style of the component.
key: A unique key for the component.
id: The id for the component.
ref: The Var to pass as the ref to the component.
class_name: The class name for the component.
custom_attrs: Attributes passed directly to the component.
on_focus: Fired when the element (or some element inside of it) receives focus. For example, it is called when the user clicks on a text input.
on_blur: Fired when focus has left the element (or left some element inside of it). For example, it is called when the user clicks outside of a focused text input.
on_click: Fired when the user clicks on an element. For example, it's called when the user clicks on a button.
on_context_menu: Fired when the user right-clicks on an element.
on_double_click: Fired when the user double-clicks on an element.
on_mouse_down: Fired when the user presses a mouse button on an element.
on_mouse_enter: Fired when the mouse pointer enters the element.
on_mouse_leave: Fired when the mouse pointer leaves the element.
on_mouse_move: Fired when the mouse pointer moves over the element.
on_mouse_out: Fired when the mouse pointer moves out of the element.
on_mouse_over: Fired when the mouse pointer moves onto the element.
on_mouse_up: Fired when the user releases a mouse button on an element.
on_scroll: Fired when the user scrolls the element.
on_scroll_end: Fired when scrolling ends on the element.
on_mount: Fired when the component is mounted to the page.
on_unmount: Fired when the component is removed from the page. Only called during navigation, not on page refresh.
**props: The props of the component.

Returns:
The component.
"""
12 changes: 12 additions & 0 deletions tests/units/reflex_base/utils/pyi_generator/test_unit.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,18 @@ def test_get_type_hint_union_without_none(type_hint_globals):
assert result == "int | str"


def test_get_type_hint_union_without_none_optional(type_hint_globals):
"""A union prop still gets `| None`, since create() defaults it to None."""
result = _get_type_hint(Union[str, int], type_hint_globals, is_optional=True) # noqa: UP007
assert result == "int | str | None"


def test_get_type_hint_str_union_without_none_optional(type_hint_globals):
"""A union given as a string annotation is treated the same way."""
result = _get_type_hint("Union[str, int]", type_hint_globals, is_optional=True)
assert result == "str | int | None"


def test_get_type_hint_union_with_none(type_hint_globals):
result = _get_type_hint(Union[str, int, None], type_hint_globals) # noqa: UP007
assert result == "int | str | None"
Expand Down
Loading