Skip to content

Commit

Permalink
refactor model class
Browse files Browse the repository at this point in the history
  • Loading branch information
guyskk committed Dec 13, 2023
1 parent c04f01f commit 648fb85
Show file tree
Hide file tree
Showing 2 changed files with 95 additions and 92 deletions.
101 changes: 92 additions & 9 deletions src/validr/_validator_c.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -198,23 +198,14 @@ cdef bint is_dict(obj):
# hasattr check can speed up about 30%
return hasattr(obj, '__getitem__') and hasattr(obj, 'get')

def py_is_dict(obj):
return is_dict(obj)


cdef inline get_dict_value(obj, str key):
return obj.get(key, None)

def py_get_dict_value(obj, key):
return get_dict_value(obj, key)


cdef inline get_object_value(obj, str key):
return getattr(obj, key, None)

def py_get_object_value(obj, key):
return get_object_value(obj, key)


cdef inline bint _is_empty(value):
return value is None or value == ''
Expand Down Expand Up @@ -1279,3 +1270,95 @@ def create_enum_validator(str name, items, bint string=True):
return validator(accept=str, output=str)(enum_validator)
else:
return validator(accept=object, output=object)(enum_validator)


cdef class _Field:

cdef str name

def __init__(self, str name, schema, compiler):
self.name = name
self.__schema__ = schema
with mark_key(self.name):
self.validate = compiler.compile(schema)

def __repr__(self):
info = "schema={!r}".format(self.__schema__)
return "Field(name={!r}, {})".format(self.name, info)

def __get__(self, obj, obj_type):
if obj is None:
return self
return obj.__dict__.get(self.name, None)

def __set__(self, obj, value):
with mark_key(self.name):
value = self.validate(value)
obj.__dict__[self.name] = value


class Field(_Field): pass


cdef _value_asdict(value):
if hasattr(value, '__asdict__'):
return value.__asdict__()
elif is_dict(value):
return {k: _value_asdict(v) for k, v in value.items()}
elif isinstance(value, (list, tuple, set)):
return [_value_asdict(x) for x in value]
else:
return value


def py_model_init(self, obj, params):
params_set = set(params)
errors = []
cdef str k
if obj:
if len(obj) > 1:
msg = (
"__init__() takes 2 positional arguments "
"but {} were given".format(len(obj) + 1)
)
raise TypeError(msg)
obj = obj[0]
if is_dict(obj):
getter = get_dict_value
else:
getter = get_object_value
for k in self.__fields__ - params_set:
try:
setattr(self, k, getter(obj, k))
except Invalid as ex:
errors.append(ex)
else:
for k in self.__fields__ - params_set:
try:
setattr(self, k, None)
except Invalid as ex:
errors.append(ex)
for k in self.__fields__ & params_set:
try:
setattr(self, k, params[k])
except Invalid as ex:
errors.append(ex)
for k in params_set - self.__fields__:
errors.append(Invalid("undesired key").mark_key(k))
if errors:
raise ModelInvalid(errors)


def py_model_asdict(self, keys=None):
if not keys:
keys = self.__fields__
else:
keys = set(keys) & self.__fields__
ret = {}
cdef str k
for k in keys:
v = getattr(self, k)
if v is not None:
v = _value_asdict(v)
ret[k] = v
return ret
86 changes: 3 additions & 83 deletions src/validr/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,7 @@
but works differently.
"""
from .schema import Compiler, Schema, T
from .validator import Invalid, ModelInvalid
from .validator import py_get_dict_value as get_dict_value
from .validator import py_get_object_value as get_object_value
from .validator import py_is_dict as is_dict
from .validator import py_mark_key as mark_key
from .validator import Field, py_model_asdict, py_model_init


class ImmutableInstanceError(AttributeError):
Expand All @@ -24,17 +20,6 @@ def decorator(cls):
return decorator


def _value_asdict(value):
if hasattr(value, '__asdict__'):
return value.__asdict__()
elif is_dict(value):
return {k: _value_asdict(v) for k, v in value.items()}
elif isinstance(value, (list, tuple, set)):
return [_value_asdict(x) for x in value]
else:
return value


def _extract_schemas(cls):
schemas = {}
for k, v in vars(cls).items():
Expand All @@ -54,28 +39,6 @@ def _extract_post_init(cls):
return f


class Field:
def __init__(self, name, schema, compiler):
self.name = name
self.__schema__ = schema
with mark_key(self.name):
self.validate = compiler.compile(schema)

def __repr__(self):
info = "schema={!r}".format(self.__schema__)
return "Field(name={!r}, {})".format(self.name, info)

def __get__(self, obj, obj_type):
if obj is None:
return self
return obj.__dict__.get(self.name, None)

def __set__(self, obj, value):
with mark_key(self.name):
value = self.validate(value)
obj.__dict__[self.name] = value


def _create_model_class(model_cls, compiler, immutable):

compiler = compiler or Compiler()
Expand Down Expand Up @@ -127,40 +90,7 @@ class Model(model_cls, metaclass=ModelMeta):

def __init__(self, *obj, **params):
self.__dict__["__immutable__"] = False
params_set = set(params)
errors = []
if obj:
if len(obj) > 1:
msg = (
"__init__() takes 2 positional arguments "
"but {} were given".format(len(obj) + 1)
)
raise TypeError(msg)
obj = obj[0]
if is_dict(obj):
getter = get_dict_value
else:
getter = get_object_value
for k in self.__fields__ - params_set:
try:
setattr(self, k, getter(obj, k))
except Invalid as ex:
errors.append(ex)
else:
for k in self.__fields__ - params_set:
try:
setattr(self, k, None)
except Invalid as ex:
errors.append(ex)
for k in self.__fields__ & params_set:
try:
setattr(self, k, params[k])
except Invalid as ex:
errors.append(ex)
for k in params_set - self.__fields__:
errors.append(Invalid("undesired key").mark_key(k))
if errors:
raise ModelInvalid(errors)
py_model_init(self, obj, params)
type(self).post_init(self)
self.__dict__["__immutable__"] = immutable

Expand Down Expand Up @@ -211,17 +141,7 @@ def __eq__(self, other):
return True

def __asdict__(self, *, keys=None):
if not keys:
keys = self.__fields__
else:
keys = set(keys) & self.__fields__
ret = {}
for k in keys:
v = getattr(self, k)
if v is not None:
v = _value_asdict(v)
ret[k] = v
return ret
return py_model_asdict(self, keys=keys)

Model.__module__ = model_cls.__module__
Model.__name__ = model_cls.__name__
Expand Down

0 comments on commit 648fb85

Please sign in to comment.