Skip to content

pyi_generator regression tests#6318

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

pyi_generator regression tests#6318
masenf merged 8 commits intomainfrom
masenf/pyi-generator-regression-test

Conversation

@masenf
Copy link
Copy Markdown
Collaborator

@masenf masenf commented Apr 10, 2026

There are some issues present in the dataset that represent real issues in the pyi_generator that need to be fixed. Wanted to get the regression test in before fixing those, so we can see how the files change in relation to the fixes.

@codspeed-hq
Copy link
Copy Markdown

codspeed-hq bot commented Apr 10, 2026

Merging this PR will not alter performance

✅ 9 untouched benchmarks


Comparing masenf/pyi-generator-regression-test (b744122) with main (6559413)

Open in CodSpeed

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Apr 10, 2026

Greptile Summary

Adds a golden-file regression test suite for pyi_generator, complemented by focused unit tests for individual translation functions. The dataset covers 11 scenarios (event handlers, inheritance, namespaces, Literal types, etc.) and the --update CLI makes it easy to regenerate golden files when the generator changes.

  • Missing inverse coverage: test_no_extra_golden_files verifies that existing goldens have stubs, but no test checks that every generated stub has a golden — adding a dataset file without running --update would silently pass CI.
  • Cleanup gap in _run_generator: generated .pyi files are removed inside the loop body, so an exception partway through leaves orphaned files in DATASET_DIR with no .gitignore guard.

Confidence Score: 4/5

Safe to merge with the noted test gap addressed; the infrastructure is solid but the missing inverse check leaves a real CI blind-spot.

One P1 finding (no test catches a dataset file whose stub has no golden), two P2 findings (cleanup gap and incomplete --check CLI). The P1 gap means the intended regression contract isn't fully enforced and could be silently bypassed by future contributors.

tests/units/reflex_base/utils/pyi_generator/test_regression.py — test_no_extra_golden_files and _run_generator cleanup logic

Important Files Changed

