mirror of
https://github.com/kennethreitz/pydantic.git
synced 2026-06-05 23:00:18 +00:00
102 lines
3.4 KiB
Python
102 lines
3.4 KiB
Python
from datetime import datetime
|
|
from typing import List, Optional
|
|
|
|
import attr
|
|
import cattr
|
|
from dateutil.parser import parse
|
|
|
|
|
|
class TestCAttrs:
|
|
package = 'attrs + cattrs'
|
|
version = attr.__version__
|
|
|
|
def __init__(self, allow_extra):
|
|
# cf. https://github.com/Tinche/cattrs/issues/26 why at least structure_str is needed
|
|
def structure_str(s, _):
|
|
if not isinstance(s, str):
|
|
raise ValueError()
|
|
return s
|
|
|
|
def structure_int(i, _):
|
|
if not isinstance(i, int):
|
|
raise ValueError()
|
|
return i
|
|
|
|
class PositiveInt(int):
|
|
...
|
|
|
|
def structure_posint(i, x):
|
|
i = PositiveInt(i)
|
|
if not isinstance(i, PositiveInt):
|
|
raise ValueError()
|
|
if i <= 0:
|
|
raise ValueError()
|
|
return i
|
|
|
|
cattr.register_structure_hook(datetime, lambda isostring, _: parse(isostring))
|
|
cattr.register_structure_hook(str, structure_str)
|
|
cattr.register_structure_hook(int, structure_int)
|
|
cattr.register_structure_hook(PositiveInt, structure_posint)
|
|
|
|
def str_len_val(max_len: int, min_len: int = 0, required: bool = False):
|
|
# validate the max len of a string and optionally its min len and whether None is
|
|
# an acceptable value
|
|
def _check_str_len(self, attribute, value):
|
|
if value is None:
|
|
if required:
|
|
raise ValueError("")
|
|
else:
|
|
return
|
|
if len(value) > max_len:
|
|
raise ValueError("")
|
|
if min_len and len(value) < min_len:
|
|
raise ValueError("")
|
|
|
|
return _check_str_len
|
|
|
|
def pos_int(self, attribute, value):
|
|
# Validate that value is a positive >0 integer; None is allowed
|
|
if value is None:
|
|
return
|
|
if value <= 0:
|
|
raise ValueError("")
|
|
|
|
@attr.s(auto_attribs=True, frozen=True, kw_only=True)
|
|
class Skill:
|
|
subject: str
|
|
subject_id: int
|
|
category: str
|
|
qual_level: str
|
|
qual_level_id: int
|
|
qual_level_ranking: float = 0
|
|
|
|
@attr.s(auto_attribs=True, frozen=True, kw_only=True)
|
|
class Location:
|
|
latitude: float = None
|
|
longitude: float = None
|
|
|
|
@attr.s(auto_attribs=True, frozen=True, kw_only=True)
|
|
class Model:
|
|
id: int
|
|
sort_index: float
|
|
client_name: str = attr.ib(validator=str_len_val(255))
|
|
# client_email: EmailStr = None
|
|
client_phone: Optional[str] = attr.ib(default=None, validator=str_len_val(255))
|
|
location: Optional[Location] = None
|
|
|
|
contractor: Optional[PositiveInt]
|
|
upstream_http_referrer: Optional[str] = attr.ib(default=None, validator=str_len_val(1023))
|
|
grecaptcha_response: str = attr.ib(validator=str_len_val(1000, 20, required=True))
|
|
last_updated: Optional[datetime] = None
|
|
skills: List[Skill] = []
|
|
|
|
self.model = Model
|
|
|
|
def validate(self, data):
|
|
try:
|
|
return True, cattr.structure(data, self.model)
|
|
except ValueError as e:
|
|
return False, str(e)
|
|
except TypeError as e:
|
|
return False, str(e)
|