diff --git a/news/6760.bugfix.md b/news/6760.bugfix.md new file mode 100644 index 00000000000..2e7810067ac --- /dev/null +++ b/news/6760.bugfix.md @@ -0,0 +1 @@ +Fix `reflex component build` crashing with `AttributeError` on Python 3.10 and 3.11 by delegating recursive stub generation to the Python 3.10-compatible `PyiGenerator` scanner. diff --git a/reflex/custom_components/custom_components.py b/reflex/custom_components/custom_components.py index 6b612a0628f..6f7acbdb288 100644 --- a/reflex/custom_components/custom_components.py +++ b/reflex/custom_components/custom_components.py @@ -615,13 +615,15 @@ def _make_pyi_files(): """Create pyi files for the custom component.""" from reflex_base.utils.pyi_generator import PyiGenerator - for top_level_dir in Path.cwd().iterdir(): - if not top_level_dir.is_dir() or top_level_dir.name.startswith("."): - continue - for dir, _, _ in top_level_dir.walk(): - if "__pycache__" in dir.name: - continue - PyiGenerator().scan_all([dir]) + targets = [ + path + for path in Path.cwd().iterdir() + if path.is_dir() + and not path.name.startswith(".") + and path.name != "__pycache__" + ] + if targets: + PyiGenerator().scan_all(targets) def _run_build(): diff --git a/tests/units/custom_components/__init__.py b/tests/units/custom_components/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/units/custom_components/test_custom_components.py b/tests/units/custom_components/test_custom_components.py new file mode 100644 index 00000000000..2a8ce8abee6 --- /dev/null +++ b/tests/units/custom_components/test_custom_components.py @@ -0,0 +1,44 @@ +"""Unit tests for reflex/custom_components/custom_components.py.""" + +from __future__ import annotations + +from pathlib import Path + +from reflex.custom_components import custom_components + + +def test_make_pyi_files_delegates_recursive_scan_without_path_walk( + monkeypatch, tmp_path: Path +): + """``_make_pyi_files`` delegates recursive scanning without ``Path.walk``. + + ``pathlib.Path.walk`` only exists on Python 3.12+, but Reflex supports 3.10 + and 3.11 too, so the build must not depend on it. ``Path.walk`` is removed + here to reproduce the 3.10/3.11 environment on any interpreter. + + Args: + monkeypatch: The pytest monkeypatch fixture. + tmp_path: A temporary directory used as the project root. + """ + package = tmp_path / "my_component" + nested = package / "sub" + nested.mkdir(parents=True) + (package / "__pycache__").mkdir() + (tmp_path / "__pycache__").mkdir() + (tmp_path / ".hidden").mkdir() + + scans: list[list[Path]] = [] + + class _RecordingGenerator: + def scan_all(self, targets, *_args, **_kwargs): + scans.append([Path(target) for target in targets]) + + monkeypatch.setattr( + "reflex_base.utils.pyi_generator.PyiGenerator", _RecordingGenerator + ) + monkeypatch.delattr(Path, "walk", raising=False) + monkeypatch.chdir(tmp_path) + + custom_components._make_pyi_files() + + assert scans == [[package]]