feat: add env var validators
This commit is contained in:
parent
000938414f
commit
bb093c7873
@ -15,6 +15,7 @@ from pathlib import Path
|
|||||||
from typing import (
|
from typing import (
|
||||||
TYPE_CHECKING,
|
TYPE_CHECKING,
|
||||||
Any,
|
Any,
|
||||||
|
Callable,
|
||||||
Dict,
|
Dict,
|
||||||
Generic,
|
Generic,
|
||||||
List,
|
List,
|
||||||
@ -314,6 +315,8 @@ def interpret_env_var_value(
|
|||||||
|
|
||||||
T = TypeVar("T")
|
T = TypeVar("T")
|
||||||
|
|
||||||
|
VALIDATOR_TYPE = List[Callable[[T], None]]
|
||||||
|
|
||||||
|
|
||||||
class EnvVar(Generic[T]):
|
class EnvVar(Generic[T]):
|
||||||
"""Environment variable."""
|
"""Environment variable."""
|
||||||
@ -321,18 +324,39 @@ class EnvVar(Generic[T]):
|
|||||||
name: str
|
name: str
|
||||||
default: Any
|
default: Any
|
||||||
type_: T
|
type_: T
|
||||||
|
validators: VALIDATOR_TYPE
|
||||||
|
|
||||||
def __init__(self, name: str, default: Any, type_: T) -> None:
|
def __init__(
|
||||||
|
self,
|
||||||
|
name: str,
|
||||||
|
default: Any,
|
||||||
|
type_: T,
|
||||||
|
validators: Optional[VALIDATOR_TYPE] = None,
|
||||||
|
) -> None:
|
||||||
"""Initialize the environment variable.
|
"""Initialize the environment variable.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
name: The environment variable name.
|
name: The environment variable name.
|
||||||
default: The default value.
|
default: The default value.
|
||||||
type_: The type of the value.
|
type_: The type of the value.
|
||||||
|
validators: The validators to apply to the value.
|
||||||
"""
|
"""
|
||||||
self.name = name
|
self.name = name
|
||||||
self.default = default
|
self.default = default
|
||||||
self.type_ = type_
|
self.type_ = type_
|
||||||
|
self.validators = validators or []
|
||||||
|
|
||||||
|
self.validate(default)
|
||||||
|
self.validate(self.get())
|
||||||
|
|
||||||
|
def validate(self, value: T | None = None) -> None:
|
||||||
|
"""Validate the environment variable.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
value: The value to validate.
|
||||||
|
"""
|
||||||
|
for validator in self.validators:
|
||||||
|
validator(value)
|
||||||
|
|
||||||
def interpret(self, value: str) -> T:
|
def interpret(self, value: str) -> T:
|
||||||
"""Interpret the environment variable value.
|
"""Interpret the environment variable value.
|
||||||
@ -381,6 +405,7 @@ class EnvVar(Generic[T]):
|
|||||||
Args:
|
Args:
|
||||||
value: The value to set.
|
value: The value to set.
|
||||||
"""
|
"""
|
||||||
|
self.validate(value)
|
||||||
if value is None:
|
if value is None:
|
||||||
_ = os.environ.pop(self.name, None)
|
_ = os.environ.pop(self.name, None)
|
||||||
else:
|
else:
|
||||||
@ -395,16 +420,24 @@ class env_var: # type: ignore
|
|||||||
name: str
|
name: str
|
||||||
default: Any
|
default: Any
|
||||||
internal: bool = False
|
internal: bool = False
|
||||||
|
validators: VALIDATOR_TYPE
|
||||||
|
|
||||||
def __init__(self, default: Any, internal: bool = False) -> None:
|
def __init__(
|
||||||
|
self,
|
||||||
|
default: Any,
|
||||||
|
internal: bool = False,
|
||||||
|
validators: Optional[VALIDATOR_TYPE] = None,
|
||||||
|
) -> None:
|
||||||
"""Initialize the descriptor.
|
"""Initialize the descriptor.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
default: The default value.
|
default: The default value.
|
||||||
internal: Whether the environment variable is reflex internal.
|
internal: Whether the environment variable is reflex internal.
|
||||||
|
validators: The validators to apply to the value.
|
||||||
"""
|
"""
|
||||||
self.default = default
|
self.default = default
|
||||||
self.internal = internal
|
self.internal = internal
|
||||||
|
self.validators = validators or []
|
||||||
|
|
||||||
def __set_name__(self, owner, name):
|
def __set_name__(self, owner, name):
|
||||||
"""Set the name of the descriptor.
|
"""Set the name of the descriptor.
|
||||||
@ -429,17 +462,22 @@ class env_var: # type: ignore
|
|||||||
env_name = self.name
|
env_name = self.name
|
||||||
if self.internal:
|
if self.internal:
|
||||||
env_name = f"__{env_name}"
|
env_name = f"__{env_name}"
|
||||||
return EnvVar(name=env_name, default=self.default, type_=type_)
|
return EnvVar(
|
||||||
|
name=env_name, default=self.default, type_=type_, validators=self.validators
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
|
|
||||||
def env_var(default, internal=False) -> EnvVar:
|
def env_var(
|
||||||
|
default, internal=False, validators: Optional[VALIDATOR_TYPE[T]] = None
|
||||||
|
) -> EnvVar[T]:
|
||||||
"""Typing helper for the env_var descriptor.
|
"""Typing helper for the env_var descriptor.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
default: The default value.
|
default: The default value.
|
||||||
internal: Whether the environment variable is reflex internal.
|
internal: Whether the environment variable is reflex internal.
|
||||||
|
validators: The validators to apply to the EnvVar.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
The EnvVar instance.
|
The EnvVar instance.
|
||||||
|
@ -248,11 +248,17 @@ def test_interpret_bool_env(value: str, expected: bool) -> None:
|
|||||||
|
|
||||||
|
|
||||||
def test_env_var():
|
def test_env_var():
|
||||||
|
def validate_positive(value: int) -> None:
|
||||||
|
if value < 0:
|
||||||
|
raise ValueError("Value must be positive")
|
||||||
|
|
||||||
class TestEnv:
|
class TestEnv:
|
||||||
BLUBB: EnvVar[str] = env_var("default")
|
BLUBB: EnvVar[str] = env_var("default")
|
||||||
INTERNAL: EnvVar[str] = env_var("default", internal=True)
|
INTERNAL: EnvVar[str] = env_var("default", internal=True)
|
||||||
BOOLEAN: EnvVar[bool] = env_var(False)
|
BOOLEAN: EnvVar[bool] = env_var(False)
|
||||||
|
|
||||||
|
VALIDATE: EnvVar[int] = env_var(0, validators=[validate_positive])
|
||||||
|
|
||||||
assert TestEnv.BLUBB.get() == "default"
|
assert TestEnv.BLUBB.get() == "default"
|
||||||
assert TestEnv.BLUBB.name == "BLUBB"
|
assert TestEnv.BLUBB.name == "BLUBB"
|
||||||
TestEnv.BLUBB.set("new")
|
TestEnv.BLUBB.set("new")
|
||||||
@ -280,3 +286,9 @@ def test_env_var():
|
|||||||
assert TestEnv.BOOLEAN.get() is False
|
assert TestEnv.BOOLEAN.get() is False
|
||||||
TestEnv.BOOLEAN.set(None)
|
TestEnv.BOOLEAN.set(None)
|
||||||
assert "BOOLEAN" not in os.environ
|
assert "BOOLEAN" not in os.environ
|
||||||
|
|
||||||
|
assert TestEnv.VALIDATE.get() == 0
|
||||||
|
TestEnv.VALIDATE.set(1)
|
||||||
|
assert TestEnv.VALIDATE.get() == 1
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
TestEnv.VALIDATE.set(-1)
|
||||||
|
Loading…
Reference in New Issue
Block a user