Filename Overview
tests/units/reflex_base/utils/pyi_generator/test_regression.py Main regression harness; missing inverse check (no golden for stub) and no cleanup guard on exception in _run_generator
tests/units/reflex_base/utils/pyi_generator/test_unit.py Comprehensive unit tests for individual pyi_generator functions; minor type annotation issue in _generate_stub_from_source
tests/units/reflex_base/utils/pyi_generator/main.py Simple CLI entry point delegating to main() in test_regression; no issues
tests/units/reflex_base/utils/pyi_generator/dataset/simple_component.py Well-documented dataset fixture covering basic prop types and method visibility rules
tests/units/reflex_base/utils/pyi_generator/dataset/classvar_and_private.py Dataset fixture targeting ClassVar and private attribute edge cases; intentionally captures known generator bugs
pyproject.toml Adds ruff extend-exclude for golden/ directory so generated .pyi stubs aren't linted; appropriate change
tests/units/reflex_base/utils/pyi_generator/golden/simple_component.pyi Golden reference for simple_component; intentionally captures known bug (SOME_CONSTANT preserved instead of stripped)

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[pytest collects test_regression.py] --> B[_existing_golden_cases\nscans golden/*.pyi at collection time]
    B --> C{Golden files exist?}
    C -- No --> D[No test_pyi_golden cases collected]
    C -- Yes --> E[Parametrize test_pyi_golden\nper golden file]

    E --> F[generated_stubs fixture\nscope=module]
    F --> G[PyiGenerator.scan_all\nwrites .pyi into DATASET_DIR]
    G --> H[Read each .pyi to results dict]
    H --> I[pyi_path.unlink for each file]
    I --> J[Return source to content map]

    J --> K[test_pyi_golden\ncompare normalized stub vs golden]
    J --> L[test_no_extra_golden_files\nexisting goldens subset of expected_goldens]

    subgraph gap [Missing test]
      M[test_all_generated_stubs_have_goldens\ngenerated_stubs keys subset of golden files on disk]
    end

    J -.->|not implemented| M

    N[CLI: python -m ... --update] --> O[update_golden_files\ncalls _run_generator]
    O --> P[Write normalized stubs to golden/]
    P --> Q[Remove stale golden files]
Loading

Reviews (1): Last reviewed commit: "fix ruff issues in dataset" | Re-trigger Greptile

Comment on lines +177 to +186
"""Ensure no golden files exist without corresponding dataset sources.

Args:
generated_stubs: The mapping of dataset source paths to generated .pyi content.
"""
expected_goldens = {_golden_path_for(s) for s in generated_stubs}
for existing in GOLDEN_DIR.rglob("*.pyi"):
assert existing in expected_goldens, (
f"Stale golden file {existing.relative_to(_HERE)} has no dataset source. "
f"Run `{_UPDATE_CMD}` to clean up."
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 No test for stubs missing a golden file

test_no_extra_golden_files checks the one direction (every existing golden has a stub), but the inverse is never checked: when a new dataset file generates a stub and no golden yet exists, it simply doesn't appear in GOLDEN_DIR.rglob("*.pyi") and no assertion ever fires. A developer could add a dataset file, skip running --update, and CI would stay green while the new scenario goes untested.

def test_all_generated_stubs_have_golden_files(generated_stubs: dict[Path, str]):
    """Ensure every generated stub has a corresponding golden file."""
    for source_path in sorted(generated_stubs):
        golden_path = _golden_path_for(source_path)
        assert golden_path.exists(), (
            f"No golden file for {source_path.name}. "
            f"Run `{_UPDATE_CMD}` to generate."
        )

Comment on lines +68 to +82
Returns:
A mapping from dataset source .py files to their generated .pyi content.
"""
gen = PyiGenerator()
gen.scan_all([str(DATASET_DIR)])

results: dict[Path, str] = {}
for pyi_str, _hash in gen.written_files:
pyi_path = Path(pyi_str)
source_path = pyi_path.with_suffix(".py")
results[source_path] = pyi_path.read_text()
pyi_path.unlink(missing_ok=True)
return results


Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 No cleanup guard for temporary .pyi files on failure

_run_generator calls pyi_path.unlink(missing_ok=True) inside the loop, so if an exception fires after scan_all but before all unlink calls complete (e.g., a read error on one file), the remaining generated .pyi files stay in DATASET_DIR indefinitely. There's also no .gitignore in the dataset directory to prevent accidentally staging them. A try/finally would guarantee cleanup:

pyi_files: list[Path] = []
try:
    for pyi_str, _hash in gen.written_files:
        pyi_path = Path(pyi_str)
        pyi_files.append(pyi_path)
        source_path = pyi_path.with_suffix(".py")
        results[source_path] = pyi_path.read_text()
finally:
    for pyi_path in pyi_files:
        pyi_path.unlink(missing_ok=True)

Comment on lines +196 to +222
)
parser.add_argument(
"--check",
action="store_true",
help="Check that generated stubs match golden files (CI mode).",
)
args = parser.parse_args()

if args.update:
print(f"Regenerating golden files from {DATASET_DIR} ...")
updated = update_golden_files()
print(
f"\nDone. {len(updated)} file(s) updated in {GOLDEN_DIR.relative_to(_HERE)}/"
)
print("Review the changes and commit them with your PR.")
elif args.check:
print("Checking generated stubs against golden files...")
generated = _run_generator()
failures = []
for source_path, content in sorted(generated.items()):
golden_path = _golden_path_for(source_path)
if not golden_path.exists():
continue
if _normalize_stub(content) != golden_path.read_text():
failures.append(f" {source_path.name}: differs from golden")
if failures:
print("FAILED:")
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 --check mode is silent about stale golden files

The --check CLI path skips missing goldens (if not golden_path.exists(): continue) but never reports the inverse: golden files that have no corresponding generated stub. test_no_extra_golden_files catches stale goldens in pytest, but the --check mode used in CI-style runs doesn't replicate this check, so the two modes can diverge.

@masenf masenf merged commit c979b46 into main Apr 10, 2026
40 checks passed
@masenf masenf deleted the masenf/pyi-generator-regression-test branch April 10, 2026 18:11
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