Skip to content

Usability improvements for generated type hints#6300

Merged
masenf merged 10 commits intomainfrom
masenf/pyi-generator-hints
Apr 8, 2026
Merged

Usability improvements for generated type hints#6300
masenf merged 10 commits intomainfrom
masenf/pyi-generator-hints

Conversation

@masenf
Copy link
Copy Markdown
Collaborator

@masenf masenf commented Apr 8, 2026

  • unbreak docstring formatting by doing ruff format prior to ruff check --fix
  • include current component props before inherited props
  • pull documentation from ComponentField.doc and DEFAULT_TRIGGERS_AND_DESC
  • use get_type_hints to resolve Component type hints
    • required some fixes for rx.moment date prop and rx.list shadowing the builtin list in its module
  • properly implement the lazy import alias system that was described in the docstring but for all intents non-functional

compare the IDE hinting between main and this branch and you will surely notice a difference

masenf added 6 commits April 7, 2026 21:27
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
@codspeed-hq
Copy link
Copy Markdown

codspeed-hq bot commented Apr 8, 2026

Merging this PR will not alter performance

✅ 9 untouched benchmarks


Comparing masenf/pyi-generator-hints (7834651) with main (2f8b816)

Open in CodSpeed

@masenf
Copy link
Copy Markdown
Collaborator Author

masenf commented Apr 8, 2026

@greptile-apps please re-review this PR

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Apr 8, 2026

Greptile Summary

This PR improves the quality and correctness of generated type hint stub files (.pyi) for Reflex components, fixing several usability issues that were impacting IDE auto-complete and documentation display.

Key changes:

  • Docstring formatting fix: ruff format now runs before ruff check --fix, fixing previously broken docstring output.
  • Props ordering: Current component props now appear before inherited props in generated stubs by using a deque + appendleft pattern with reversed MRO traversal.
  • Documentation from multiple sources: The generator now pulls from both ComponentField.doc and DEFAULT_TRIGGERS_AND_DESC for richer generated docstrings.
  • get_type_hints adoption: Replaced raw __annotations__ access with typing.get_type_hints() to properly resolve ForwardRefs and filter ClassVar annotations.
  • rx.moment date type fix: Changed from datetime import date, ... to import datetime to avoid the module-level name date shadowing the builtin when resolving type hints.
  • rx.list builtin shadowing fix: Removed the list = list_ns = List() assignment to avoid shadowing Python's builtin list, and wired up the mapping to use (\"list_ns\", \"list\") tuple alias convention.
  • Lazy import alias system: Properly implemented tuple-based (internal_name, public_alias) aliases in lazy_loader.attach, replacing the previously non-functional implementation.
  • Type annotations: Introduced SubmodAttrsType to standardize and improve typing of mapping dictionaries across multiple packages.

Confidence Score: 5/5

Safe 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).

Vulnerabilities

No security concerns identified. The PR is limited to build-time stub generation tooling and lazy-import wiring — no user input is processed, no secrets are handled, and no network calls are made.

Important Files Changed

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
Loading

Comments Outside Diff (1)

  1. packages/reflex-base/src/reflex_base/utils/pyi_generator.py, line 496-502 (link)

    P2 Base-class docs overwrite derived-class docs

    clzs follows MRO order (most-derived first), so this loop visits DerivedClass before BaseClass. 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 setdefault to give priority to the most-specific (first-seen) class:

    Note: if you intentionally want field.doc to win over source comments for the same class (current inner-loop behaviour), you can keep the field.doc branch as an assignment but guard it with if prop not in props_comments.

Reviews (1): Last reviewed commit: "exclude ClassVar from prop/docstring gen..." | Re-trigger Greptile

masenf and others added 2 commits April 8, 2026 10:24
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>
adhami3310
adhami3310 previously approved these changes Apr 8, 2026
@masenf masenf merged commit 8a35867 into main Apr 8, 2026
40 checks passed
@masenf masenf deleted the masenf/pyi-generator-hints branch April 8, 2026 18:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants