Usability improvements for generated type hints#6300
Conversation
Previously the code was just checking in `__annotations__`, which was missing quite a few props that were only available as ForwardRef. This change brings the pyi_generator closer to full deferred evaluation type hinting mode compatibility. Fix `_get_type_hint` visible name determination: use `_get_visible_type_name` to ensure the fallback name is actually defined in the pyi globals. Otherwise use the fully qualified module.name construct as a fallback.
When rendering the props and docstring, include the subclasses props first, following the flattened MRO. Include descriptions for default event handlers. Include doc from ComponentField Run ruff format before ruff check --fix to avoid corrupting docstring indentation.
There's no practical reason for this to be a prop, so make it a ClassVar.
ClassVar should never be accepted in a `create` function
|
@greptile-apps please re-review this PR |
Greptile SummaryThis PR improves the quality and correctness of generated type hint stub files ( Key changes:
Confidence Score: 5/5Safe to merge; all findings are minor style/documentation issues that do not affect runtime correctness. The two open findings are both P2: one is an inaccurate 'or None' phrase in a docstring, and the other is a subtle doc-precedence quirk in the generated stubs that only manifests when a derived and base class both explicitly document the same prop (an uncommon scenario). Neither blocks correctness. The core logic — alias resolution, props ordering via deque+reversed MRO, get_type_hints adoption, list builtin-shadowing fix, and ruff format/check ordering — is all correct. packages/reflex-base/src/reflex_base/utils/pyi_generator.py (doc precedence in _generate_docstrings) and packages/reflex-base/src/reflex_base/utils/lazy_loader.py (comp_alias docstring).
|
| Filename | Overview |
|---|---|
| packages/reflex-base/src/reflex_base/utils/pyi_generator.py | Core stub generator: uses get_type_hints, deque+reversed for correct props ordering, adds field.doc support, and fixes ruff format/check order; minor doc-precedence issue when base and derived classes both define docs for the same prop. |
| packages/reflex-base/src/reflex_base/utils/lazy_loader.py | Properly implements the tuple-based (internal_name, public_alias) lazy-import system; introduces SubmodAttrsType and three helper functions; docstring for comp_alias incorrectly states it can return None. |
| packages/reflex-base/src/reflex_base/components/component.py | Improves custom_attrs doc string and adds a late import of reflex.state at module end to resolve ForwardRef type hints without circular import issues. |
| packages/reflex-components-radix/src/reflex_components_radix/mappings.py | Migrates all mapping dicts to SubmodAttrsType; uses ("list_ns", "list") tuple to expose list_ns as the public "list" alias, fixing the builtin shadowing issue. |
| packages/reflex-components-moment/src/reflex_components_moment/moment.py | Switches to qualified datetime.date/datetime.time etc. to prevent the module-level name "date" from shadowing the builtin when get_type_hints resolves annotations. |
| packages/reflex-components-radix/src/reflex_components_radix/themes/layout/list.py | Removes the list = list_ns = List() assignment that was shadowing Python's builtin list; only list_ns is exported now, with "list" exposed via the alias tuple in mappings.py. |
| packages/reflex-components-core/src/reflex_components_core/core/markdown_component_map.py | Converts _explicit_return from a misused dataclasses.field instance variable to a properly typed ClassVar[bool], fixing a bug where get_type_hints would previously not filter it out. |
| reflex/init.py | Updates mapping dict type annotations from dict[str, list[str]] to lazy_loader.SubmodAttrsType, and fixes _COMPONENT_NAME_TO_PATH to use comp_alias/comp_path helpers for correct alias resolution. |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart TD
A["SubmodAttrsType mapping\n(module → list of str | tuple)"] --> B["lazy_loader.attach()"]
B --> C{"comp is tuple?"}
C -- yes --> D["comp_alias → public name\ncomp_name → internal attr"]
C -- no --> E["public name = internal attr"]
D --> F["alias_to_module_and_attr\n{public_name: (module, internal_attr)}"]
E --> F
F --> G["__getattr__(name)"]
G --> H["import package.module\ngetattr(submod, internal_attr)"]
subgraph pyi_generator["pyi_generator: prop ordering"]
I["all_classes = clz.__mro__\n(derived → base)"] --> J["reversed(all_classes)\n= base → derived"]
J --> K["get_type_hints(target_class)"]
K --> L["reversed(type_hints.items())"]
L --> M["kwargs.appendleft()\n→ derived props first"]
end
subgraph docs["pyi_generator: docstring sources"]
N["_get_class_prop_comments(clz)\n(source comments)"]
O["clz._fields[prop].doc\n(field doc string)"]
P["DEFAULT_TRIGGERS_AND_DESC\n(event handler docs)"]
N --> Q["props_comments dict"]
O --> Q
P --> R["_maybe_default_event_handler_docstring\n(fallback)"]
Q --> S["_generate_docstrings output"]
R --> S
end
Comments Outside Diff (1)
-
packages/reflex-base/src/reflex_base/utils/pyi_generator.py, line 496-502 (link)Base-class docs overwrite derived-class docs
clzsfollows MRO order (most-derived first), so this loop visitsDerivedClassbeforeBaseClass. Because the dict is unconditionally overwritten on every iteration, any prop that appears on both classes ends up with the base class documentation — the opposite of the expected behaviour.Use
setdefaultto give priority to the most-specific (first-seen) class:Note: if you intentionally want
field.docto win over source comments for the same class (current inner-loop behaviour), you can keep thefield.docbranch as an assignment but guard it withif prop not in props_comments.
Reviews (1): Last reviewed commit: "exclude ClassVar from prop/docstring gen..." | Re-trigger Greptile
Instead, the pyi_generator just pops out the `State` attribute from `__annotations__` because it was in EXCLUDED_PROPS anyway.
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
get_type_hintsto resolve Component type hintsrx.momentdateprop andrx.listshadowing the builtinlistin its modulecompare the IDE hinting between
mainand this branch and you will surely notice a difference