Repository navigation
reregister memo in app harness #6799
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
94402e8
40fed7d
384dec7
a29d8ea
40d116c
746aa05
46648d2
377a848
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| `AppHarness` no longer reloads an existing app (one created without `app_source`), which dropped the pages, states and `@rx.memo` components defined in the app's submodules. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| Fix `@rx.memo` definitions from a module shared by several `AppHarness` apps in one test process going missing from later apps, which left their imports unresolved at runtime. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -267,32 +267,44 @@ def _initialize_app(self): | |
| # prerendering, so the dev server serves prerendered page HTML whose | ||
| # hydration failures break event delivery (notably under vite >= 8.2). | ||
| environment.REFLEX_ENV_MODE.set(reflex.constants.Env.DEV) | ||
| # Reset the global memo registry so previous AppHarness apps do not | ||
| # leak compiled component definitions into the next test app. | ||
| MEMOS.clear() | ||
| self.app_path.mkdir(parents=True, exist_ok=True) | ||
| if self.app_source is not None: | ||
| app_globals = self._get_globals_from_signature(self.app_source) | ||
| if isinstance(self.app_source, functools.partial): | ||
| self.app_source = self.app_source.func | ||
| # get the source from a function or module object | ||
| source_code = "\n".join([ | ||
| "\n".join([ | ||
| self.get_app_global_source(k, v) for k, v in app_globals.items() | ||
| ]), | ||
| self._get_source_from_app_source(self.app_source), | ||
| ]) | ||
| get_config().loglevel = reflex.constants.LogLevel.INFO | ||
| with chdir(self.app_path): | ||
| reflex.reflex._init( | ||
| name=self.app_name, | ||
| template=reflex.constants.Templates.DEFAULT, | ||
| ) | ||
| self.app_module_path.write_text(source_code) | ||
| else: | ||
| # Just initialize the web folder. | ||
|
|
||
| if self.app_source is None: | ||
| # Real, pre-existing app: import once and use it as-is. Reloading an | ||
| # already-imported app re-runs only the top module, dropping every | ||
| # submodule page/state/@rx.memo, so use reload=False (no reset). | ||
| with chdir(self.app_path): | ||
| # Load this app's config before .web is initialized from it. | ||
| reload_config() | ||
| reflex.utils.prerequisites.initialize_frontend_dependencies() | ||
|
greptile-apps[bot] marked this conversation as resolved.
|
||
| os.environ.pop(reflex.constants.PYTEST_CURRENT_TEST, None) | ||
| os.environ[reflex.constants.APP_HARNESS_FLAG] = "true" | ||
| self.app_instance, self.app_module = ( | ||
| reflex.utils.prerequisites.get_and_validate_app(reload=False) | ||
| ) | ||
| self.app_asgi = self.app_instance() | ||
| return | ||
|
Comment on lines
+282
to
+286
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
If a test process starts a real-app harness after another app, this early return skips the registration-context fork. Importing the next app then tries to use a context already associated with the previous app, raising |
||
|
|
||
| # Generated app: reset the global memo registry so a previous AppHarness | ||
| # app does not leak compiled component definitions into the next test app. | ||
| MEMOS.clear() | ||
| app_globals = self._get_globals_from_signature(self.app_source) | ||
| if isinstance(self.app_source, functools.partial): | ||
| self.app_source = self.app_source.func | ||
| # get the source from a function or module object | ||
| source_code = "\n".join([ | ||
| "\n".join([ | ||
| self.get_app_global_source(k, v) for k, v in app_globals.items() | ||
| ]), | ||
| self._get_source_from_app_source(self.app_source), | ||
| ]) | ||
| get_config().loglevel = reflex.constants.LogLevel.INFO | ||
| with chdir(self.app_path): | ||
| reflex.reflex._init( | ||
| name=self.app_name, | ||
| template=reflex.constants.Templates.DEFAULT, | ||
| ) | ||
| self.app_module_path.write_text(source_code) | ||
| with chdir(self.app_path): | ||
| # Use a new registration context for a new app. | ||
| if AppHarness._base_registration_context is None: | ||
|
|
@@ -303,19 +315,12 @@ def _initialize_app(self): | |
| new_registration_context = AppHarness._base_registration_context.fork() | ||
| self._registry_token = RegistrationContext.set(new_registration_context) | ||
| # ensure config and app are reloaded when testing different app | ||
| config = reload_config() | ||
| reload_config() | ||
| # Ensure the AppHarness test does not skip State assignment due to running via pytest | ||
| os.environ.pop(reflex.constants.PYTEST_CURRENT_TEST, None) | ||
| os.environ[reflex.constants.APP_HARNESS_FLAG] = "true" | ||
| # Ensure we compile generated apps, and reload pre-existing app modules | ||
| # that were already imported so they can re-register memo definitions. | ||
| should_reload_app = ( | ||
| self.app_source is not None or config.module in sys.modules | ||
| ) | ||
| self.app_instance, self.app_module = ( | ||
| reflex.utils.prerequisites.get_and_validate_app( | ||
| reload=should_reload_app | ||
| ) | ||
| reflex.utils.prerequisites.get_and_validate_app(reload=True) | ||
| ) | ||
| self.app_asgi = self.app_instance() | ||
|
|
||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.