Merge branch 'master' into pr/168

This commit is contained in:
Michael Merickel
2016-01-15 20:37:57 -06:00
31 changed files with 962 additions and 508 deletions

3
.gitignore vendored
View File

@@ -8,7 +8,8 @@
*.swp
.coverage
.tox/
nosetests.xml
coverage-*.xml
nosetests*.xml
env26/
env25/
env24/

39
.travis.yml Normal file
View File

@@ -0,0 +1,39 @@
# Wire up travis
language: python
sudo: false
matrix:
include:
- python: 2.6
env: TOXENV=py26
- python: 2.7
env: TOXENV=py27
- python: 3.2
env: TOXENV=py32
- python: 3.3
env: TOXENV=py33
- python: 3.4
env: TOXENV=py34
- python: 3.5
env: TOXENV=py35
- python: pypy
env: TOXENV=pypy
- python: pypy3
env: TOXENV=pypy3
- python: 3.5
env: TOXENV=py2-cover,py3-cover,coverage
- python: 3.5
env: TOXENV=docs
install:
- travis_retry pip install tox
script:
- travis_retry tox
notifications:
email:
- pyramid-checkins@lists.repoze.org
irc:
channels:
- "chat.freenode.net#pyramid"

View File

@@ -1,34 +1,103 @@
Unreleased
----------
unreleased
==========
Bug Fixes
~~~~~~~~~
- Un-break wrapping of callable instances as ``colander.deferred``.
See https://github.com/Pylons/colander/issues/141.
- Set the max length TLD to 22 in ``Email`` validator based on the
current list of valid TLDs.
See https://github.com/Pylons/colander/issues/159
- Fix an issue where ``drop`` was not recognized as a default and was
returning the ``drop`` instance instead of omitting the value.
https://github.com/Pylons/colander/issues/139
- We implicitly assign the schema type to MappingSchema, SequenceSchema, and
TupleSchema with types of Mapping, Sequence, and Tuple explicitly. This is
a minor backwards incompatibility, because we no longer allow any of those
schema variants to accept the implied type as a first argument.
schema variants to accept the implied type as a first argument.
i.e. MappingSchema(Mapping()) will no longer work.
1.1 (2016-01-15)
================
Platform
--------
- Add explicit support for Python 3.4, Python 3.5 and PyPy3.
Features
~~~~~~~~
--------
- Add `Any` validator which succeeds if at least one of its subvalidators
succeeded.
- Add ``min_err`` and ``max_err`` arguments to ``Length``, allowing
customization of its error messages.
1.0b1 (2013-09-01)
------------------
- Add ``colander.Any`` validator: succeeds if at least one of its
subvalidators succeeded.
- Allow localization of error messages returned by ``colander.Invalid.asdict``
by adding an optional ``translate`` callable argument.
- Add a ``missing_msg`` argument to ``SchemaNode``, allowing customization
of the error message used when the node is required and missing.
- Add `NoneOf` validator wich succeeds if the value is none of the choices.
- Add ``normalize`` option to ``Decimal``, stripping the rightmost
trailing zeros.
Bug Fixes
~~~~~~~~~
---------
- Update translations: ``de``, ``ja``, ``fr``.
- Fix an issue where the ``flatten()`` method produces an invalid name
(ex: "answer.0.") for the type ``Sequence``. See
https://github.com/Pylons/colander/issues/179
- Fixed issue with ``String`` not being properly encoded when non-string
values were passed into ``serialize()``
See `#235 <https://github.com/Pylons/colander/pull/235>`_
- ``title`` was being overwritten when made a child through defining a schema
as a class. See `#239 <https://github.com/Pylons/colander/pull/239>`_
1.0 (2014-11-26)
================
Bug Fixes
---------
- Removed forked ``iso8601`` and change to dependency on PyPI ``iso8601``
(due to float rounding bug on microsecond portion when parsing
iso8601 datetime string). Left an ``iso8601.py`` stub for backwards
compatibility.
- Time of "00:00" no longer gives ``colander.Invalid``.
- Un-break wrapping of callable instances as ``colander.deferred``.
See https://github.com/Pylons/colander/issues/141.
- Set the max length TLD to 22 in ``Email`` validator based on the
current list of valid TLDs.
See https://github.com/Pylons/colander/issues/159
- Fix an issue where ``drop`` was not recognized as a default and was
returning the ``drop`` instance instead of omitting the value.
https://github.com/Pylons/colander/issues/139
- Fix an issue where the ``SchemaNode.title`` was clobbered by the ``name``
when defined as a class attribute.
See https://github.com/Pylons/colander/pull/183 and
https://github.com/Pylons/colander/pull/185
- Updated translations: ``fr``, ``de``, ``ja``
Backwards Incompatibilities
---------------------------
- ``SchemaNode.deserialize`` will now raise an
``UnboundDeferredError`` if the node has an unbound deferred
validator. Previously, deferred validators were silently ignored.
See https://github.com/Pylons/colander/issues/47
1.0b1 (2013-09-01)
==================
Bug Fixes
---------
- In 1.0a1, there was a change merged from
https://github.com/Pylons/colander/pull/73 which made it possible to supply
@@ -58,7 +127,7 @@ Bug Fixes
See https://github.com/Pylons/colander/issues/100
Features
~~~~~~~~
--------
- Add ``colander.List`` type, modeled on ``deform.List``: this type
preserves ordering, and allows duplicates.
@@ -79,18 +148,17 @@ Features
- The ``typ`` of a ``SchemaNode`` can optionally be pased in as a keyword
argument. See https://github.com/Pylons/colander/issues/90
- Add a ``missing_msg`` argument to ``SchemaNode`` that specifies the error
message to be used when the node is required and missing
- Allow interpolation of `missing_msg` with properties `title` and `name`
1.0a5 (2013-05-31)
------------------
==================
- Fix bug introduced by supporting spec-mandated truncations of ISO-8601
timezones. A TypeError would be raised instead of Invalid. See
https://github.com/Pylons/colander/issues/111.
1.0a4 (2013-05-21)
------------------
==================
- Loosen Email validator regex (permit apostrophes, bang, etc in localpart).
@@ -99,10 +167,10 @@ Features
https://github.com/Pylons/colander/pull/108.
1.0a3 (2013-05-16)
------------------
==================
Features
~~~~~~~~
--------
- Support spec-mandated truncations of ISO-8601 timezones.
@@ -111,7 +179,7 @@ Features
- Allow specifying custom representations of values for boolean fields.
Bug Fixes
~~~~~~~~~
---------
- Ensure that ``colander.iso8601.FixedOffset`` instances can be unpickled.
@@ -122,10 +190,10 @@ Bug Fixes
1.0a2 (2013-01-30)
------------------
==================
Features
~~~~~~~~
--------
- Add ``colander.ContainsOnly`` and ``colander.url`` validators.
@@ -133,10 +201,10 @@ Features
mappings and sequences more succinctly.
1.0a1 (2013-01-10)
------------------
==================
Bug Fixes
~~~~~~~~~
---------
- Work around a regression in Python 3.3 for ``colander.Decimal`` when it's
used with a ``quant`` argument but without a ``rounding`` argument.
@@ -152,7 +220,7 @@ Bug Fixes
https://github.com/Pylons/colander/pull/96).
Features
~~~~~~~~
--------
- Add ``colander.Set`` type, ported from ``deform.Set``
@@ -391,7 +459,7 @@ Features
id='a2',
)
c = colander.SchemaNode(
colander.String(),
colander.String(),
id='c2',
)
e = colander.SchemaNode(
@@ -447,7 +515,7 @@ Features
id='a2',
)
c = colander.SchemaNode(
colander.String(),
colander.String(),
id='c2',
)
e = colander.SchemaNode(
@@ -476,17 +544,17 @@ Features
MRO deepest-first ordering (``One``, then ``Two``, then ``Three``).
Backwards Incompatibilities
~~~~~~~~~~~~~~~~~~~~~~~~~~~
---------------------------
- Passing non-SchemaNode derivative instances as ``*children`` into a
SchemaNode constructor is no longer supported. Symptom: ``AttributeError:
name`` when constructing a SchemaNode.
0.9.9 (2012-09-24)
------------------
==================
Features
~~~~~~~~
--------
- Allow the use of ``missing=None`` for Number. See
https://github.com/Pylons/colander/pull/59 .
@@ -525,7 +593,7 @@ Features
custom type).
Backwards Incompatibilities
~~~~~~~~~~~~~~~~~~~~~~~~~~~
---------------------------
- The inheritance changes required a minor backwards incompatibility: calling
``__setitem__`` on a SchemaNode will no longer raise ``KeyError`` when
@@ -534,13 +602,13 @@ Backwards Incompatibilities
the child list.
Documentation
~~~~~~~~~~~~~
-------------
- A "Schema Inheritance" section was added to the Basics chapter
documentation.
0.9.8 (2012-04-27)
------------------
==================
- False evaluating values are now serialized to colander.null for
String, Date, and Time. This resolves the issue where a None value
@@ -560,7 +628,7 @@ Documentation
- Add ``dev`` and ``docs`` setup.py aliases (e.g. ``python setup.py dev``).
0.9.7 (2012-03-20)
------------------
==================
- Using ``schema.flatten(...)`` against a mapping schema node without a name
produced incorrectly dot-prefixed keys. See
@@ -587,7 +655,7 @@ Documentation
error message. See https://github.com/Pylons/colander/pull/41
0.9.6 (2012-02-14)
------------------
==================
- No longer runs on Python 2.4 or 2.5. Python 2.6+ is now required.
@@ -600,7 +668,7 @@ Documentation
LICENSE.txt.
0.9.5 (2012-01-13)
------------------
==================
- Added Czech translation.
@@ -611,7 +679,7 @@ Documentation
- Documentation added about flatten and unflatten.
0.9.4 (2011-10-14)
------------------
==================
- ``flatten`` now only includes leaf nodes in the flattened dict.
@@ -629,7 +697,7 @@ Documentation
- Add Swedish, French, Chinese translations.
0.9.3 (2011-06-23)
------------------
==================
- Add ``Time`` type.
@@ -650,7 +718,7 @@ Documentation
documentation.
0.9.2 (2011-03-28)
------------------
==================
- Added Polish translation, thanks to Jedrzej Nowak.
@@ -674,7 +742,7 @@ Documentation
cstruct during ``deserialize``.
0.9.1 (2010-12-02)
------------------
==================
- When ``colander.null`` was unpickled, the reference created during
unpickling was *not* a reference to the singleton but rather a new instance
@@ -684,7 +752,7 @@ Documentation
pickled is unpickled as the singleton itself.
0.9 (2010-11-28)
-----------------
=================
- SchemaNode constructor now accepts arbitrary keyword arguments. It
sets any unknown values within the ``**kw`` sequence as attributes
@@ -708,7 +776,7 @@ Documentation
``colander.SchemaType`` to get a default implementation.
0.8 (2010/09/08)
-----------------
=================
- Docstring fixes to ``colander.SchemaNode`` (``missing`` is not the
``null`` value when required, it's a special marker value).
@@ -724,7 +792,7 @@ Documentation
been properly documented.
0.7.3 (2010/09/02)
------------------
==================
- The title of a schema node now defaults to a titleization of the
``name``. Underscores in the ``name`` are replaced with empty
@@ -739,7 +807,7 @@ Documentation
single-element list containing the ``msg`` value is returned.
0.7.2 (2010/08/30)
------------------
==================
- Add an ``colander.SchemaNode.__iter__`` method, which iterates over
the children nodes of a schema node.
@@ -749,7 +817,7 @@ Documentation
internally).
0.7.1 (2010/06/12)
------------------
==================
- Make it possible to use ``colander.null`` as a ``missing`` argument
to ``colander.SchemaNode`` for roundtripping purposes.
@@ -757,13 +825,13 @@ Documentation
- Make it possible to pickle ``colander.null``.
0.7.0
-----
=====
A release centered around normalizing the treatment of default and
missing values.
Bug Fixes
~~~~~~~~~
---------
- Allow ``colander.Regex`` validator to accept a pattern object
instead of just a string.
@@ -779,7 +847,7 @@ Bug Fixes
``colander.SchemaNode``.
Backwards Incompatiblities / New Features
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-----------------------------------------
- ``missing`` constructor arg to SchemaNode: signifies
*deserialization* default, disambiguated from ``default`` which acted
@@ -845,7 +913,7 @@ Backwards Incompatiblities / New Features
no longer a keyword argument that has a default.
0.6.2 (2010-05-08)
------------------
==================
- The default ``encoding`` parameter value to the ``colander.String``
type is still ``None``, however its meaning has changed. An
@@ -864,13 +932,13 @@ Backwards Incompatiblities / New Features
a bool value when a default value was found for the schema node.
0.6.1 (2010-05-04)
------------------
==================
- Add a Decimal type (number type which uses ``decimal.Decimal`` as a
deserialization target).
0.6.0 (2010-05-02)
------------------
==================
- (Hopefully) fix intermittent datetime-granularity-related test
failures.
@@ -893,7 +961,7 @@ Backwards Incompatiblities / New Features
to its interface within the API documentation.
0.5.2 (2010-04-09)
------------------
==================
- Add Email and Regex validators (courtesy Steve Howe).
@@ -911,7 +979,7 @@ Backwards Incompatiblities / New Features
add something like it back later if we need it.
0.5.1 (2010-04-02)
------------------
==================
- The constructor arguments to a the ``colander.Schema`` class are now
sent to the constructed SchemaNode rather than to the type it represents.
@@ -935,20 +1003,20 @@ Backwards Incompatiblities / New Features
the type).
0.5 (2010-03-31)
----------------
================
- 0.4 was mispackaged (CHANGES.txt missing); no code changes from 0.4
however.
0.4 (2010-03-30)
----------------
================
- Add ``colander.DateTime`` and ``colander.Date`` data types.
- Depend on the ``iso8601`` package for date support.
0.3 (2010-03-29)
----------------
================
- Subnodes of a schema node are now kept in the ``children`` attribute
rather than the ``nodes`` attribute.
@@ -965,7 +1033,7 @@ Backwards Incompatiblities / New Features
- Add ``colander.Length`` validator class.
0.2 (2010-03-23)
----------------
================
- Make nodetype overrideable.
@@ -995,6 +1063,6 @@ Backwards Incompatiblities / New Features
0.1 (2010-03-14)
----------------
================
- Initial release.

