[BACKEND] Allow list validators to support length-1 lists without defining a yaml list (#126)

This commit is contained in:
Jesse Bannon 2022-07-26 09:10:51 -07:00 committed by GitHub
parent 3d6b0410f3
commit 962b26d1ec
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 46 additions and 14 deletions

View file

@ -11,7 +11,8 @@ from typing import final
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):
@ -40,8 +41,10 @@ class Validator(ABC):
)
def _validation_exception(
self, error_message: str | Exception, exception_class: Type[V] = ValidationException
) -> V:
self,
error_message: str | Exception,
exception_class: Type[ValidationExceptionT] = ValidationException,
) -> ValidationExceptionT:
"""
Parameters
----------
@ -92,10 +95,7 @@ class StringValidator(Validator):
return self._value
T = TypeVar("T", bound=Validator)
class ListValidator(Validator, ABC, Generic[T]):
class ListValidator(Validator, ABC, Generic[ValidatorT]):
"""
Validates a list of objects to validate
"""
@ -103,17 +103,22 @@ class ListValidator(Validator, ABC, Generic[T]):
_expected_value_type = list
_expected_value_type_name = "list"
_inner_list_type: Type[T]
_inner_list_type: Type[ValidatorT]
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)
self._list: List[T] = [
self._list: List[ValidatorT] = [
self._inner_list_type(name=f"{name}.{i+1}", value=val)
for i, val in enumerate(self._value)
]
@property
def list(self) -> List[T]:
def list(self) -> List[ValidatorT]:
"""
Returns
-------
@ -168,9 +173,9 @@ class DictValidator(Validator):
def _validate_key(
self,
key: str,
validator: Type[T],
validator: Type[ValidatorT],
default: Optional[Any] = None,
) -> T:
) -> ValidatorT:
"""
Parameters
----------
@ -201,9 +206,9 @@ class DictValidator(Validator):
def _validate_key_if_present(
self,
key: str,
validator: Type[T],
validator: Type[ValidatorT],
default: Optional[Any] = None,
) -> Optional[T]:
) -> Optional[ValidatorT]:
"""
If the key does not exist in the dict, and no default is provided, return None.
Otherwise, validate the key.
@ -239,3 +244,24 @@ class LiteralDictValidator(DictValidator):
def keys(self) -> List[str]:
"""Returns a sorted list of the dict's 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

View file

@ -37,6 +37,12 @@ def test_regex_list_validator_matches():
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():
regex_list_validator_raw_value = ["try (.+) this", "try (.+) that", "how (.+) this"]