Add single-pass component tree collector and compiler plugin foundations#6260
Add single-pass component tree collector and compiler plugin foundations#6260FarhanAliRaza wants to merge 6 commits intoreflex-dev:mainfrom
Conversation
Replace 6 separate recursive tree walks (_get_all_imports, _get_all_hooks, _get_all_custom_code, _get_all_dynamic_imports, _get_all_refs, _get_all_app_wrap_components) with a single collect_component_tree_artifacts walk that gathers all compilation data in one pass. Wire the new collector into app.py, compiler.py, and utils.py. Add CompilerPlugin protocol, CompilerHooks dispatcher, and BaseContext/ PageContext/CompileContext types as foundations for the async plugin pipeline.
Greptile SummaryThis PR lays the plugin-architecture foundations for single-pass page compilation in Reflex, introducing Key changes:
Findings:
Confidence Score: 5/5Safe to merge — all remaining findings are P2 style and documentation suggestions with no impact on runtime correctness. The core plugin machinery is well-designed and the test coverage is thorough. All three findings are P2: a missing inline comment on the intentional prop-component side-effect-only traversal, a defensive assertion that would improve error messages for malformed plugin generators (not triggerable by correctly-typed plugins), and duplicated test helper logic. None affect correctness or reliability. reflex/compiler/plugins.py — the prop-component traversal asymmetry and the unwind-loop None-children path are worth a second look before the plugin API is widely adopted. Important Files Changed
Sequence DiagramsequenceDiagram
participant CC as CompileContext.compile()
participant CH as CompilerHooks
participant PC as PageContext
participant P as Plugin (ordered chain)
CC->>CH: eval_page(page_fn) [stop-on-first]
CH->>P: eval_page() — first plugin returning non-None wins
P-->>CH: PageContext
CH-->>CC: PageContext
CC->>PC: async with page_ctx (attach ContextVar)
loop For each structural child
CC->>CH: compile_component(comp)
CH->>P: plugin.compile_component() → AsyncGenerator
Note over P: PRE: anext(gen) — enter phase (registration order)
CH->>CH: _compile_children(structural_children) [recursive]
CH->>CH: compile_component(prop_components) [side-effects only]
Note over P: POST: gen.asend((comp, children)) — unwind (reverse order)
CH-->>CC: compiled_component
end
CC->>CH: compile_page(page_ctx)
CH->>P: compile_page() — all plugins, registration order
P-->>CH: (void)
CC->>PC: exit async with (detach ContextVar)
CC->>CC: compiled_pages[route] = page_ctx
Reviews (2): Last reviewed commit: "removed the extra stuff no related to is..." | Re-trigger Greptile |
masenf
left a comment
There was a problem hiding this comment.
we need a test case for the child replacement logic when traversing the tree; we might not strictly need it now, but it will be important when moving the StatefulComponent compilation into the plugin system.
|
|
||
| if isinstance(compiled_component, Component): | ||
| for prop_component in compiled_component._get_components_in_props(): | ||
| await self.compile_component(prop_component, **kwargs) |
There was a problem hiding this comment.
at first glance it seems like we might need to do something with the potential replacement value returned by this function, like applying it as the prop value on the compiled_component if it differs
This branch introduces compiler plugin foundations for single-pass page compilation in Reflex.
Changes
New
reflex/compiler/plugins.pymodule — Defines the plugin architecture:CompilerPluginprotocol with three async hooks:eval_page,compile_page, andcompile_component(async generator for pre/post tree traversal)CompilerHooksdispatcher that runs hooks across an ordered plugin chain with stop-on-first-result semantics foreval_pageand middleware-style enter/unwind forcompile_componentBaseContextasync context manager backed byContextVarfor task-local state, with each subclass getting its own context varPageContextfor mutable per-page compilation state (imports, hooks, custom code, dynamic imports, refs, app-wrap components)CompileContextfor top-level compilation orchestration across all pagesComprehensive test suite (
tests/units/compiler/test_plugins.py, 435 lines) — Covers plugin dispatch ordering, stop-on-result, component tree traversal (children before prop-components), context lifecycle/cleanup, data accumulation, duplicate route rejection, and factory isolationFlaky Redis test fix — Replaces a racy
modify_2_startedevent with a counter-basedcontenders_startedmechanism that only fires after both contending tasks have started, fixing intermittent failures intest_oplock_contention_queueandtest_oplock_contention_no_leaseMinor: Added
asendto codespell ignore list, fixed benchmark script import, removed problematic__slots__usageAll Submissions:
Type of change
Please delete options that are not relevant.
New Feature Submission:
Changes To Core Features:
closes #6210