diff --git a/README.txt b/README.txt index 8832c8b..881d706 100644 --- a/README.txt +++ b/README.txt @@ -1,5 +1,5 @@ -cereal -====== +colander +======== An extensible package which can be used to: diff --git a/cereal/__init__.py b/colander/__init__.py similarity index 92% rename from cereal/__init__.py rename to colander/__init__.py index 0c2a9d8..3ac0a22 100644 --- a/cereal/__init__.py +++ b/colander/__init__.py @@ -9,7 +9,7 @@ class Invalid(Exception): the value for a particular structure was not valid. The constructor receives a mandatory ``struct`` argument. This - must be an instance of the :class:`cereal.Structure` class. + must be an instance of the :class:`colander.Structure` class. The constructor also receives an optional ``msg`` keyword argument, defaulting to ``None``. The ``msg`` argument is a @@ -26,7 +26,7 @@ class Invalid(Exception): def add(self, exc): """ Add a child exception; ``exc`` must be an instance of - :class:`cereal.Invalid`""" + :class:`colander.Invalid`""" exc.parent = self self.children.append(exc) @@ -68,7 +68,7 @@ class Invalid(Exception): class All(object): """ Composite validator which succeeds if none of its - subvalidators raises an :class:`cereal.Invalid` exception""" + subvalidators raises an :class:`colander.Invalid` exception""" def __init__(self, *validators): self.validators = validators @@ -119,8 +119,8 @@ class OneOf(object): class Mapping(object): """ A type which represents a mapping of names to structures. - The substructures of the :class:`cereal.Structure` that wraps this - type imply the named keys and values in the mapping. + The substructures of the :class:`colander.Structure` that wraps + this type imply the named keys and values in the mapping. The constructor of a mapping type accepts a single optional keyword argument named ``unknown_keys``. By default, this @@ -132,8 +132,8 @@ class Mapping(object): associated with this type will be ignored during deserialization. - - ``raise`` will cause a :exc:`cereal.Invalid` exception to - be raised when unknown keys are present during deserialization. + - ``raise`` will cause a :exc:`colander.Invalid` exception to be + raised when unknown keys are present during deserialization. - ``preserve`` will preserve the 'raw' unknown keys and values in the returned data structure during deserialization. @@ -215,9 +215,9 @@ class Positional(object): class Tuple(Positional): """ A type which represents a fixed-length sequence of structures. - The substructures of the :class:`cereal.Structure` that wraps this - type imply the positional elements of the tuple in the order they - are added. + The substructures of the :class:`colander.Structure` that wraps + this type imply the positional elements of the tuple in the order + they are added. This type is willing to serialize and deserialized iterables that, when converted to a tuple, have the same number of elements as the @@ -270,14 +270,14 @@ class Tuple(Positional): class Sequence(Positional): """ A type which represents a variable-length sequence of structures, - all of which must be of the same type. This type is defined by the - the :class:`cereal.Structure` instance passed to the constructor - as ``struct``. + all of which must be of the same type. This type is defined by + the the :class:`colander.Structure` instance passed to the + constructor as ``struct``. The ``struct`` argument to this type's constructor is required. - The substructures of the :class:`cereal.Structure` that wraps this - type are ignored. + The substructures of the :class:`colander.Structure` that wraps + this type are ignored. """ def __init__(self, struct): self.struct = struct @@ -326,8 +326,8 @@ class String(object): which should be applied to object serialization. It defaults to ``utf-8`` if not provided. - The substructures of the :class:`cereal.Structure` that wraps this - type are ignored. + The substructures of the :class:`colander.Structure` that wraps + this type are ignored. """ def __init__(self, encoding=None): self.encoding = encoding @@ -353,8 +353,8 @@ Str = String class Integer(object): """ A type representing an integer. - The substructures of the :class:`cereal.Structure` that wraps this - type are ignored. + The substructures of the :class:`colander.Structure` that wraps + this type are ignored. """ def deserialize(self, struct, value): try: @@ -373,8 +373,8 @@ Int = Integer class Float(object): """ A type representing a float. - The substructures of the :class:`cereal.Structure` that wraps this - type are ignored. + The substructures of the :class:`colander.Structure` that wraps + this type are ignored. """ def deserialize(self, struct, value): try: @@ -400,8 +400,8 @@ class Boolean(object): Serialization will produce ``true`` or ``false`` based on the value. - The substructures of the :class:`cereal.Structure` that wraps this - type are ignored. + The substructures of the :class:`colander.Structure` that wraps + this type are ignored. """ def deserialize(self, struct, value): @@ -448,11 +448,11 @@ class GlobalObject(object): argument to this type was passed the ``xml`` module object, the resulting import would be for ``xml.minidom``. If a relative package name is supplied to ``deserialize``, and no ``package`` - was supplied to the constructor, an :exc:`cereal.Invalid` error + was supplied to the constructor, an :exc:`colander.Invalid` error will be raised. - The substructures of the :class:`cereal.Structure` that wraps this - type are ignored. + The substructures of the :class:`colander.Structure` that wraps + this type are ignored. """ def __init__(self, package): self.package = package @@ -533,7 +533,7 @@ class Structure(object): - ``typ`` (required): The 'type' for this structure. It should be an instance of a class that implements the - :class:`cereal.interfaces.Type` interface. + :class:`colander.interfaces.Type` interface. - ``structs``: a sequence of substructures. If the substructures of this structure are not known at construction time, they can @@ -545,9 +545,9 @@ class Structure(object): provided, this structure has no default value and it will be considered 'required' (the ``required`` attribute will be True). - - ``validator``: Optional validator for this structure. It should be - an object that implements the - :class:`cereal.interfaces.Validator` interface. + - ``validator``: Optional validator for this structure. It should + be an object that implements the + :class:`colander.interfaces.Validator` interface. """ _counter = itertools.count() diff --git a/cereal/interfaces.py b/colander/interfaces.py similarity index 74% rename from cereal/interfaces.py rename to colander/interfaces.py index f41014a..a9334b6 100644 --- a/cereal/interfaces.py +++ b/colander/interfaces.py @@ -2,10 +2,10 @@ def Validator(struct, value): """ A validator is called after deserialization of a value. - If ``value`` is not valid, raise a :class:`cereal.Invalid` + If ``value`` is not valid, raise a :class:`colander.Invalid` instance as an exception after. - ``struct`` is a :class:`cereal.Structure` instance which + ``struct`` is a :class:`colander.Structure` instance which contains, among other things, the default value, the name of the value, and a ``required`` flag indicating whether this value is required. It is often ignored in simple validators. @@ -17,18 +17,18 @@ class Type(object): Serialize the object represented by ``value`` to a data structure. The serialization should be composed of one or more objects which can be deserialized by the - :meth:`cereal.interfaces.Type.deserialize` method of this + :meth:`colander.interfaces.Type.deserialize` method of this type. This method should also do type validation of ``value``. - ``struct`` is a :class:`cereal.Structure` instance which + ``struct`` is a :class:`colander.Structure` instance which contains, among other things, the default value, the name of the value, and a ``required`` flag indicating whether this value is required. If the object cannot be serialized, or type validation for - ``value`` fails, a :exc:`cereal.Invalid` exception should be + ``value`` fails, a :exc:`colander.Invalid` exception should be raised. """ @@ -38,18 +38,18 @@ class Type(object): Deserialze the serialization represented by ``value`` to a data structure. The deserialization should be composed of one or more objects which can be serialized by the - :meth:`cereal.interfaces.Type.serialize` method of this + :meth:`colander.interfaces.Type.serialize` method of this type. This method should also do type validation of ``value``. - ``struct`` is a :class:`cereal.Structure` instance which + ``struct`` is a :class:`colander.Structure` instance which contains, among other things, the default value, the name of the value, and a ``required`` flag indicating whether this value is required. If the object cannot be deserialized, or type validation for - ``value`` fails, a :exc:`cereal.Invalid` exception should be + ``value`` fails, a :exc:`colander.Invalid` exception should be raised. """ diff --git a/cereal/tests.py b/colander/tests.py similarity index 85% rename from cereal/tests.py rename to colander/tests.py index a5b71a5..de61b25 100644 --- a/cereal/tests.py +++ b/colander/tests.py @@ -1,7 +1,7 @@ import unittest def invalid_exc(func, *arg, **kw): - from cereal import Invalid + from colander import Invalid try: func(*arg, **kw) except Invalid, e: @@ -11,7 +11,7 @@ def invalid_exc(func, *arg, **kw): class TestInvalid(unittest.TestCase): def _makeOne(self, struct, msg=None, pos=None): - from cereal import Invalid + from colander import Invalid exc = Invalid(struct, msg) exc.pos = pos return exc @@ -36,7 +36,7 @@ class TestInvalid(unittest.TestCase): self.assertEqual(exc._keyname(), 'name') def test__keyname_positional_parent(self): - from cereal import Positional + from colander import Positional parent = Dummy() parent.struct = DummyStructure(Positional()) exc = self._makeOne(None, '') @@ -65,7 +65,7 @@ class TestInvalid(unittest.TestCase): self.assertEqual(paths, [(exc1, exc2, exc3), (exc1, exc4)]) def test_asdict(self): - from cereal import Positional + from colander import Positional struct1 = DummyStructure(None, 'struct1') struct2 = DummyStructure(Positional(), 'struct2') struct3 = DummyStructure(Positional(), 'struct3') @@ -84,7 +84,7 @@ class TestInvalid(unittest.TestCase): class TestAll(unittest.TestCase): def _makeOne(self, validators): - from cereal import All + from colander import All return All(*validators) def test_success(self): @@ -102,7 +102,7 @@ class TestAll(unittest.TestCase): class TestRange(unittest.TestCase): def _makeOne(self, min=None, max=None): - from cereal import Range + from colander import Range return Range(min=min, max=max) def test_success_no_bounds(self): @@ -133,7 +133,7 @@ class TestRange(unittest.TestCase): class TestOneOf(unittest.TestCase): def _makeOne(self, values): - from cereal import OneOf + from colander import OneOf return OneOf(values) def test_success(self): @@ -147,7 +147,7 @@ class TestOneOf(unittest.TestCase): class TestMapping(unittest.TestCase): def _makeOne(self, unknown_keys='ignore'): - from cereal import Mapping + from colander import Mapping return Mapping(unknown_keys=unknown_keys) def test_ctor_bad_unknown_keys(self): @@ -295,7 +295,7 @@ class TestMapping(unittest.TestCase): class TestTuple(unittest.TestCase): def _makeOne(self): - from cereal import Tuple + from colander import Tuple return Tuple() def test_deserialize_not_iterable(self): @@ -398,12 +398,12 @@ class TestTuple(unittest.TestCase): class TestSequence(unittest.TestCase): def _makeOne(self, substruct): - from cereal import Sequence + from colander import Sequence return Sequence(substruct) def test_alias(self): - from cereal import Seq - from cereal import Sequence + from colander import Seq + from colander import Sequence self.assertEqual(Seq, Sequence) def test_deserialize_not_iterable(self): @@ -466,12 +466,12 @@ class TestSequence(unittest.TestCase): class TestString(unittest.TestCase): def _makeOne(self, encoding='utf-8'): - from cereal import String + from colander import String return String(encoding) def test_alias(self): - from cereal import Str - from cereal import String + from colander import Str + from colander import String self.assertEqual(Str, String) def test_deserialize_uncooperative(self): @@ -529,12 +529,12 @@ class TestString(unittest.TestCase): class TestInteger(unittest.TestCase): def _makeOne(self): - from cereal import Integer + from colander import Integer return Integer() def test_alias(self): - from cereal import Int - from cereal import Integer + from colander import Int + from colander import Integer self.assertEqual(Int, Integer) def test_deserialize_fails(self): @@ -567,7 +567,7 @@ class TestInteger(unittest.TestCase): class TestFloat(unittest.TestCase): def _makeOne(self): - from cereal import Float + from colander import Float return Float() def test_deserialize_fails(self): @@ -600,12 +600,12 @@ class TestFloat(unittest.TestCase): class TestBoolean(unittest.TestCase): def _makeOne(self): - from cereal import Boolean + from colander import Boolean return Boolean() def test_alias(self): - from cereal import Bool - from cereal import Boolean + from colander import Bool + from colander import Boolean self.assertEqual(Bool, Boolean) def test_deserialize(self): @@ -633,37 +633,37 @@ class TestBoolean(unittest.TestCase): class TestGlobalObject(unittest.TestCase): def _makeOne(self, package=None): - from cereal import GlobalObject + from colander import GlobalObject return GlobalObject(package) def test_zope_dottedname_style_resolve_absolute(self): typ = self._makeOne() result = typ._zope_dottedname_style(None, - 'cereal.tests.TestGlobalObject') + 'colander.tests.TestGlobalObject') self.assertEqual(result, self.__class__) def test_zope_dottedname_style_irrresolveable_absolute(self): typ = self._makeOne() self.assertRaises(ImportError, typ._zope_dottedname_style, None, - 'cereal.tests.nonexisting') + 'colander.tests.nonexisting') def test__zope_dottedname_style_resolve_relative(self): - import cereal - typ = self._makeOne(package=cereal) + import colander + typ = self._makeOne(package=colander) result = typ._zope_dottedname_style(None, '.tests.TestGlobalObject') self.assertEqual(result, self.__class__) def test__zope_dottedname_style_resolve_relative_leading_dots(self): - import cereal - typ = self._makeOne(package=cereal.tests) + import colander + typ = self._makeOne(package=colander.tests) result = typ._zope_dottedname_style(None, '..tests.TestGlobalObject') self.assertEqual(result, self.__class__) def test__zope_dottedname_style_resolve_relative_is_dot(self): - import cereal.tests - typ = self._makeOne(package=cereal.tests) + import colander.tests + typ = self._makeOne(package=colander.tests) result = typ._zope_dottedname_style(None, '.') - self.assertEqual(result, cereal.tests) + self.assertEqual(result, colander.tests) def test__zope_dottedname_style_irresolveable_relative_is_dot(self): typ = self._makeOne() @@ -679,57 +679,57 @@ class TestGlobalObject(unittest.TestCase): e.msg, "relative name '.whatever' irresolveable without package") def test_zope_dottedname_style_irrresolveable_relative(self): - import cereal.tests - typ = self._makeOne(package=cereal) + import colander.tests + typ = self._makeOne(package=colander) self.assertRaises(ImportError, typ._zope_dottedname_style, None, '.notexisting') def test__zope_dottedname_style_resolveable_relative(self): - import cereal - typ = self._makeOne(package=cereal) + import colander + typ = self._makeOne(package=colander) result = typ._zope_dottedname_style(None, '.tests') - from cereal import tests + from colander import tests self.assertEqual(result, tests) def test__zope_dottedname_style_irresolveable_absolute(self): typ = self._makeOne() self.assertRaises(ImportError, - typ._zope_dottedname_style, None, 'cereal.fudge.bar') + typ._zope_dottedname_style, None, 'colander.fudge.bar') def test__zope_dottedname_style_resolveable_absolute(self): typ = self._makeOne() result = typ._zope_dottedname_style(None, - 'cereal.tests.TestGlobalObject') + 'colander.tests.TestGlobalObject') self.assertEqual(result, self.__class__) def test__pkg_resources_style_resolve_absolute(self): typ = self._makeOne() result = typ._pkg_resources_style(None, - 'cereal.tests:TestGlobalObject') + 'colander.tests:TestGlobalObject') self.assertEqual(result, self.__class__) def test__pkg_resources_style_irrresolveable_absolute(self): typ = self._makeOne() self.assertRaises(ImportError, typ._pkg_resources_style, None, - 'cereal.tests:nonexisting') + 'colander.tests:nonexisting') def test__pkg_resources_style_resolve_relative_startswith_colon(self): - import cereal.tests - typ = self._makeOne(package=cereal.tests) + import colander.tests + typ = self._makeOne(package=colander.tests) result = typ._pkg_resources_style(None, ':TestGlobalObject') self.assertEqual(result, self.__class__) def test__pkg_resources_style_resolve_relative_startswith_dot(self): - import cereal - typ = self._makeOne(package=cereal) + import colander + typ = self._makeOne(package=colander) result = typ._pkg_resources_style(None, '.tests:TestGlobalObject') self.assertEqual(result, self.__class__) def test__pkg_resources_style_resolve_relative_is_dot(self): - import cereal.tests - typ = self._makeOne(package=cereal.tests) + import colander.tests + typ = self._makeOne(package=colander.tests) result = typ._pkg_resources_style(None, '.') - self.assertEqual(result, cereal.tests) + self.assertEqual(result, colander.tests) def test__pkg_resources_style_resolve_relative_nocurrentpackage(self): typ = self._makeOne() @@ -737,8 +737,8 @@ class TestGlobalObject(unittest.TestCase): '.whatever') def test__pkg_resources_style_irrresolveable_relative(self): - import cereal.tests - typ = self._makeOne(package=cereal) + import colander.tests + typ = self._makeOne(package=colander) self.assertRaises(ImportError, typ._pkg_resources_style, None, ':notexisting') @@ -749,12 +749,12 @@ class TestGlobalObject(unittest.TestCase): def test_deserialize_using_pkgresources_style(self): typ = self._makeOne() - result = typ.deserialize(None, 'cereal.tests:TestGlobalObject') + result = typ.deserialize(None, 'colander.tests:TestGlobalObject') self.assertEqual(result, self.__class__) def test_deserialize_using_zope_dottedname_style(self): typ = self._makeOne() - result = typ.deserialize(None, 'cereal.tests.TestGlobalObject') + result = typ.deserialize(None, 'colander.tests.TestGlobalObject') self.assertEqual(result, self.__class__) def test_deserialize_style_raises(self): @@ -764,10 +764,10 @@ class TestGlobalObject(unittest.TestCase): "The dotted name 'cant.be.found' cannot be imported") def test_serialize_ok(self): - import cereal.tests + import colander.tests typ = self._makeOne() - result = typ.serialize(None, cereal.tests) - self.assertEqual(result, 'cereal.tests') + result = typ.serialize(None, colander.tests) + self.assertEqual(result, 'colander.tests') def test_serialize_fail(self): typ = self._makeOne() @@ -776,7 +776,7 @@ class TestGlobalObject(unittest.TestCase): class TestStructure(unittest.TestCase): def _makeOne(self, *arg, **kw): - from cereal import Structure + from colander import Structure return Structure(*arg, **kw) def test_new_sets_order(self): @@ -825,50 +825,50 @@ class TestStructure(unittest.TestCase): class TestSchema(unittest.TestCase): def test_alias(self): - from cereal import Schema - from cereal import MappingSchema + from colander import Schema + from colander import MappingSchema self.assertEqual(Schema, MappingSchema) def test_it(self): - import cereal - class MySchema(cereal.Schema): - thing = cereal.Structure(cereal.String()) + import colander + class MySchema(colander.Schema): + thing = colander.Structure(colander.String()) structure = MySchema(unknown_keys='raise') self.failUnless(hasattr(structure, '_order')) - self.assertEqual(structure.__class__, cereal.Structure) - self.assertEqual(structure.typ.__class__, cereal.Mapping) + self.assertEqual(structure.__class__, colander.Structure) + self.assertEqual(structure.typ.__class__, colander.Mapping) self.assertEqual(structure.typ.unknown_keys, 'raise') - self.assertEqual(structure.structs[0].typ.__class__, cereal.String) + self.assertEqual(structure.structs[0].typ.__class__, colander.String) class TestSequenceSchema(unittest.TestCase): def test_it(self): - import cereal - class MySchema(cereal.SequenceSchema): + import colander + class MySchema(colander.SequenceSchema): pass - inner = cereal.Structure(cereal.String()) + inner = colander.Structure(colander.String()) structure = MySchema(inner) self.failUnless(hasattr(structure, '_order')) - self.assertEqual(structure.__class__, cereal.Structure) - self.assertEqual(structure.typ.__class__, cereal.Sequence) + self.assertEqual(structure.__class__, colander.Structure) + self.assertEqual(structure.typ.__class__, colander.Sequence) self.assertEqual(structure.typ.struct, inner) class TestTupleSchema(unittest.TestCase): def test_it(self): - import cereal - class MySchema(cereal.TupleSchema): - thing = cereal.Structure(cereal.String()) + import colander + class MySchema(colander.TupleSchema): + thing = colander.Structure(colander.String()) structure = MySchema() self.failUnless(hasattr(structure, '_order')) - self.assertEqual(structure.__class__, cereal.Structure) - self.assertEqual(structure.typ.__class__, cereal.Tuple) - self.assertEqual(structure.structs[0].typ.__class__, cereal.String) + self.assertEqual(structure.__class__, colander.Structure) + self.assertEqual(structure.typ.__class__, colander.Tuple) + self.assertEqual(structure.structs[0].typ.__class__, colander.String) class TestFunctional(object): def test_deserialize_ok(self): - import cereal.tests + import colander.tests data = { 'int':'10', - 'ob':'cereal.tests', + 'ob':'colander.tests', 'seq':[('1', 's'),('2', 's'), ('3', 's'), ('4', 's')], 'seq2':[{'key':'1', 'key2':'2'}, {'key':'3', 'key2':'4'}], 'tup':('1', 's'), @@ -876,7 +876,7 @@ class TestFunctional(object): schema = self._makeSchema() result = schema.deserialize(data) self.assertEqual(result['int'], 10) - self.assertEqual(result['ob'], cereal.tests) + self.assertEqual(result['ob'], colander.tests) self.assertEqual(result['seq'], [(1, 's'), (2, 's'), (3, 's'), (4, 's')]) self.assertEqual(result['seq2'], @@ -911,47 +911,47 @@ class TestFunctional(object): class TestImperative(unittest.TestCase, TestFunctional): def _makeSchema(self): - import cereal + import colander - integer = cereal.Structure( - cereal.Integer(), + integer = colander.Structure( + colander.Integer(), name='int', - validator=cereal.Range(0, 10) + validator=colander.Range(0, 10) ) - ob = cereal.Structure( - cereal.GlobalObject(package=cereal), + ob = colander.Structure( + colander.GlobalObject(package=colander), name='ob', ) - tup = cereal.Structure( - cereal.Tuple(), - cereal.Structure( - cereal.Integer(), + tup = colander.Structure( + colander.Tuple(), + colander.Structure( + colander.Integer(), name='tupint', ), - cereal.Structure( - cereal.String(), + colander.Structure( + colander.String(), name='tupstring', ), name='tup', ) - seq = cereal.Structure( - cereal.Sequence(tup), + seq = colander.Structure( + colander.Sequence(tup), name='seq', ) - seq2 = cereal.Structure( - cereal.Sequence( - cereal.Structure( - cereal.Mapping(), - cereal.Structure( - cereal.Integer(), + seq2 = colander.Structure( + colander.Sequence( + colander.Structure( + colander.Mapping(), + colander.Structure( + colander.Integer(), name='key', ), - cereal.Structure( - cereal.Integer(), + colander.Structure( + colander.Integer(), name='key2', ), name='mapping', @@ -960,8 +960,8 @@ class TestImperative(unittest.TestCase, TestFunctional): name='seq2', ) - schema = cereal.Structure( - cereal.Mapping(), + schema = colander.Structure( + colander.Mapping(), integer, ob, tup, @@ -974,22 +974,23 @@ class TestDeclarative(unittest.TestCase, TestFunctional): def _makeSchema(self): - import cereal + import colander - class TupleSchema(cereal.TupleSchema): - tupint = cereal.Structure(cereal.Int()) - tupstring = cereal.Structure(cereal.String()) + class TupleSchema(colander.TupleSchema): + tupint = colander.Structure(colander.Int()) + tupstring = colander.Structure(colander.String()) - class MappingSchema(cereal.MappingSchema): - key = cereal.Structure(cereal.Int()) - key2 = cereal.Structure(cereal.Int()) + class MappingSchema(colander.MappingSchema): + key = colander.Structure(colander.Int()) + key2 = colander.Structure(colander.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())) + class MainSchema(colander.MappingSchema): + int = colander.Structure(colander.Int(), + validator=colander.Range(0, 10)) + ob = colander.Structure(colander.GlobalObject(package=colander)) + seq = colander.Structure(colander.Sequence(TupleSchema())) tup = TupleSchema() - seq2 = cereal.SequenceSchema(MappingSchema()) + seq2 = colander.SequenceSchema(MappingSchema()) schema = MainSchema() return schema @@ -1007,13 +1008,13 @@ class DummyStructure(object): self.structs = [] def deserialize(self, val): - from cereal import Invalid + from colander import Invalid if self.exc: raise Invalid(self, self.exc) return val def serialize(self, val): - from cereal import Invalid + from colander import Invalid if self.exc: raise Invalid(self, self.exc) return val @@ -1023,7 +1024,7 @@ class DummyValidator(object): self.msg = msg def __call__(self, struct, value): - from cereal import Invalid + from colander import Invalid if self.msg: raise Invalid(struct, self.msg) diff --git a/docs/api.rst b/docs/api.rst index e42946a..bee87c8 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -1,10 +1,10 @@ -Cereal API +Colander API ---------- Exceptions ~~~~~~~~~~ -.. automodule:: cereal +.. automodule:: colander .. autoclass:: Invalid :members: diff --git a/docs/conf.py b/docs/conf.py index 43249dd..2ee36fb 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # -# cereal documentation build configuration file +# colander documentation build configuration file # # This file is execfile()d with the current directory set to its containing # dir. @@ -45,7 +45,7 @@ source_suffix = '.rst' master_doc = 'index' # General substitutions. -project = 'cereal' +project = 'colander' copyright = '2010, Repoze Developers ' # The default replacements for |version| and |release|, also used in various @@ -172,7 +172,7 @@ htmlhelp_basename = 'atemplatedoc' # (source start file, target name, title, # author, document class [howto/manual]). latex_documents = [ - ('index', 'atemplate.tex', 'cereal Documentation', + ('index', 'atemplate.tex', 'colander Documentation', 'Repoze Developers', 'manual'), ] diff --git a/docs/index.rst b/docs/index.rst index 8b59158..d84554c 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,9 +1,9 @@ -Cereal -====== +Colander +======== -Cereal is useful as a system for validating and deserializing data +Colander is useful as a system for validating and deserializing data obtained via XML, JSON, an HTML form post or any other equally simple -data serialization. Cereal can be used to: +data serialization. Colander can be used to: - Define a data schema @@ -14,8 +14,8 @@ data serialization. Cereal can be used to: - Serialize an arbitrary Python structure to a data structure composed of strings, mappings, and lists. -Out of the box, Cereal can serialize and deserialize various types of -objects, including: +Out of the box, Colander can serialize and deserialize various types +of objects, including: - A mapping object (e.g. dictionary) @@ -35,11 +35,11 @@ objects, including: - An importable Python object (to a dotted Python object path). -Cereal allows additional data structures to be serialized and +Colander allows additional data structures to be serialized and deserialized by allowing a developer to define new "types". -Defining A Cereal Schema ------------------------- +Defining A Colander Schema +-------------------------- Imagine you want to deserialize and validate a serialization of data you've obtained by reading a YAML document. An example of such a data @@ -69,22 +69,24 @@ different types. .. code-block:: python :linenos: - import cereal + import colander - class Friend(cereal.TupleSchema): - rank = cereal.Structure(cereal.Int(), validator=cereal.Range(0, 9999)) - name = cereal.Structure(cereal.String()) + class Friend(colander.TupleSchema): + rank = colander.Structure(colander.Int(), + validator=colander.Range(0, 9999)) + name = colander.Structure(colander.String()) - class Phone(cereal.MappingSchema): - location = cereal.Structure(cereal.String(), - validator=cereal.OneOf(['home', 'work'])) - number = cereal.Structure(cereal.String()) + class Phone(colander.MappingSchema): + location = colander.Structure(colander.String(), + validator=colander.OneOf(['home', 'work'])) + number = colander.Structure(colander.String()) - class Person(cereal.MappingSchema): - name = cereal.Structure(cereal.String()) - age = cereal.Structure(cereal.Int(), validator=cereal.Range(0, 200)) - friends = cereal.Structure(cereal.Sequence(Friend())) - phones = cereal.Structure(cereal.Sequence(Phone())) + class Person(colander.MappingSchema): + name = colander.Structure(colander.String()) + age = colander.Structure(colander.Int(), + validator=colander.Range(0, 200)) + friends = colander.Structure(colander.Sequence(Friend())) + phones = colander.Structure(colander.Sequence(Phone())) For ease of reading, we've actually defined *three* schemas above, but we coalesce them all into a single ``Person`` schema. As the result @@ -115,12 +117,12 @@ optional deserialization *validator*, an optional *default*, and a slightly less optional *name*. The *type* of a structure indicates its data type (such as -``cereal.Int`` or ``cereal.String``). +``colander.Int`` or ``colander.String``). The *validator* of a structure is called after deserialization; it makes sure the deserialized value matches a constraint. An example of such a validator is provided in the schema above: -``validator=cereal.Range(0, 200)``. A validator is not called after +``validator=colander.Range(0, 200)``. A validator is not called after serialization, only after deserialization. The *default* of a structure indicates its default value if a value @@ -131,35 +133,35 @@ If a structure does not have a default, it is considered required. The *name* of a structure appears in error reports. The name of a structure that is introduced as a class-level attribute -of a ``cereal.MappingSchema`` or ``cereal.TupleSchema`` is its class -attribute name. For example: +of a ``colander.MappingSchema`` or ``colander.TupleSchema`` is its +class attribute name. For example: .. code-block:: python :linenos: - import cereal + import colander - class Phone(cereal.MappingSchema): - location = cereal.Structure(cereal.String(), - validator=cereal.OneOf(['home', 'work'])) - number = cereal.Structure(cereal.String()) + class Phone(colander.MappingSchema): + location = colander.Structure(colander.String(), + validator=colander.OneOf(['home', 'work'])) + number = colander.Structure(colander.String()) The name of the structure defined via ``location = -cereal.Structure(..)`` within the schema above is ``location``. +colander.Structure(..)`` within the schema above is ``location``. Schema Objects ~~~~~~~~~~~~~~ -The result of creating an instance of a ``cereal.MappingSchema`` or -``cereal.TupleSchema`` object is also a *structure* object. +The result of creating an instance of a ``colander.MappingSchema`` or +``colander.TupleSchema`` object is also a *structure* object. -Instantiating a ``cereal.MappingSchema`` creates a structure which has -a *type* value of ``cereal.Mapping``. Instantiating a -``cereal.TupleSchema`` creates a structure which has a *type* value of -``cereal.Tuple``. +Instantiating a ``colander.MappingSchema`` creates a structure which +has a *type* value of ``colander.Mapping``. Instantiating a +``colander.TupleSchema`` creates a structure which has a *type* value +of ``colander.Tuple``. -A structure defined by instantiating a ``cereal.MappingSchema`` or a -``cereal.TupleSchema`` usually has no validator, and has the empty +A structure defined by instantiating a ``colander.MappingSchema`` or a +``colander.TupleSchema`` usually has no validator, and has the empty string as its name. Deserializing A Data Structure Using a Schema @@ -170,22 +172,24 @@ Earlier we defined a schema: .. code-block:: python :linenos: - import cereal + import colander - class Friend(cereal.TupleSchema): - rank = cereal.Structure(cereal.Int(), validator=cereal.Range(0, 9999)) - name = cereal.Structure(cereal.String()) + class Friend(colander.TupleSchema): + rank = colander.Structure(colander.Int(), + validator=colander.Range(0, 9999)) + name = colander.Structure(colander.String()) - class Phone(cereal.MappingSchema): - location = cereal.Structure(cereal.String(), - validator=cereal.OneOf(['home', 'work'])) - number = cereal.Structure(cereal.String()) + class Phone(colander.MappingSchema): + location = colander.Structure(colander.String(), + validator=colander.OneOf(['home', 'work'])) + number = colander.Structure(colander.String()) - class Person(cereal.MappingSchema): - name = cereal.Structure(cereal.String()) - age = cereal.Structure(cereal.Int(), validator=cereal.Range(0, 200)) - friends = cereal.Structure(cereal.Sequence(Friend())) - phones = cereal.Structure(cereal.Sequence(Phone())) + class Person(colander.MappingSchema): + name = colander.Structure(colander.String()) + age = colander.Structure(colander.Int(), + validator=colander.Range(0, 200)) + friends = colander.Structure(colander.Sequence(Friend())) + phones = colander.Structure(colander.Sequence(Phone())) Let's now use this schema to try to deserialize some concrete data structures. @@ -237,7 +241,7 @@ or a validation error? .. code-block:: python :linenos: - import cereal + import colander data = { 'name':'keith', @@ -249,7 +253,7 @@ or a validation error? schema = Person() try: schema.deserialize(data) - except cereal.Invalid, e: + except colander.Invalid, e: print e.asdict() The ``deserialize`` method will raise an exception, and the ``except`` @@ -278,53 +282,56 @@ Defining A Schema Imperatively The above schema we defined was defined declaratively via a set of ``class`` statements. It's often useful to create schemas more -dynamically. For this reason, Cereal offers an "imperative" mode of +dynamically. For this reason, Colander offers an "imperative" mode of schema configuration. Here's our previous declarative schema: .. code-block:: python :linenos: - import cereal + import colander - class Friend(cereal.TupleSchema): - rank = cereal.Structure(cereal.Int(), validator=cereal.Range(0, 9999)) - name = cereal.Structure(cereal.String()) + class Friend(colander.TupleSchema): + rank = colander.Structure(colander.Int(), + validator=colander.Range(0, 9999)) + name = colander.Structure(colander.String()) - class Phone(cereal.MappingSchema): - location = cereal.Structure(cereal.String(), - validator=cereal.OneOf(['home', 'work'])) - number = cereal.Structure(cereal.String()) + class Phone(colander.MappingSchema): + location = colander.Structure(colander.String(), + validator=colander.OneOf(['home', 'work'])) + number = colander.Structure(colander.String()) - class Person(cereal.MappingSchema): - name = cereal.Structure(cereal.String()) - age = cereal.Structure(cereal.Int(), validator=cereal.Range(0, 200)) - friends = cereal.Structure(cereal.Sequence(Friend())) - phones = cereal.Structure(cereal.Sequence(Phone())) + class Person(colander.MappingSchema): + name = colander.Structure(colander.String()) + age = colander.Structure(colander.Int(), + validator=colander.Range(0, 200)) + friends = colander.Structure(colander.Sequence(Friend())) + phones = colander.Structure(colander.Sequence(Phone())) We can imperatively construct a completely equivalent schema like so: .. code-block:: python :linenos: - import cereal + import colander - friend = cereal.Structure(Tuple()) - friend.add(cereal.Structure(cereal.Int(), validator=cereal.Range(0, 9999), + friend = colander.Structure(Tuple()) + friend.add(colander.Structure(colander.Int(), + validator=colander.Range(0, 9999), name='rank')) - friend.add(cereal.Structure(cereal.String()), name='name') + friend.add(colander.Structure(colander.String()), name='name') - phone = cereal.Structure(Mapping()) - phone.add(cereal.Structure(cereal.String(), - validator=cereal.OneOf(['home', 'work']), - name='location')) - phone.add(cereal.Structure(cereal.String(), name='number')) + phone = colander.Structure(Mapping()) + phone.add(colander.Structure(colander.String(), + validator=colander.OneOf(['home', 'work']), + name='location')) + phone.add(colander.Structure(colander.String(), name='number')) - schema = cereal.Structure(Mapping()) - schema.add(cereal.Structure(cereal.String(), name='name')) - schema.add(cereal.Structure(cereal.Int(), name='age'), - validator=cereal.Range(0, 200)) - schema.add(cereal.Structure(cereal.Sequence(friend), name='friends')) - schema.add(cereal.Structure(cereal.Sequence(phone), name='phones')) + schema = colander.Structure(Mapping()) + schema.add(colander.Structure(colander.String(), name='name')) + schema.add(colander.Structure(colander.Int(), name='age'), + validator=colander.Range(0, 200)) + schema.add(colander.Structure(colander.Sequence(friend), name='friends')) + schema.add(colander.Structure(colander.Sequence(phone), name='phones')) Defining a schema imperatively is a lot uglier than defining a schema declaratively, but it's often more useful when you need to define a @@ -385,10 +392,10 @@ Here's how you would use the resulting class as part of a schema: .. code-block:: python :linenos: - import cereal + import colander - class Schema(cereal.MappingSchema): - interested = cereal.Structure(Boolean()) + class Schema(colander.MappingSchema): + interested = colander.Structure(Boolean()) The above schema has a member named ``interested`` which will now be serialized and deserialized as a boolean, according to the logic @@ -399,15 +406,16 @@ Note that the only real constraint of a type class is that its by its ``deserialize`` method and vice versa. For a more formal definition of a the interface of a type, see -:class:`cereal.interfaces.Type`. +:class:`colander.interfaces.Type`. Defining a New Validator ------------------------ A validator is a callable which accepts two positional arguments: ``struct`` and ``value``. It returns ``None`` if the value is valid. -It raises a ``cereal.Invalid`` exception if the value is not valid. -Here's a validator that checks if the value is a valid credit card number. +It raises a ``colander.Invalid`` exception if the value is not valid. +Here's a validator that checks if the value is a valid credit card +number. .. code-block:: python :linenos: @@ -438,18 +446,18 @@ schema: .. code-block:: python :linenos: - import cereal + import colander - class Schema(cereal.MappingSchema): - cc_number = cereal.Structure(cereal.String(), validator=lunhnok) + class Schema(colander.MappingSchema): + cc_number = colander.Structure(colander.String(), validator=lunhnok) Note that the validator doesn't need to check if the ``value`` is a string: this has already been done as the result of the type of the -``cc_number`` structure being ``cereal.String``. Validators are always -passed the *deserialized* value when they are invoked. +``cc_number`` structure being ``colander.String``. Validators are +always passed the *deserialized* value when they are invoked. For a more formal definition of a the interface of a validator, see -:class:`cereal.interfaces.Validator`. +:class:`colander.interfaces.Validator`. Interface and API Documentation ------------------------------- diff --git a/docs/interfaces.rst b/docs/interfaces.rst index 81bd038..633070e 100644 --- a/docs/interfaces.rst +++ b/docs/interfaces.rst @@ -1,7 +1,7 @@ Interfaces ---------- -.. automodule:: cereal.interfaces +.. automodule:: colander.interfaces .. autofunction:: Validator diff --git a/setup.cfg b/setup.cfg index 3a3605d..996032c 100644 --- a/setup.cfg +++ b/setup.cfg @@ -3,8 +3,8 @@ zip_ok = false [nosetests] match=^test -where=cereal +where=colander nocapture=1 -cover-package=cereal +cover-package=colander cover-erase=1 diff --git a/setup.py b/setup.py index b96bf59..c14b4bc 100644 --- a/setup.py +++ b/setup.py @@ -23,7 +23,7 @@ CHANGES = open(os.path.join(here, 'CHANGES.txt')).read() requires = [] -setup(name='cereal', +setup(name='colander', version='0.0', description=('A simple schema-based serialization and deserialization ' 'library'), @@ -35,13 +35,13 @@ setup(name='cereal', keywords='serialize deserialize validate schema validation', author="Agendaless Consulting", author_email="repoze-dev@lists.repoze.org", - url="http://docs.repoze.org/cereal", + url="http://docs.repoze.org/colander", license="BSD-derived (http://www.repoze.org/LICENSE.txt)", packages=find_packages(), include_package_data=True, zip_safe=False, tests_require = requires, install_requires = requires, - test_suite="cereal", + test_suite="colander", )