From c9f7ddd4d0e9c2a105ef13e5f6cbe43321fea552 Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Wed, 21 Aug 2024 19:24:55 -0700 Subject: [PATCH] reorder page evaluation --- reflex/app.py | 102 ++++++++++++++---------------------- reflex/compiler/compiler.py | 37 +++++++------ 2 files changed, 60 insertions(+), 79 deletions(-) diff --git a/reflex/app.py b/reflex/app.py index 78cba33b8d8..c9c84b24b23 100644 --- a/reflex/app.py +++ b/reflex/app.py @@ -9,6 +9,7 @@ import functools import inspect import io +import dill import multiprocess from pathos import multiprocessing, pools import os @@ -47,7 +48,10 @@ from reflex.base import Base from reflex.compiler import compiler from reflex.compiler import utils as compiler_utils -from reflex.compiler.compiler import ExecutorSafeFunctions +from reflex.compiler.compiler import ( + ExecutorSafeFunctions, + compile_uncompiled_page_helper, +) from reflex.components.base.app_wrap import AppWrap from reflex.components.base.error_boundary import ErrorBoundary from reflex.components.base.fragment import Fragment @@ -554,49 +558,8 @@ def _compile_page(self, route: str): Args: route: The route of the page to compile. """ - uncompiled_page = self.uncompiled_pages[route] - - on_load = uncompiled_page.on_load - - # Generate the component if it is a callable. - component = self._generate_component(uncompiled_page.component) - - # unpack components that return tuples in an rx.fragment. - if isinstance(component, tuple): - component = Fragment.create(*component) - - # Ensure state is enabled if this page uses state. - if self.state is None: - if on_load or component._has_stateful_event_triggers(): - self._enable_state() - else: - for var in component._get_vars(include_children=True): - if not var._var_data: - continue - if not var._var_data.state: - continue - self._enable_state() - break - - component = OverlayFragment.create(component) - - meta_args = { - "title": ( - uncompiled_page.title - if uncompiled_page.title is not None - else format.make_default_page_title(get_config().app_name, route) - ), - "image": uncompiled_page.image, - "meta": uncompiled_page.meta, - } - - if uncompiled_page.description is not None: - meta_args["description"] = uncompiled_page.description - - # Add meta information to the component. - compiler_utils.add_meta( - component, - **meta_args, + component = compiler.compile_uncompiled_page_helper( + route, self.uncompiled_pages[route] ) # Add the page. @@ -892,6 +855,10 @@ def get_compilation_time() -> str: self._add_optional_endpoints() if not self._should_compile(): + for route in self.uncompiled_pages: + if route in self.pages: + continue + self._compile_page(route) return self._validate_var_dependencies() @@ -957,11 +924,6 @@ def get_compilation_time() -> str: progress.advance(task) - for route, uncompiled_page in self.uncompiled_pages.items(): - ExecutorSafeFunctions.UNCOMPILED_PAGES[route] = uncompiled_page - - ExecutorSafeFunctions.STYLE = self.style - # Use a forking process pool, if possible. Much faster, especially for large sites. # Fallback to ThreadPoolExecutor as something that will always work. executor = None @@ -969,6 +931,9 @@ def get_compilation_time() -> str: platform.system() in ("Linux", "Darwin") and os.environ.get("REFLEX_COMPILE_PROCESSES") is not None ): + for route in self.uncompiled_pages: + self._compile_page(route) + executor = pools.ProcessPool() else: executor = pools.ThreadPool() @@ -979,27 +944,40 @@ def get_compilation_time() -> str: result_futures = [] pages_futures = [] - # def _mark_complete(_=None): - # progress.advance(task) - def _submit_work(fn, *args, **kwargs): f = executor.apipe(fn, *args, **kwargs) - # f.add_done_callback(_mark_complete) result_futures.append(f) # Compile all page components. - for route in self.uncompiled_pages: + for route, page in self.uncompiled_pages.items(): + if route in self.pages: + continue + f = executor.apipe( - ExecutorSafeFunctions.compile_uncompiled_page, route + ExecutorSafeFunctions.compile_uncompiled_page, + route, + page, + self.state, + self.style, + self.theme, ) - # f.add_done_callback(_mark_complete) pages_futures.append((route, f)) + # Compile the pre-compiled pages. + for route, component in self.pages.items(): + component._add_style_recursive(self.style, self.theme) + _submit_work( + ExecutorSafeFunctions.compile_page, + route, + component, + self.state, + ) + # Compile the root stylesheet with base styles. _submit_work(compiler.compile_root_stylesheet, self.stylesheets) # Compile the theme. - _submit_work(ExecutorSafeFunctions.compile_theme) + _submit_work(ExecutorSafeFunctions.compile_theme, self.style) # Compile the Tailwind config. if config.tailwind is not None: @@ -1013,18 +991,18 @@ def _submit_work(fn, *args, **kwargs): # Wait for all compilation tasks to complete. for future in result_futures: compile_results.append(future.get()) + progress.advance(task) for route, future in pages_futures: - print(f"Compiled {route}") pages_results.append(future.get()) + progress.advance(task) for route, component, compiled_page in pages_results: - self.pages[compiled_page] = component + self._check_routes_conflict(route) + self.pages[route] = component compile_results.append(compiled_page) - # Merge the component style with the app style. - component._add_style_recursive(self.style, self.theme) - + for route, component in self.pages.items(): # Add component._get_all_imports() to all_imports. all_imports.update(component._get_all_imports()) diff --git a/reflex/compiler/compiler.py b/reflex/compiler/compiler.py index 90af159578b..fe7270ee765 100644 --- a/reflex/compiler/compiler.py +++ b/reflex/compiler/compiler.py @@ -517,9 +517,7 @@ def purge_web_pages_dir(): from reflex.event import EventHandler, EventSpec -def compile_uncompiled_page( - route: str, page: UncompiledPage -) -> tuple[EventHandler | EventSpec | list[EventHandler | EventSpec] | None, Fragment]: +def compile_uncompiled_page_helper(route: str, page: UncompiledPage) -> Component: """Compiles an uncompiled page into a component and adds meta information. Args: @@ -527,7 +525,7 @@ def compile_uncompiled_page( page (UncompiledPage): The uncompiled page object. Returns: - tuple[EventHandler | EventSpec | list[EventHandler | EventSpec] | None, Fragment]: The on_load event handler or spec, and the compiled component. + Component: The compiled component. """ # Generate the component if it is a callable. component = page.component @@ -589,13 +587,10 @@ class ExecutorSafeFunctions: """ - UNCOMPILED_PAGES = {} - COMPILED_COMPONENTS = {} - STATE: Type[BaseState] | None = None - STYLE: ComponentStyle | None = None - @classmethod - def compile_page(cls, route: str): + def compile_page( + cls, route: str, component: Component, state: Type[BaseState] + ) -> tuple[str, str]: """Compile a page. Args: @@ -604,10 +599,17 @@ def compile_page(cls, route: str): Returns: The path and code of the compiled page. """ - return compile_page(route, cls.COMPILED_COMPONENTS[route], cls.STATE) + return compile_page(route, component, state) @classmethod - def compile_uncompiled_page(cls, route: str): + def compile_uncompiled_page( + cls, + route: str, + page: UncompiledPage, + state: Type[BaseState], + style: ComponentStyle, + theme: Component, + ) -> tuple[str, Component, tuple[str, str]]: """Compile an uncompiled page. Args: @@ -616,12 +618,13 @@ def compile_uncompiled_page(cls, route: str): Returns: The path and code of the compiled page. """ - component = compile_uncompiled_page(route, cls.UNCOMPILED_PAGES[route]) + component = compile_uncompiled_page_helper(route, page) component = component if isinstance(component, Component) else component() - return route, component, compile_page(route, component, cls.STATE) + component._add_style_recursive(style, theme) + return route, component, compile_page(route, component, state) @classmethod - def compile_theme(cls): + def compile_theme(cls, style: ComponentStyle | None) -> tuple[str, str]: """Compile the theme. Returns: @@ -630,6 +633,6 @@ def compile_theme(cls): Raises: ValueError: If the style is not set. """ - if cls.STYLE is None: + if style is None: raise ValueError("STYLE should be set") - return compile_theme(cls.STYLE) + return compile_theme(style)