Add a declarative mode.
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
import pkg_resources
|
import pkg_resources
|
||||||
|
import itertools
|
||||||
|
|
||||||
def resolve_dotted(dottedname, package=None):
|
def resolve_dotted(dottedname, package=None):
|
||||||
if dottedname.startswith('.') or dottedname.startswith(':'):
|
if dottedname.startswith('.') or dottedname.startswith(':'):
|
||||||
@@ -310,6 +311,8 @@ class Integer(object):
|
|||||||
def deserialize(self, struct, value):
|
def deserialize(self, struct, value):
|
||||||
return self._validate(struct, value)
|
return self._validate(struct, value)
|
||||||
|
|
||||||
|
Int = Integer
|
||||||
|
|
||||||
class GlobalObject(object):
|
class GlobalObject(object):
|
||||||
""" A type representing an importable Python object """
|
""" A type representing an importable Python object """
|
||||||
def __init__(self, package):
|
def __init__(self, package):
|
||||||
@@ -331,12 +334,19 @@ class GlobalObject(object):
|
|||||||
'The dotted name %r cannot be imported' % value)
|
'The dotted name %r cannot be imported' % value)
|
||||||
|
|
||||||
class Structure(object):
|
class Structure(object):
|
||||||
def __init__(self, name, typ, *structs, **kw):
|
_counter = itertools.count()
|
||||||
self.name = name
|
|
||||||
|
def __new__(cls, *arg, **kw):
|
||||||
|
inst = object.__new__(cls)
|
||||||
|
inst._order = cls._counter.next()
|
||||||
|
return inst
|
||||||
|
|
||||||
|
def __init__(self, typ, *structs, **kw):
|
||||||
self.typ = typ
|
self.typ = typ
|
||||||
self.validator = kw.get('validator', None)
|
self.validator = kw.get('validator', None)
|
||||||
self.default = kw.get('default', None)
|
self.default = kw.get('default', None)
|
||||||
self.required = kw.get('required', True)
|
self.required = kw.get('required', True)
|
||||||
|
self.name = kw.get('name', '')
|
||||||
self.structs = list(structs)
|
self.structs = list(structs)
|
||||||
|
|
||||||
def serialize(self, value):
|
def serialize(self, value):
|
||||||
@@ -351,3 +361,44 @@ class Structure(object):
|
|||||||
def add(self, struct):
|
def add(self, struct):
|
||||||
self.structs.append(struct)
|
self.structs.append(struct)
|
||||||
|
|
||||||
|
class _SchemaMeta(type):
|
||||||
|
def __init__(cls, name, bases, clsattrs):
|
||||||
|
structs = []
|
||||||
|
for name, value in clsattrs.items():
|
||||||
|
if isinstance(value, Structure):
|
||||||
|
value.name = name
|
||||||
|
structs.append((value._order, value))
|
||||||
|
cls.__schema_structures__ = structs
|
||||||
|
# Combine all attrs from this class and its subclasses.
|
||||||
|
extended = []
|
||||||
|
for c in cls.__mro__:
|
||||||
|
extended.extend(getattr(c, '__schema_structures__', []))
|
||||||
|
# Sort the attrs to maintain the order as defined, and assign to the
|
||||||
|
# class.
|
||||||
|
extended.sort()
|
||||||
|
cls.structs = [x[1] for x in extended]
|
||||||
|
|
||||||
|
class Schema(object):
|
||||||
|
struct_type = Mapping
|
||||||
|
__metaclass__ = _SchemaMeta
|
||||||
|
|
||||||
|
def __new__(cls, *args, **kw):
|
||||||
|
inst = object.__new__(Structure)
|
||||||
|
inst.name = None
|
||||||
|
inst._order = Structure._counter.next()
|
||||||
|
struct = cls.struct_type(*args, **kw)
|
||||||
|
inst.__init__(struct)
|
||||||
|
for s in cls.structs:
|
||||||
|
inst.add(s)
|
||||||
|
return inst
|
||||||
|
|
||||||
|
MappingSchema = Schema
|
||||||
|
|
||||||
|
class SequenceSchema(Schema):
|
||||||
|
struct_type = Sequence
|
||||||
|
|
||||||
|
class TupleSchema(Schema):
|
||||||
|
struct_type = Tuple
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
150
cereal/tests.py
150
cereal/tests.py
@@ -1,67 +1,6 @@
|
|||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
class TestFunctional(unittest.TestCase):
|
class TestFunctional(object):
|
||||||
def _makeSchema(self):
|
|
||||||
import cereal
|
|
||||||
|
|
||||||
integer = cereal.Structure(
|
|
||||||
'int',
|
|
||||||
cereal.Integer(),
|
|
||||||
validator=cereal.Range(0, 10)
|
|
||||||
)
|
|
||||||
|
|
||||||
ob = cereal.Structure(
|
|
||||||
'ob',
|
|
||||||
cereal.GlobalObject(package=cereal),
|
|
||||||
)
|
|
||||||
|
|
||||||
tup = cereal.Structure(
|
|
||||||
'tup',
|
|
||||||
cereal.Tuple(),
|
|
||||||
cereal.Structure(
|
|
||||||
'tupint',
|
|
||||||
cereal.Integer(),
|
|
||||||
),
|
|
||||||
cereal.Structure(
|
|
||||||
'tupstring',
|
|
||||||
cereal.String(),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
seq = cereal.Structure(
|
|
||||||
'seq',
|
|
||||||
cereal.Sequence(tup),
|
|
||||||
)
|
|
||||||
|
|
||||||
seq2 = cereal.Structure(
|
|
||||||
'seq2',
|
|
||||||
cereal.Sequence(
|
|
||||||
cereal.Structure(
|
|
||||||
'mapping',
|
|
||||||
cereal.Mapping(),
|
|
||||||
cereal.Structure(
|
|
||||||
'key',
|
|
||||||
cereal.Integer(),
|
|
||||||
),
|
|
||||||
cereal.Structure(
|
|
||||||
'key2',
|
|
||||||
cereal.Integer(),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
schema = cereal.Structure(
|
|
||||||
None,
|
|
||||||
cereal.Mapping(),
|
|
||||||
integer,
|
|
||||||
ob,
|
|
||||||
tup,
|
|
||||||
seq,
|
|
||||||
seq2)
|
|
||||||
|
|
||||||
return schema
|
|
||||||
|
|
||||||
def test_deserialize_ok(self):
|
def test_deserialize_ok(self):
|
||||||
import cereal.tests
|
import cereal.tests
|
||||||
data = {
|
data = {
|
||||||
@@ -108,3 +47,90 @@ class TestFunctional(unittest.TestCase):
|
|||||||
except cereal.Invalid, e:
|
except cereal.Invalid, e:
|
||||||
errors = e.asdict()
|
errors = e.asdict()
|
||||||
self.assertEqual(errors, expected)
|
self.assertEqual(errors, expected)
|
||||||
|
|
||||||
|
class TestImperative(unittest.TestCase, TestFunctional):
|
||||||
|
|
||||||
|
def _makeSchema(self):
|
||||||
|
import cereal
|
||||||
|
|
||||||
|
integer = cereal.Structure(
|
||||||
|
cereal.Integer(),
|
||||||
|
name='int',
|
||||||
|
validator=cereal.Range(0, 10)
|
||||||
|
)
|
||||||
|
|
||||||
|
ob = cereal.Structure(
|
||||||
|
cereal.GlobalObject(package=cereal),
|
||||||
|
name='ob',
|
||||||
|
)
|
||||||
|
|
||||||
|
tup = cereal.Structure(
|
||||||
|
cereal.Tuple(),
|
||||||
|
cereal.Structure(
|
||||||
|
cereal.Integer(),
|
||||||
|
name='tupint',
|
||||||
|
),
|
||||||
|
cereal.Structure(
|
||||||
|
cereal.String(),
|
||||||
|
name='tupstring',
|
||||||
|
),
|
||||||
|
name='tup',
|
||||||
|
)
|
||||||
|
|
||||||
|
seq = cereal.Structure(
|
||||||
|
cereal.Sequence(tup),
|
||||||
|
name='seq',
|
||||||
|
)
|
||||||
|
|
||||||
|
seq2 = cereal.Structure(
|
||||||
|
cereal.Sequence(
|
||||||
|
cereal.Structure(
|
||||||
|
cereal.Mapping(),
|
||||||
|
cereal.Structure(
|
||||||
|
cereal.Integer(),
|
||||||
|
name='key',
|
||||||
|
),
|
||||||
|
cereal.Structure(
|
||||||
|
cereal.Integer(),
|
||||||
|
name='key2',
|
||||||
|
),
|
||||||
|
name='mapping',
|
||||||
|
)
|
||||||
|
),
|
||||||
|
name='seq2',
|
||||||
|
)
|
||||||
|
|
||||||
|
schema = cereal.Structure(
|
||||||
|
cereal.Mapping(),
|
||||||
|
integer,
|
||||||
|
ob,
|
||||||
|
tup,
|
||||||
|
seq,
|
||||||
|
seq2)
|
||||||
|
|
||||||
|
return schema
|
||||||
|
|
||||||
|
class TestDeclarative(unittest.TestCase, TestFunctional):
|
||||||
|
|
||||||
|
def _makeSchema(self):
|
||||||
|
|
||||||
|
import cereal
|
||||||
|
|
||||||
|
class TupleSchema(cereal.TupleSchema):
|
||||||
|
tupint = cereal.Structure(cereal.Int())
|
||||||
|
tupstring = cereal.Structure(cereal.String())
|
||||||
|
|
||||||
|
class MappingSchema(cereal.MappingSchema):
|
||||||
|
key = cereal.Structure(cereal.Int())
|
||||||
|
key2 = cereal.Structure(cereal.Int())
|
||||||
|
|
||||||
|
class MainSchema(cereal.MappingSchema):
|
||||||
|
int = cereal.Structure(cereal.Int(), validator=cereal.Range(0, 10))
|
||||||
|
ob = cereal.Structure(cereal.GlobalObject(package=cereal))
|
||||||
|
seq = cereal.Structure(cereal.Sequence(TupleSchema()))
|
||||||
|
tup = TupleSchema()
|
||||||
|
seq2 = cereal.SequenceSchema(MappingSchema())
|
||||||
|
|
||||||
|
schema = MainSchema()
|
||||||
|
return schema
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user