View File

@@ -101,7 +101,8 @@ and agrees to the terms above in the section within this document entitled
Contributors
------------
- Chris McDonough, 2011/02/16
- Chris McDonough, 2010/03/11
- Tres Seaver, 2010/04/05
- Chris Withers, 2011/05/45
- Mathieu Le Marec - Pasquet (kiorky), 2011/07/11
- Atsushi Odagiri, 2012/02/04
@@ -115,4 +116,14 @@ Contributors
- Peter Lamut, 2013/08/16
- Veeti Paananen, 2013/08/20
- Michael Howitz, 2013/12/05
- Alex Marandon, 2013/12/21
- Joe Dallago, 2014/2/10
- Jaseem Abid, 2014/06/16
- Cédric Messiant, 2014/06/27
- Gouji Ochiai, 2014/08/21
- Tim Tisdall, 2014/09/10
- Romain Commandé, 2014/10/11
- Nando Florestan, 2014/11/27
- Amos Latteier, 2014/11/30
- Jimmy Thrasibule, 2014/12/11
- Tinne Cahy, 2015/12/22

View File

@@ -39,6 +39,3 @@ License
THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.
This package uses code from the "pyiso8601" package by Michael Twomey,
licensed under the MIT license. See the source file named "iso8601.py" for
copyright information and license text.

View File

@@ -1,6 +1,15 @@
Colander
========
.. image:: https://travis-ci.org/Pylons/colander.svg?branch=master
:target: https://travis-ci.org/Pylons/colander
.. image:: https://readthedocs.org/projects/colander/badge/?version=master
:target: http://docs.pylonsproject.org/projects/colander/en/master/
:alt: Documentation Status
An extensible package which can be used to:
- deserialize and validate a data structure composed of strings,
@@ -9,7 +18,8 @@ An extensible package which can be used to:
- serialize an arbitrary data structure to a data structure composed
of strings, mappings, and lists.
It runs on Python 2.6, 2.7, 3.2, and 3.3.
It runs on Python 2.6, 2.7, 3.2, 3.3, 3.4, and 3.5, and on current PyPy
and PyPy3 versions.
Please see http://docs.pylonsproject.org/projects/colander/en/latest/
for further documentation.

View File

