Merge branch 'master' into deserialize-unbound-validators
This commit is contained in:
22
.travis.yml
Normal file
22
.travis.yml
Normal file
@@ -0,0 +1,22 @@
|
||||
# Wire up travis
|
||||
language: python
|
||||
|
||||
env:
|
||||
- TOXENV=py26
|
||||
- TOXENV=py27
|
||||
- TOXENV=py32
|
||||
- TOXENV=py33
|
||||
- TOXENV=py34
|
||||
- TOXENV=pypy
|
||||
- TOXENV=pypy3
|
||||
- TOXENV=cover
|
||||
|
||||
install:
|
||||
- travis_retry pip install tox
|
||||
|
||||
script:
|
||||
- travis_retry tox
|
||||
|
||||
notifications:
|
||||
email:
|
||||
- pyramid-checkins@lists.repoze.org
|
@@ -1,9 +1,34 @@
|
||||
Unreleased
|
||||
----------
|
||||
|
||||
Platform
|
||||
--------
|
||||
|
||||
- Addd explicit support for Python 3.4 and PyPy3.
|
||||
|
||||
Features
|
||||
~~~~~~~~
|
||||
|
||||
- Add `Any` validator which 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`` that specifies the error
|
||||
message to be used when the node is required and missing
|
||||
|
||||
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``.
|
||||
@@ -17,14 +42,13 @@ Bug Fixes
|
||||
returning the ``drop`` instance instead of omitting the value.
|
||||
https://github.com/Pylons/colander/issues/139
|
||||
|
||||
Features
|
||||
~~~~~~~~
|
||||
- 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
|
||||
|
||||
- Add `Any` validator which succeeds if at least one of its subvalidators
|
||||
succeeded.
|
||||
- Updated translations: ``fr``, ``de``, ``ja``
|
||||
|
||||
- Allow localization of error messages returned by ``colander.Invalid.asdict``
|
||||
by adding an optional ``translate`` callable argument.
|
||||
|
||||
Backwards Incompatibilities
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
@@ -90,9 +114,6 @@ 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
|
||||
|
||||
1.0a5 (2013-05-31)
|
||||
------------------
|
||||
|
@@ -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
|
||||
@@ -116,3 +117,6 @@ Contributors
|
||||
- Veeti Paananen, 2013/08/20
|
||||
- Michael Howitz, 2013/12/05
|
||||
- Alex Marandon, 2013/12/21
|
||||
- Cédric Messiant, 2014/06/27
|
||||
- Gouji Ochiai, 2014/08/21
|
||||
- Tim Tisdall, 2014/09/10
|
||||
|
@@ -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.
|
||||
|
@@ -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, and 3.4, and on current PyPy
|
||||
and PyPy3 versions.
|
||||
|
||||
Please see http://docs.pylonsproject.org/projects/colander/en/latest/
|
||||
for further documentation.
|
@@ -1822,7 +1822,7 @@ class _SchemaNode(object):
|
||||
missing_msg = _('Required')
|
||||
name = ''
|
||||
raw_title = _marker
|
||||
title = ''
|
||||
title = _marker
|
||||
description = ''
|
||||
widget = None
|
||||
after_bind = None
|
||||
@@ -1849,8 +1849,9 @@ class _SchemaNode(object):
|
||||
# bw compat forces us to manufacture a title if one is not supplied
|
||||
title = kw.get('title', _marker)
|
||||
if title is _marker:
|
||||
name = kw.get('name', self.name)
|
||||
kw['title'] = name.replace('_', ' ').title()
|
||||
if self.title is _marker:
|
||||
name = kw.get('name', self.name)
|
||||
kw['title'] = name.replace('_', ' ').title()
|
||||
else:
|
||||
kw['raw_title'] = title
|
||||
|
||||
|
@@ -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)
|
||||
|
@@ -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.
@@ -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"
|
||||
|
@@ -2739,6 +2739,22 @@ 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_subclass_value_overridden_by_constructor(self):
|
||||
import colander
|
||||
class MyNode(colander.SchemaNode):
|
||||
|
@@ -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
|
||||
|
@@ -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):
|
||||
|
11
setup.py
11
setup.py
@@ -24,31 +24,32 @@ 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']
|
||||
|
||||
setup(name='colander',
|
||||
version='1.0b1',
|
||||
version='1.1dev',
|
||||
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 :: Implementation :: CPython",
|
||||
"Programming Language :: Python :: Implementation :: PyPy",
|
||||
],
|
||||
|
5
tox.ini
5
tox.ini
@@ -1,6 +1,6 @@
|
||||
[tox]
|
||||
envlist =
|
||||
py26,py27,py32,py33,pypy,cover,docs
|
||||
py26,py27,py32,py33,py34,pypy,pypy3,cover,docs
|
||||
|
||||
[testenv]
|
||||
commands =
|
||||
@@ -11,7 +11,7 @@ commands =
|
||||
basepython =
|
||||
python2.6
|
||||
commands =
|
||||
python setup.py nosetests --with-xunit --with-xcoverage
|
||||
python setup.py nosetests --with-xunit --with-xcoverage --cover-min-percentage=100
|
||||
deps =
|
||||
nosexcover
|
||||
|
||||
@@ -20,6 +20,5 @@ 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
|
||||
deps =
|
||||
Sphinx
|
||||
|
Reference in New Issue
Block a user