Skip to content

fix: use os.walk to build custom components on Python 3.10 and 3.11#6760

Open
anxkhn wants to merge 1 commit into
reflex-dev:mainfrom
anxkhn:patch-1
Open

fix: use os.walk to build custom components on Python 3.10 and 3.11#6760
anxkhn wants to merge 1 commit into
reflex-dev:mainfrom
anxkhn:patch-1

Conversation

@anxkhn

@anxkhn anxkhn commented Jul 14, 2026

Copy link
Copy Markdown
Contributor
### All Submissions:

- [x] Have you followed the guidelines stated in CONTRIBUTING.md file?
- [x] Have you checked to ensure there aren't any other open Pull Requests for the desired change?

### Type of change

- [x] Bug fix (non-breaking change which fixes an issue)

### Changes To Core Features:

- [x] Have you added an explanation of what your changes do and why you'd like us to include them?
- [x] Have you written new tests for your core changes, as applicable?
- [x] Have you successfully ran tests with your changes locally?

## What

`reflex component build` crashes on Python 3.10 and 3.11 with:

AttributeError: 'PosixPath' object has no attribute 'walk'


`_make_pyi_files` in `reflex/custom_components/custom_components.py` generates the
`.pyi` stubs for a custom component by walking its source tree. That loop used
`pathlib.Path.walk()`, which was only added in Python 3.12. Since the project
supports `requires-python = ">=3.10,<4.0"`, every custom-component author on 3.10
or 3.11 hits a hard crash when building their component.

## Why / root cause

`Path.walk()` does not exist before Python 3.12, and there is no version guard or
fallback around the call. The sibling helper `_walk_files` in
`packages/reflex-base/src/reflex_base/utils/pyi_generator.py` already avoids
`Path.walk()` for exactly this reason (its comment notes it can only be used once
the floor is 3.12), so this second call site had regressed relative to the rest
of the codebase.

## Fix

Switch the walk to `os.walk()` (`os` is already imported in the module) and read
the directory basename via `Path(dir).name` for the `__pycache__` skip.
`PyiGenerator.scan_all` normalizes its targets with `Path(...)`, so the directory
strings that `os.walk` yields work unchanged. This mirrors the existing
`_walk_files` approach and keeps the behavior identical on 3.12+.

## Tests

Added `tests/units/custom_components/test_custom_components.py`. It deletes
`Path.walk` via `monkeypatch` to reproduce the 3.10/3.11 environment on any
interpreter (so the guard also holds on 3.12+, where a naive test would silently
pass), then asserts `_make_pyi_files` recurses into component directories,
skips `__pycache__`, and skips top-level dotdirs, without raising.

- Fails before the fix with the reported `AttributeError`; passes after.
- `uv run pytest tests/units` -> 6617 passed, 18 skipped (no regressions).
- `uv run ruff check .` / `uv run ruff format --check` clean;
  `uv run pyright reflex tests` -> 0 errors.

Towncrier / CHANGELOG fragment

Committed news/6714.bugfix.md (placeholder number 6714) to satisfy the
CHANGELOG CI gate (.github/workflows/changelog.yml), which requires a towncrier
fragment for any change under reflex/. Verified locally:
uv run towncrier check --config pyproject.toml --dir . --compare-with origin/main
-> exit 0.

ACTION AFTER OPENING THE PR: rename news/6714.bugfix.md to
news/<real-PR-number>.bugfix.md and amend/commit. 6714 was the plausible next
number at draft time (latest activity was #6711-#6713), but the true PR number
is only known once the PR exists. Note news/6710.bugfix.md already exists in the
tree, so if the real PR lands on a number already taken by another fragment,
pick the actual PR number regardless.

`reflex component build` generates .pyi files for the custom component by
walking its source tree in `_make_pyi_files`. That loop used
`pathlib.Path.walk()`, which was only added in Python 3.12, so on Python
3.10 and 3.11 (both supported by `requires-python = ">=3.10,<4.0"`) the
build crashed with `AttributeError: 'PosixPath' object has no attribute
'walk'`.

Switch the walk to `os.walk()`, mirroring the existing `_walk_files`
helper in reflex-base whose comment already notes `Path.walk()` cannot be
used until the floor is 3.12. `PyiGenerator.scan_all` accepts str or Path
targets, so the yielded directory strings work unchanged.

Signed-off-by: Anas Khan <83116240+anxkhn@users.noreply.github.com>
@anxkhn anxkhn requested a review from a team as a code owner July 14, 2026 07:47
@greptile-apps

greptile-apps Bot commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR restores custom-component builds on Python 3.10 and 3.11. The main changes are:

  • Replaces Path.walk() with os.walk() during stub generation.
  • Adds tests for recursive scanning and excluded directories.
  • Adds a changelog fragment for the fix.

Confidence Score: 5/5

This looks safe to merge.

  • os.walk() avoids the unsupported Python API.
  • PyiGenerator.scan_all converts string targets to Path objects.
  • The added test exercises the affected build helper.
  • No blocking issues were found in the changed code.

Important Files Changed

Filename Overview
reflex/custom_components/custom_components.py Uses os.walk() while preserving the target handling expected by PyiGenerator.scan_all.
tests/units/custom_components/test_custom_components.py Tests compatibility without Path.walk, recursive scanning, and directory exclusions.
tests/units/custom_components/init.py Initializes the new custom-components unit-test package.
news/6714.bugfix.md Documents the supported-Python build fix.

Reviews (2): Last reviewed commit: "fix: use os.walk to build custom compone..." | Re-trigger Greptile

@codspeed-hq

codspeed-hq Bot commented Jul 14, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 26 untouched benchmarks
⏩ 8 skipped benchmarks1


Comparing anxkhn:patch-1 (72a7764) with main (65a2889)2

Open in CodSpeed

Footnotes

  1. 8 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

  2. No successful run was found on main (127e4d8) during the generation of this report, so 65a2889 was used instead as the comparison base. There might be some changes unrelated to this pull request in this report.

@Alek99 Alek99 closed this Jul 15, 2026
@Alek99 Alek99 reopened this Jul 15, 2026
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