committed by
GitHub
13 changed files with 345 additions and 314 deletions
@ -1,30 +1,156 @@ |
|||||
import skema |
import six |
||||
|
import inspect |
||||
import functools |
import functools |
||||
|
|
||||
from disco.util import skema_find_recursive_by_type |
from datetime import datetime as real_datetime |
||||
# from disco.util.types import DeferredModel |
|
||||
|
|
||||
|
DATETIME_FORMATS = [ |
||||
|
'%Y-%m-%dT%H:%M:%S.%f', |
||||
|
'%Y-%m-%dT%H:%M:%S' |
||||
|
] |
||||
|
|
||||
|
|
||||
|
def _make(typ, data, client): |
||||
|
args, _, _, _ = inspect.getargspec(typ) |
||||
|
if 'client' in args: |
||||
|
return typ(data, client) |
||||
|
return typ(data) |
||||
|
|
||||
|
|
||||
|
def snowflake(data): |
||||
|
return int(data) if data else None |
||||
|
|
||||
|
|
||||
|
def enum(typ): |
||||
|
def _f(data): |
||||
|
return typ.get(data) if data else None |
||||
|
return _f |
||||
|
|
||||
|
|
||||
|
def listof(typ): |
||||
|
def _f(data, client=None): |
||||
|
if not data: |
||||
|
return [] |
||||
|
return [_make(typ, obj, client) for obj in data] |
||||
|
return _f |
||||
|
|
||||
|
|
||||
|
def dictof(typ, key=None): |
||||
|
def _f(data, client=None): |
||||
|
if not data: |
||||
|
return {} |
||||
|
|
||||
|
if key: |
||||
|
return { |
||||
|
getattr(v, key): v for v in ( |
||||
|
_make(typ, i, client) for i in data |
||||
|
)} |
||||
|
else: |
||||
|
return {k: _make(typ, v, client) for k, v in six.iteritems(data)} |
||||
|
return _f |
||||
|
|
||||
|
|
||||
|
def alias(typ, name): |
||||
|
return ('alias', name, typ) |
||||
|
|
||||
|
|
||||
|
def datetime(data): |
||||
|
if not data: |
||||
|
return None |
||||
|
|
||||
|
for fmt in DATETIME_FORMATS: |
||||
|
try: |
||||
|
return real_datetime.strptime(data.rsplit('+', 1)[0], fmt) |
||||
|
except (ValueError, TypeError): |
||||
|
continue |
||||
|
|
||||
|
raise ValueError('Failed to conver `{}` to datetime'.format(data)) |
||||
|
|
||||
|
|
||||
|
def text(obj): |
||||
|
return six.text_type(obj) if obj else six.text_type() |
||||
|
|
||||
|
|
||||
|
def binary(obj): |
||||
|
return six.text_type(obj) if obj else six.text_type() |
||||
|
|
||||
|
|
||||
|
class ModelMeta(type): |
||||
|
def __new__(cls, name, parents, dct): |
||||
|
fields = {} |
||||
|
for k, v in six.iteritems(dct): |
||||
|
if isinstance(v, tuple): |
||||
|
if v[0] == 'alias': |
||||
|
fields[v[1]] = (k, v[2]) |
||||
|
continue |
||||
|
|
||||
|
if inspect.isclass(v): |
||||
|
fields[k] = v |
||||
|
elif callable(v): |
||||
|
args, _, _, _ = inspect.getargspec(v) |
||||
|
if 'self' in args: |
||||
|
continue |
||||
|
|
||||
|
fields[k] = v |
||||
|
|
||||
|
dct['_fields'] = fields |
||||
|
return super(ModelMeta, cls).__new__(cls, name, parents, dct) |
||||
|
|
||||
|
|
||||
|
class Model(six.with_metaclass(ModelMeta)): |
||||
|
def __init__(self, obj, client=None): |
||||
|
self.client = client |
||||
|
|
||||
|
for name, typ in self.__class__._fields.items(): |
||||
|
dest_name = name |
||||
|
|
||||
|
if isinstance(typ, tuple): |
||||
|
dest_name, typ = typ |
||||
|
|
||||
|
if name not in obj or not obj[name]: |
||||
|
if inspect.isclass(typ) and issubclass(typ, Model): |
||||
|
res = None |
||||
|
elif isinstance(typ, type): |
||||
|
res = typ() |
||||
|
else: |
||||
|
res = typ(None) |
||||
|
setattr(self, dest_name, res) |
||||
|
continue |
||||
|
|
||||
|
try: |
||||
|
if client: |
||||
|
args, _, _, _ = inspect.getargspec(typ) |
||||
|
if 'client' in args: |
||||
|
v = typ(obj[name], client) |
||||
|
else: |
||||
|
v = typ(obj[name]) |
||||
|
else: |
||||
|
v = typ(obj[name]) |
||||
|
except Exception: |
||||
|
print('Failed during parsing of field {} => {} (`{}`)'.format(name, typ, obj[name])) |
||||
|
raise |
||||
|
|
||||
|
if client and isinstance(v, Model): |
||||
|
v.client = client |
||||
|
|
||||
|
setattr(self, dest_name, v) |
||||
|
|
||||
class BaseType(skema.Model): |
|
||||
def update(self, other): |
def update(self, other): |
||||
for name, field in other.__class__._fields.items(): |
for name in six.iterkeys(self.__class__._fields): |
||||
value = getattr(other, name) |
value = getattr(other, name) |
||||
if value: |
if value: |
||||
setattr(self, name, value) |
setattr(self, name, value) |
||||
|
|
||||
@classmethod |
# Clear cached properties |
||||
def create(cls, client, data): |
for name in dir(type(self)): |
||||
obj = cls(data) |
if isinstance(getattr(type(self), name), property): |
||||
|
delattr(self, name) |
||||
# Valdiate |
|
||||
obj.validate() |
|
||||
|
|
||||
for field, value in skema_find_recursive_by_type(obj, skema.ModelType): |
|
||||
value.client = client |
|
||||
|
|
||||
obj.client = client |
@classmethod |
||||
return obj |
def create(cls, client, data): |
||||
|
return cls(data, client) |
||||
|
|
||||
@classmethod |
@classmethod |
||||
def create_map(cls, client, data): |
def create_map(cls, client, data): |
||||
return map(functools.partial(cls.create, client), data) |
return list(map(functools.partial(cls.create, client), data)) |
||||
|
@ -1,36 +0,0 @@ |
|||||
from skema import BaseType, DictType |
|
||||
|
|
||||
|
|
||||
class PreHookType(BaseType): |
|
||||
_hashable = False |
|
||||
|
|
||||
def __init__(self, func, field, **kwargs): |
|
||||
self.func = func |
|
||||
self.field = field |
|
||||
|
|
||||
super(PreHookType, self).__init__(**kwargs) |
|
||||
|
|
||||
def to_python(self, value): |
|
||||
value = self.func(value) |
|
||||
return self.field.to_python(value) |
|
||||
|
|
||||
def to_storage(self, *args, **kwargs): |
|
||||
return self.field.to_storage(*args, **kwargs) |
|
||||
|
|
||||
|
|
||||
class ListToDictType(DictType): |
|
||||
def __init__(self, key, *args, **kwargs): |
|
||||
super(ListToDictType, self).__init__(*args, **kwargs) |
|
||||
self.key = key |
|
||||
|
|
||||
def to_python(self, value): |
|
||||
if not value: |
|
||||
return {} |
|
||||
|
|
||||
to_python = self.field.to_python |
|
||||
|
|
||||
obj = {} |
|
||||
for item in value: |
|
||||
item = to_python(item) |
|
||||
obj[getattr(item, self.key)] = item |
|
||||
return obj |
|
Loading…
Reference in new issue