@@ -22,7 +22,12 @@ from . import iso8601
_ = translationstring.TranslationStringFactory('colander')
required = object()
class _required(object):
""" Represents a required value in colander-related operations. """
def __repr__(self):
return '<colander.required>'
required = _required()
_marker = required # bw compat
class _null(object):
@@ -42,11 +47,12 @@ class _null(object):
null = _null()
class _drop(object):
""" Represents a value that will be dropped from the schema if it
is missing during *deserialization*. Passed as a value to the
`missing` keyword argument of :class:`SchemaNode`.
"""
Represents a value that should be dropped if it is missing during
deserialization.
"""
pass
def __repr__(self):
return '<colander.drop>'
drop = _drop()
@@ -57,6 +63,13 @@ def interpolate(msgs):
else:
yield s
class UnboundDeferredError(Exception):
"""
An exception raised by :meth:`SchemaNode.deserialize` when an attempt
is made to deserialize a node which has an unbound :class:`deferred`
validator.
"""
class Invalid(Exception):
"""
An exception raised by data types and validators indicating that
@@ -165,9 +178,14 @@ class Invalid(Exception):
return str(self.pos)
return str(self.node.name)
def asdict(self):
def asdict(self, translate=None):
""" Return a dictionary containing a basic
(non-language-translated) error report for this exception"""
(non-language-translated) error report for this exception.
If ``translate`` is supplied, it must be a callable taking a
translation string as its sole argument and returning a localized,
interpolated string.
"""
paths = self.paths()
errors = {}
for path in paths:
@@ -177,6 +195,8 @@ class Invalid(Exception):
exc.msg and msgs.extend(exc.messages())
keyname = exc._keyname()
keyname and keyparts.append(keyname)
if translate:
msgs = [translate(msg) for msg in msgs]
errors['.'.join(keyparts)] = '; '.join(interpolate(msgs))
return errors
@@ -279,6 +299,9 @@ class Regex(object):
error message to be used; otherwise, defaults to 'String does
not match expected pattern'.
The ``regex`` expression behaviour can be modified by specifying
any ``flags`` value taken by ``re.compile``.
The ``regex`` argument may also be a pattern object (the
result of ``re.compile``) instead of a string.
@@ -286,9 +309,9 @@ class Regex(object):
validation succeeds; otherwise, :exc:`colander.Invalid` is
raised with the ``msg`` error message.
"""
def __init__(self, regex, msg=None):
def __init__(self, regex, msg=None, flags=0):
if isinstance(regex, string_types):
self.match_object = re.compile(regex)
self.match_object = re.compile(regex, flags)
else:
self.match_object = regex
if msg is None:
@@ -307,7 +330,7 @@ class Email(Regex):
the error message to be used when raising :exc:`colander.Invalid`;
otherwise, defaults to 'Invalid email address'.
"""
def __init__(self, msg=None):
email_regex = text_(EMAIL_RE)
if msg is None:
@@ -339,49 +362,74 @@ class Range(object):
provided, it defaults to ``'${val} is greater than maximum value
${max}'``.
"""
min_err = _('${val} is less than minimum value ${min}')
max_err = _('${val} is greater than maximum value ${max}')
_MIN_ERR = _('${val} is less than minimum value ${min}')
_MAX_ERR = _('${val} is greater than maximum value ${max}')
def __init__(self, min=None, max=None, min_err=None, max_err=None):
def __init__(self, min=None, max=None, min_err=_MIN_ERR, max_err=_MAX_ERR):
self.min = min
self.max = max
if min_err is not None:
self.min_err = min_err
if max_err is not None:
self.max_err = max_err
self.min_err = min_err
self.max_err = max_err
def __call__(self, node, value):
if self.min is not None:
if value < self.min:
min_err = _(self.min_err, mapping={'val':value, 'min':self.min})
min_err = _(
self.min_err, mapping={'val':value, 'min':self.min})
raise Invalid(node, min_err)
if self.max is not None:
if value > self.max:
max_err = _(self.max_err, mapping={'val':value, 'max':self.max})
max_err = _(
self.max_err, mapping={'val':value, 'max':self.max})
raise Invalid(node, max_err)
class Length(object):
""" Validator which succeeds if the value passed to it has a
length between a minimum and maximum. The value is most often a
string."""
def __init__(self, min=None, max=None):
"""Validator which succeeds if the value passed to it has a
length between a minimum and maximum, expressed in the
optional ``min`` and ``max`` arguments.
The value can be any sequence, most often a string.
If ``min`` is not specified, or is specified as ``None``,
no lower bound exists. If ``max`` is not specified, or
is specified as ``None``, no upper bound exists.
The default error messages are "Shorter than minimum length ${min}"
and "Longer than maximum length ${max}". These can be customized:
``min_err`` is used to form the ``msg`` of the
:exc:`colander.Invalid` error when reporting a validation failure
caused by a value not meeting the minimum length. If ``min_err`` is
specified, it must be a string. The string may contain the
replacement target ``${min}``.
``max_err`` is used to form the ``msg`` of the
:exc:`colander.Invalid` error when reporting a validation failure
caused by a value exceeding the maximum length. If ``max_err`` is
specified, it must be a string. The string may contain the
replacement target ``${max}``.
"""
_MIN_ERR = _('Shorter than minimum length ${min}')
_MAX_ERR = _('Longer than maximum length ${max}')
def __init__(self, min=None, max=None, min_err=_MIN_ERR, max_err=_MAX_ERR):
self.min = min
self.max = max
self.min_err = min_err
self.max_err = max_err
def __call__(self, node, value):
if self.min is not None:
if len(value) < self.min:
min_err = _('Shorter than minimum length ${min}',
mapping={'min':self.min})
min_err = _(self.min_err, mapping={'min': self.min})
raise Invalid(node, min_err)
if self.max is not None:
if len(value) > self.max:
max_err = _('Longer than maximum length ${max}',
mapping={'max':self.max})
max_err = _(self.max_err, mapping={'max': self.max})
raise Invalid(node, max_err)
class OneOf(object):
""" Validator which succeeds if the value passed to it is one of
a fixed set of values """
@@ -395,6 +443,33 @@ class OneOf(object):
mapping={'val':value, 'choices':choices})
raise Invalid(node, err)
class NoneOf(object):
""" Validator which succeeds if the value passed to it is none of a
fixed set of values.
``msg_err`` is used to form the ``msg`` of the :exc:`colander.Invalid`
error when reporting a validation failure. If ``msg_err`` is specified,
it must be a string. The string may contain the replacement targets
``${choices}`` and ``${val}``, representing the set of forbidden values
and the provided value respectively.
"""
_MSG_ERR = _('"${val}" must not be one of ${choices}')
def __init__(self, choices, msg_err=_MSG_ERR):
self.forbidden = choices
self.msg_err = msg_err
def __call__(self, node, value):
if value not in self.forbidden:
return
choices = ', '.join(['%s' % x for x in self.forbidden])
err = _(self.msg_err, mapping={'val': value, 'choices': choices})
raise Invalid(node, err)
class ContainsOnly(object):
""" Validator which succeeds if the value passed to is a sequence and each
element in the sequence is also in the sequence passed as ``choices``.
@@ -451,6 +526,11 @@ URL_REGEX = r"""(?i)\b((?:[a-z][\w-]+:(?:/{1,3}|[a-z0-9%])|www\d{0,3}[.]|[a-z0-9
url = Regex(URL_REGEX, _('Must be a URL'))
UUID_REGEX = r"""^(?:urn:uuid:)?\{?[a-f0-9]{8}(?:-?[a-f0-9]{4}){3}-?[a-f0-9]{12}\}?$"""
uuid = Regex(UUID_REGEX, _('Invalid UUID string'), re.IGNORECASE)
class SchemaType(object):
""" Base class for all schema types """
def flatten(self, node, appstruct, prefix='', listitem=False):
@@ -459,7 +539,7 @@ class SchemaType(object):
selfname = prefix
else:
selfname = '%s%s' % (prefix, node.name)
result[selfname] = appstruct
result[selfname.rstrip('.')] = appstruct
return result
def unflatten(self, node, paths, fstruct):
@@ -910,7 +990,7 @@ class Sequence(Positional, SchemaType):
def _validate(self, node, value, accept_scalar):
if (hasattr(value, '__iter__') and
not hasattr(value, 'get') and
not hasattr(value, 'get') and
not isinstance(value, string_types)):
return list(value)
if accept_scalar:
@@ -1095,7 +1175,7 @@ class String(SchemaType):
- A non-Unicode input value to ``serialize`` is converted to a
Unicode using the encoding (``unicode(value, encoding)``);
subsequently the Unicode object is reeencoded to a ``str``
subsequently the Unicode object is re-encoded to a ``str``
object using the encoding and returned.
- A Unicode input value to ``deserialize`` is returned
@@ -1132,6 +1212,8 @@ class String(SchemaType):
result = text_type(appstruct)
else:
result = text_type(appstruct)
if self.encoding:
result = result.encode(self.encoding)
return result
except Exception as e:
raise Invalid(node,
@@ -1223,24 +1305,27 @@ class Decimal(Number):
method of this class, the :attr:`colander.null` value will be
returned.
The Decimal constructor takes two optional arguments, ``quant`` and
``rounding``. If supplied, ``quant`` should be a string,
The Decimal constructor takes three optional arguments, ``quant``,
``rounding`` and ``normalize``. If supplied, ``quant`` should be a string,
(e.g. ``1.00``). If supplied, ``rounding`` should be one of the Python
``decimal`` module rounding options (e.g. ``decimal.ROUND_UP``,
``decimal.ROUND_DOWN``, etc). The serialized and deserialized result
will be quantized and rounded via
``result.quantize(decimal.Decimal(quant), rounding)``. ``rounding`` is
ignored if ``quant`` is not supplied.
ignored if ``quant`` is not supplied. If ``normalize`` is ``True``,
the serialized and deserialized result will be normalized by stripping
the rightmost trailing zeros.
The subnodes of the :class:`colander.SchemaNode` that wraps
this type are ignored.
"""
def __init__(self, quant=None, rounding=None):
def __init__(self, quant=None, rounding=None, normalize=False):
if quant is None:
self.quant = None
else:
self.quant = decimal.Decimal(quant)
self.rounding = rounding
self.normalize = normalize
def num(self, val):
result = decimal.Decimal(str(val))
@@ -1249,6 +1334,8 @@ class Decimal(Number):
result = result.quantize(self.quant)
else:
result = result.quantize(self.quant, self.rounding)
if self.normalize:
result = result.normalize()
return result
class Money(Decimal):
@@ -1264,8 +1351,7 @@ class Money(Decimal):
this type are ignored.
"""
def __init__(self):
self.quant = decimal.Decimal('.01')
self.rounding = decimal.ROUND_UP
super(Money, self).__init__(decimal.Decimal('.01'), decimal.ROUND_UP)
class Boolean(SchemaType):
""" A type representing a boolean object.
@@ -1293,7 +1379,7 @@ class Boolean(SchemaType):
are considered ``True``, and an Invalid exception would be raised
for values outside of both :attr:`false_choices` and :attr:`true_choices`.
Serialization will produce :attr:`true_val` or :attr:`false_val`
Serialization will produce :attr:`true_val` or :attr:`false_val`
based on the value.
If the :attr:`colander.null` value is passed to the serialize
@@ -1340,8 +1426,8 @@ class Boolean(SchemaType):
else:
raise Invalid(node,
_('"${val}" is neither in (${false_choices}) '
'nor in (${true_choices})',
mapping={'val':cstruct,
'nor in (${true_choices})',
mapping={'val':cstruct,
'false_choices': self.false_reprs,
'true_choices': self.true_reprs })
)
@@ -1524,9 +1610,7 @@ class DateTime(SchemaType):
"""
err_template = _('Invalid date')
def __init__(self, default_tzinfo=_marker):
if default_tzinfo is _marker:
default_tzinfo = iso8601.Utc()
def __init__(self, default_tzinfo=iso8601.UTC):
self.default_tzinfo = default_tzinfo
def serialize(self, node, appstruct):
@@ -1635,7 +1719,7 @@ class Time(SchemaType):
This type serializes python ``datetime.time`` objects to a
`ISO8601 <http://en.wikipedia.org/wiki/ISO_8601>`_ string format.
The format includes the date only.
The format includes the time only.
The constructor accepts no arguments.
@@ -1673,13 +1757,12 @@ class Time(SchemaType):
err_template = _('Invalid time')
def serialize(self, node, appstruct):
if not appstruct:
return null
if isinstance(appstruct, datetime.datetime):
appstruct = appstruct.time()
if not isinstance(appstruct, datetime.time):
if not appstruct:
return null
raise Invalid(node,
_('"${val}" is not a time object',
mapping={'val':appstruct})
@@ -1735,7 +1818,9 @@ class _SchemaNode(object):
- ``typ``: The 'type' for this node. It should be an
instance of a class that implements the
:class:`colander.interfaces.Type` interface. If ``typ`` is not passed,
it defaults to ``colander.Mapping()``.
a call to the ``schema_type()`` method on this class is made to
get a default type. (When subclassing, ``schema_type()`` should
be overridden to provide a reasonable default type).
- ``*children``: a sequence of subnodes. If the subnodes of this
node are not known at construction time, they can later be added
@@ -1806,10 +1891,10 @@ class _SchemaNode(object):
validator = None
default = null
missing = required
missing_msg = _('Required')
missing_msg = 'Required'
name = ''
raw_title = _marker
title = ''
raw_title = _marker # only changes if title is explicitly set
title = _marker
description = ''
widget = None
after_bind = None
@@ -1834,7 +1919,7 @@ class _SchemaNode(object):
self.typ = self.schema_type()
# bw compat forces us to manufacture a title if one is not supplied
title = kw.get('title', _marker)
title = kw.get('title', self.title)
if title is _marker:
name = kw.get('name', self.name)
kw['title'] = name.replace('_', ' ').title()
@@ -1846,7 +1931,7 @@ class _SchemaNode(object):
@staticmethod
def schema_type():
raise NotImplementedError(
'Schema node construction without a typ argument or '
'Schema node construction without a `typ` argument or '
'a schema_type() callable present on the node class '
)
@@ -1948,15 +2033,21 @@ class _SchemaNode(object):
if appstruct is null:
appstruct = self.missing
if appstruct is required:
raise Invalid(self, self.missing_msg)
raise Invalid(self, _(self.missing_msg,
mapping={'title': self.title,
'name':self.name}))
if isinstance(appstruct, deferred): # unbound schema with deferreds
raise Invalid(self, self.missing_msg)
# We never deserialize or validate the missing value
return appstruct
if self.validator is not None:
if not isinstance(self.validator, deferred): # unbound
self.validator(self, appstruct)
if isinstance(self.validator, deferred): # unbound
raise UnboundDeferredError(
"Schema node {node} has an unbound deferred validator"
.format(node=self))
self.validator(self, appstruct)
return appstruct
def add(self, node):
@@ -2214,9 +2305,9 @@ class instantiate(object):
All parameters passed to the decorator and passed along to the
:class:`SchemaNode` during instantiation.
"""
def __init__(self,*args,**kw):
self.args,self.kw = args,kw
def __call__(self,class_):
return class_(*self.args,**self.kw)

View File

@@ -1,13 +1,14 @@
import sys
PY2 = sys.version_info[0] == 2
PY3 = sys.version_info[0] == 3
if PY3: # pragma: no cover
string_types = str,
text_type = str
else: # pragma: no cover
if PY2:
string_types = basestring,
text_type = unicode
else:
string_types = str,
text_type = str
def text_(s, encoding='latin-1', errors='strict'):
""" If ``s`` is an instance of ``bytes``, return ``s.decode(encoding,
@@ -16,14 +17,14 @@ def text_(s, encoding='latin-1', errors='strict'):
return s.decode(encoding, errors)
return s # pragma: no cover
if PY3: # pragma: no cover
if PY2:
def is_nonstr_iter(v):
return hasattr(v, '__iter__')
else:
def is_nonstr_iter(v):
if isinstance(v, str):
return False
return hasattr(v, '__iter__')
else: # pragma: no cover
def is_nonstr_iter(v):
return hasattr(v, '__iter__')
try:
xrange = xrange

View File

@@ -1,152 +1,5 @@
"""
Copyright (c) 2007 Michael Twomey
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
ISO 8601 date time string parsing
Basic usage:
>>> import iso8601
>>> iso8601.parse_date("2007-01-25T12:00:00Z")
datetime.datetime(2007, 1, 25, 12, 0, tzinfo=<iso8601.iso8601.Utc ...>)
>>>
"""
from datetime import datetime, timedelta, tzinfo
import re
from .compat import string_types
from __future__ import absolute_import
import iso8601
from iso8601.iso8601 import (parse_date, ParseError, Utc, FixedOffset, UTC, ZERO, ISO8601_REGEX)
__all__ = ["parse_date", "ParseError", "Utc", "FixedOffset"]
# Adapted from http://delete.me.uk/2005/03/iso8601.html
ISO8601_REGEX = re.compile(
r"(?P<year>[0-9]{4})(-(?P<month>[0-9]{1,2})(-(?P<day>[0-9]{1,2})"
r"((?P<separator>.)(?P<hour>[0-9]{2})(:(?P<minute>[0-9]{2})(:(?P<second>[0-9]{2})(\.(?P<fraction>[0-9]+))?)?)?"
r"(?P<timezone>Z|(([-+])([0-9]{2})(:?([0-9]{2}))?))?)?)?)?"
)
TIMEZONE_REGEX = re.compile(
"(?P<prefix>[+-])(?P<hours>[0-9]{2})(:?(?P<minutes>[0-9]{2}))?")
class ParseError(Exception):
"""Raised when there is a problem parsing a date string"""
# Yoinked from python docs
ZERO = timedelta(0)
class Utc(tzinfo):
"""UTC
"""
def utcoffset(self, dt):
return ZERO
def tzname(self, dt):
return "UTC"
def dst(self, dt):
return ZERO
UTC = Utc()
class FixedOffset(tzinfo):
"""Fixed offset in hours and minutes from UTC
"""
def __init__(self, offset_hours, offset_minutes, name):
self.__offset = timedelta(hours=offset_hours, minutes=offset_minutes)
self.__name = name
def __getinitargs__(self):
# tzinfo.__reduce__ returns the type as the factory: supply
# defaults here, rather than in __init__.
return 0, 0, 'unknown'
def utcoffset(self, dt):
return self.__offset
def tzname(self, dt):
return self.__name
def dst(self, dt):
return ZERO
def __repr__(self):
return "<FixedOffset %r>" % self.__name
def parse_timezone(tzstring, default_timezone=UTC):
"""Parses ISO 8601 time zone specs into tzinfo offsets
"""
if tzstring == "Z":
return UTC
# This isn't strictly correct, but it's common to encounter dates without
# timezones so I'll assume the default (which defaults to UTC).
# Addresses issue 4.
if tzstring is None:
return default_timezone
m = TIMEZONE_REGEX.match(tzstring)
prefix = m.group('prefix')
hours = int(m.group('hours'))
minutes = m.group('minutes')
if minutes is None:
minutes = 0
else:
minutes = int(minutes)
if prefix == "-":
hours = -hours
minutes = -minutes
return FixedOffset(hours, minutes, tzstring)
def parse_date(datestring, default_timezone=UTC):
"""Parses ISO 8601 dates into datetime objects
The timezone is parsed from the date string. However it is quite common to
have dates without a timezone (not strictly correct). In this case the
default timezone specified in default_timezone is used. This is UTC by
default.
"""
if not isinstance(datestring, string_types):
raise ParseError("Expecting a string %r" % datestring)
m = ISO8601_REGEX.match(datestring)
if not m:
raise ParseError("Unable to parse date string %r" % datestring)
groups = m.groupdict()
tz = parse_timezone(groups["timezone"], default_timezone=default_timezone)
if (groups['year'] is None or
groups['month'] is None or
groups['day'] is None):
raise ParseError('Unable to parse date string %r' % datestring)
if groups["hour"] is None:
groups["hour"] = 0
if groups["minute"] is None:
groups["minute"] = 0
if groups["second"] is None:
groups["second"] = 0
if groups["fraction"] is None:
groups["fraction"] = 0
else:
groups["fraction"] = int(float("0.%s" % groups["fraction"]) * 1e6)
try:
return datetime(
int(groups["year"]), int(groups["month"]), int(groups["day"]),
int(groups["hour"]), int(groups["minute"]), int(groups["second"]),
int(groups["fraction"]), tz)
except ValueError as e:
raise ParseError(*e.args)

View File

@@ -31,11 +31,11 @@ msgstr "Ungültige E-Mail-Adresse"
#: colander/__init__.py:315
msgid "${val} is less than minimum value ${min}"
msgstr "${val} ist kleiner als der erlaubter Mindestwert (${min})"
msgstr "${val} ist kleiner als der erlaubte Mindestwert (${min})"
#: colander/__init__.py:316
msgid "${val} is greater than maximum value ${max}"
msgstr "${val} ist größer als der erlaubter Höchstwert (${max})"
msgstr "${val} ist größer als der erlaubte Höchstwert (${max})"
#: colander/__init__.py:348
msgid "Shorter than minimum length ${min}"
@@ -51,11 +51,11 @@ msgstr "\"${val}\" ist nicht in der Auswahl ${choices}"
#: colander/__init__.py:377
msgid "One or more of the choices you made was not acceptable"
msgstr ""
msgstr "Mindestens eine Auswahl war nicht akzeptabel"
#: colander/__init__.py:423
msgid "Must be a URL"
msgstr ""
msgstr "Muss eine URL sein"
#: colander/__init__.py:519
msgid "\"${val}\" is not a mapping type: ${err}"
@@ -99,7 +99,7 @@ msgstr "${val} ist keine Zeichenkette"
#: colander/__init__.py:1279
msgid "\"${val}\" is neither in (${false_choices}) nor in (${true_choices})"
msgstr ""
msgstr "\"${val}\" ist weder in (${false_choices}) noch in (${true_choices}) enthalten"
#: colander/__init__.py:1339 colander/__init__.py:1356
#: colander/__init__.py:1366

Binary file not shown.

View File

@@ -0,0 +1,156 @@
# Greek translations for colander.
# Copyright (C) 2013 ORGANIZATION
# This file is distributed under the same license as the colander project.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2013.
#
msgid ""
msgstr ""
"Project-Id-Version: colander 1.0b1\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2013-11-16 14:23+0900\n"
"PO-Revision-Date: 2015-03-04 10:03+0200\n"
"Last-Translator: Daniel Dourvaris <dan@car.gr>\n"
"Language-Team: el <LL@li.org>\n"
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 1.3\n"
#: colander/__init__.py:240
msgid "Invalid value"
msgstr "Λάθος στοιχείο"
#: colander/__init__.py:283
msgid "String does not match expected pattern"
msgstr "Το κείμενο δέν αντιστοιχεί στην μακέτα"
#: colander/__init__.py:302
msgid "Invalid email address"
msgstr "Λάθος διεύθηνση email"
#: colander/__init__.py:330
msgid "${val} is less than minimum value ${min}"
msgstr "${val} είναι λιγότερο απο το ελάχιστο ${min}"
#: colander/__init__.py:331
msgid "${val} is greater than maximum value ${max}"
msgstr "${val} είναι μεγαλύτερο απο το μέγιστο ${max}"
#: colander/__init__.py:363
msgid "Shorter than minimum length ${min}"
msgstr "Λιγότερο απο το ελάχιστο ${min} στοιχεία"
#: colander/__init__.py:369
msgid "Longer than maximum length ${max}"
msgstr "Μεγαλύτερο απο το μέγιστο ${max} χαρακτήρες"
#: colander/__init__.py:382
msgid "\"${val}\" is not one of ${choices}"
msgstr "\"${val}\" δέν είναι ενα απο ${choices}"
#: colander/__init__.py:392
msgid "One or more of the choices you made was not acceptable"
msgstr "Ενα η παραπάνω απο τις επιλογές δέν ήταν αποδεχτό"
#: colander/__init__.py:414 colander/__init__.py:419
msgid "\"${val}\" is not a valid credit card number"
msgstr "\"${val}'" δέν είναι έγκυρη πιστοτική κάρτα"
#: colander/__init__.py:440
msgid "Must be a URL"
msgstr "Πρέπει να είναι URL"
#: colander/__init__.py:536
msgid "\"${val}\" is not a mapping type: ${err}"
msgstr "\"${val}\" δέν είναι τύπου mapping: ${err}"
#: colander/__init__.py:578
msgid "Unrecognized keys in mapping: \"${val}\""
msgstr "Αγνωστα keys στο mapping: \"${val}\""
#: colander/__init__.py:674 colander/__init__.py:905
msgid "\"${val}\" is not iterable"
msgstr "\"${val}\" δέν είναι λίστα"
#: colander/__init__.py:682
msgid ""
"\"${val}\" has an incorrect number of elements (expected ${exp}, was "
"${was})"
msgstr ""
"\"${val}\" έχει λάθος νούμερο στοιχέιων (ήθελε ${exp}, έχει "
"${was})"
#: colander/__init__.py:821 colander/__init__.py:852
msgid "${cstruct} is not iterable"
msgstr "${cstruct} δέν είναι λίστα"
#: colander/__init__.py:1124
msgid "${val} cannot be serialized: ${err}"
msgstr "${val} δέν μπορεί να γίνει serialize:${err}"
#: colander/__init__.py:1142
msgid "${val} is not a string: ${err}"
msgstr "${val} δέν είναι κείμενο: ${err}"
#: colander/__init__.py:1162 colander/__init__.py:1173
msgid "\"${val}\" is not a number"
msgstr "\"${val}\" δέν είναι νούμερο"
#: colander/__init__.py:1317
msgid "${val} is not a string"
msgstr "${val} δέν είναι κείμενο"
#: colander/__init__.py:1328
msgid "\"${val}\" is neither in (${false_choices}) nor in (${true_choices})"
msgstr "\"${val}\" ούτε στο (${false_choices}) είναι, ούτε στο (${true_choices})"
#: colander/__init__.py:1388 colander/__init__.py:1405
#: colander/__init__.py:1415
msgid "relative name \"${val}\" irresolveable without package"
msgstr ""
#: colander/__init__.py:1445
msgid "\"${val}\" has no __name__"
msgstr "\"${val}\" δέν έχει __name__"
#: colander/__init__.py:1454
msgid "\"${val}\" is not a string"
msgstr "\"${val}\" δέν είναι κείμενο"
#: colander/__init__.py:1463
msgid "The dotted name \"${name}\" cannot be imported"
msgstr ""
#: colander/__init__.py:1511 colander/__init__.py:1587
msgid "Invalid date"
msgstr "Λάθος ημερομηνία"
#: colander/__init__.py:1527
msgid "\"${val}\" is not a datetime object"
msgstr "\"${val}\" δέν είναι τύπου datetime"
#: colander/__init__.py:1598
msgid "\"${val}\" is not a date object"
msgstr "\"${val}\" δέν είναι τύπου date"
#: colander/__init__.py:1659
msgid "Invalid time"
msgstr "Λάθος ώρα"
#: colander/__init__.py:1670
msgid "\"${val}\" is not a time object"
msgstr "\"${val}\" δέν είναι τύπου time"
#: colander/__init__.py:1795
msgid "Required"
msgstr "Απαραίτητο"
#: colander/tests/test_colander.py:295 colander/tests/test_colander.py:302
msgid "fail ${val}"
msgstr "λάθος ${val}"
#: colander/tests/test_colander.py:469
msgid "${val}: ${choices}"
msgstr "${val}: ${choices}"

View File

@@ -8,14 +8,15 @@ msgstr ""
"Project-Id-Version: colander 0.8\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2013-05-19 13:15+0200\n"
"PO-Revision-Date: 2011-10-02 21:38+0100\n"
"Last-Translator: Bard Stéphane <stephane.bard@gmail.com>\n"
"PO-Revision-Date: 2014-06-27 11:46+0100\n"
"Last-Translator: Cédric Messiant <cedric.messiant@gmail.com>\n"
"Language-Team: fr <LL@li.org>\n"
"Plural-Forms: nplurals=2; plural=(n > 1)\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 1.3\n"
"X-Generator: Poedit 1.5.4\n"
#: colander/__init__.py:233
msgid "Invalid value"
@@ -23,11 +24,11 @@ msgstr "Valeur incorrecte"
#: colander/__init__.py:270
msgid "String does not match expected pattern"
msgstr "La chaîne de caractères n'a pas vérifié le modèle attendu"
msgstr "La chaîne de caractères ne correspond pas au modèle attendu"
#: colander/__init__.py:287
msgid "Invalid email address"
msgstr "Invalide adresse email"
msgstr "Adresse email invalide"
#: colander/__init__.py:315
msgid "${val} is less than minimum value ${min}"
@@ -39,7 +40,7 @@ msgstr "${val} est plus grand que la valeur maximum autorisée (${max})"
#: colander/__init__.py:348
msgid "Shorter than minimum length ${min}"
msgstr "La longueur est inférieur à la taille minimum autorisée (${min})"
msgstr "La longueur est inférieure à la taille minimum autorisée (${min})"
#: colander/__init__.py:354
msgid "Longer than maximum length ${max}"
@@ -73,11 +74,10 @@ msgstr "\"${val}\" n'est pas itérable"
#: colander/__init__.py:664
msgid ""
"\"${val}\" has an incorrect number of elements (expected ${exp}, was "
"${was})"
"\"${val}\" has an incorrect number of elements (expected ${exp}, was ${was})"
msgstr ""
"\"${val}\" possède un nombre incorrecte d'éléments (attendu ${exp}, "
"trouvé ${was})"
"\"${val}\" possède un nombre incorrect d'éléments (attendu ${exp}, trouvé "
"${was})"
#: colander/__init__.py:803
msgid "${cstruct} is not iterable"
@@ -97,11 +97,12 @@ msgstr "\"${val}\" n'est pas un nombre"
#: colander/__init__.py:1268
msgid "${val} is not a string"
msgstr "${val} n'est pas une chaîne de carctères"
msgstr "${val} n'est pas une chaîne de caractères"
#: colander/__init__.py:1279
msgid "\"${val}\" is neither in (${false_choices}) nor in (${true_choices})"
msgstr "\"${val}\" ne fait partie ni de (${false_choices}) ni de (${true_choices})"
msgstr ""
"\"${val}\" ne fait partie ni de (${false_choices}) ni de (${true_choices})"
#: colander/__init__.py:1339 colander/__init__.py:1356
#: colander/__init__.py:1366
@@ -133,16 +134,14 @@ msgid "\"${val}\" is not a date object"
msgstr "\"${val}\" n'est pas un objet date"
#: colander/__init__.py:1610
#, fuzzy
#| msgid "Invalid date"
msgid "Invalid time"
msgstr "Date invalide"
msgstr "Heure invalide"
#: colander/__init__.py:1621
#, fuzzy
#| msgid "\"${val}\" is not a datetime object"
msgid "\"${val}\" is not a time object"
msgstr "\"${val}\" n'est pas un objet datetime"
msgstr "\"${val}\" n'est pas un objet time"
#: colander/__init__.py:1878 colander/__init__.py:1880
msgid "Required"

View File

@@ -7,141 +7,152 @@ msgid ""
msgstr ""
"Project-Id-Version: colander 0.8\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2013-05-19 13:15+0200\n"
"POT-Creation-Date: 2013-11-16 14:23+0900\n"
"PO-Revision-Date: 2011-06-03 00:22+0200\n"
"Last-Translator: Wichert Akkerman <wichert@wiggy.net>\n"
"Last-Translator: Tinne Cahy <cahytinne@gmail.com>\n"
"Language-Team: nl <LL@li.org>\n"
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
"Generated-By: Babel 1.3\n"
#: colander/__init__.py:233
#: colander/__init__.py:240
msgid "Invalid value"
msgstr "Ongeldige waarde"
#: colander/__init__.py:270
#: colander/__init__.py:283
msgid "String does not match expected pattern"
msgstr "Tekst is niet in het juiste formaat"
#: colander/__init__.py:287
#: colander/__init__.py:302
msgid "Invalid email address"
msgstr "Ongeldig e-mail adres"
#: colander/__init__.py:315
#: colander/__init__.py:330
msgid "${val} is less than minimum value ${min}"
msgstr "${val} is minder dan de minimum waarde ${min}"
#: colander/__init__.py:316
#: colander/__init__.py:331
msgid "${val} is greater than maximum value ${max}"
msgstr "${val} is groter dan de maximale toegestaane waarde ${max}"
msgstr "${val} is groter dan de maximale toegestane waarde ${max}"
#: colander/__init__.py:348
#: colander/__init__.py:363
msgid "Shorter than minimum length ${min}"
msgstr "De minimale lengte is ${min}"
#: colander/__init__.py:354
#: colander/__init__.py:369
msgid "Longer than maximum length ${max}"
msgstr "De maximale lengte is ${max}"
#: colander/__init__.py:367
#: colander/__init__.py:382
msgid "\"${val}\" is not one of ${choices}"
msgstr "\"${val}\" is geen toegestaande waarde. Kies één van ${choices}."
msgstr "\"${val}\" is geen toegestane waarde. Kies één van ${choices}."
#: colander/__init__.py:377
#: colander/__init__.py:392
msgid "One or more of the choices you made was not acceptable"
msgstr ""
msgstr "Één of meer van de keuzes die je maakte zijn niet aanvaardbaar"
#: colander/__init__.py:423
#: colander/__init__.py:414 colander/__init__.py:419
#, fuzzy
msgid "\"${val}\" is not a valid credit card number"
msgstr "\"${val}\" is geen geldige kredietkaartnummer"
#: colander/__init__.py:440
msgid "Must be a URL"
msgstr ""
msgstr "Moet een URL zijn"
#: colander/__init__.py:519
#: colander/__init__.py:536
msgid "\"${val}\" is not a mapping type: ${err}"
msgstr "\"${val}\" is geen map type: ${err}"
msgstr "\"${val}\" is geen mapping type: ${err}"
#: colander/__init__.py:560
#: colander/__init__.py:578
msgid "Unrecognized keys in mapping: \"${val}\""
msgstr "Onbekende waardes in map: \"${val}\""
msgstr "Onbekende waardes in mapping: \"${val}\""
#: colander/__init__.py:656 colander/__init__.py:856
#: colander/__init__.py:674 colander/__init__.py:905
msgid "\"${val}\" is not iterable"
msgstr "\"${val}\" is niet itereerbaar"
#: colander/__init__.py:664
#: colander/__init__.py:682
msgid ""
"\"${val}\" has an incorrect number of elements (expected ${exp}, was "
"${was})"
"\"${val}\" has an incorrect number of elements (expected ${exp}, was ${was})"
msgstr ""
"\"${val}\" bevat niet het juiste aantal element (${was} in plaats van "
"${exp})"
"\"${val}\" bevat niet het juiste aantal elementen (${was} in plaats van ${exp})"
#: colander/__init__.py:803
#: colander/__init__.py:821 colander/__init__.py:852
msgid "${cstruct} is not iterable"
msgstr "${cstruct} is niet itereerbaar"
#: colander/__init__.py:1075
#: colander/__init__.py:1124
msgid "${val} cannot be serialized: ${err}"
msgstr "${val} kan niet worden opgeslagen: ${err}"
#: colander/__init__.py:1093
#: colander/__init__.py:1142
msgid "${val} is not a string: ${err}"
msgstr "${val} is geen tekst: ${err}"
#: colander/__init__.py:1113 colander/__init__.py:1124
#: colander/__init__.py:1162 colander/__init__.py:1173
msgid "\"${val}\" is not a number"
msgstr "\"${val}\" is geen getal"
#: colander/__init__.py:1268
#: colander/__init__.py:1317
msgid "${val} is not a string"
msgstr "${val} is geen tekst"
#: colander/__init__.py:1279
#: colander/__init__.py:1328
msgid "\"${val}\" is neither in (${false_choices}) nor in (${true_choices})"
msgstr ""
msgstr "\"${val}\" is niet aanwezig in (${false_choices}) noch aanwezig in (${true_choices})"
#: colander/__init__.py:1339 colander/__init__.py:1356
#: colander/__init__.py:1366
#: colander/__init__.py:1388 colander/__init__.py:1405
#: colander/__init__.py:1415
msgid "relative name \"${val}\" irresolveable without package"
msgstr "relatieve aanduiding \"${val}\" kan niet worden opgezocht zonder package"
#: colander/__init__.py:1396
#: colander/__init__.py:1445
msgid "\"${val}\" has no __name__"
msgstr "\"${val}\" heeft geen __name__"
#: colander/__init__.py:1405
#: colander/__init__.py:1454
msgid "\"${val}\" is not a string"
msgstr "\"${val}\" is geen tekst"
#: colander/__init__.py:1414
#: colander/__init__.py:1463
msgid "The dotted name \"${name}\" cannot be imported"
msgstr "Kan \"${name}\" niet importeren"
#: colander/__init__.py:1462 colander/__init__.py:1538
#: colander/__init__.py:1511 colander/__init__.py:1587
msgid "Invalid date"
msgstr "Geen geldige datum"
#: colander/__init__.py:1478
#: colander/__init__.py:1527
msgid "\"${val}\" is not a datetime object"
msgstr "\"${val}\" is geen datetime object"
#: colander/__init__.py:1549
#: colander/__init__.py:1598
msgid "\"${val}\" is not a date object"
msgstr "\"${val}\" is geen date object"
#: colander/__init__.py:1610
#: colander/__init__.py:1659
#, fuzzy
#| msgid "Invalid date"
msgid "Invalid time"
msgstr "Geen geldige datum"
#: colander/__init__.py:1621
#: colander/__init__.py:1670
#, fuzzy
#| msgid "\"${val}\" is not a datetime object"
msgid "\"${val}\" is not a time object"
msgstr "\"${val}\" is geen datetime object"
#: colander/__init__.py:1878 colander/__init__.py:1880
#: colander/__init__.py:1795
msgid "Required"
msgstr "Verplicht"
#: colander/tests/test_colander.py:295 colander/tests/test_colander.py:302
msgid "fail ${val}"
msgstr "faal ${val}"
#: colander/tests/test_colander.py:469
#, fuzzy
msgid "${val}: ${choices}"
msgstr "\"${val}\" is geen toegestane waarde. Kies één van ${choices}."

View File

@@ -110,9 +110,10 @@ class TestInvalid(unittest.TestCase):
exc1.add(exc2, 2)
exc2.add(exc3, 3)
d = exc1.asdict()
self.assertEqual(d,
{'node1.node2.3': 'exc1; exc2; validator1; validator2',
'node1.node3': 'exc1; message1'})
self.assertEqual(
d,
{'node1.node2.3': 'exc1; exc2; validator1; validator2',
'node1.node3': 'exc1; message1'})
def test_asdict_with_all_validator_functional(self):
# see https://github.com/Pylons/colander/issues/2
@@ -153,7 +154,8 @@ class TestInvalid(unittest.TestCase):
result = str(exc1)
self.assertEqual(
result,
"{'node1.node2.3': 'exc1; exc2; exc3', 'node1.node4': 'exc1; exc4'}"
"{'node1.node2.3': 'exc1; exc2; exc3', "
"'node1.node4': 'exc1; exc4'}"
)
def test___setitem__fails(self):
@@ -425,15 +427,17 @@ class TestEmail(unittest.TestCase):
validator = self._makeOne()
from colander import Invalid
self.assertRaises(Invalid, validator, None, 'me@here.')
self.assertRaises(Invalid, validator, None, 'name@here.tldiswaytoolooooooooong')
self.assertRaises(Invalid,
validator, None, 'name@here.tldiswaytoolooooooooong')
self.assertRaises(Invalid, validator, None, '@here.us')
self.assertRaises(Invalid, validator, None, 'me@here..com')
self.assertRaises(Invalid, validator, None, 'me@we-here-.com')
class TestLength(unittest.TestCase):
def _makeOne(self, min=None, max=None):
def _makeOne(self, **kw):
from colander import Length
return Length(min=min, max=max)
return Length(**kw)
def test_success_no_bounds(self):
validator = self._makeOne()
@@ -461,6 +465,17 @@ class TestLength(unittest.TestCase):
e = invalid_exc(validator, None, 'ab')
self.assertEqual(e.msg.interpolate(), 'Longer than maximum length 1')
def test_min_failure_msg_override(self):
validator = self._makeOne(min=1, min_err='Need at least ${min}, mate')
e = invalid_exc(validator, None, [])
self.assertEqual(e.msg.interpolate(), 'Need at least 1, mate')
def test_max_failure_msg_override(self):
validator = self._makeOne(max=1, max_err='No more than ${max}, mate')
e = invalid_exc(validator, None, [1, 2])
self.assertEqual(e.msg.interpolate(), 'No more than 1, mate')
class TestOneOf(unittest.TestCase):
def _makeOne(self, values):
from colander import OneOf
@@ -475,6 +490,22 @@ class TestOneOf(unittest.TestCase):
e = invalid_exc(validator, None, None)
self.assertEqual(e.msg.interpolate(), '"None" is not one of 1, 2')
class TestNoneOf(unittest.TestCase):
def _makeOne(self, values):
from colander import NoneOf
return NoneOf(values)
def test_success(self):
validator = self._makeOne([1, 2])
self.assertEqual(validator(None, 3), None)
def test_failure(self):
validator = self._makeOne([1, 2])
e = invalid_exc(validator, None, 2)
self.assertEqual(e.msg.interpolate(), '"2" must not be one of 1, 2')
class TestContainsOnly(unittest.TestCase):
def _makeOne(self, values):
from colander import ContainsOnly
@@ -538,6 +569,57 @@ class Test_url_validator(unittest.TestCase):
from colander import Invalid
self.assertRaises(Invalid, self._callFUT, val)
class TestUUID(unittest.TestCase):
def _callFUT(self, val):
from colander import uuid
return uuid(None, val)
def test_success_hexadecimal(self):
val = '123e4567e89b12d3a456426655440000'
result = self._callFUT(val)
self.assertEqual(result, None)
def test_success_with_dashes(self):
val = '123e4567-e89b-12d3-a456-426655440000'
result = self._callFUT(val)
self.assertEqual(result, None)
def test_success_upper_case(self):
val = '123E4567-E89B-12D3-A456-426655440000'
result = self._callFUT(val)
self.assertEqual(result, None)
def test_success_with_braces(self):
val = '{123e4567-e89b-12d3-a456-426655440000}'
result = self._callFUT(val)
self.assertEqual(result, None)
def test_success_with_urn_ns(self):
val = 'urn:uuid:{123e4567-e89b-12d3-a456-426655440000}'
result = self._callFUT(val)
self.assertEqual(result, None)
def test_failure_random_string(self):
val = 'not-a-uuid'
from colander import Invalid
self.assertRaises(Invalid, self._callFUT, val)
def test_failure_not_hexadecimal(self):
val = '123zzzzz-uuuu-zzzz-uuuu-42665544zzzz'
from colander import Invalid
self.assertRaises(Invalid, self._callFUT, val)
def test_failure_invalid_length(self):
# Correct UUID: 8-4-4-4-12
val = '88888888-333-4444-333-cccccccccccc'
from colander import Invalid
self.assertRaises(Invalid, self._callFUT, val)
def test_failure_with_invalid_urn_ns(self):
val = 'urn:abcd:{123e4567-e89b-12d3-a456-426655440000}'
from colander import Invalid
self.assertRaises(Invalid, self._callFUT, val)
class TestSchemaType(unittest.TestCase):
def _makeOne(self, *arg, **kw):
from colander import SchemaType
@@ -725,7 +807,7 @@ class TestMapping(unittest.TestCase):
typ = self._makeOne()
result = typ.serialize(node, {'a':drop})
self.assertEqual(result, {})
def test_flatten(self):
node = DummySchemaNode(None, name='node')
int1 = DummyType()
@@ -1309,6 +1391,16 @@ class TestSequence(unittest.TestCase):
result = typ.flatten(node, [1, 2])
self.assertEqual(result, {'node.0': 1, 'node.1': 2})
def test_flatten_with_integer(self):
from colander import Integer
node = DummySchemaNode(None, name='node')
node.children = [
DummySchemaNode(Integer(), name='foo'),
]
typ = self._makeOne()
result = typ.flatten(node, [1, 2])
self.assertEqual(result, {'node.0': 1, 'node.1': 2})
def test_flatten_listitem(self):
node = DummySchemaNode(None, name='node')
node.children = [
@@ -1481,6 +1573,13 @@ class TestString(unittest.TestCase):
e = invalid_exc(typ.serialize, node, not_utf8)
self.assertTrue('cannot be serialized' in e.msg)
def test_serialize_encoding_with_non_string_type(self):
utf8 = text_type('123').encode('utf-8')
node = DummySchemaNode(None)
typ = self._makeOne('utf-8')
result = typ.serialize(node, 123)
self.assertEqual(result, utf8)
class TestInteger(unittest.TestCase):
def _makeOne(self):
from colander import Integer
@@ -1499,7 +1598,7 @@ class TestInteger(unittest.TestCase):
result = typ.serialize(node, val)
self.assertEqual(result, colander.null)
def test_serialize_emptystring(self):
def test_deserialize_emptystring(self):
import colander
val = ''
node = DummySchemaNode(None)
@@ -1592,9 +1691,9 @@ class TestFloat(unittest.TestCase):
self.assertEqual(result, '1.0')
class TestDecimal(unittest.TestCase):
def _makeOne(self, quant=None, rounding=None):
def _makeOne(self, quant=None, rounding=None, normalize=False):
from colander import Decimal
return Decimal(quant, rounding)
return Decimal(quant, rounding, normalize)
def test_serialize_null(self):
import colander
@@ -1626,6 +1725,14 @@ class TestDecimal(unittest.TestCase):
result = typ.serialize(node, val)
self.assertEqual(result, '0.01')
def test_serialize_normalize(self):
from decimal import Decimal
val = Decimal('1.00')
node = DummySchemaNode(None)
typ = self._makeOne(normalize=True)
result = typ.serialize(node, val)
self.assertEqual(result, '1')
def test_deserialize_fails(self):
val = 'P'
node = DummySchemaNode(None)
@@ -1649,6 +1756,15 @@ class TestDecimal(unittest.TestCase):
result = typ.deserialize(node, val)
self.assertEqual(result, decimal.Decimal('1.01'))
def test_deserialize_with_normalize(self):
from decimal import Decimal
val = '1.00'
node = DummySchemaNode(None)
typ = self._makeOne(normalize=True)
result = typ.deserialize(node, val)
self.assertEqual(result, Decimal('1'))
self.assertEqual(str(result), '1')
def test_serialize_fails(self):
val = 'P'
node = DummySchemaNode(None)
@@ -2257,6 +2373,15 @@ class TestTime(unittest.TestCase):
expected = time.isoformat().split('.')[0]
self.assertEqual(result, expected)
def test_serialize_with_zero_time(self):
import datetime
typ = self._makeOne()
time = datetime.time(0)
node = DummySchemaNode(None)
result = typ.serialize(node, time)
expected = time.isoformat().split('.')[0]
self.assertEqual(result, expected)
def test_serialize_with_datetime(self):
typ = self._makeOne()
dt = self._dt()
@@ -2333,8 +2458,9 @@ class TestSchemaNode(unittest.TestCase):
def test_ctor_no_title(self):
child = DummySchemaNode(None, name='fred')
node = self._makeOne(None, child, validator=1, default=2, name='name_a',
missing='missing')
node = self._makeOne(
None, child, validator=1, default=2,
name='name_a', missing='missing')
self.assertEqual(node.typ, None)
self.assertEqual(node.children, [child])
self.assertEqual(node.validator, 1)
@@ -2453,6 +2579,19 @@ class TestSchemaNode(unittest.TestCase):
e = invalid_exc(node.deserialize, 1)
self.assertEqual(e.msg, 'Wrong')
def test_deserialize_with_unbound_validator(self):
from colander import Invalid
from colander import deferred
from colander import UnboundDeferredError
typ = DummyType()
def validator(node, kw):
def _validate(node, value):
node.raise_invalid('Invalid')
return _validate
node = self._makeOne(typ, validator=deferred(validator))
self.assertRaises(UnboundDeferredError, node.deserialize, None)
self.assertRaises(Invalid, node.bind(foo='foo').deserialize, None)
def test_deserialize_value_is_null_no_missing(self):
from colander import null
from colander import Invalid
@@ -2474,6 +2613,14 @@ class TestSchemaNode(unittest.TestCase):
e = invalid_exc(node.deserialize, null)
self.assertEqual(e.msg, 'Missing')
def test_deserialize_value_with_interpolated_missing_msg(self):
from colander import null
typ = DummyType()
node = self._makeOne(typ, missing_msg='Missing attribute ${title}',
name='name_a')
e = invalid_exc(node.deserialize, null)
self.assertEqual(e.msg.interpolate(), 'Missing attribute Name A')
def test_deserialize_noargs_uses_default(self):
typ = DummyType()
node = self._makeOne(typ)
@@ -2717,6 +2864,32 @@ class TestSchemaNodeSubclassing(unittest.TestCase):
result = node.deserialize(colander.null)
self.assertEqual(result, 10)
def test_subclass_uses_title(self):
import colander
class MyNode(colander.SchemaNode):
schema_type = colander.Int
title = 'some title'
node = MyNode(name='my')
self.assertEqual(node.title, 'some title')
def test_subclass_title_overwritten_by_constructor(self):
import colander
class MyNode(colander.SchemaNode):
schema_type = colander.Int
title = 'some title'
node = MyNode(name='my', title='other title')
self.assertEqual(node.title, 'other title')
def test_subelement_title_not_overwritten(self):
import colander
class SampleNode(colander.SchemaNode):
schema_type = colander.String
title = 'Some Title'
class SampleSchema(colander.Schema):
node = SampleNode()
schema = SampleSchema()
self.assertEqual('Some Title', schema.children[0].title)
def test_subclass_value_overridden_by_constructor(self):
import colander
class MyNode(colander.SchemaNode):
@@ -3436,7 +3609,8 @@ class TestFunctional(object):
def test_invalid_asdict(self):
expected = {
'schema.int': '20 is greater than maximum value 10',
'schema.ob': 'The dotted name "no.way.this.exists" cannot be imported',
'schema.ob': 'The dotted name "no.way.this.exists" '
'cannot be imported',
'schema.seq.0.0': '"q" is not a number',
'schema.seq.1.0': '"w" is not a number',
'schema.seq.2.0': '"e" is not a number',
@@ -3458,6 +3632,39 @@ class TestFunctional(object):
errors = e.asdict()
self.assertEqual(errors, expected)
def test_invalid_asdict_translation_callback(self):
from translationstring import TranslationString
expected = {
'schema.int': 'translated',
'schema.ob': 'translated',
'schema.seq.0.0': 'translated',
'schema.seq.1.0': 'translated',
'schema.seq.2.0': 'translated',
'schema.seq.3.0': 'translated',
'schema.seq2.0.key': 'translated',
'schema.seq2.0.key2': 'translated',
'schema.seq2.1.key': 'translated',
'schema.seq2.1.key2': 'translated',
'schema.tup.0': 'translated',
}
data = {
'int': '20',
'ob': 'no.way.this.exists',
'seq': [('q', 's'), ('w', 's'), ('e', 's'), ('r', 's')],
'seq2': [{'key': 't', 'key2': 'y'}, {'key':'u', 'key2':'i'}],
'tup': ('s', 's'),
}
schema = self._makeSchema()
e = invalid_exc(schema.deserialize, data)
def translation_function(string):
return TranslationString('translated')
errors = e.asdict(translate=translation_function)
self.assertEqual(errors, expected)
class TestImperative(unittest.TestCase, TestFunctional):
def _makeSchema(self, name='schema'):
@@ -3598,7 +3805,7 @@ class TestUltraDeclarative(unittest.TestCase, TestFunctional):
return schema
class TestDeclarativeWithInstantiate(unittest.TestCase, TestFunctional):
def _makeSchema(self, name='schema'):
import colander
@@ -3612,20 +3819,20 @@ class TestDeclarativeWithInstantiate(unittest.TestCase, TestFunctional):
ob = colander.SchemaNode(colander.GlobalObject(package=colander))
@colander.instantiate()
class seq(colander.SequenceSchema):
@colander.instantiate()
class tup(colander.TupleSchema):
tupint = colander.SchemaNode(colander.Int())
tupstring = colander.SchemaNode(colander.String())
@colander.instantiate()
class tup(colander.TupleSchema):
tupint = colander.SchemaNode(colander.Int())
tupstring = colander.SchemaNode(colander.String())
@colander.instantiate()
class seq2(colander.SequenceSchema):
@colander.instantiate()
class mapping(colander.MappingSchema):
key = colander.SchemaNode(colander.Int())
@@ -3647,6 +3854,16 @@ class Test_null(unittest.TestCase):
import pickle
self.assertTrue(pickle.loads(pickle.dumps(null)) is null)
class Test_required(unittest.TestCase):
def test___repr__(self):
from colander import required
self.assertEqual(repr(required), '<colander.required>')
class Test_drop(unittest.TestCase):
def test___repr__(self):
from colander import drop
self.assertEqual(repr(drop), '<colander.drop>')
class Dummy(object):
pass

View File

@@ -0,0 +1,2 @@
def test_interfaces():
from colander import interfaces

View File

@@ -68,12 +68,17 @@ class Test_FixedOffset(unittest.TestCase):
def test___repr__(self):
inst = self._makeOne()
result = inst.__repr__()
self.assertEqual(result, "<FixedOffset 'oneandahalf'>")
self.assertEqual(result, "<FixedOffset 'oneandahalf' datetime.timedelta(0, 5400)>")
class Test_parse_timezone(unittest.TestCase):
def _callFUT(self, tzstring, **kw):
from ..iso8601 import parse_timezone
return parse_timezone(tzstring, **kw)
# mimic old parse_timezone() by returning a FixedOffset
from datetime import tzinfo
from ..iso8601 import (parse_date, FixedOffset)
if tzstring is None:
tzstring = ''
dt = parse_date("2006-10-11T00:14:33{0}".format(tzstring), **kw)
return dt.tzinfo
def test_default_Z(self):
from ..iso8601 import UTC

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

View File

@@ -1,33 +0,0 @@
@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;
}
/* override the justify text align of the default */
div.body p {
text-align: left !important;
}
/* fix google chrome <pre> tag renderings */
pre {
line-height: normal !important;
}

View File

@@ -48,6 +48,9 @@ Exceptions
from a widget as the value which should be redisplayed when an
error is shown.
.. autoclass:: UnboundDeferredError
Validators
~~~~~~~~~~
@@ -61,6 +64,8 @@ Validators
.. autoclass:: OneOf
.. autoclass:: NoneOf
.. autoclass:: ContainsOnly
.. autoclass:: Function
@@ -75,6 +80,11 @@ Validators
A validator which ensures the value is a URL (via regex).
.. attribute:: uuid
A UUID hexadecimal string validator via regular expression
using :class:`colander.Regex`.
Types
~~~~~
@@ -139,16 +149,12 @@ Schema-Related
.. autoclass:: instantiate
.. attribute:: null
.. autodata:: null
:annotation:
Represents a null value in colander-related operations.
.. autodata:: required
:annotation:
.. attribute:: required
.. autodata:: drop
:annotation:
Represents a required value in colander-related operations.
.. attribute:: drop
Represents a value that will be dropped from the schema if it is missing
during *deserialization*. Passed as a value to the `missing` keyword
argument of :class:`SchemaNode`.

View File

@@ -171,7 +171,7 @@ The imperative style that looks like this still works, of course:
.. code-block:: python
ranged_int = colander.SchemaNode(
typ=colander.Int(),
schema_type=colander.Int,
validator=colander.Range(0, 10),
default=10,
title='Ranged Int'
@@ -182,7 +182,7 @@ But in 1.0a1+, you can alternately now do something like this:
.. code-block:: python
class RangedInt(colander.SchemaNode):
typ = colander.Int()
schema_type = colander.Int
validator = colander.Range(0, 10)
default = 10
title = 'Ranged Int'
@@ -195,7 +195,7 @@ the schemanode subclass instead of plain attributes:
.. code-block:: python
class RangedInt(colander.SchemaNode):
typ = colander.Int()
schema_type = colander.Int
default = 10
title = 'Ranged Int'
@@ -220,7 +220,7 @@ example this will *not* work:
.. code-block:: python
class RangedInt(colander.SchemaNode):
typ = colander.Int()
schema_type = colander.Int
default = 10
title = 'Ranged Int'
@@ -247,7 +247,7 @@ indeed work):
.. code-block:: python
class RangedInt(colander.SchemaNode):
typ = colander.Int()
schema_type = colander.Int
default = 10
title = 'Ranged Int'
@@ -271,7 +271,7 @@ the bind parameters within values that are plain old methods:
.. code-block:: python
class RangedInt(colander.SchemaNode):
typ = colander.Int()
schema_type = colander.Int
default = 10
title = 'Ranged Int'
@@ -292,7 +292,7 @@ attributes of the schemanode that rely on binding variables:
.. code-block:: python
class UserIdSchemaNode(colander.SchemaNode):
typ = colander.String()
schema_type = colander.String
title = 'User Id'
def after_bind(self, node, kw):
@@ -304,7 +304,7 @@ constructor:
.. code-block:: python
class RangedInt(colander.SchemaNode):
typ = colander.Int()
schema_type = colander.Int
default = 10
title = 'Ranged Int'
validator = colander.Range(0, 10)
@@ -395,7 +395,7 @@ Earlier we defined a schema:
Let's now use this schema to try to deserialize some concrete data
structures.
Each of thse concrete data structures is called a :term:`cstruct`.
Each of these concrete data structures is called a :term:`cstruct`.
"cstruct" is an abbreviation of "colander structure": you can think of
a cstruct as a serialized representation of some application data. A
"cstruct" is usually generated by the
@@ -999,7 +999,7 @@ schema, then the schema definition can be made more succinct using the
@colander.instantiate()
class friend(colander.TupleSchema):
rank = colander.SchemaNode(colander.Int(),
rank = colander.SchemaNode(colander.Int(),
validator=colander.Range(0, 9999))
name = colander.SchemaNode(colander.String())
@@ -1008,7 +1008,7 @@ schema, then the schema definition can be made more succinct using the
@colander.instantiate()
class phone(colander.MappingSchema):
location = colander.SchemaNode(colander.String(),
location = colander.SchemaNode(colander.String(),
validator=colander.OneOf(['home', 'work']))
number = colander.SchemaNode(colander.String())
@@ -1076,22 +1076,22 @@ We can imperatively construct a completely equivalent schema like so:
import colander
friend = colander.SchemaNode(Tuple())
friend = colander.SchemaNode(colander.Tuple())
friend.add(colander.SchemaNode(colander.Int(),
validator=colander.Range(0, 9999),
name='rank'))
friend.add(colander.SchemaNode(colander.String(), name='name')
friend.add(colander.SchemaNode(colander.String(), name='name'))
phone = colander.SchemaNode(Mapping())
phone = colander.SchemaNode(colander.Mapping())
phone.add(colander.SchemaNode(colander.String(),
validator=colander.OneOf(['home', 'work']),
name='location'))
phone.add(colander.SchemaNode(colander.String(), name='number'))
schema = colander.SchemaNode(Mapping())
schema = colander.SchemaNode(colander.Mapping())
schema.add(colander.SchemaNode(colander.String(), name='name'))
schema.add(colander.SchemaNode(colander.Int(), name='age'),
validator=colander.Range(0, 200))
schema.add(colander.SchemaNode(colander.Int(), name='age',
validator=colander.Range(0, 200)))
schema.add(colander.SchemaNode(colander.Sequence(), friend, name='friends'))
schema.add(colander.SchemaNode(colander.Sequence(), phone, name='phones'))
@@ -1129,7 +1129,7 @@ For example, in a Python module, you might have code that looks like this:
.. code-block:: python
from colander import MappingSchema
from colander import SchemaNode, MappingSchema
from colander import Int
class MySchema1(MappingSchema):

View File

@@ -248,7 +248,8 @@ If you use a schema with deferred ``validator``, ``missing`` or
``default`` attributes, but you use it to perform serialization and
deserialization without calling its ``bind`` method:
- If ``validator`` is deferred, no validation will be performed.
- If ``validator`` is deferred, :meth:`~colander.SchemaNode.deserialize` will
raise an :exc:`~colander.UnboundDeferredError`.
- If ``missing`` is deferred, the field will be considered *required*.

View File

@@ -1 +1 @@
.. include:: ../CHANGES.txt
.. include:: ../CHANGES.rst

View File

@@ -12,7 +12,9 @@
# All configuration values have a default value; values that are commented
# out serve to show the default value.
import sys, os
import sys, os, datetime
import pkg_resources
import pylons_sphinx_themes
# General configuration
# ---------------------
@@ -32,13 +34,14 @@ master_doc = 'index'
# General substitutions.
project = 'colander'
copyright = '2012, Agendaless Consulting <pylons-discuss@googlegroups.com>'
thisyear = datetime.datetime.now().year
copyright = '2012-%s, Agendaless Consulting <pylons-discuss@googlegroups.com>' % thisyear
# The default replacements for |version| and |release|, also used in various
# other places throughout the built documents.
#
# The short X.Y version.
version = '1.0b1'
version = pkg_resources.get_distribution('colander').version
# The full version, including alpha/beta/rc tags.
release = version
@@ -49,7 +52,7 @@ release = version
today_fmt = '%B %d, %Y'
# List of documents that shouldn't be included in the build.
unused_docs = ['_themes/README']
#unused_docs = ['_themes/README']
# List of directories, relative to source directories, that shouldn't be
# searched for source files.
@@ -73,43 +76,11 @@ unused_docs = ['_themes/README']
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
# Add and use Pylons theme
if 'sphinx-build' in ' '.join(sys.argv): # protect against dumb importers
from subprocess import call, Popen, PIPE
p = Popen('which git', shell=True, stdout=PIPE)
git = p.stdout.read().strip()
cwd = os.getcwd()
_themes = os.path.join(cwd, '_themes')
if not os.path.isdir(_themes):
call([git, 'clone', 'git://github.com/Pylons/pylons_sphinx_theme.git',
'_themes'])
else:
os.chdir(_themes)
call([git, 'checkout', 'master'])
call([git, 'pull'])
os.chdir(cwd)
sys.path.append(os.path.abspath('_themes'))
parent = os.path.dirname(os.path.dirname(__file__))
sys.path.append(os.path.abspath(parent))
wd = os.getcwd()
os.chdir(parent)
os.system('%s setup.py test -q' % sys.executable)
os.chdir(wd)
for item in os.listdir(parent):
if item.endswith('.egg'):
sys.path.append(os.path.join(parent, item))
# Options for HTML output
# -----------------------
sys.path.append(os.path.abspath('_themes'))
html_theme_path = ['_themes']
html_theme = 'pylons'
# sys.path.append(os.path.abspath('_themes'))
html_theme = 'pyramid'
html_theme_path = pylons_sphinx_themes.get_html_themes_path()
html_theme_options = dict(github_url='https://github.com/Pylons/colander')
# The style sheet to use for HTML and HTML Help pages. A file of that name
@@ -146,7 +117,7 @@ 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
html_use_smartypants = False
# Custom sidebar templates, maps document names to template names.
#html_sidebars = {}
@@ -177,7 +148,7 @@ html_last_updated_fmt = '%b %d, %Y'
#html_file_suffix = ''
# Output file base name for HTML help builder.
htmlhelp_basename = 'atemplatedoc'
htmlhelp_basename = 'colanderdoc'
# Options for LaTeX output
@@ -193,13 +164,13 @@ htmlhelp_basename = 'atemplatedoc'
# (source start file, target name, title,
# author, document class [howto/manual]).
latex_documents = [
('index', 'atemplate.tex', 'colander Documentation',
('index', 'colander.tex', 'colander Documentation',
'Pylons 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'
#latex_logo = '.static/logo_hi.gif'
# For "manual" documents, if this is true, then toplevel headings are
# parts, not chapters.

View File

@@ -6,7 +6,8 @@ Colander
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. It runs on Python 2.6, 2.7 and 3.2. Colander can be used to:
serialization. It runs on Python 2.6, 2.7, 3.2, 3.3, 3.4, pypy and pypy3.
Colander can be used to:
- Define a data schema.

1
rtd.txt Normal file
View File

@@ -0,0 +1 @@
-e .[docs]

View File

@@ -1,3 +1,6 @@
[wheel]
universal = 1
[easy_install]
zip_ok = false

View File

@@ -24,31 +24,37 @@ def read(fname):
return fp.read()
try:
README = read(os.path.join(here, 'README.txt'))
CHANGES = read(os.path.join(here, 'CHANGES.txt'))
README = read(os.path.join(here, 'README.rst'))
CHANGES = read(os.path.join(here, 'CHANGES.rst'))
except:
README = ''
CHANGES = ''
requires = ['translationstring']
requires = ['translationstring', 'iso8601']
testing_extras = ['nose', 'coverage']
docs_extras = ['Sphinx']
docs_extras = [
'Sphinx >= 1.3.1',
'docutils',
'pylons-sphinx-themes',
]
setup(name='colander',
version='1.0b1',
version='1.1',
description=('A simple schema-based serialization and deserialization '
'library'),
long_description=README + '\n\n' + CHANGES,
classifiers=[
"Intended Audience :: Developers",
"Programming Language :: Python",
"Programming Language :: Python",
"Programming Language :: Python :: 2",
"Programming Language :: Python :: 2.6",
"Programming Language :: Python :: 2.7",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.2",
"Programming Language :: Python :: 3.3",
"Programming Language :: Python :: 3.4",
"Programming Language :: Python :: 3.5",
"Programming Language :: Python :: Implementation :: CPython",
"Programming Language :: Python :: Implementation :: PyPy",
],

69
tox.ini
View File

@@ -1,25 +1,62 @@
[tox]
envlist =
py26,py27,py32,py33,pypy,cover,docs
py26,py27,py32,py33,py34,py35,pypy,pypy3,
docs,
{py2,py3}-cover,coverage
[testenv]
commands =
python setup.py dev
python -Wd setup.py test -q
[testenv:cover]
# Most of these are defaults but if you specify any you can't fall back
# to defaults for others.
basepython =
python2.6
py26: python2.6
py27: python2.7
py32: python3.2
py33: python3.3
py34: python3.4
py35: python3.5
pypy: pypy
pypy3: pypy3
py2: python2.7
py3: python3.5
commands =
python setup.py nosetests --with-xunit --with-xcoverage
deps =
nosexcover
pip install colander[testing]
nosetests --with-xunit --xunit-file=nosetests-{envname}.xml {posargs:}
[testenv:docs]
basepython =
python2.6
commands =
sphinx-build -b html -d docs/_build/doctrees docs docs/_build/html
# sphinx-build -b doctest -d docs/_build/doctrees docs docs/_build/doctest
basepython = python3.5
whitelist_externals = make
commands =
pip install colander[docs]
make -C docs html epub BUILDDIR={envdir} "SPHINXOPTS=-W -E"
# we separate coverage into its own testenv because a) "last run wins" wrt
# cobertura jenkins reporting and b) pypy and jython can't handle any
# combination of versions of coverage and nosexcover that i can find.
[testenv:py2-cover]
commands =
pip install colander[testing]
coverage run --source=colander {envbindir}/nosetests
coverage xml -o coverage-py2.xml
setenv =
COVERAGE_FILE=.coverage.py2
[testenv:py3-cover]
commands =
pip install colander[testing]
coverage run --source=colander {envbindir}/nosetests
coverage xml -o coverage-py3.xml
setenv =
COVERAGE_FILE=.coverage.py3
[testenv:coverage]
basepython = python3.5
commands =
coverage erase
coverage combine
coverage xml
coverage report --show-missing --fail-under=100
deps =
Sphinx
coverage
setenv =
COVERAGE_FILE=.coverage