Conversation
masenf
commented
Apr 10, 2026
- use collections.abc instead of typing
- replace typing imports with collections.abc where relevant (generated stubs standardize on collections.abc)
- parse post-prop docstrings
Where possible, use new collections.abc imports instead of importing from typing. If the same-named typing imports are present, they will be removed.
Include plain names that are present in the DEFAULT_IMPORTS
This allows old names to be automatically translated to new names in the stub files. For example, this allows the actual module to write `typing.Callable` and have it turn into `collections.abc.Callable`
Greptile SummaryThis PR migrates
Confidence Score: 4/5Safe to merge after fixing the closing triple-quote or-expression bug; the loop-ordering issue is minor but worth addressing. One P1 bug (incorrect end_text when closing triple-quote is on its own line) and one P2 ordering issue (def-boundary check before in_docstring guard) in the new docstring parser. The existing tests don't cover the closing-quote-on-its-own-line case, so the bug would be silently shipped. All other changes look correct. packages/reflex-base/src/reflex_base/utils/pyi_generator.py — specifically the in_docstring end-detection block (lines 329-342) and the loop guard ordering (lines 320-326). Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[Next source line] --> B{in_docstring?}
B -- Yes --> C{closing triple-quote found?}
C -- Yes --> D[Extract end_text via partition OR-chain]
D --> E[Save to props_comments]
E --> F[Reset in_docstring and last_prop]
C -- No --> G[Append line to docstring_lines]
B -- No --> H{re.search def in line?}
H -- Yes --> I[Break loop]
H -- No --> J{empty line?}
J -- Yes --> K[Clear comments and last_prop]
J -- No --> L{line starts with hash?}
L -- Yes --> M[Append to comments]
L -- No --> N{regex matches word-colon?}
N -- No --> O[Clear last_prop]
N -- Yes --> P[Extract prop name]
P --> Q[Save comments to props_comments]
Q --> R[Set last_prop equals prop]
R --> S{last_prop set and line starts with triple-quote?}
S -- Yes --> T{closing quote on same line?}
T -- Yes --> U[Single-line docstring stored]
T -- No --> V[Set in_docstring = True]
style D fill:#f99,stroke:#c00
style H fill:#f99,stroke:#c00
Reviews (1): Last reviewed commit: "pyi_generator: Recognize post-prop docst..." | Re-trigger Greptile |
| if '"""' in stripped or "'''" in stripped: | ||
| # End of multi-line docstring. | ||
| end_text = stripped.partition('"""')[0] or stripped.partition("'''")[0] | ||
| if end_text: | ||
| docstring_lines.append(end_text.strip()) | ||
| if last_prop and docstring_lines: | ||
| props_comments[last_prop] = tuple(docstring_lines) | ||
| in_docstring = False |
There was a problem hiding this comment.
Triple-quote on its own line incorrectly appends quotes to docstring content
When a multi-line docstring ends with the closing triple-quote alone on a line (e.g. """), stripped.partition('"""')[0] returns "" (empty, falsy), so the or falls through to stripped.partition("'''")[0], which returns '"""' (the whole string, since ''' is absent). That truthy value gets appended to docstring_lines, poisoning the stored comment with literal triple-quote characters.
# stripped = '"""' (closing quote on its own line)
stripped.partition('"""')[0] # → '' (falsy)
stripped.partition("'''")[0] # → '"""' (truthy) ← appended to docstring_lines by mistakeFix by selecting the partition based on which quote type is actually present:
| if '"""' in stripped or "'''" in stripped: | |
| # End of multi-line docstring. | |
| end_text = stripped.partition('"""')[0] or stripped.partition("'''")[0] | |
| if end_text: | |
| docstring_lines.append(end_text.strip()) | |
| if last_prop and docstring_lines: | |
| props_comments[last_prop] = tuple(docstring_lines) | |
| in_docstring = False | |
| if in_docstring: | |
| if '"""' in stripped or "'''" in stripped: | |
| # End of multi-line docstring. | |
| if '"""' in stripped: | |
| end_text = stripped.partition('"""')[0].strip() | |
| else: | |
| end_text = stripped.partition("'''")[0].strip() | |
| if end_text: | |
| docstring_lines.append(end_text) | |
| if last_prop and docstring_lines: | |
| props_comments[last_prop] = tuple(docstring_lines) | |
| in_docstring = False | |
| docstring_lines = [] | |
| last_prop = "" |
| for line in _get_source(clz).splitlines(): | ||
| reached_functions = re.search(r"def ", line) | ||
| if reached_functions: | ||
| # We've reached the functions, so stop. | ||
| break | ||
|
|
||
| stripped = line.strip() |
There was a problem hiding this comment.
reached_functions check fires inside an open docstring
The def search runs on every line before the in_docstring guard is consulted. A prop docstring that mentions def in its prose (e.g., "Defines the callback spec.") will prematurely break the loop, silently dropping all subsequent prop comments. The in_docstring block should be evaluated first so that docstring content is never mistaken for a function boundary.
| for line in _get_source(clz).splitlines(): | |
| reached_functions = re.search(r"def ", line) | |
| if reached_functions: | |
| # We've reached the functions, so stop. | |
| break | |
| stripped = line.strip() | |
| for line in _get_source(clz).splitlines(): | |
| stripped = line.strip() | |
| # Handle triple-quoted docstrings after prop definitions. | |
| if in_docstring: | |
| if '"""' in stripped or "'''" in stripped: | |
| # End of multi-line docstring. | |
| end_text = stripped.partition('"""')[0] or stripped.partition("'''")[0] | |
| if end_text: | |
| docstring_lines.append(end_text.strip()) | |
| if last_prop and docstring_lines: | |
| props_comments[last_prop] = tuple(docstring_lines) | |
| in_docstring = False | |
| docstring_lines = [] | |
| last_prop = "" | |
| else: | |
| docstring_lines.append(stripped) | |
| continue | |
| reached_functions = re.search(r"def ", line) | |
| if reached_functions: | |
| # We've reached the functions, so stop. | |
| break |