Add partial work towards schema / validation / serialization / deserialization thingy.
This commit is contained in:
7
CHANGES.txt
Normal file
7
CHANGES.txt
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
Changes
|
||||||
|
=======
|
||||||
|
|
||||||
|
0.0 (unreleased)
|
||||||
|
----------------
|
||||||
|
|
||||||
|
- Initial release.
|
3
COPYRIGHT.txt
Normal file
3
COPYRIGHT.txt
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
Copyright (c) 2010 Agendaless Consulting and Contributors.
|
||||||
|
(http://www.agendaless.com), All Rights Reserved
|
||||||
|
|
41
LICENSE.txt
Normal file
41
LICENSE.txt
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
License
|
||||||
|
|
||||||
|
A copyright notice accompanies this license document that identifies
|
||||||
|
the copyright holders.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are
|
||||||
|
met:
|
||||||
|
|
||||||
|
1. Redistributions in source code must retain the accompanying
|
||||||
|
copyright notice, this list of conditions, and the following
|
||||||
|
disclaimer.
|
||||||
|
|
||||||
|
2. Redistributions in binary form must reproduce the accompanying
|
||||||
|
copyright notice, this list of conditions, and the following
|
||||||
|
disclaimer in the documentation and/or other materials provided
|
||||||
|
with the distribution.
|
||||||
|
|
||||||
|
3. Names of the copyright holders must not be used to endorse or
|
||||||
|
promote products derived from this software without prior
|
||||||
|
written permission from the copyright holders.
|
||||||
|
|
||||||
|
4. If any files are modified, you must cause the modified files to
|
||||||
|
carry prominent notices stating that you changed the files and
|
||||||
|
the date of any change.
|
||||||
|
|
||||||
|
Disclaimer
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND
|
||||||
|
ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||||
|
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||||
|
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||||
|
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||||
|
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
|
||||||
|
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
||||||
|
THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
|
SUCH DAMAGE.
|
||||||
|
|
12
README.txt
Normal file
12
README.txt
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
cereal
|
||||||
|
======
|
||||||
|
|
||||||
|
An extensible package which can be used to:
|
||||||
|
|
||||||
|
- deserialize and validate a data structure composed of strings,
|
||||||
|
mappings, and lists.
|
||||||
|
|
||||||
|
- serialize an arbitrary data structure to a data structure composed
|
||||||
|
of strings, mappings, and lists.
|
||||||
|
|
||||||
|
Please see docs/index.rst for further documentation.
|
371
cereal/__init__.py
Normal file
371
cereal/__init__.py
Normal file
@@ -0,0 +1,371 @@
|
|||||||
|
import pkg_resources
|
||||||
|
|
||||||
|
def resolve_dotted(dottedname, package=None):
|
||||||
|
if dottedname.startswith('.') or dottedname.startswith(':'):
|
||||||
|
if not package:
|
||||||
|
raise ImportError('name "%s" is irresolveable (no package)' %
|
||||||
|
dottedname)
|
||||||
|
if dottedname in ['.', ':']:
|
||||||
|
dottedname = package.__name__
|
||||||
|
else:
|
||||||
|
dottedname = package.__name__ + dottedname
|
||||||
|
return pkg_resources.EntryPoint.parse(
|
||||||
|
'x=%s' % dottedname).load(False)
|
||||||
|
|
||||||
|
class Invalid(Exception):
|
||||||
|
|
||||||
|
pos = None
|
||||||
|
parent = None
|
||||||
|
|
||||||
|
def __init__(self, struct, msg=None):
|
||||||
|
Exception.__init__(self, struct, msg)
|
||||||
|
self.struct = struct
|
||||||
|
self.msg = msg
|
||||||
|
self.subexceptions = []
|
||||||
|
|
||||||
|
def add(self, error):
|
||||||
|
if self.msg is not None:
|
||||||
|
raise ValueError(
|
||||||
|
'Exceptions with a message cannot have subexceptions')
|
||||||
|
error.parent = self
|
||||||
|
self.subexceptions.append(error)
|
||||||
|
|
||||||
|
def expand(self):
|
||||||
|
L = []
|
||||||
|
L.append(self.msg)
|
||||||
|
for exc in self.subexceptions:
|
||||||
|
L.append((exc.pos, self.struct, exc.expand()))
|
||||||
|
return L
|
||||||
|
|
||||||
|
def pprint(self, indent=0):
|
||||||
|
istring = ' ' * indent
|
||||||
|
for exc in self.subexceptions:
|
||||||
|
if exc.msg:
|
||||||
|
print '%s%s (%s): %s' % (
|
||||||
|
istring, exc.struct.name, exc.pos, exc.msg)
|
||||||
|
else:
|
||||||
|
print '%s%s (%s):' % (istring, exc.struct.name, exc.pos)
|
||||||
|
exc.pprint(indent+2)
|
||||||
|
|
||||||
|
def paths(self):
|
||||||
|
# thanks chris rossi ;-)
|
||||||
|
def traverse(node, stack):
|
||||||
|
stack.append(node)
|
||||||
|
|
||||||
|
if not node.subexceptions:
|
||||||
|
yield tuple(stack)
|
||||||
|
|
||||||
|
for child in node.subexceptions:
|
||||||
|
for path in traverse(child, stack):
|
||||||
|
yield path
|
||||||
|
|
||||||
|
stack.pop()
|
||||||
|
|
||||||
|
return traverse(self, [])
|
||||||
|
|
||||||
|
def asdict(self):
|
||||||
|
paths = list(self.paths())
|
||||||
|
D = {}
|
||||||
|
for path in paths:
|
||||||
|
L = []
|
||||||
|
msg = None
|
||||||
|
for exc in path:
|
||||||
|
msg = exc.msg
|
||||||
|
if exc.parent is not None:
|
||||||
|
if isinstance(exc.parent.struct.typ, Positional):
|
||||||
|
L.append(str(exc.pos))
|
||||||
|
else:
|
||||||
|
L.append(exc.struct.name)
|
||||||
|
D['.'.join(L)] = msg
|
||||||
|
return D
|
||||||
|
|
||||||
|
class All(object):
|
||||||
|
def __init__(self, *validators):
|
||||||
|
self.validators = validators
|
||||||
|
|
||||||
|
def __call__(self, struct, value):
|
||||||
|
msgs = []
|
||||||
|
for validator in self.validators:
|
||||||
|
try:
|
||||||
|
validator(struct, value)
|
||||||
|
except Invalid, e:
|
||||||
|
msgs.append(e.msg)
|
||||||
|
|
||||||
|
if msgs:
|
||||||
|
raise Invalid(struct, msgs)
|
||||||
|
|
||||||
|
class Range(object):
|
||||||
|
def __init__(self, min=None, max=None):
|
||||||
|
self.min = min
|
||||||
|
self.max = max
|
||||||
|
|
||||||
|
def __call__(self, struct, value):
|
||||||
|
if self.min is not None:
|
||||||
|
if value < self.min:
|
||||||
|
raise Invalid(
|
||||||
|
struct,
|
||||||
|
'%r is less than minimum value %r' % (value, self.min))
|
||||||
|
|
||||||
|
if self.max is not None:
|
||||||
|
if value > self.max:
|
||||||
|
raise Invalid(
|
||||||
|
struct,
|
||||||
|
'%r is greater than maximum value %r' % (value, self.max))
|
||||||
|
|
||||||
|
class Mapping(object):
|
||||||
|
def _validate(self, struct, value):
|
||||||
|
if not isinstance(value, dict):
|
||||||
|
raise Invalid(struct, '%r is not a mapping type' % value)
|
||||||
|
return value
|
||||||
|
|
||||||
|
def serialize(self, struct, value):
|
||||||
|
value = self._validate(struct, value)
|
||||||
|
result = {}
|
||||||
|
|
||||||
|
error = None
|
||||||
|
|
||||||
|
for num, substruct in enumerate(struct.structs):
|
||||||
|
name = substruct.name
|
||||||
|
subval = value.get(name)
|
||||||
|
try:
|
||||||
|
if subval is None:
|
||||||
|
if substruct.required and substruct.default is None:
|
||||||
|
raise Invalid(
|
||||||
|
substruct,
|
||||||
|
'%r is required but empty' % substruct.name)
|
||||||
|
result[name] = substruct.serialize(struct.default)
|
||||||
|
else:
|
||||||
|
result[name] = substruct.serialize(subval)
|
||||||
|
except Invalid, e:
|
||||||
|
if error is None:
|
||||||
|
error = Invalid(substruct)
|
||||||
|
e.pos = num
|
||||||
|
error.add(e)
|
||||||
|
|
||||||
|
if error is not None:
|
||||||
|
raise error
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
def deserialize(self, struct, value):
|
||||||
|
value = self._validate(struct, value)
|
||||||
|
|
||||||
|
error = None
|
||||||
|
result = {}
|
||||||
|
|
||||||
|
for num, substruct in enumerate(struct.structs):
|
||||||
|
name = substruct.name
|
||||||
|
subval = value.get(name)
|
||||||
|
|
||||||
|
try:
|
||||||
|
if subval is None:
|
||||||
|
if substruct.required and substruct.default is None:
|
||||||
|
raise Invalid(
|
||||||
|
substruct,
|
||||||
|
'%r is required but empty' % substruct.name)
|
||||||
|
result[name] = substruct.default
|
||||||
|
else:
|
||||||
|
result[name] = substruct.deserialize(subval)
|
||||||
|
except Invalid, e:
|
||||||
|
if error is None:
|
||||||
|
error = Invalid(struct)
|
||||||
|
e.pos = num
|
||||||
|
error.add(e)
|
||||||
|
|
||||||
|
if error is not None:
|
||||||
|
raise error
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
class Positional(object):
|
||||||
|
"""
|
||||||
|
Marker abstract base class meaning 'this type has children which
|
||||||
|
should be addressed by position instead of name' (e.g. via seq[0],
|
||||||
|
but never seq['name']). This is consulted by Invalid.asdict when
|
||||||
|
creating a dictionary representation of an error structure.
|
||||||
|
"""
|
||||||
|
|
||||||
|
class Tuple(Positional):
|
||||||
|
""" A type which represents a fixed-length sequence of data
|
||||||
|
structures, each one of which may be different as denoted by the
|
||||||
|
types of the associated structure's children."""
|
||||||
|
def _validate(self, struct, value):
|
||||||
|
if not hasattr(value, '__iter__'):
|
||||||
|
raise Invalid(struct, '%r is not an iterable value' % value)
|
||||||
|
return list(value)
|
||||||
|
|
||||||
|
def serialize(self, struct, value):
|
||||||
|
value = self._validate(struct, value)
|
||||||
|
|
||||||
|
error = None
|
||||||
|
result = []
|
||||||
|
|
||||||
|
for num, substruct in enumerate(struct.structs):
|
||||||
|
try:
|
||||||
|
subval = value[num]
|
||||||
|
except IndexError:
|
||||||
|
raise Invalid(struct, 'Wrong number of elements in %r' % value)
|
||||||
|
try:
|
||||||
|
result.append(substruct.serialize(subval))
|
||||||
|
except Invalid, e:
|
||||||
|
if error is None:
|
||||||
|
error = Invalid(struct)
|
||||||
|
e.pos = num
|
||||||
|
e.sequence_child = True
|
||||||
|
error.add(e)
|
||||||
|
|
||||||
|
if error:
|
||||||
|
raise error
|
||||||
|
|
||||||
|
return tuple(result)
|
||||||
|
|
||||||
|
def deserialize(self, struct, value):
|
||||||
|
value = self._validate(struct, value)
|
||||||
|
|
||||||
|
error = None
|
||||||
|
result = []
|
||||||
|
|
||||||
|
for num, substruct in enumerate(struct.structs):
|
||||||
|
try:
|
||||||
|
subval = value[num]
|
||||||
|
except IndexError:
|
||||||
|
raise Invalid(struct, 'Wrong number of elements in %r' % value)
|
||||||
|
try:
|
||||||
|
result.append(substruct.deserialize(subval))
|
||||||
|
except Invalid, e:
|
||||||
|
if error is None:
|
||||||
|
error = Invalid(struct)
|
||||||
|
e.pos = num
|
||||||
|
e.sequence_child = True
|
||||||
|
error.add(e)
|
||||||
|
|
||||||
|
if error:
|
||||||
|
raise error
|
||||||
|
|
||||||
|
return tuple(result)
|
||||||
|
|
||||||
|
class Sequence(Positional):
|
||||||
|
""" A type which represents a variable-length sequence of values,
|
||||||
|
all of which must be of the same type as denoted by the type of
|
||||||
|
``substruct``"""
|
||||||
|
def __init__(self, substruct):
|
||||||
|
self.substruct = substruct
|
||||||
|
|
||||||
|
def _validate(self, struct, value):
|
||||||
|
if not hasattr(value, '__iter__'):
|
||||||
|
raise Invalid(struct, '%r is not an iterable value' % value)
|
||||||
|
return list(value)
|
||||||
|
|
||||||
|
def serialize(self, struct, value):
|
||||||
|
value = self._validate(struct, value)
|
||||||
|
|
||||||
|
error = None
|
||||||
|
result = []
|
||||||
|
for num, subval in enumerate(value):
|
||||||
|
try:
|
||||||
|
result.append(self.substruct.serialize(subval))
|
||||||
|
except Invalid, e:
|
||||||
|
if error is None:
|
||||||
|
error = Invalid(struct)
|
||||||
|
e.pos = num
|
||||||
|
error.add(e)
|
||||||
|
|
||||||
|
if error:
|
||||||
|
raise error
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
def deserialize(self, struct, value):
|
||||||
|
value = self._validate(struct, value)
|
||||||
|
|
||||||
|
error = None
|
||||||
|
result = []
|
||||||
|
for num, sub in enumerate(value):
|
||||||
|
try:
|
||||||
|
result.append(self.substruct.deserialize(sub))
|
||||||
|
except Invalid, e:
|
||||||
|
if error is None:
|
||||||
|
error = Invalid(struct)
|
||||||
|
e.pos = num
|
||||||
|
error.add(e)
|
||||||
|
|
||||||
|
if error:
|
||||||
|
raise error
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
class String(object):
|
||||||
|
""" A type representing a Unicode string """
|
||||||
|
def __init__(self, encoding='utf-8'):
|
||||||
|
self.encoding = encoding
|
||||||
|
|
||||||
|
def _validate(self, struct, value):
|
||||||
|
try:
|
||||||
|
if isinstance(value, unicode):
|
||||||
|
return value
|
||||||
|
return unicode(value, self.encoding)
|
||||||
|
except:
|
||||||
|
raise Invalid(struct, '%r is not a string' % value)
|
||||||
|
|
||||||
|
def serialize(self, struct, value):
|
||||||
|
decoded = self._validate(struct, value)
|
||||||
|
return decoded.encode(struct.encoding)
|
||||||
|
|
||||||
|
def deserialize(self, struct, value):
|
||||||
|
return self._validate(struct, value)
|
||||||
|
|
||||||
|
class Integer(object):
|
||||||
|
""" A type representing an integer """
|
||||||
|
def _validate(self, struct, value):
|
||||||
|
try:
|
||||||
|
return int(value)
|
||||||
|
except:
|
||||||
|
raise Invalid(struct, '%r is not a number' % value)
|
||||||
|
|
||||||
|
def serialize(self, struct, value):
|
||||||
|
return str(self._validate(struct, value))
|
||||||
|
|
||||||
|
def deserialize(self, struct, value):
|
||||||
|
return self._validate(struct, value)
|
||||||
|
|
||||||
|
class GlobalObject(object):
|
||||||
|
""" A type representing an importable Python object """
|
||||||
|
def __init__(self, package):
|
||||||
|
self.package = package
|
||||||
|
|
||||||
|
def serialize(self, struct, value):
|
||||||
|
try:
|
||||||
|
return value.__name__
|
||||||
|
except AttributeError:
|
||||||
|
raise Invalid(struct, '%r has no __name__' % value)
|
||||||
|
|
||||||
|
def deserialize(self, struct, value):
|
||||||
|
if not isinstance(value, basestring):
|
||||||
|
raise Invalid(struct, '%r is not a global object specification')
|
||||||
|
try:
|
||||||
|
return resolve_dotted(value, package=self.package)
|
||||||
|
except ImportError:
|
||||||
|
raise Invalid(struct,
|
||||||
|
'The dotted name %r cannot be imported' % value)
|
||||||
|
|
||||||
|
class Structure(object):
|
||||||
|
def __init__(self, name, typ, validator=None, default=None, required=True):
|
||||||
|
self.typ = typ
|
||||||
|
self.name = name
|
||||||
|
self.validator = validator
|
||||||
|
self.default = default
|
||||||
|
self.required = required
|
||||||
|
self.structs = []
|
||||||
|
|
||||||
|
def serialize(self, value):
|
||||||
|
return self.typ.serialize(self, value)
|
||||||
|
|
||||||
|
def deserialize(self, value):
|
||||||
|
value = self.typ.deserialize(self, value)
|
||||||
|
if self.validator is not None:
|
||||||
|
self.validator(self, value)
|
||||||
|
return value
|
||||||
|
|
||||||
|
def add(self, struct):
|
||||||
|
self.structs.append(struct)
|
||||||
|
|
76
cereal/tests.py
Normal file
76
cereal/tests.py
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
import unittest
|
||||||
|
|
||||||
|
class TestFunctional(unittest.TestCase):
|
||||||
|
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())
|
||||||
|
tup.add(cereal.Structure('tupint', cereal.Integer()))
|
||||||
|
tup.add(cereal.Structure('tupstring', cereal.String()))
|
||||||
|
|
||||||
|
seq = cereal.Structure('seq', cereal.Sequence(tup))
|
||||||
|
|
||||||
|
mapping = cereal.Structure('mapping', cereal.Mapping())
|
||||||
|
seq2 = cereal.Structure('seq2', cereal.Sequence(mapping))
|
||||||
|
mapping.add(cereal.Structure('key', cereal.Integer()))
|
||||||
|
mapping.add(cereal.Structure('key2', cereal.Integer()))
|
||||||
|
|
||||||
|
schema = cereal.Structure('', cereal.Mapping())
|
||||||
|
|
||||||
|
schema.add(integer)
|
||||||
|
schema.add(ob)
|
||||||
|
schema.add(tup)
|
||||||
|
schema.add(seq)
|
||||||
|
schema.add(seq2)
|
||||||
|
return schema
|
||||||
|
|
||||||
|
def test_deserialize_ok(self):
|
||||||
|
import cereal.tests
|
||||||
|
data = {
|
||||||
|
'int':'10',
|
||||||
|
'ob':'cereal.tests',
|
||||||
|
'tup':('1', 's'),
|
||||||
|
'seq':[('1', 's'),('2', 's'), ('3', 's'), ('4', 's')],
|
||||||
|
'seq2':[{'key':'1', 'key2':'2'}, {'key':'3', 'key2':'4'}],
|
||||||
|
}
|
||||||
|
schema = self._makeSchema()
|
||||||
|
result = schema.deserialize(data)
|
||||||
|
self.assertEqual(result['int'], 10)
|
||||||
|
self.assertEqual(result['ob'], cereal.tests)
|
||||||
|
self.assertEqual(result['tup'], (1, 's'))
|
||||||
|
self.assertEqual(result['seq'],
|
||||||
|
[(1, 's'), (2, 's'), (3, 's'), (4, 's')])
|
||||||
|
self.assertEqual(result['seq2'],
|
||||||
|
[{'key':1, 'key2':2}, {'key':3, 'key2':4}])
|
||||||
|
|
||||||
|
def test_invalid_asdict(self):
|
||||||
|
expected = {
|
||||||
|
'int': '20 is greater than maximum value 10',
|
||||||
|
'ob': "The dotted name 'no.way.this.exists' cannot be imported",
|
||||||
|
'seq.0.0': "'q' is not a number",
|
||||||
|
'seq.1.0': "'w' is not a number",
|
||||||
|
'seq.2.0': "'e' is not a number",
|
||||||
|
'seq.3.0': "'r' is not a number",
|
||||||
|
'seq2.0.key': "'t' is not a number",
|
||||||
|
'seq2.0.key2': "'y' is not a number",
|
||||||
|
'seq2.1.key': "'u' is not a number",
|
||||||
|
'seq2.1.key2': "'i' is not a number",
|
||||||
|
'tup.0': "'s' is not a number"}
|
||||||
|
import cereal
|
||||||
|
data = {
|
||||||
|
'int':'20',
|
||||||
|
'ob':'no.way.this.exists',
|
||||||
|
'tup':('s', 's'),
|
||||||
|
'seq':[('q', 's'),('w', 's'), ('e', 's'), ('r', 's')],
|
||||||
|
'seq2':[{'key':'t', 'key2':'y'}, {'key':'u', 'key2':'i'}],
|
||||||
|
}
|
||||||
|
schema = self._makeSchema()
|
||||||
|
try:
|
||||||
|
schema.deserialize(data)
|
||||||
|
except cereal.Invalid, e:
|
||||||
|
errors = e.asdict()
|
||||||
|
self.assertEqual(errors, expected)
|
BIN
docs/.static/logo_hi.gif
Normal file
BIN
docs/.static/logo_hi.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.8 KiB |
22
docs/.static/repoze.css
Normal file
22
docs/.static/repoze.css
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
@import url('default.css');
|
||||||
|
body {
|
||||||
|
background-color: #006339;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.document {
|
||||||
|
background-color: #dad3bd;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.sphinxsidebar h3, h4, h5, a {
|
||||||
|
color: #127c56 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.related {
|
||||||
|
color: #dad3bd !important;
|
||||||
|
background-color: #00744a;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.related a {
|
||||||
|
color: #dad3bd !important;
|
||||||
|
}
|
||||||
|
|
70
docs/Makefile
Normal file
70
docs/Makefile
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
# Makefile for Sphinx documentation
|
||||||
|
#
|
||||||
|
|
||||||
|
# You can set these variables from the command line.
|
||||||
|
SPHINXOPTS =
|
||||||
|
SPHINXBUILD = sphinx-build
|
||||||
|
PAPER =
|
||||||
|
|
||||||
|
# Internal variables.
|
||||||
|
PAPEROPT_a4 = -D latex_paper_size=a4
|
||||||
|
PAPEROPT_letter = -D latex_paper_size=letter
|
||||||
|
ALLSPHINXOPTS = -d .build/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
|
||||||
|
|
||||||
|
.PHONY: help clean html web pickle htmlhelp latex changes linkcheck
|
||||||
|
|
||||||
|
help:
|
||||||
|
@echo "Please use \`make <target>' where <target> is one of"
|
||||||
|
@echo " html to make standalone HTML files"
|
||||||
|
@echo " pickle to make pickle files (usable by e.g. sphinx-web)"
|
||||||
|
@echo " htmlhelp to make HTML files and a HTML help project"
|
||||||
|
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
|
||||||
|
@echo " changes to make an overview over all changed/added/deprecated items"
|
||||||
|
@echo " linkcheck to check all external links for integrity"
|
||||||
|
|
||||||
|
clean:
|
||||||
|
-rm -rf .build/*
|
||||||
|
|
||||||
|
html:
|
||||||
|
mkdir -p .build/html .build/doctrees
|
||||||
|
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) .build/html
|
||||||
|
@echo
|
||||||
|
@echo "Build finished. The HTML pages are in .build/html."
|
||||||
|
|
||||||
|
pickle:
|
||||||
|
mkdir -p .build/pickle .build/doctrees
|
||||||
|
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) .build/pickle
|
||||||
|
@echo
|
||||||
|
@echo "Build finished; now you can process the pickle files or run"
|
||||||
|
@echo " sphinx-web .build/pickle"
|
||||||
|
@echo "to start the sphinx-web server."
|
||||||
|
|
||||||
|
web: pickle
|
||||||
|
|
||||||
|
htmlhelp:
|
||||||
|
mkdir -p .build/htmlhelp .build/doctrees
|
||||||
|
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) .build/htmlhelp
|
||||||
|
@echo
|
||||||
|
@echo "Build finished; now you can run HTML Help Workshop with the" \
|
||||||
|
".hhp project file in .build/htmlhelp."
|
||||||
|
|
||||||
|
latex:
|
||||||
|
mkdir -p .build/latex .build/doctrees
|
||||||
|
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) .build/latex
|
||||||
|
@echo
|
||||||
|
@echo "Build finished; the LaTeX files are in .build/latex."
|
||||||
|
@echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \
|
||||||
|
"run these through (pdf)latex."
|
||||||
|
|
||||||
|
changes:
|
||||||
|
mkdir -p .build/changes .build/doctrees
|
||||||
|
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) .build/changes
|
||||||
|
@echo
|
||||||
|
@echo "The overview file is in .build/changes."
|
||||||
|
|
||||||
|
linkcheck:
|
||||||
|
mkdir -p .build/linkcheck .build/doctrees
|
||||||
|
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) .build/linkcheck
|
||||||
|
@echo
|
||||||
|
@echo "Link check complete; look for any errors in the above output " \
|
||||||
|
"or in .build/linkcheck/output.txt."
|
4
docs/api.rst
Normal file
4
docs/api.rst
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
API Documentation
|
||||||
|
=================
|
||||||
|
|
||||||
|
XXX
|
185
docs/conf.py
Normal file
185
docs/conf.py
Normal file
@@ -0,0 +1,185 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
# cereal documentation build configuration file
|
||||||
|
#
|
||||||
|
# This file is execfile()d with the current directory set to its containing
|
||||||
|
# dir.
|
||||||
|
#
|
||||||
|
# The contents of this file are pickled, so don't put values in the
|
||||||
|
# namespace that aren't pickleable (module imports are okay, they're
|
||||||
|
# removed automatically).
|
||||||
|
#
|
||||||
|
# All configuration values have a default value; values that are commented
|
||||||
|
# out serve to show the default value.
|
||||||
|
|
||||||
|
import sys, os
|
||||||
|
|
||||||
|
# If your extensions are in another directory, add it here. If the
|
||||||
|
# directory is relative to the documentation root, use os.path.abspath to
|
||||||
|
# make it absolute, like shown here.
|
||||||
|
#sys.path.append(os.path.abspath('some/directory'))
|
||||||
|
|
||||||
|
# General configuration
|
||||||
|
# ---------------------
|
||||||
|
|
||||||
|
# Add any Sphinx extension module names here, as strings. They can be
|
||||||
|
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
|
||||||
|
extensions = ['sphinx.ext.autodoc']
|
||||||
|
|
||||||
|
# Add any paths that contain templates here, relative to this directory.
|
||||||
|
templates_path = ['.templates']
|
||||||
|
|
||||||
|
# The suffix of source filenames.
|
||||||
|
source_suffix = '.rst'
|
||||||
|
|
||||||
|
# The master toctree document.
|
||||||
|
master_doc = 'index'
|
||||||
|
|
||||||
|
# General substitutions.
|
||||||
|
project = 'cereal'
|
||||||
|
copyright = '2010, Repoze Developers <repoze-dev@lists.repoze.org>'
|
||||||
|
|
||||||
|
# The default replacements for |version| and |release|, also used in various
|
||||||
|
# other places throughout the built documents.
|
||||||
|
#
|
||||||
|
# The short X.Y version.
|
||||||
|
version = '0.0'
|
||||||
|
# The full version, including alpha/beta/rc tags.
|
||||||
|
release = '0.0'
|
||||||
|
|
||||||
|
# There are two options for replacing |today|: either, you set today to
|
||||||
|
# some non-false value, then it is used:
|
||||||
|
#today = ''
|
||||||
|
# Else, today_fmt is used as the format for a strftime call.
|
||||||
|
today_fmt = '%B %d, %Y'
|
||||||
|
|
||||||
|
# List of documents that shouldn't be included in the build.
|
||||||
|
#unused_docs = []
|
||||||
|
|
||||||
|
# List of directories, relative to source directories, that shouldn't be
|
||||||
|
# searched for source files.
|
||||||
|
#exclude_dirs = []
|
||||||
|
|
||||||
|
# The reST default role (used for this markup: `text`) to use for all
|
||||||
|
# documents.
|
||||||
|
#default_role = None
|
||||||
|
|
||||||
|
# If true, '()' will be appended to :func: etc. cross-reference text.
|
||||||
|
#add_function_parentheses = True
|
||||||
|
|
||||||
|
# If true, the current module name will be prepended to all description
|
||||||
|
# unit titles (such as .. function::).
|
||||||
|
#add_module_names = True
|
||||||
|
|
||||||
|
# If true, sectionauthor and moduleauthor directives will be shown in the
|
||||||
|
# output. They are ignored by default.
|
||||||
|
#show_authors = False
|
||||||
|
|
||||||
|
# The name of the Pygments (syntax highlighting) style to use.
|
||||||
|
pygments_style = 'sphinx'
|
||||||
|
|
||||||
|
|
||||||
|
# Options for HTML output
|
||||||
|
# -----------------------
|
||||||
|
|
||||||
|
# The style sheet to use for HTML and HTML Help pages. A file of that name
|
||||||
|
# must exist either in Sphinx' static/ path, or in one of the custom paths
|
||||||
|
# given in html_static_path.
|
||||||
|
html_style = 'repoze.css'
|
||||||
|
|
||||||
|
# The name for this set of Sphinx documents. If None, it defaults to
|
||||||
|
# "<project> v<release> documentation".
|
||||||
|
#html_title = None
|
||||||
|
|
||||||
|
# A shorter title for the navigation bar. Default is the same as
|
||||||
|
# html_title.
|
||||||
|
#html_short_title = None
|
||||||
|
|
||||||
|
# The name of an image file (within the static path) to place at the top of
|
||||||
|
# the sidebar.
|
||||||
|
html_logo = '.static/logo_hi.gif'
|
||||||
|
|
||||||
|
# The name of an image file (within the static path) to use as favicon of
|
||||||
|
# the docs. This file should be a Windows icon file (.ico) being 16x16 or
|
||||||
|
# 32x32 pixels large.
|
||||||
|
#html_favicon = None
|
||||||
|
|
||||||
|
# Add any paths that contain custom static files (such as style sheets)
|
||||||
|
# here, relative to this directory. They are copied after the builtin
|
||||||
|
# static files, so a file named "default.css" will overwrite the builtin
|
||||||
|
# "default.css".
|
||||||
|
html_static_path = ['.static']
|
||||||
|
|
||||||
|
# If not '', a 'Last updated on:' timestamp is inserted at every page
|
||||||
|
# bottom, using the given strftime format.
|
||||||
|
html_last_updated_fmt = '%b %d, %Y'
|
||||||
|
|
||||||
|
# If true, SmartyPants will be used to convert quotes and dashes to
|
||||||
|
# typographically correct entities.
|
||||||
|
#html_use_smartypants = True
|
||||||
|
|
||||||
|
# Custom sidebar templates, maps document names to template names.
|
||||||
|
#html_sidebars = {}
|
||||||
|
|
||||||
|
# Additional templates that should be rendered to pages, maps page names to
|
||||||
|
# template names.
|
||||||
|
#html_additional_pages = {}
|
||||||
|
|
||||||
|
# If false, no module index is generated.
|
||||||
|
#html_use_modindex = True
|
||||||
|
|
||||||
|
# If false, no index is generated.
|
||||||
|
#html_use_index = True
|
||||||
|
|
||||||
|
# If true, the index is split into individual pages for each letter.
|
||||||
|
#html_split_index = False
|
||||||
|
|
||||||
|
# If true, the reST sources are included in the HTML build as
|
||||||
|
# _sources/<name>.
|
||||||
|
#html_copy_source = True
|
||||||
|
|
||||||
|
# If true, an OpenSearch description file will be output, and all pages
|
||||||
|
# will contain a <link> tag referring to it. The value of this option must
|
||||||
|
# be the base URL from which the finished HTML is served.
|
||||||
|
#html_use_opensearch = ''
|
||||||
|
|
||||||
|
# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml").
|
||||||
|
#html_file_suffix = ''
|
||||||
|
|
||||||
|
# Output file base name for HTML help builder.
|
||||||
|
htmlhelp_basename = 'atemplatedoc'
|
||||||
|
|
||||||
|
|
||||||
|
# Options for LaTeX output
|
||||||
|
# ------------------------
|
||||||
|
|
||||||
|
# The paper size ('letter' or 'a4').
|
||||||
|
#latex_paper_size = 'letter'
|
||||||
|
|
||||||
|
# The font size ('10pt', '11pt' or '12pt').
|
||||||
|
#latex_font_size = '10pt'
|
||||||
|
|
||||||
|
# Grouping the document tree into LaTeX files. List of tuples
|
||||||
|
# (source start file, target name, title,
|
||||||
|
# author, document class [howto/manual]).
|
||||||
|
latex_documents = [
|
||||||
|
('index', 'atemplate.tex', 'cereal Documentation',
|
||||||
|
'Repoze Developers', 'manual'),
|
||||||
|
]
|
||||||
|
|
||||||
|
# The name of an image file (relative to this directory) to place at the
|
||||||
|
# top of the title page.
|
||||||
|
latex_logo = '.static/logo_hi.gif'
|
||||||
|
|
||||||
|
# For "manual" documents, if this is true, then toplevel headings are
|
||||||
|
# parts, not chapters.
|
||||||
|
#latex_use_parts = False
|
||||||
|
|
||||||
|
# Additional stuff for the LaTeX preamble.
|
||||||
|
#latex_preamble = ''
|
||||||
|
|
||||||
|
# Documents to append as an appendix to all manuals.
|
||||||
|
#latex_appendices = []
|
||||||
|
|
||||||
|
# If false, no module index is generated.
|
||||||
|
#latex_use_modindex = True
|
23
docs/index.rst
Normal file
23
docs/index.rst
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
cereal
|
||||||
|
======
|
||||||
|
|
||||||
|
Cereal is an extensible package which can be used to:
|
||||||
|
|
||||||
|
- deserialize and validate a data structure composed of strings,
|
||||||
|
mappings, and lists.
|
||||||
|
|
||||||
|
- serialize an arbitrary data structure to a data structure composed
|
||||||
|
of strings, mappings, and lists.
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:maxdepth: 2
|
||||||
|
|
||||||
|
api.rst
|
||||||
|
|
||||||
|
|
||||||
|
Indices and tables
|
||||||
|
------------------
|
||||||
|
|
||||||
|
* :ref:`genindex`
|
||||||
|
* :ref:`modindex`
|
||||||
|
* :ref:`search`
|
10
setup.cfg
Normal file
10
setup.cfg
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
[easy_install]
|
||||||
|
zip_ok = false
|
||||||
|
|
||||||
|
[nosetests]
|
||||||
|
match=^test
|
||||||
|
where=cereal
|
||||||
|
nocapture=1
|
||||||
|
cover-package=cereal
|
||||||
|
cover-erase=1
|
||||||
|
|
46
setup.py
Normal file
46
setup.py
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
##############################################################################
|
||||||
|
#
|
||||||
|
# Copyright (c) 2010 Agendaless Consulting and Contributors.
|
||||||
|
# All Rights Reserved.
|
||||||
|
#
|
||||||
|
# This software is subject to the provisions of the BSD-like license at
|
||||||
|
# http://www.repoze.org/LICENSE.txt. A copy of the license should accompany
|
||||||
|
# this distribution. THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL
|
||||||
|
# EXPRESS OR IMPLIED WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
# THE IMPLIED WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
#
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
from setuptools import setup
|
||||||
|
from setuptools import find_packages
|
||||||
|
|
||||||
|
here = os.path.abspath(os.path.dirname(__file__))
|
||||||
|
README = open(os.path.join(here, 'README.txt')).read()
|
||||||
|
CHANGES = open(os.path.join(here, 'CHANGES.txt')).read()
|
||||||
|
|
||||||
|
requires = []
|
||||||
|
|
||||||
|
setup(name='cereal',
|
||||||
|
version='0.0',
|
||||||
|
description='A schema-based serialization and deserialization library',
|
||||||
|
long_description=README + '\n\n' + CHANGES,
|
||||||
|
classifiers=[
|
||||||
|
"Intended Audience :: Developers",
|
||||||
|
"Programming Language :: Python",
|
||||||
|
],
|
||||||
|
keywords='serialize deserialize validate schema',
|
||||||
|
author="Agendaless Consulting",
|
||||||
|
author_email="repoze-dev@lists.repoze.org",
|
||||||
|
url="http://www.repoze.org",
|
||||||
|
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",
|
||||||
|
)
|
||||||
|
|
Reference in New Issue
Block a user