|
@@ -243,12 +243,20 @@ class ConfigValue(Generic[T]):
|
|
|
the actual setting instead of the always-truthy wrapper object.
|
|
the actual setting instead of the always-truthy wrapper object.
|
|
|
"""
|
|
"""
|
|
|
|
|
|
|
|
- __slots__ = ("env_name", "field_name", "value")
|
|
|
|
|
|
|
+ __slots__ = ("env_name", "field_name", "value", "explicit")
|
|
|
|
|
|
|
|
- def __init__(self, value: T, field_name: str, env_name: str) -> None:
|
|
|
|
|
|
|
+ def __init__(
|
|
|
|
|
+ self, value: T, field_name: str, env_name: str, explicit: bool = False
|
|
|
|
|
+ ) -> None:
|
|
|
self.value = value
|
|
self.value = value
|
|
|
self.field_name = field_name
|
|
self.field_name = field_name
|
|
|
self.env_name = env_name
|
|
self.env_name = env_name
|
|
|
|
|
+ # explicit: True when the value came from the user (env var or extra),
|
|
|
|
|
+ # False when it is merely the declared default. Defaults are
|
|
|
|
|
+ # structurally indistinguishable from user input by value alone —
|
|
|
|
|
+ # a user *may* deliberately set base_url to the default string —
|
|
|
|
|
+ # so the origin must be carried alongside the value.
|
|
|
|
|
+ self.explicit = explicit
|
|
|
|
|
|
|
|
def __bool__(self) -> bool:
|
|
def __bool__(self) -> bool:
|
|
|
return bool(self.value)
|
|
return bool(self.value)
|
|
@@ -322,19 +330,25 @@ class ConfigField(Generic[T]):
|
|
|
def resolve(self, extra: dict[str, Any]) -> ConfigValue:
|
|
def resolve(self, extra: dict[str, Any]) -> ConfigValue:
|
|
|
"""Read this field from the environment, then from ``extra``, then the default."""
|
|
"""Read this field from the environment, then from ``extra``, then the default."""
|
|
|
raw = extra.get(self.config_key)
|
|
raw = extra.get(self.config_key)
|
|
|
|
|
+ raw_present = raw is not None
|
|
|
|
|
|
|
|
if self.kind == "list":
|
|
if self.kind == "list":
|
|
|
value: Any = _get_env_or_extra_list(self.env_name, raw)
|
|
value: Any = _get_env_or_extra_list(self.env_name, raw)
|
|
|
|
|
+ explicit = os.getenv(self.env_name) is not None or raw_present
|
|
|
elif self.kind == "bool":
|
|
elif self.kind == "bool":
|
|
|
value = _get_env_or_extra_truthy(self.env_name, raw, bool(self.default))
|
|
value = _get_env_or_extra_truthy(self.env_name, raw, bool(self.default))
|
|
|
|
|
+ explicit = os.getenv(self.env_name) is not None or raw_present
|
|
|
elif self.kind == "int":
|
|
elif self.kind == "int":
|
|
|
value = _get_env_or_extra_int(self.env_name, raw, int(self.default))
|
|
value = _get_env_or_extra_int(self.env_name, raw, int(self.default))
|
|
|
|
|
+ explicit = os.getenv(self.env_name) is not None or raw_present
|
|
|
elif self.kind == "str_opt":
|
|
elif self.kind == "str_opt":
|
|
|
value = _get_env_or_extra_str_opt(self.env_name, raw, self.default)
|
|
value = _get_env_or_extra_str_opt(self.env_name, raw, self.default)
|
|
|
|
|
+ explicit = os.getenv(self.env_name) is not None or raw_present
|
|
|
else:
|
|
else:
|
|
|
value = _get_env_or_extra_str(self.env_name, raw, self.default)
|
|
value = _get_env_or_extra_str(self.env_name, raw, self.default)
|
|
|
|
|
+ explicit = os.getenv(self.env_name) is not None or raw_present
|
|
|
|
|
|
|
|
- return ConfigValue(value, self.field_name, self.env_name)
|
|
|
|
|
|
|
+ return ConfigValue(value, self.field_name, self.env_name, explicit=explicit)
|
|
|
|
|
|
|
|
|
|
|
|
|
class ChattoConfiguration:
|
|
class ChattoConfiguration:
|