Exclude old-style imports when they will be replaced with subpackage imports#6294
Exclude old-style imports when they will be replaced with subpackage imports#6294
Conversation
…imports Escape hatch for packages outside of the reflex repo to generate __init__.py stubs without assuming "components" is "reflex_components_core"
Greptile SummaryThis PR adds an
Two issues are worth addressing: (1) the first import a file sees bypasses the exclusion check due to the Confidence Score: 4/5Mostly safe, but the first-import ordering bug can produce duplicate symbols in generated .pyi stubs. One P1 logic issue (excluded imports bypassed for the first import) warrants a 4/5 — the generated stubs could end up with duplicate EventHandler/Var/etc. definitions in files whose first real import is one of the excluded pre-0.9 paths. The P2 is a consistency nit that is unlikely to bite in practice but should be addressed for correctness of the external-package escape hatch. packages/reflex-base/src/reflex_base/utils/pyi_generator.py — specifically visit_Import (inserted_imports guard ordering) and _rewrite_component_import (unguarded startswith branch). Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[visit_Import called] --> B{inserted_imports?}
B -- False --> C[Set inserted_imports=True]
C --> D[Generate default_imports]
D --> E[Return default_imports + node]
E --> Z[⚠️ Exclusion check SKIPPED for first import]
B -- True --> F{node is ImportFrom
and module in EXCLUDED_IMPORTS?}
F -- Yes --> G[Filter node.names]
G --> H{node.names empty?}
H -- Yes --> I[Return empty list
drop import]
H -- No --> J[Return filtered node]
F -- No --> K[Return node unchanged]
subgraph _rewrite_component_import
R1{is_reflex_package
AND module == components} -- Yes --> R2[Return reflex_components_core]
R1 -- No --> R3{module.startswith
components. ⚠️ no is_reflex_package guard}
R3 -- Yes --> R4[Lookup in _COMPONENT_SUBPACKAGE_TARGETS]
R4 -- Found --> R5[Return absolute target]
R4 -- Not found --> R6[Return .module]
R3 -- No --> R6
end
|
| @@ -1357,7 +1381,7 @@ def _rewrite_component_import(module: str) -> str: | |||
| return f".{module}" | |||
There was a problem hiding this comment.
components.* subpackage lookup not guarded by is_reflex_package
The PR correctly guards the module == "components" branch with is_reflex_package, but the immediately-following if module.startswith("components."): branch on line 1371 is left unguarded. If a non-reflex package's _SUBMOD_ATTRS happens to contain a key like "components.recharts" (coincidentally matching an entry in _COMPONENT_SUBPACKAGE_TARGETS), it would be silently rewritten to "reflex_components_recharts", producing a broken import for that package.
For consistency, the same guard should wrap the startswith branch:
| if is_reflex_package and module == "components": | |
| # "components": ["el", "radix", ...] — these are re-exported submodules. | |
| # Can't map to a single package, but the pyi generator handles each attr individually. | |
| return "reflex_components_core" | |
| if is_reflex_package and module.startswith("components."): | |
| rest = module[len("components."):] | |
| # Try progressively deeper matches (e.g. "datadisplay.code" before "datadisplay"). | |
| parts = rest.split(".") | |
| for depth in range(min(len(parts), 2), 0, -1): | |
| key = ".".join(parts[:depth]) | |
| target = _COMPONENT_SUBPACKAGE_TARGETS.get(key) | |
| if target is not None: | |
| remainder = ".".join(parts[depth:]) | |
| return f"{target}.{remainder}" if remainder else target | |
| return f".{module}" |
* better visit_Import handling to avoid duplicate imports and missing default imports * add is_reflex_package escape hatch to second condition in _rewrite_component_import
Includes escape hatch for packages outside of the reflex repo to generate init.py stubs without assuming "components" is "reflex_components_core"