From 4dbab9d7dc5f7e815c62d24afdce5587d9d9868b Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 9 Jun 2026 21:49:07 +0000 Subject: [PATCH 1/2] Hash pyi files after ruff post-processing in pyi_generator pyi_hashes.json entries were computed as each stub was written, before scan_all ran `ruff format` and `ruff check --fix` over the generated files. Since ruff materially rewrites the stubs (quoting, wrapping, import fixes), the registry recorded hashes of intermediate content that never exists on disk afterwards: any generator change affecting only the pre-format output flagged hash changes even when the final .pyi files were byte-for-byte identical. _scan_file now just records which .pyi files were written, and scan_all computes the md5 hashes from the final on-disk content after ruff post-processing. written_files becomes a per-instance list (it was a mutable class attribute shared across generator instances) and the dead modules/root/current_module class attributes are dropped. pyi_hashes.json is regenerated with the new scheme (one-time value churn for every entry; keys unchanged). Verified idempotent across repeated --force runs and explicit-target merge runs. --- .../news/+pyi_hashes_post_ruff.bugfix.md | 1 + .../src/reflex_base/utils/pyi_generator.py | 48 ++-- pyi_hashes.json | 244 +++++++++--------- .../utils/pyi_generator/test_hashes.py | 71 +++++ .../utils/pyi_generator/test_regression.py | 2 +- 5 files changed, 222 insertions(+), 144 deletions(-) create mode 100644 packages/reflex-base/news/+pyi_hashes_post_ruff.bugfix.md create mode 100644 tests/units/reflex_base/utils/pyi_generator/test_hashes.py diff --git a/packages/reflex-base/news/+pyi_hashes_post_ruff.bugfix.md b/packages/reflex-base/news/+pyi_hashes_post_ruff.bugfix.md new file mode 100644 index 00000000000..804c0453e38 --- /dev/null +++ b/packages/reflex-base/news/+pyi_hashes_post_ruff.bugfix.md @@ -0,0 +1 @@ +`pyi_hashes.json` entries are now computed from the final `.pyi` content after `ruff format` / `ruff check --fix` post-processing, instead of the intermediate generator output. A pyi_generator change that only affects pre-format output no longer flags hash changes for stubs whose final content is identical. diff --git a/packages/reflex-base/src/reflex_base/utils/pyi_generator.py b/packages/reflex-base/src/reflex_base/utils/pyi_generator.py index 2b7ee41fb59..89da4b9c916 100644 --- a/packages/reflex-base/src/reflex_base/utils/pyi_generator.py +++ b/packages/reflex-base/src/reflex_base/utils/pyi_generator.py @@ -1412,7 +1412,13 @@ def _path_to_module_name(path: Path) -> str: return _relative_to_pwd(path).with_suffix("").as_posix().replace("/", ".") -def _write_pyi_file(module_path: Path, source: str) -> str: +def _write_pyi_file(module_path: Path, source: str) -> None: + """Write the .pyi stub next to its source module. + + Args: + module_path: The path of the source .py module. + source: The generated stub source, without the header. + """ relpath = str(_relative_to_pwd(module_path)).replace("\\", "/") pyi_content = ( "\n".join([ @@ -1428,7 +1434,6 @@ def _write_pyi_file(module_path: Path, source: str) -> str: pyi_path = module_path.with_suffix(".pyi") pyi_path.write_text(pyi_content) logger.info(f"Wrote {relpath}") - return md5(pyi_content.encode()).hexdigest() # Mapping from component subpackage name to its target Python package. @@ -1560,14 +1565,14 @@ def _get_init_lazy_imports(mod: ModuleType, new_tree: ast.AST): return text -def _scan_file(module_path: Path) -> tuple[str, str] | None: +def _scan_file(module_path: Path) -> str | None: """Process a single Python file and generate its .pyi stub. Args: module_path: Path to the Python source file. Returns: - Tuple of (pyi_path, content_hash) or None if no stub needed. + The path of the written .pyi file, or None if no stub is needed. """ module_import = _path_to_module_name(module_path) module = importlib.import_module(module_import) @@ -1597,11 +1602,11 @@ def _scan_file(module_path: Path) -> tuple[str, str] | None: init_imports = _get_init_lazy_imports(module, new_tree) if not init_imports: return None - content_hash = _write_pyi_file(module_path, init_imports) + _write_pyi_file(module_path, init_imports) else: new_tree = StubGenerator(module, class_names).visit(ast.parse(source)) - content_hash = _write_pyi_file(module_path, ast.unparse(new_tree)) - return str(module_path.with_suffix(".pyi").resolve()), content_hash + _write_pyi_file(module_path, ast.unparse(new_tree)) + return str(module_path.with_suffix(".pyi").resolve()) class PyiGenerator: @@ -1609,10 +1614,9 @@ class PyiGenerator: generate the appropriate stub. """ - modules: list = [] - root: str = "" - current_module: Any = {} - written_files: list[tuple[str, str]] = [] + def __init__(self) -> None: + """Initialize the generator with no written .pyi files.""" + self.written_files: list[str] = [] def _scan_files(self, files: list[Path]): max_workers = min(multiprocessing.cpu_count() or 1, len(files), 8) @@ -1714,19 +1718,22 @@ def scan_all( self._scan_files(file_targets) - file_paths, hashes = ( - [f[0] for f in self.written_files], - [f[1] for f in self.written_files], - ) + file_paths = self.written_files # Fix generated pyi files with ruff. if file_paths: subprocess.run(["ruff", "format", *file_paths]) subprocess.run(["ruff", "check", "--fix", *file_paths]) - if use_json: - if file_paths and prune_stale: - file_paths = list(map(Path, file_paths)) + if use_json and file_paths: + file_paths = list(map(Path, file_paths)) + # Hash only after ruff post-processing, so the registry reflects the + # final on-disk content rather than the intermediate generator output. + hashes = [ + md5(file_path.read_text().encode()).hexdigest() + for file_path in file_paths + ] + if prune_stale: top_dir = file_paths[0].parent for file_path in file_paths: file_parent = file_path.parent @@ -1764,8 +1771,7 @@ def scan_all( ) + "\n", ) - elif file_paths: - file_paths = list(map(Path, file_paths)) + else: pyi_hashes_parent = file_paths[0].parent while ( not pyi_hashes_parent.samefile(pyi_hashes_parent.parent) @@ -1777,7 +1783,7 @@ def scan_all( if pyi_hashes_file.exists(): pyi_hashes = json.loads(pyi_hashes_file.read_text()) for file_path, hashed_content in zip( - file_paths, hashes, strict=False + file_paths, hashes, strict=True ): formatted_path = file_path.relative_to( pyi_hashes_parent diff --git a/pyi_hashes.json b/pyi_hashes.json index bb44c790e32..19338d7ce91 100644 --- a/pyi_hashes.json +++ b/pyi_hashes.json @@ -1,124 +1,124 @@ { - "packages/reflex-components-code/src/reflex_components_code/code.pyi": "6ef91a4a4976e66b2761539e16d4f28e", - "packages/reflex-components-code/src/reflex_components_code/shiki_code_block.pyi": "d3e0c33fdc34f5c154ac387d550c0d29", - "packages/reflex-components-core/src/reflex_components_core/__init__.pyi": "82b29d23f2490161d42fd21021bd39c3", - "packages/reflex-components-core/src/reflex_components_core/base/__init__.pyi": "7009187aaaf191814d031e5462c48318", - "packages/reflex-components-core/src/reflex_components_core/base/app_wrap.pyi": "ecccfd8a9b0e8b2f4128ff13ff27a9da", - "packages/reflex-components-core/src/reflex_components_core/base/body.pyi": "2535814d409e5feaf57da63dcf0abeaf", - "packages/reflex-components-core/src/reflex_components_core/base/document.pyi": "a2e67a9814dc61853ca2299d9d9c698d", - "packages/reflex-components-core/src/reflex_components_core/base/error_boundary.pyi": "59170074a1a228ce58685f3f207954f2", - "packages/reflex-components-core/src/reflex_components_core/base/fragment.pyi": "e4cbfc46eabb904596be4372392add35", - "packages/reflex-components-core/src/reflex_components_core/base/link.pyi": "005866cf4d1cc8ac7693ed6baeca2289", - "packages/reflex-components-core/src/reflex_components_core/base/meta.pyi": "0cfa2d8c52321ce7440e887d03007d5b", - "packages/reflex-components-core/src/reflex_components_core/base/script.pyi": "bfc7fb609b822f597d1141595f8090fe", - "packages/reflex-components-core/src/reflex_components_core/base/strict_mode.pyi": "8ee129808abb4389cbd77a1736190eae", - "packages/reflex-components-core/src/reflex_components_core/core/__init__.pyi": "dd5142b3c9087bf2bf22651adf6f2724", - "packages/reflex-components-core/src/reflex_components_core/core/auto_scroll.pyi": "918dfad4d5925addd0f741e754b3b076", - "packages/reflex-components-core/src/reflex_components_core/core/banner.pyi": "6040fbada9b96c55637a9c8cc21a5e10", - "packages/reflex-components-core/src/reflex_components_core/core/clipboard.pyi": "e3950e0963a6d04299ff58294687e407", - "packages/reflex-components-core/src/reflex_components_core/core/debounce.pyi": "58138b5f1d5901839729d839620ea4da", - "packages/reflex-components-core/src/reflex_components_core/core/helmet.pyi": "7fd81a99bde5b0ff94bb52523597fd5c", - "packages/reflex-components-core/src/reflex_components_core/core/html.pyi": "753d6ae315369530dad450ed643f5be6", - "packages/reflex-components-core/src/reflex_components_core/core/sticky.pyi": "ba60a7d9cba75b27a1133bd63a9fbd59", - "packages/reflex-components-core/src/reflex_components_core/core/upload.pyi": "0810ae4f1aa3c8fcaa228e7555c59f9a", - "packages/reflex-components-core/src/reflex_components_core/core/window_events.pyi": "5e1dcb1130bc8af282783fae329ae6a6", - "packages/reflex-components-core/src/reflex_components_core/datadisplay/__init__.pyi": "c96fed4da42a13576d64f84e3c7cb25c", - "packages/reflex-components-core/src/reflex_components_core/el/__init__.pyi": "f09129ddefb57ab4c7769c86dc9a3153", - "packages/reflex-components-core/src/reflex_components_core/el/element.pyi": "ff68d843c5987d3f0d773a6367eb9c63", - "packages/reflex-components-core/src/reflex_components_core/el/elements/__init__.pyi": "e6c845f2f29eb079697a2e31b0c2f23a", - "packages/reflex-components-core/src/reflex_components_core/el/elements/base.pyi": "a3ef8bcb5fe8e4bfb22a8f6d714611b8", - "packages/reflex-components-core/src/reflex_components_core/el/elements/forms.pyi": "21e51ccc7307c3c41f2556ffa7019f2c", - "packages/reflex-components-core/src/reflex_components_core/el/elements/inline.pyi": "9c1432e70e6b9349f44df04a244a4303", - "packages/reflex-components-core/src/reflex_components_core/el/elements/media.pyi": "f51120c31a1a8b79da9ecf58f19005b9", - "packages/reflex-components-core/src/reflex_components_core/el/elements/metadata.pyi": "73d19f3d9e389447ad8bbb68e1b7d1c9", - "packages/reflex-components-core/src/reflex_components_core/el/elements/other.pyi": "c86abf00384b5f15725a0daf2533848d", - "packages/reflex-components-core/src/reflex_components_core/el/elements/scripts.pyi": "903432e316a781b342f2b8d334952da1", - "packages/reflex-components-core/src/reflex_components_core/el/elements/sectioning.pyi": "fbbe0bf222d4196c32c88d05cb077997", - "packages/reflex-components-core/src/reflex_components_core/el/elements/tables.pyi": "93a69aab9a6f519e3f293d439a39786b", - "packages/reflex-components-core/src/reflex_components_core/el/elements/typography.pyi": "2b434f2231d6f21b12d32995ac185e79", - "packages/reflex-components-core/src/reflex_components_core/react_router/dom.pyi": "1074a512195ae23d479c4a2d553954e1", - "packages/reflex-components-dataeditor/src/reflex_components_dataeditor/dataeditor.pyi": "8e379fa038c7c6c0672639eb5902934d", - "packages/reflex-components-gridjs/src/reflex_components_gridjs/datatable.pyi": "d2dc211d707c402eb24678a4cba945f7", - "packages/reflex-components-lucide/src/reflex_components_lucide/icon.pyi": "d16d77881afaae71578177db4d479c13", - "packages/reflex-components-markdown/src/reflex_components_markdown/markdown.pyi": "e04f22f5d3d2b5dfd99f9fbedb2b4f3d", - "packages/reflex-components-moment/src/reflex_components_moment/moment.pyi": "d6a02e447dfd3c91bba84bcd02722aed", - "packages/reflex-components-plotly/src/reflex_components_plotly/plotly.pyi": "91e956633778c6992f04940c69ff7140", - "packages/reflex-components-radix/src/reflex_components_radix/__init__.pyi": "19216eb3618f68c8a76e5e43801cf4af", - "packages/reflex-components-radix/src/reflex_components_radix/primitives/__init__.pyi": "5404a8da97e8b5129133d7f300e3f642", - "packages/reflex-components-radix/src/reflex_components_radix/primitives/accordion.pyi": "e8ef2b44f2afe3e9b8d678d523673882", - "packages/reflex-components-radix/src/reflex_components_radix/primitives/base.pyi": "e779c6739baee98c8a588768a88de45a", - "packages/reflex-components-radix/src/reflex_components_radix/primitives/dialog.pyi": "ffb06f3aa8722c2345a952869118e224", - "packages/reflex-components-radix/src/reflex_components_radix/primitives/drawer.pyi": "cc724f697e62efba294e19b58c6f1bd8", - "packages/reflex-components-radix/src/reflex_components_radix/primitives/form.pyi": "4d6121ccc963c64e33c49acd4295eb7a", - "packages/reflex-components-radix/src/reflex_components_radix/primitives/progress.pyi": "b3b66ec57525c53ea741897e2bc8370e", - "packages/reflex-components-radix/src/reflex_components_radix/primitives/slider.pyi": "c86bc8d4604e3d8c8d40baad2ac6dc17", - "packages/reflex-components-radix/src/reflex_components_radix/themes/__init__.pyi": "b433b9a099dc5b0ab008d02c85d38059", - "packages/reflex-components-radix/src/reflex_components_radix/themes/base.pyi": "e75cbf2a34620721432b1556f3c875cd", - "packages/reflex-components-radix/src/reflex_components_radix/themes/color_mode.pyi": "ed020269e4728cc6abe72354193146b7", - "packages/reflex-components-radix/src/reflex_components_radix/themes/components/__init__.pyi": "f10f0169f81c78290333da831915762f", - "packages/reflex-components-radix/src/reflex_components_radix/themes/components/alert_dialog.pyi": "d5e0419729df4ddf2caf214f40ae7845", - "packages/reflex-components-radix/src/reflex_components_radix/themes/components/aspect_ratio.pyi": "613abb9870259547c99eb434a3a17512", - "packages/reflex-components-radix/src/reflex_components_radix/themes/components/avatar.pyi": "1671e796449b236386d8f53d33e42b2f", - "packages/reflex-components-radix/src/reflex_components_radix/themes/components/badge.pyi": "9fde9929ca5197e0e1880bce9a08e926", - "packages/reflex-components-radix/src/reflex_components_radix/themes/components/button.pyi": "e5d6387a93c74dafaa0d6f1719e08bac", - "packages/reflex-components-radix/src/reflex_components_radix/themes/components/callout.pyi": "31da62c4d8c1d459089aab32cd232feb", - "packages/reflex-components-radix/src/reflex_components_radix/themes/components/card.pyi": "76afb58340c6be1f26b7b110473efa55", - "packages/reflex-components-radix/src/reflex_components_radix/themes/components/checkbox.pyi": "6cbf013e21d7280118dfd7383998b3bf", - "packages/reflex-components-radix/src/reflex_components_radix/themes/components/checkbox_cards.pyi": "63b4134246f68f9f556896d6ce194462", - "packages/reflex-components-radix/src/reflex_components_radix/themes/components/checkbox_group.pyi": "ec3f89e7d187303344d4127a83522b22", - "packages/reflex-components-radix/src/reflex_components_radix/themes/components/context_menu.pyi": "99541ee46f112eb4096f903a99f5ffb8", - "packages/reflex-components-radix/src/reflex_components_radix/themes/components/data_list.pyi": "1dfd91741ff402b3ed93b6daca4939f3", - "packages/reflex-components-radix/src/reflex_components_radix/themes/components/dialog.pyi": "ed9198da4a7950a8579e50ad970c34ef", - "packages/reflex-components-radix/src/reflex_components_radix/themes/components/dropdown_menu.pyi": "15f9cee0584414f2d2e0fb82c167f216", - "packages/reflex-components-radix/src/reflex_components_radix/themes/components/hover_card.pyi": "3f328bb0ba5225e4478febf8c7623833", - "packages/reflex-components-radix/src/reflex_components_radix/themes/components/icon_button.pyi": "be8eed28e19221a406e554829809ff0d", - "packages/reflex-components-radix/src/reflex_components_radix/themes/components/inset.pyi": "9b2adf18f7d239b8e7431f39042ed301", - "packages/reflex-components-radix/src/reflex_components_radix/themes/components/popover.pyi": "4d5813a47b8f8b6ac317ca01d87d9afb", - "packages/reflex-components-radix/src/reflex_components_radix/themes/components/progress.pyi": "1ab01f45a4c5ef4211eacc00cc99e4a5", - "packages/reflex-components-radix/src/reflex_components_radix/themes/components/radio.pyi": "38a7412205a98617f98218a5b213ada1", - "packages/reflex-components-radix/src/reflex_components_radix/themes/components/radio_cards.pyi": "d84b16ac16083a534199fd23659aaa06", - "packages/reflex-components-radix/src/reflex_components_radix/themes/components/radio_group.pyi": "51fda6313f1ce86d5b1ffdfd68ae8b74", - "packages/reflex-components-radix/src/reflex_components_radix/themes/components/scroll_area.pyi": "bba40e5eae75314157378c9e8b0eea73", - "packages/reflex-components-radix/src/reflex_components_radix/themes/components/segmented_control.pyi": "44770b1f5eb91502bfef3aadd209d0b8", - "packages/reflex-components-radix/src/reflex_components_radix/themes/components/select.pyi": "0f2f7b6ea8d5e4d2e35ff2069364ab75", - "packages/reflex-components-radix/src/reflex_components_radix/themes/components/separator.pyi": "519781d33b99c675a12014d400e54d08", - "packages/reflex-components-radix/src/reflex_components_radix/themes/components/skeleton.pyi": "f4848f7d89abb4c78f6db52c624cdabf", - "packages/reflex-components-radix/src/reflex_components_radix/themes/components/slider.pyi": "61a08374fa19a0bb3f52b8654effc0f2", - "packages/reflex-components-radix/src/reflex_components_radix/themes/components/spinner.pyi": "530c51742031389d4b2ae43548ff0f03", - "packages/reflex-components-radix/src/reflex_components_radix/themes/components/switch.pyi": "23b21bc11a0012e13ce9bb79b47ba146", - "packages/reflex-components-radix/src/reflex_components_radix/themes/components/table.pyi": "8364f40600870bafa585528d9cadedf8", - "packages/reflex-components-radix/src/reflex_components_radix/themes/components/tabs.pyi": "3a52910c327f55656eb59309f9362361", - "packages/reflex-components-radix/src/reflex_components_radix/themes/components/text_area.pyi": "fcf562b2f61ecdcc2de6f70d2ebf9907", - "packages/reflex-components-radix/src/reflex_components_radix/themes/components/text_field.pyi": "250b7e77b67e7d8cd3fff2b40526c04c", - "packages/reflex-components-radix/src/reflex_components_radix/themes/components/tooltip.pyi": "799acce0af81899a3a310bdcd43c403b", - "packages/reflex-components-radix/src/reflex_components_radix/themes/layout/__init__.pyi": "9e452af27229b676ad0146e40f75bed5", - "packages/reflex-components-radix/src/reflex_components_radix/themes/layout/base.pyi": "5b262189e235cac17182e79188b1681a", - "packages/reflex-components-radix/src/reflex_components_radix/themes/layout/box.pyi": "e06c8fd64132765d61b9edb87a48558b", - "packages/reflex-components-radix/src/reflex_components_radix/themes/layout/center.pyi": "5aa934d7c6ba3889fa943eabee7dc05f", - "packages/reflex-components-radix/src/reflex_components_radix/themes/layout/container.pyi": "c67fafd1aec105cb5a9927ff0e6d2071", - "packages/reflex-components-radix/src/reflex_components_radix/themes/layout/flex.pyi": "aa68061a8e5dfd4adf336d1d1cb000fb", - "packages/reflex-components-radix/src/reflex_components_radix/themes/layout/grid.pyi": "06b92d31331c6f08b5083fcc811b754a", - "packages/reflex-components-radix/src/reflex_components_radix/themes/layout/list.pyi": "e7cd3a9cea1c34e21f731f1bd05c1ceb", - "packages/reflex-components-radix/src/reflex_components_radix/themes/layout/section.pyi": "8c968fead3155b2d51c687459811b5df", - "packages/reflex-components-radix/src/reflex_components_radix/themes/layout/spacer.pyi": "cfc8a927642e5b68feabc80080aeb8dc", - "packages/reflex-components-radix/src/reflex_components_radix/themes/layout/stack.pyi": "cf88cf870eefaacaf765ead10fb4593b", - "packages/reflex-components-radix/src/reflex_components_radix/themes/typography/__init__.pyi": "de7ee994f66a4c1d1a6ac2ad3370c30e", - "packages/reflex-components-radix/src/reflex_components_radix/themes/typography/blockquote.pyi": "92d5a2df77a69a28a4d591000ee46bd1", - "packages/reflex-components-radix/src/reflex_components_radix/themes/typography/code.pyi": "8a1e4376cadf4961212d39a5128a0e4f", - "packages/reflex-components-radix/src/reflex_components_radix/themes/typography/heading.pyi": "34c7ed3fe1e5f702a98d72751b0052fa", - "packages/reflex-components-radix/src/reflex_components_radix/themes/typography/link.pyi": "619a9d8351748fffe76136002931e583", - "packages/reflex-components-radix/src/reflex_components_radix/themes/typography/text.pyi": "4919daa4483b7c12f6fafd02a2275e0f", - "packages/reflex-components-react-player/src/reflex_components_react_player/audio.pyi": "0817c9232a6e4790cff8ea8aa6001950", - "packages/reflex-components-react-player/src/reflex_components_react_player/react_player.pyi": "6c1c26149d57c708fab04b82de0eb515", - "packages/reflex-components-react-player/src/reflex_components_react_player/video.pyi": "75207a9fe4f37ec2a2f1becbbbd5237b", - "packages/reflex-components-recharts/src/reflex_components_recharts/__init__.pyi": "7b8b69840a3637c1f1cac45ba815cccf", - "packages/reflex-components-recharts/src/reflex_components_recharts/cartesian.pyi": "277bbf09d72e0c450241f0b7d39ebb60", - "packages/reflex-components-recharts/src/reflex_components_recharts/charts.pyi": "be20d1d71c3b16f7e973a0329c3d81d6", - "packages/reflex-components-recharts/src/reflex_components_recharts/general.pyi": "5a1a479924ad6184abafe4d796cb04c5", - "packages/reflex-components-recharts/src/reflex_components_recharts/polar.pyi": "1979bb6c22bb7a0d3342b2d63fb19d74", - "packages/reflex-components-recharts/src/reflex_components_recharts/recharts.pyi": "c5288f311fe37b23539518ba2a3d4482", - "packages/reflex-components-sonner/src/reflex_components_sonner/toast.pyi": "2c5fadcc014056f041cd4d916137d9e7", - "reflex/__init__.pyi": "674cc55e646deb97c0e414e1d0e850ef", - "reflex/components/__init__.pyi": "f39a2af77f438fa243c58c965f19d42e", - "reflex/experimental/memo.pyi": "79573b03f5cd29222d9c7edd926541b1" + "packages/reflex-components-code/src/reflex_components_code/code.pyi": "3b89c09ccc89eeeafe3e38ca539607a0", + "packages/reflex-components-code/src/reflex_components_code/shiki_code_block.pyi": "53c8f35514ebcf40ac5cf70c2342ac1c", + "packages/reflex-components-core/src/reflex_components_core/__init__.pyi": "d89c7cfa0920f664bc41d292ef882a64", + "packages/reflex-components-core/src/reflex_components_core/base/__init__.pyi": "d46e910c03263a8d3456d473de9e76c7", + "packages/reflex-components-core/src/reflex_components_core/base/app_wrap.pyi": "4a701f7eaf84ec041c8fd5c8af4e48ff", + "packages/reflex-components-core/src/reflex_components_core/base/body.pyi": "8550ac0cdb7f57b35dc639b605294b98", + "packages/reflex-components-core/src/reflex_components_core/base/document.pyi": "92be3177fff806cd44281cb912b21463", + "packages/reflex-components-core/src/reflex_components_core/base/error_boundary.pyi": "2bec3beeafea96787d10838d73e47bdc", + "packages/reflex-components-core/src/reflex_components_core/base/fragment.pyi": "04de5816bc98f33bbf14370af7f787b3", + "packages/reflex-components-core/src/reflex_components_core/base/link.pyi": "d39ded31424a9af8532b9d4eb0ce8abf", + "packages/reflex-components-core/src/reflex_components_core/base/meta.pyi": "c0e97a357ac2521c9b1c48b9368513e7", + "packages/reflex-components-core/src/reflex_components_core/base/script.pyi": "14cd6b846459f469168e78c305ee696a", + "packages/reflex-components-core/src/reflex_components_core/base/strict_mode.pyi": "135dd395a27f01be303b3879ece179c1", + "packages/reflex-components-core/src/reflex_components_core/core/__init__.pyi": "931b222daf3755f67281b6c4d7155f85", + "packages/reflex-components-core/src/reflex_components_core/core/auto_scroll.pyi": "2d83dc211884423496c31d372d3f3734", + "packages/reflex-components-core/src/reflex_components_core/core/banner.pyi": "473666c89f74a1fcd82f12cc2cd34f43", + "packages/reflex-components-core/src/reflex_components_core/core/clipboard.pyi": "21d51b69ab11279e864f7aca9307462f", + "packages/reflex-components-core/src/reflex_components_core/core/debounce.pyi": "86b2bea0aeec5334dbb64b3addae91df", + "packages/reflex-components-core/src/reflex_components_core/core/helmet.pyi": "cbbfc51195fb5cef671ff9ce65586d29", + "packages/reflex-components-core/src/reflex_components_core/core/html.pyi": "bedb5c94e690d50721ddc7bfdfbe8a25", + "packages/reflex-components-core/src/reflex_components_core/core/sticky.pyi": "a910d65f42f620531c959677d22977cb", + "packages/reflex-components-core/src/reflex_components_core/core/upload.pyi": "d903297d188fe6fe56a824127e0db925", + "packages/reflex-components-core/src/reflex_components_core/core/window_events.pyi": "3e39f7b3282570da5d2d3ac503d1f278", + "packages/reflex-components-core/src/reflex_components_core/datadisplay/__init__.pyi": "f46edcf2c5bd4b80894b4d0f623e059b", + "packages/reflex-components-core/src/reflex_components_core/el/__init__.pyi": "ca9bfe7ba432aa47d982a144ef374126", + "packages/reflex-components-core/src/reflex_components_core/el/element.pyi": "930b020d7c933ab0e29ba7971ba1abd2", + "packages/reflex-components-core/src/reflex_components_core/el/elements/__init__.pyi": "ded6fc0acc37c9260da5cfd5f4309079", + "packages/reflex-components-core/src/reflex_components_core/el/elements/base.pyi": "d075a1420b5a08cccd45aa58599ab3dc", + "packages/reflex-components-core/src/reflex_components_core/el/elements/forms.pyi": "9e4dbd88fbda4cbaa2b93b1d062d162b", + "packages/reflex-components-core/src/reflex_components_core/el/elements/inline.pyi": "c4085b9ee43847b6765cbf775341bc0b", + "packages/reflex-components-core/src/reflex_components_core/el/elements/media.pyi": "c2515454828415b78799f0ee0fb7a6af", + "packages/reflex-components-core/src/reflex_components_core/el/elements/metadata.pyi": "2d8d722c68f5a3082c0d3fa4883bb5c0", + "packages/reflex-components-core/src/reflex_components_core/el/elements/other.pyi": "da35b7e4a4496c7a84e79bc447b69ba1", + "packages/reflex-components-core/src/reflex_components_core/el/elements/scripts.pyi": "ddb26aa7e3f437579ac48dbe1da3bcfc", + "packages/reflex-components-core/src/reflex_components_core/el/elements/sectioning.pyi": "1f91a452bceebd410056e4fbdad0dfe9", + "packages/reflex-components-core/src/reflex_components_core/el/elements/tables.pyi": "7b4c2eaff78f6a412f804457570014c4", + "packages/reflex-components-core/src/reflex_components_core/el/elements/typography.pyi": "a3d0d5b0ad9d896261091603df6d2c03", + "packages/reflex-components-core/src/reflex_components_core/react_router/dom.pyi": "7617635157b816f70354e2db98b38642", + "packages/reflex-components-dataeditor/src/reflex_components_dataeditor/dataeditor.pyi": "5d12a0b0e9b4d76a9e4ba1d486094c1c", + "packages/reflex-components-gridjs/src/reflex_components_gridjs/datatable.pyi": "2ce1c076ecf5c2fa4945b4abdbf2f91d", + "packages/reflex-components-lucide/src/reflex_components_lucide/icon.pyi": "2e1da186a37e2bb8a1d90e16ee9a63b5", + "packages/reflex-components-markdown/src/reflex_components_markdown/markdown.pyi": "79d0a59b1ba12a2f2c4a09fa6b5c776f", + "packages/reflex-components-moment/src/reflex_components_moment/moment.pyi": "eabf233471bc5b94084914f6f35ecd66", + "packages/reflex-components-plotly/src/reflex_components_plotly/plotly.pyi": "6b84ff7659294b20d21b0ef7781531fa", + "packages/reflex-components-radix/src/reflex_components_radix/__init__.pyi": "a77352f60fb6f4135b5d08a6e56efa6d", + "packages/reflex-components-radix/src/reflex_components_radix/primitives/__init__.pyi": "bbd4d1a4fa73275a882c33ba485d0165", + "packages/reflex-components-radix/src/reflex_components_radix/primitives/accordion.pyi": "2639b56ce9ccb8b404c6dc12eaace19d", + "packages/reflex-components-radix/src/reflex_components_radix/primitives/base.pyi": "c6500b29e90ca0c04e6bac65ad19936e", + "packages/reflex-components-radix/src/reflex_components_radix/primitives/dialog.pyi": "e67999e6a7433d0eb8c18f4dd185761b", + "packages/reflex-components-radix/src/reflex_components_radix/primitives/drawer.pyi": "172048f74ea5cb7b0e68829d5de3a1fa", + "packages/reflex-components-radix/src/reflex_components_radix/primitives/form.pyi": "323fc4577f7d7d889218eda2c41025b7", + "packages/reflex-components-radix/src/reflex_components_radix/primitives/progress.pyi": "849b07e27c3916bd24bb576b57e9e2c4", + "packages/reflex-components-radix/src/reflex_components_radix/primitives/slider.pyi": "2846f40bff377ee688d46055c4f7390a", + "packages/reflex-components-radix/src/reflex_components_radix/themes/__init__.pyi": "6c7e1b9a0a7ca00f029bf6ba0c514fdb", + "packages/reflex-components-radix/src/reflex_components_radix/themes/base.pyi": "bf24641c7b44efcacaf67e2e49947c2c", + "packages/reflex-components-radix/src/reflex_components_radix/themes/color_mode.pyi": "efcec0a98ceb064556bcbcd8718ac9cc", + "packages/reflex-components-radix/src/reflex_components_radix/themes/components/__init__.pyi": "758646fcd894b0acb09bfb6b97f2d475", + "packages/reflex-components-radix/src/reflex_components_radix/themes/components/alert_dialog.pyi": "c4fc4b724c07f7cce647e8c72046d01a", + "packages/reflex-components-radix/src/reflex_components_radix/themes/components/aspect_ratio.pyi": "376e58873fa3431e178420d6c5806967", + "packages/reflex-components-radix/src/reflex_components_radix/themes/components/avatar.pyi": "ca8e7b05994c4bfb0e7f6ca611d48275", + "packages/reflex-components-radix/src/reflex_components_radix/themes/components/badge.pyi": "73ebf22c8fbb2d1388a3f1cd55eb1919", + "packages/reflex-components-radix/src/reflex_components_radix/themes/components/button.pyi": "3cd7cc1df19d44e14da3b291e4689ba2", + "packages/reflex-components-radix/src/reflex_components_radix/themes/components/callout.pyi": "030a61bddc63006470b856fa2d29fe47", + "packages/reflex-components-radix/src/reflex_components_radix/themes/components/card.pyi": "88cb5e549b3a4e099fe4a0795a2d4977", + "packages/reflex-components-radix/src/reflex_components_radix/themes/components/checkbox.pyi": "cd5187687dc614b908bf6ac5216c56fa", + "packages/reflex-components-radix/src/reflex_components_radix/themes/components/checkbox_cards.pyi": "f1632234039d89dc08a06e1fae7952d9", + "packages/reflex-components-radix/src/reflex_components_radix/themes/components/checkbox_group.pyi": "2c6b06167afb6a3fad615ae689fc706c", + "packages/reflex-components-radix/src/reflex_components_radix/themes/components/context_menu.pyi": "61f55df2ce771eb3d3d50b0441a6db76", + "packages/reflex-components-radix/src/reflex_components_radix/themes/components/data_list.pyi": "5891cd0930bcdbd55b37a6d945e99cd3", + "packages/reflex-components-radix/src/reflex_components_radix/themes/components/dialog.pyi": "6533a2e38a2cdfe8e0c798d975a566fe", + "packages/reflex-components-radix/src/reflex_components_radix/themes/components/dropdown_menu.pyi": "1368f79dd2a9d3e5d628ffb18d0f15fa", + "packages/reflex-components-radix/src/reflex_components_radix/themes/components/hover_card.pyi": "4566d59581a7bd7d870994a1a6267349", + "packages/reflex-components-radix/src/reflex_components_radix/themes/components/icon_button.pyi": "d16568436493dce1a35dd9b706dbb6ba", + "packages/reflex-components-radix/src/reflex_components_radix/themes/components/inset.pyi": "eaeb2399578e423ce7887d92fc276012", + "packages/reflex-components-radix/src/reflex_components_radix/themes/components/popover.pyi": "f0e2ce71c899e7029e0dffa9a88df1d2", + "packages/reflex-components-radix/src/reflex_components_radix/themes/components/progress.pyi": "e90d1f64301e17ca54a1edf2878bea9f", + "packages/reflex-components-radix/src/reflex_components_radix/themes/components/radio.pyi": "e14c994517828f84d9fc9f85501eee7a", + "packages/reflex-components-radix/src/reflex_components_radix/themes/components/radio_cards.pyi": "45470676b80c15d722b142c27521a64f", + "packages/reflex-components-radix/src/reflex_components_radix/themes/components/radio_group.pyi": "e933b49dab8027e1829cdd90ba99cc5d", + "packages/reflex-components-radix/src/reflex_components_radix/themes/components/scroll_area.pyi": "b83dd81a37aaad75dcb6341e94e6d41f", + "packages/reflex-components-radix/src/reflex_components_radix/themes/components/segmented_control.pyi": "dfdde032961bda7905ef4256eb19152d", + "packages/reflex-components-radix/src/reflex_components_radix/themes/components/select.pyi": "98818bc17dd54d4a0bfb48f7d971875a", + "packages/reflex-components-radix/src/reflex_components_radix/themes/components/separator.pyi": "b97507dd3c8ee6464b733fb61558c0ba", + "packages/reflex-components-radix/src/reflex_components_radix/themes/components/skeleton.pyi": "77e709d2f460b19617c9fc2e45537e18", + "packages/reflex-components-radix/src/reflex_components_radix/themes/components/slider.pyi": "27ae71e52b3a6abce5d94e83c354afb9", + "packages/reflex-components-radix/src/reflex_components_radix/themes/components/spinner.pyi": "a44624cea06503e76fbc1d90132faa72", + "packages/reflex-components-radix/src/reflex_components_radix/themes/components/switch.pyi": "ced70bf91fa0b598db0d1d976f2caeb7", + "packages/reflex-components-radix/src/reflex_components_radix/themes/components/table.pyi": "20b17107bb0ce5289a89b13fcef023e4", + "packages/reflex-components-radix/src/reflex_components_radix/themes/components/tabs.pyi": "4bb81b1bbb020add0bff1a5cbd541442", + "packages/reflex-components-radix/src/reflex_components_radix/themes/components/text_area.pyi": "4eba432001b75e809105dde744cc987c", + "packages/reflex-components-radix/src/reflex_components_radix/themes/components/text_field.pyi": "1adb24a4b4d8dde64c1d6bb19807675b", + "packages/reflex-components-radix/src/reflex_components_radix/themes/components/tooltip.pyi": "7159fb41c6e8e5375d70e01b3db5878d", + "packages/reflex-components-radix/src/reflex_components_radix/themes/layout/__init__.pyi": "dcb8926703a1015583fc12daadb0ee21", + "packages/reflex-components-radix/src/reflex_components_radix/themes/layout/base.pyi": "3722a5c6ed8cdd5f483b112b673a460d", + "packages/reflex-components-radix/src/reflex_components_radix/themes/layout/box.pyi": "91c0636f8347bb3861b98679129054fe", + "packages/reflex-components-radix/src/reflex_components_radix/themes/layout/center.pyi": "97a48bf51881c96c48230e97495ef526", + "packages/reflex-components-radix/src/reflex_components_radix/themes/layout/container.pyi": "c1b4dd2f75d37b95ccce31d95bce339c", + "packages/reflex-components-radix/src/reflex_components_radix/themes/layout/flex.pyi": "ff5f0a7671b3b52253d1e9c01091e383", + "packages/reflex-components-radix/src/reflex_components_radix/themes/layout/grid.pyi": "4623cb3f6cacd49d9ace0f582eec6821", + "packages/reflex-components-radix/src/reflex_components_radix/themes/layout/list.pyi": "8b7a051ef85c0e0c14a61ee3f50ca260", + "packages/reflex-components-radix/src/reflex_components_radix/themes/layout/section.pyi": "9eedd4b6a85daee106ee4a4ca2e8b1a5", + "packages/reflex-components-radix/src/reflex_components_radix/themes/layout/spacer.pyi": "02655879b13b3edf07bc18f21cec2d39", + "packages/reflex-components-radix/src/reflex_components_radix/themes/layout/stack.pyi": "94d5d4aef75d03be26d07ee0bf20fac1", + "packages/reflex-components-radix/src/reflex_components_radix/themes/typography/__init__.pyi": "a904755b3ecbd65b826328431342b8b5", + "packages/reflex-components-radix/src/reflex_components_radix/themes/typography/blockquote.pyi": "377d220f6ecb0e734cfe4a8b7cc834da", + "packages/reflex-components-radix/src/reflex_components_radix/themes/typography/code.pyi": "2bd706e29877dfdeda6ca46900f7414b", + "packages/reflex-components-radix/src/reflex_components_radix/themes/typography/heading.pyi": "6691a55131a893676a68960cf64116e3", + "packages/reflex-components-radix/src/reflex_components_radix/themes/typography/link.pyi": "e4d9e52ce7f2e97a4d2136eade8bf7cd", + "packages/reflex-components-radix/src/reflex_components_radix/themes/typography/text.pyi": "2f94a562aa24860cf6754c86128ffba1", + "packages/reflex-components-react-player/src/reflex_components_react_player/audio.pyi": "39e4144cef066bbff8c27b36a205a54b", + "packages/reflex-components-react-player/src/reflex_components_react_player/react_player.pyi": "86fc106181638c6a0a2a199332be817f", + "packages/reflex-components-react-player/src/reflex_components_react_player/video.pyi": "2682dc6e825d25307d8390d01e2bd653", + "packages/reflex-components-recharts/src/reflex_components_recharts/__init__.pyi": "359e123d9a046557ce05a96ce10313f5", + "packages/reflex-components-recharts/src/reflex_components_recharts/cartesian.pyi": "3485aaabbfd3ca89908ae19425c7297e", + "packages/reflex-components-recharts/src/reflex_components_recharts/charts.pyi": "280a7cd51298ee676f3d076104133f44", + "packages/reflex-components-recharts/src/reflex_components_recharts/general.pyi": "f5e3491c4e1f69ba89085682888888a9", + "packages/reflex-components-recharts/src/reflex_components_recharts/polar.pyi": "99ebcfc07868061bdc3c2010d85a153f", + "packages/reflex-components-recharts/src/reflex_components_recharts/recharts.pyi": "4f6c26f8c76543cc41e2b9dc400ece8a", + "packages/reflex-components-sonner/src/reflex_components_sonner/toast.pyi": "58521fcd1b514804f534d97624e82c9a", + "reflex/__init__.pyi": "56385a4f0d9431eb0056dbc5553a58f9", + "reflex/components/__init__.pyi": "9facd05a776d0641432696bbf8e34388", + "reflex/experimental/memo.pyi": "58d97de180cc34a8aa605cd76d97ee57" } diff --git a/tests/units/reflex_base/utils/pyi_generator/test_hashes.py b/tests/units/reflex_base/utils/pyi_generator/test_hashes.py new file mode 100644 index 00000000000..f94376b5aff --- /dev/null +++ b/tests/units/reflex_base/utils/pyi_generator/test_hashes.py @@ -0,0 +1,71 @@ +"""Regression test: pyi_hashes.json must hash the final post-processed content. + +``PyiGenerator.scan_all`` writes the raw ``ast.unparse`` output to disk and then +runs ``ruff format`` / ``ruff check --fix`` over the generated stubs. The hash +registry must be computed from the post-processed file contents: hashing the +intermediate pre-ruff output flags a change whenever the generator's raw output +shifts (quoting, line wrapping, ...), even when the ``.pyi`` file the user sees +is byte-for-byte identical. +""" + +from __future__ import annotations + +import json +import subprocess +import sys +import textwrap +from hashlib import md5 +from pathlib import Path + +_WIDGET_SOURCE = textwrap.dedent( + '''\ + """A fake component module.""" + + from reflex_base.components.component import Component + from reflex_base.vars.base import Var + + + class Widget(Component): + """A widget.""" + + value: Var[str] + ''' +) + +_SCAN_SCRIPT = ( + "from reflex_base.utils.pyi_generator import PyiGenerator; " + "PyiGenerator().scan_all(['fake_pkg'], None, use_json=True)" +) + + +def test_pyi_hashes_reflect_post_ruff_content(tmp_path: Path): + """The recorded hash matches the .pyi content after ruff post-processing. + + Args: + tmp_path: A pytest-provided temporary directory (outside the repo). + """ + root = tmp_path / "fake-root" + pkg = root / "fake_pkg" + pkg.mkdir(parents=True) + (pkg / "__init__.py").write_text("") + (pkg / "widget.py").write_text(_WIDGET_SOURCE) + + # An empty registry that scan_all discovers by walking up from the stub. + hashes_file = root / "pyi_hashes.json" + hashes_file.write_text("{}\n") + + # Run from root so _path_to_module_name produces a valid import name. + subprocess.run( + [sys.executable, "-c", _SCAN_SCRIPT], + cwd=root, + check=True, + ) + + pyi_path = pkg / "widget.pyi" + assert pyi_path.exists() + + hashes = json.loads(hashes_file.read_text()) + assert list(hashes) == ["fake_pkg/widget.pyi"] + assert hashes["fake_pkg/widget.pyi"] == ( + md5(pyi_path.read_text().encode()).hexdigest() + ) diff --git a/tests/units/reflex_base/utils/pyi_generator/test_regression.py b/tests/units/reflex_base/utils/pyi_generator/test_regression.py index f52ca4ec07d..c918162c234 100644 --- a/tests/units/reflex_base/utils/pyi_generator/test_regression.py +++ b/tests/units/reflex_base/utils/pyi_generator/test_regression.py @@ -72,7 +72,7 @@ def _run_generator() -> dict[Path, str]: gen = PyiGenerator() gen.scan_all([str(DATASET_DIR)]) - pyi_paths = [Path(pyi_str) for pyi_str, _hash in gen.written_files] + pyi_paths = [Path(pyi_str) for pyi_str in gen.written_files] try: results: dict[Path, str] = {} for pyi_path in pyi_paths: From 780ed9b275d7c4f9f190c04fcd0f38e367c7764c Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 10 Jun 2026 01:21:52 +0000 Subject: [PATCH 2/2] Re-trigger CI integration-app-harness-playwright (redis, 3.11) failed to pull the redis image from Docker Hub (context deadline exceeded) before any tests ran; all 105 other checks passed. Empty commit to re-roll. https://claude.ai/code/session_01APPJC9ZSmcHQy9WkzfxqVs