fix(config): stop rx.asset in rxconfig.py from recursing

VJ
3d ago
+108-2

Summary

Calling rx.asset(...) at the top level of rxconfig.py raises RecursionError.

AssetPathStr.__new__ calls get_config() to prepend frontend_path. During the first config load, the context has no config yet, so get_config() calls _get_config(). That pops rxconfig from sys.modules and imports it again (_load_config_lock is an RLock, so the same thread re-enters). The re-import calls rx.asset again, and the loop continues until the stack overflows.

Changes:

  • _get_config() sets _loading_config while it imports rxconfig.py (under _load_config_lock). If it is called again during that import, it returns Config(app_name="", _skip_plugins_checks=True) instead of importing the file again. This is the env and default config it already returns when no rxconfig.py exists.
  • get_config() does not cache that stand-in on the RegistrationContext. Without this, a load through _get_config() directly (or the deprecated _load_config()) would leave the context holding the stand-in.

Known limitation: an asset created in rxconfig.py before rx.Config(...) runs only sees frontend_path from the environment (REFLEX_FRONTEND_PATH) or the default. It cannot see a frontend_path passed to the rx.Config(...) call further down the same file, because that config does not exist yet.

Verification

  • New test test_get_config_inside_rxconfig_does_not_reload_it in tests/units/test_config.py:
    • On main it fails with RecursionError.
    • Without the get_config() no-cache branch, it fails with assert ctx._config is None.
    • With this change it passes.
  • The reproduction from the issue (ASSET = rx.asset("favicon.ico") above rx.Config(...)) now loads and returns '/favicon.ico?v=e3b0c442'.
  • uv run pytest tests/units/test_config.py: 160 passed.
  • uv run pytest tests/units: 12348 passed, 17 failed. The same 17 fail on main on my Windows machine: tests/units/assets shared-asset symlink tests, test_create_resolves_shared_asset and test_initialize_agents_md_handles_symlinked_claude. None of them are related to this change.
  • ruff check, ruff format --check and pyright are clean on the changed files.
  • Added the news fragment packages/reflex-base/news/7669.bugfix.md.

Closes #7669