-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Description
Describe the Enhancement you want
- I work in an environment where we want to standardize file names to make discovery easy for newcomers (e.g., configuring
app_module_importso that i can usemain.pyinstead of being forced to use{app_name}.py, etc.). - However, I can't do this for
rxconfig.py, as it's hardcoded, as mentioned in this deepwiki chat.
Which feature do you want to improve? (and what problem does it have)
- Therefore, I suggest making this flexible as well by following the second response also mentioned in the aforementioned chat URL.
What is the benefit of the enhancement?
- This will make the project less opinionated, at least in repo-structure-naming-conventions.
- Side note: the words "less opinionated" don't have a positive/negative connotation, as it depends on the use case.
- Show an example/usecase were the improvement are needed.
- Already demonstrated in the above description.
Additional context
- The aforementioned chat URL. If it's expired, then here's the chat copy/pasted:
Deepwiki Chat
DeepWiki Q&A with Code Context for Repository: reflex-dev/reflex
Q1
question, does the file have to be renamed to rxconfig.py or can i call it any other name?
Answer
No, the config file must be named rxconfig.py. Reflex hardcodes this filename in constants and uses it throughout the CLI, initialization, and runtime systems reflex:17-21 . The framework checks for constants.Config.FILE (which resolves to rxconfig.py) in operations like reflex init, reflex run, and project validation reflex:516-520 reflex:63-67 . The config is imported as the rxconfig module during loading reflex:577-614 , and templates generate rxconfig.py by default reflex:125-142 . Renaming it would break these integrations unless you modify the framework’s constants and import logic.
Why the name is fixed
- Constant definition:
Config.MODULE = "rxconfig"andConfig.FILE = Path(f"{MODULE}{Ext.PY}")inreflex/constants/config.pyreflex:17-21 . - Existence checks: CLI commands like
reflex runandreflex renameverifyconstants.Config.FILEexists in the project root reflex:516-520 reflex:63-67 . - Module import: The config is loaded by importing the
rxconfigmodule, not by arbitrary filename reflex:577-614 . - Templates:
reflex initandreflex componentgeneraterxconfig.pyusing therxconfig_templatereflex:125-142 reflex:24-34 . - Tests: Unit tests assume the filename
rxconfig.pywhen testing rename operations reflex:173-186 .
If you need a different name, you’d have to fork and modify the framework’s constants and all references to constants.Config.FILE and the rxconfig module import.
Notes
- The hardcoded name simplifies discovery and tooling but reduces flexibility.
- Environment variables (
REFLEX_*) can override config values, but not the filename itself. - Some edge cases in hot reload and project validation explicitly check for
rxconfig.pyreflex:424-426 .
Wiki pages you might want to explore:
Citations
File: reflex/constants/config.py (L17-21)
# The name of the reflex config module.
MODULE = "rxconfig"
# The python config file.
FILE = Path(f"{MODULE}{Ext.PY}")File: reflex/utils/prerequisites.py (L516-520)
if not constants.Config.FILE.exists():
console.error(
f"[cyan]{constants.Config.FILE}[/cyan] not found. Move to the root folder of your project, or run [bold]{constants.Reflex.MODULE_NAME} init[/bold] to start a new project."
)
raise SystemExit(1)File: reflex/utils/rename.py (L63-67)
if not constants.Config.FILE.exists():
console.error(
"No rxconfig.py found. Make sure you are in the root directory of your app."
)
raise SystemExit(1)File: reflex/config.py (L577-614)
def get_config(reload: bool = False) -> Config:
"""Get the app config.
Args:
reload: Re-import the rxconfig module from disk
Returns:
The app config.
"""
cached_rxconfig = sys.modules.get(constants.Config.MODULE, None)
if cached_rxconfig is not None:
if reload:
# Remove any cached module when `reload` is requested.
del sys.modules[constants.Config.MODULE]
else:
return cached_rxconfig.config
with _config_lock:
orig_sys_path = sys.path.copy()
sys.path.clear()
sys.path.append(str(Path.cwd()))
try:
# Try to import the module with only the current directory in the path.
return _get_config()
except Exception:
# If the module import fails, try to import with the original sys.path.
sys.path.extend(orig_sys_path)
return _get_config()
finally:
# Find any entries added to sys.path by rxconfig.py itself.
extra_paths = [
p for p in sys.path if p not in orig_sys_path and p != str(Path.cwd())
]
# Restore the original sys.path.
sys.path.clear()
sys.path.extend(extra_paths)
sys.path.extend(orig_sys_path)File: reflex/compiler/templates.py (L125-142)
def rxconfig_template(app_name: str):
"""Template for the Reflex config file.
Args:
app_name: The name of the application.
Returns:
Rendered Reflex config file content as string.
"""
return f"""import reflex as rx
config = rx.Config(
app_name="{app_name}",
plugins=[
rx.plugins.SitemapPlugin(),
rx.plugins.TailwindV4Plugin(),
]
)"""File: reflex/utils/templates.py (L24-34)
def create_config(app_name: str):
"""Create a new rxconfig file.
Args:
app_name: The name of the app.
"""
# Import here to avoid circular imports.
from reflex.compiler import templates
console.debug(f"Creating {constants.Config.FILE}")
constants.Config.FILE.write_text(templates.rxconfig_template(app_name=app_name))File: tests/units/test_prerequisites.py (L173-186)
def test_rename_imports_and_app_name(temp_directory, config_code, expected):
file_path = temp_directory / "rxconfig.py"
content = f"""
config = {config_code}
"""
file_path.write_text(content)
rename_imports_and_app_name(file_path, "old_name", "new_name")
updated_content = file_path.read_text()
expected_content = f"""
config = {expected}
"""
assert updated_content == expected_contentFile: reflex/utils/exec.py (L424-426)
_has_child_file(module_path, "rxconfig.py")
and module_path == Path.cwd()
):Q2
which code should be changed if we want to make this changeable via reflex/constants/config.py ? (think of a solution similar to what you have with app_name and app_module_import config keys i guess) . Moreover, present your solution in a concise manner such that i could present it as a github issue for maintainers to act on
Answer
Proposal: Make the config filename configurable
Add two new config fields to allow users to customize the config module and filename, similar to app_name and app_module_import.
1. Add fields to BaseConfig
In reflex/config.py, add:
# The name of the config module (default: "rxconfig").
config_module: str = "rxconfig"
# The filename of the config file (default: derived from config_module).
config_file: str | None = None # If None, use f"{config_module}.py"These fields support REFLEX_CONFIG_MODULE and REFLEX_CONFIG_FILE environment overrides via update_from_env reflex:458-499 .
2. Resolve constants from loaded config
In reflex/constants/config.py, change MODULE and FILE from static values to helpers that read from the loaded config:
def _get_config_module() -> str:
from reflex.config import get_config
try:
return get_config().config_module
except Exception:
return "rxconfig" # fallback
def _get_config_file() -> Path:
cfg = _get_config_module()
from reflex.config import get_config
try:
file = get_config().config_file
return Path(file or f"{cfg}.py")
except Exception:
return Path(f"{cfg}.py")
MODULE = _get_config_module()
FILE = _get_config_file()This mirrors how app_module_import is resolved in Config.module reflex:448-456 .
3. Update import logic
In reflex/config.py, adjust _get_config and get_config to use the resolved module name:
- Replace
constants.Config.MODULEwithconstants.Config.MODULE(now dynamic) infind_specandimportlib.import_modulereflex:564-570 . - Ensure
get_config(reload=True)clears the correct module key insys.modulesreflex:586-590 .
4. Update CLI checks
- In
reflex/utils/prerequisites.pyandreflex/utils/rename.py, replace hardcodedconstants.Config.FILEchecks with the dynamic value reflex:516-520 reflex:63-67 . - In
reflex/utils/templates.py, write the file usingconstants.Config.FILEreflex:24-34 . - In hot-reload logic, adjust the filename check to use the dynamic value reflex:424-426 .
5. Update templates and tests
rxconfig_templateremains unchanged; the filename is determined by the config fields at write time.- Update unit tests to parametrize the config filename.
Why this approach
- Minimal changes: leverages existing env-var and config-loading patterns.
- Backward compatible: defaults to
rxconfig/rxconfig.py. - Consistent with
app_module_importbehavior.
Notes
- During early boot (before config is loaded), we fall back to
rxconfig/rxconfig.py. - If a user sets
config_filewithout.py, we should validate or append it inConfig._post_init. - This change does not affect the module import name used within the config file itself; users still import
rxconfigby default unless they changeconfig_module.
Wiki pages you might want to explore:
Citations
File: reflex/config.py (L448-456)
def module(self) -> str:
"""Get the module name of the app.
Returns:
The module name.
"""
if self.app_module_import is not None:
return self.app_module_import
return self.app_name + "." + self.app_nameFile: reflex/config.py (L458-499)
def update_from_env(self) -> dict[str, Any]:
"""Update the config values based on set environment variables.
If there is a set env_file, it is loaded first.
Returns:
The updated config values.
"""
if self.env_file:
_load_dotenv_from_files(_paths_from_env_files(self.env_file))
updated_values = {}
# Iterate over the fields.
for field in dataclasses.fields(self):
# The env var name is the key in uppercase.
environment_variable = None
for prefix in self._prefixes:
if environment_variable := os.environ.get(
f"{prefix}{field.name.upper()}"
):
break
# If the env var is set, override the config value.
if environment_variable and environment_variable.strip():
# Interpret the value.
value = interpret_env_var_value(
environment_variable,
field.type,
field.name,
)
# Set the value.
updated_values[field.name] = value
if field.name.upper() in _sensitive_env_vars:
environment_variable = "***"
if value != getattr(self, field.name):
console.debug(
f"Overriding config value {field.name} with env var {field.name.upper()}={environment_variable}",
dedupe=True,
)
return updated_valuesFile: reflex/config.py (L564-570)
spec = find_spec(constants.Config.MODULE)
if not spec:
# we need this condition to ensure that a ModuleNotFound error is not thrown when
# running unit/integration tests or during `reflex init`.
return Config(app_name="", _skip_plugins_checks=True)
rxconfig = importlib.import_module(constants.Config.MODULE)
return rxconfig.configFile: reflex/config.py (L586-590)
cached_rxconfig = sys.modules.get(constants.Config.MODULE, None)
if cached_rxconfig is not None:
if reload:
# Remove any cached module when `reload` is requested.
del sys.modules[constants.Config.MODULE]File: reflex/utils/prerequisites.py (L516-520)
if not constants.Config.FILE.exists():
console.error(
f"[cyan]{constants.Config.FILE}[/cyan] not found. Move to the root folder of your project, or run [bold]{constants.Reflex.MODULE_NAME} init[/bold] to start a new project."
)
raise SystemExit(1)File: reflex/utils/rename.py (L63-67)
if not constants.Config.FILE.exists():
console.error(
"No rxconfig.py found. Make sure you are in the root directory of your app."
)
raise SystemExit(1)File: reflex/utils/templates.py (L24-34)
def create_config(app_name: str):
"""Create a new rxconfig file.
Args:
app_name: The name of the app.
"""
# Import here to avoid circular imports.
from reflex.compiler import templates
console.debug(f"Creating {constants.Config.FILE}")
constants.Config.FILE.write_text(templates.rxconfig_template(app_name=app_name))File: reflex/utils/exec.py (L424-426)
_has_child_file(module_path, "rxconfig.py")
and module_path == Path.cwd()
):