|
@@ -3,7 +3,9 @@
|
|
|
from __future__ import annotations
|
|
|
|
|
|
import contextlib
|
|
|
+import inspect
|
|
|
import types
|
|
|
+from functools import wraps
|
|
|
from typing import (
|
|
|
Any,
|
|
|
Callable,
|
|
@@ -330,6 +332,69 @@ def check_prop_in_allowed_types(prop: Any, allowed_types: Iterable) -> bool:
|
|
|
return type_ in allowed_types
|
|
|
|
|
|
|
|
|
+def validate_literal(key: str, value: Any, expected_type: Type, comp_name: str):
|
|
|
+ """Check that a value is a valid literal.
|
|
|
+
|
|
|
+ Args:
|
|
|
+ key: The prop name.
|
|
|
+ value: The prop value to validate.
|
|
|
+ expected_type: The expected type(literal type).
|
|
|
+ comp_name: Name of the component.
|
|
|
+
|
|
|
+ Raises:
|
|
|
+ ValueError: When the value is not a valid literal.
|
|
|
+ """
|
|
|
+ from reflex.vars import Var
|
|
|
+
|
|
|
+ if (
|
|
|
+ is_literal(expected_type)
|
|
|
+ and not isinstance(value, Var) # validating vars is not supported yet.
|
|
|
+ and value not in expected_type.__args__
|
|
|
+ ):
|
|
|
+ allowed_values = expected_type.__args__
|
|
|
+ if value not in allowed_values:
|
|
|
+ value_str = ",".join(
|
|
|
+ [str(v) if not isinstance(v, str) else f"'{v}'" for v in allowed_values]
|
|
|
+ )
|
|
|
+ raise ValueError(
|
|
|
+ f"prop value for {str(key)} of the `{comp_name}` component should be one of the following: {value_str}. Got '{value}' instead"
|
|
|
+ )
|
|
|
+
|
|
|
+
|
|
|
+def validate_parameter_literals(func):
|
|
|
+ """Decorator to check that the arguments passed to a function
|
|
|
+ correspond to the correct function parameter if it (the parameter)
|
|
|
+ is a literal type.
|
|
|
+
|
|
|
+ Args:
|
|
|
+ func: The function to validate.
|
|
|
+
|
|
|
+ Returns:
|
|
|
+ The wrapper function.
|
|
|
+ """
|
|
|
+
|
|
|
+ @wraps(func)
|
|
|
+ def wrapper(*args, **kwargs):
|
|
|
+ func_params = list(inspect.signature(func).parameters.items())
|
|
|
+ annotations = {param[0]: param[1].annotation for param in func_params}
|
|
|
+
|
|
|
+ # validate args
|
|
|
+ for param, arg in zip(annotations.keys(), args):
|
|
|
+ if annotations[param] is inspect.Parameter.empty:
|
|
|
+ continue
|
|
|
+ validate_literal(param, arg, annotations[param], func.__name__)
|
|
|
+
|
|
|
+ # validate kwargs.
|
|
|
+ for key, value in kwargs.items():
|
|
|
+ annotation = annotations.get(key)
|
|
|
+ if not annotation or annotation is inspect.Parameter.empty:
|
|
|
+ continue
|
|
|
+ validate_literal(key, value, annotation, func.__name__)
|
|
|
+ return func(*args, **kwargs)
|
|
|
+
|
|
|
+ return wrapper
|
|
|
+
|
|
|
+
|
|
|
# Store this here for performance.
|
|
|
StateBases = get_base_class(StateVar)
|
|
|
StateIterBases = get_base_class(StateIterVar)
|