[BACKEND] Allow list validators to support length-1 lists without defining a yaml list (#126)
This commit is contained in:
parent
3d6b0410f3
commit
962b26d1ec
2 changed files with 46 additions and 14 deletions
|
|
@ -11,7 +11,8 @@ from typing import final
|
||||||
|
|
||||||
from ytdl_sub.utils.exceptions import ValidationException
|
from ytdl_sub.utils.exceptions import ValidationException
|
||||||
|
|
||||||
V = TypeVar("V", bound=ValidationException)
|
ValidationExceptionT = TypeVar("ValidationExceptionT", bound=ValidationException)
|
||||||
|
ValidatorT = TypeVar("ValidatorT", bound="Validator")
|
||||||
|
|
||||||
|
|
||||||
class Validator(ABC):
|
class Validator(ABC):
|
||||||
|
|
@ -40,8 +41,10 @@ class Validator(ABC):
|
||||||
)
|
)
|
||||||
|
|
||||||
def _validation_exception(
|
def _validation_exception(
|
||||||
self, error_message: str | Exception, exception_class: Type[V] = ValidationException
|
self,
|
||||||
) -> V:
|
error_message: str | Exception,
|
||||||
|
exception_class: Type[ValidationExceptionT] = ValidationException,
|
||||||
|
) -> ValidationExceptionT:
|
||||||
"""
|
"""
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
|
|
@ -92,10 +95,7 @@ class StringValidator(Validator):
|
||||||
return self._value
|
return self._value
|
||||||
|
|
||||||
|
|
||||||
T = TypeVar("T", bound=Validator)
|
class ListValidator(Validator, ABC, Generic[ValidatorT]):
|
||||||
|
|
||||||
|
|
||||||
class ListValidator(Validator, ABC, Generic[T]):
|
|
||||||
"""
|
"""
|
||||||
Validates a list of objects to validate
|
Validates a list of objects to validate
|
||||||
"""
|
"""
|
||||||
|
|
@ -103,17 +103,22 @@ class ListValidator(Validator, ABC, Generic[T]):
|
||||||
_expected_value_type = list
|
_expected_value_type = list
|
||||||
_expected_value_type_name = "list"
|
_expected_value_type_name = "list"
|
||||||
|
|
||||||
_inner_list_type: Type[T]
|
_inner_list_type: Type[ValidatorT]
|
||||||
|
|
||||||
def __init__(self, name, value):
|
def __init__(self, name, value):
|
||||||
|
# If the value isn't actually a list, but a single value with the same type as the
|
||||||
|
# _inner_list_type, cast it to a list with a single element
|
||||||
|
if isinstance(value, self._inner_list_type._expected_value_type):
|
||||||
|
value = [value]
|
||||||
|
|
||||||
super().__init__(name, value)
|
super().__init__(name, value)
|
||||||
self._list: List[T] = [
|
self._list: List[ValidatorT] = [
|
||||||
self._inner_list_type(name=f"{name}.{i+1}", value=val)
|
self._inner_list_type(name=f"{name}.{i+1}", value=val)
|
||||||
for i, val in enumerate(self._value)
|
for i, val in enumerate(self._value)
|
||||||
]
|
]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def list(self) -> List[T]:
|
def list(self) -> List[ValidatorT]:
|
||||||
"""
|
"""
|
||||||
Returns
|
Returns
|
||||||
-------
|
-------
|
||||||
|
|
@ -168,9 +173,9 @@ class DictValidator(Validator):
|
||||||
def _validate_key(
|
def _validate_key(
|
||||||
self,
|
self,
|
||||||
key: str,
|
key: str,
|
||||||
validator: Type[T],
|
validator: Type[ValidatorT],
|
||||||
default: Optional[Any] = None,
|
default: Optional[Any] = None,
|
||||||
) -> T:
|
) -> ValidatorT:
|
||||||
"""
|
"""
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
|
|
@ -201,9 +206,9 @@ class DictValidator(Validator):
|
||||||
def _validate_key_if_present(
|
def _validate_key_if_present(
|
||||||
self,
|
self,
|
||||||
key: str,
|
key: str,
|
||||||
validator: Type[T],
|
validator: Type[ValidatorT],
|
||||||
default: Optional[Any] = None,
|
default: Optional[Any] = None,
|
||||||
) -> Optional[T]:
|
) -> Optional[ValidatorT]:
|
||||||
"""
|
"""
|
||||||
If the key does not exist in the dict, and no default is provided, return None.
|
If the key does not exist in the dict, and no default is provided, return None.
|
||||||
Otherwise, validate the key.
|
Otherwise, validate the key.
|
||||||
|
|
@ -239,3 +244,24 @@ class LiteralDictValidator(DictValidator):
|
||||||
def keys(self) -> List[str]:
|
def keys(self) -> List[str]:
|
||||||
"""Returns a sorted list of the dict's keys"""
|
"""Returns a sorted list of the dict's keys"""
|
||||||
return super()._keys
|
return super()._keys
|
||||||
|
|
||||||
|
|
||||||
|
class UniformDictValidator(DictValidator, Generic[ValidatorT], ABC):
|
||||||
|
"""DictValidator where all values have the same validator"""
|
||||||
|
|
||||||
|
uniform_validator_class: Type[ValidatorT]
|
||||||
|
|
||||||
|
def __init__(self, name, value):
|
||||||
|
super().__init__(name, value)
|
||||||
|
|
||||||
|
for key in self._keys:
|
||||||
|
_ = self._validate_key_if_present(key=key, validator=self.uniform_validator_class)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def validator_dict(self) -> Dict[str, ValidatorT]:
|
||||||
|
"""
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
Dict of name: validator
|
||||||
|
"""
|
||||||
|
return self._validator_dict
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,12 @@ def test_regex_list_validator_matches():
|
||||||
assert regex_list.match_any(raw_val) == []
|
assert regex_list.match_any(raw_val) == []
|
||||||
|
|
||||||
|
|
||||||
|
def test_regex_list_with_string_value():
|
||||||
|
regest_list_with_string_value = "try matching this"
|
||||||
|
regex_list = RegexListValidator(name="list val", value=regest_list_with_string_value)
|
||||||
|
assert regex_list.match_any(regest_list_with_string_value) == []
|
||||||
|
|
||||||
|
|
||||||
def test_regex_list_validator_captures():
|
def test_regex_list_validator_captures():
|
||||||
regex_list_validator_raw_value = ["try (.+) this", "try (.+) that", "how (.+) this"]
|
regex_list_validator_raw_value = ["try (.+) this", "try (.+) that", "how (.+) this"]
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue