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
|
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
|
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``.
|
- Time of "00:00" no longer gives ``colander.Invalid``.
|
||||||
|
|
||||||
- Un-break wrapping of callable instances as ``colander.deferred``.
|
- Un-break wrapping of callable instances as ``colander.deferred``.
|
||||||
@@ -17,14 +42,13 @@ Bug Fixes
|
|||||||
returning the ``drop`` instance instead of omitting the value.
|
returning the ``drop`` instance instead of omitting the value.
|
||||||
https://github.com/Pylons/colander/issues/139
|
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
|
- Updated translations: ``fr``, ``de``, ``ja``
|
||||||
succeeded.
|
|
||||||
|
|
||||||
- Allow localization of error messages returned by ``colander.Invalid.asdict``
|
|
||||||
by adding an optional ``translate`` callable argument.
|
|
||||||
|
|
||||||
Backwards Incompatibilities
|
Backwards Incompatibilities
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
@@ -90,9 +114,6 @@ Features
|
|||||||
- The ``typ`` of a ``SchemaNode`` can optionally be pased in as a keyword
|
- The ``typ`` of a ``SchemaNode`` can optionally be pased in as a keyword
|
||||||
argument. See https://github.com/Pylons/colander/issues/90
|
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)
|
1.0a5 (2013-05-31)
|
||||||
------------------
|
------------------
|
||||||
|
|
@@ -101,7 +101,8 @@ and agrees to the terms above in the section within this document entitled
|
|||||||
Contributors
|
Contributors
|
||||||
------------
|
------------
|
||||||
|
|
||||||
- Chris McDonough, 2011/02/16
|
- Chris McDonough, 2010/03/11
|
||||||
|
- Tres Seaver, 2010/04/05
|
||||||
- Chris Withers, 2011/05/45
|
- Chris Withers, 2011/05/45
|
||||||
- Mathieu Le Marec - Pasquet (kiorky), 2011/07/11
|
- Mathieu Le Marec - Pasquet (kiorky), 2011/07/11
|
||||||
- Atsushi Odagiri, 2012/02/04
|
- Atsushi Odagiri, 2012/02/04
|
||||||
@@ -116,3 +117,6 @@ Contributors
|
|||||||
- Veeti Paananen, 2013/08/20
|
- Veeti Paananen, 2013/08/20
|
||||||
- Michael Howitz, 2013/12/05
|
- Michael Howitz, 2013/12/05
|
||||||
- Alex Marandon, 2013/12/21
|
- 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
|
THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
SUCH DAMAGE.
|
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
|
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:
|
An extensible package which can be used to:
|
||||||
|
|
||||||
- deserialize and validate a data structure composed of strings,
|
- 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
|
- serialize an arbitrary data structure to a data structure composed
|
||||||
of strings, mappings, and lists.
|
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/
|
Please see http://docs.pylonsproject.org/projects/colander/en/latest/
|
||||||
for further documentation.
|
for further documentation.
|
@@ -1822,7 +1822,7 @@ class _SchemaNode(object):
|
|||||||
missing_msg = _('Required')
|
missing_msg = _('Required')
|
||||||
name = ''
|
name = ''
|
||||||
raw_title = _marker
|
raw_title = _marker
|
||||||
title = ''
|
title = _marker
|
||||||
description = ''
|
description = ''
|
||||||
widget = None
|
widget = None
|
||||||
after_bind = None
|
after_bind = None
|
||||||
@@ -1849,8 +1849,9 @@ class _SchemaNode(object):
|
|||||||
# bw compat forces us to manufacture a title if one is not supplied
|
# bw compat forces us to manufacture a title if one is not supplied
|
||||||
title = kw.get('title', _marker)
|
title = kw.get('title', _marker)
|
||||||
if title is _marker:
|
if title is _marker:
|
||||||
name = kw.get('name', self.name)
|
if self.title is _marker:
|
||||||
kw['title'] = name.replace('_', ' ').title()
|
name = kw.get('name', self.name)
|
||||||
|
kw['title'] = name.replace('_', ' ').title()
|
||||||
else:
|
else:
|
||||||
kw['raw_title'] = title
|
kw['raw_title'] = title
|
||||||
|
|
||||||
|
@@ -1,152 +1,5 @@
|
|||||||
"""
|
from __future__ import absolute_import
|
||||||
Copyright (c) 2007 Michael Twomey
|
import iso8601
|
||||||
|
from iso8601.iso8601 import (parse_date, ParseError, Utc, FixedOffset, UTC, ZERO, ISO8601_REGEX)
|
||||||
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
|
|
||||||
|
|
||||||
__all__ = ["parse_date", "ParseError", "Utc", "FixedOffset"]
|
__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
|
#: colander/__init__.py:315
|
||||||
msgid "${val} is less than minimum value ${min}"
|
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
|
#: colander/__init__.py:316
|
||||||
msgid "${val} is greater than maximum value ${max}"
|
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
|
#: colander/__init__.py:348
|
||||||
msgid "Shorter than minimum length ${min}"
|
msgid "Shorter than minimum length ${min}"
|
||||||
@@ -51,11 +51,11 @@ msgstr "\"${val}\" ist nicht in der Auswahl ${choices}"
|
|||||||
|
|
||||||
#: colander/__init__.py:377
|
#: colander/__init__.py:377
|
||||||
msgid "One or more of the choices you made was not acceptable"
|
msgid "One or more of the choices you made was not acceptable"
|
||||||
msgstr ""
|
msgstr "Mindestens eine Auswahl war nicht akzeptabel"
|
||||||
|
|
||||||
#: colander/__init__.py:423
|
#: colander/__init__.py:423
|
||||||
msgid "Must be a URL"
|
msgid "Must be a URL"
|
||||||
msgstr ""
|
msgstr "Muss eine URL sein"
|
||||||
|
|
||||||
#: colander/__init__.py:519
|
#: colander/__init__.py:519
|
||||||
msgid "\"${val}\" is not a mapping type: ${err}"
|
msgid "\"${val}\" is not a mapping type: ${err}"
|
||||||
@@ -99,7 +99,7 @@ msgstr "${val} ist keine Zeichenkette"
|
|||||||
|
|
||||||
#: colander/__init__.py:1279
|
#: colander/__init__.py:1279
|
||||||
msgid "\"${val}\" is neither in (${false_choices}) nor in (${true_choices})"
|
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:1339 colander/__init__.py:1356
|
||||||
#: colander/__init__.py:1366
|
#: colander/__init__.py:1366
|
||||||
|
Binary file not shown.
@@ -8,14 +8,15 @@ msgstr ""
|
|||||||
"Project-Id-Version: colander 0.8\n"
|
"Project-Id-Version: colander 0.8\n"
|
||||||
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
|
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
|
||||||
"POT-Creation-Date: 2013-05-19 13:15+0200\n"
|
"POT-Creation-Date: 2013-05-19 13:15+0200\n"
|
||||||
"PO-Revision-Date: 2011-10-02 21:38+0100\n"
|
"PO-Revision-Date: 2014-06-27 11:46+0100\n"
|
||||||
"Last-Translator: Bard Stéphane <stephane.bard@gmail.com>\n"
|
"Last-Translator: Cédric Messiant <cedric.messiant@gmail.com>\n"
|
||||||
"Language-Team: fr <LL@li.org>\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"
|
"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"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"Generated-By: Babel 1.3\n"
|
"Generated-By: Babel 1.3\n"
|
||||||
|
"X-Generator: Poedit 1.5.4\n"
|
||||||
|
|
||||||
#: colander/__init__.py:233
|
#: colander/__init__.py:233
|
||||||
msgid "Invalid value"
|
msgid "Invalid value"
|
||||||
@@ -23,11 +24,11 @@ msgstr "Valeur incorrecte"
|
|||||||
|
|
||||||
#: colander/__init__.py:270
|
#: colander/__init__.py:270
|
||||||
msgid "String does not match expected pattern"
|
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
|
#: colander/__init__.py:287
|
||||||
msgid "Invalid email address"
|
msgid "Invalid email address"
|
||||||
msgstr "Invalide adresse email"
|
msgstr "Adresse email invalide"
|
||||||
|
|
||||||
#: colander/__init__.py:315
|
#: colander/__init__.py:315
|
||||||
msgid "${val} is less than minimum value ${min}"
|
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
|
#: colander/__init__.py:348
|
||||||
msgid "Shorter than minimum length ${min}"
|
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
|
#: colander/__init__.py:354
|
||||||
msgid "Longer than maximum length ${max}"
|
msgid "Longer than maximum length ${max}"
|
||||||
@@ -73,11 +74,10 @@ msgstr "\"${val}\" n'est pas itérable"
|
|||||||
|
|
||||||
#: colander/__init__.py:664
|
#: colander/__init__.py:664
|
||||||
msgid ""
|
msgid ""
|
||||||
"\"${val}\" has an incorrect number of elements (expected ${exp}, was "
|
"\"${val}\" has an incorrect number of elements (expected ${exp}, was ${was})"
|
||||||
"${was})"
|
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"\"${val}\" possède un nombre incorrecte d'éléments (attendu ${exp}, "
|
"\"${val}\" possède un nombre incorrect d'éléments (attendu ${exp}, trouvé "
|
||||||
"trouvé ${was})"
|
"${was})"
|
||||||
|
|
||||||
#: colander/__init__.py:803
|
#: colander/__init__.py:803
|
||||||
msgid "${cstruct} is not iterable"
|
msgid "${cstruct} is not iterable"
|
||||||
@@ -97,11 +97,12 @@ msgstr "\"${val}\" n'est pas un nombre"
|
|||||||
|
|
||||||
#: colander/__init__.py:1268
|
#: colander/__init__.py:1268
|
||||||
msgid "${val} is not a string"
|
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
|
#: colander/__init__.py:1279
|
||||||
msgid "\"${val}\" is neither in (${false_choices}) nor in (${true_choices})"
|
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:1339 colander/__init__.py:1356
|
||||||
#: colander/__init__.py:1366
|
#: colander/__init__.py:1366
|
||||||
@@ -133,16 +134,14 @@ msgid "\"${val}\" is not a date object"
|
|||||||
msgstr "\"${val}\" n'est pas un objet date"
|
msgstr "\"${val}\" n'est pas un objet date"
|
||||||
|
|
||||||
#: colander/__init__.py:1610
|
#: colander/__init__.py:1610
|
||||||
#, fuzzy
|
|
||||||
#| msgid "Invalid date"
|
#| msgid "Invalid date"
|
||||||
msgid "Invalid time"
|
msgid "Invalid time"
|
||||||
msgstr "Date invalide"
|
msgstr "Heure invalide"
|
||||||
|
|
||||||
#: colander/__init__.py:1621
|
#: colander/__init__.py:1621
|
||||||
#, fuzzy
|
|
||||||
#| msgid "\"${val}\" is not a datetime object"
|
#| msgid "\"${val}\" is not a datetime object"
|
||||||
msgid "\"${val}\" is not a time 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
|
#: colander/__init__.py:1878 colander/__init__.py:1880
|
||||||
msgid "Required"
|
msgid "Required"
|
||||||
|
@@ -2739,6 +2739,22 @@ class TestSchemaNodeSubclassing(unittest.TestCase):
|
|||||||
result = node.deserialize(colander.null)
|
result = node.deserialize(colander.null)
|
||||||
self.assertEqual(result, 10)
|
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):
|
def test_subclass_value_overridden_by_constructor(self):
|
||||||
import colander
|
import colander
|
||||||
class MyNode(colander.SchemaNode):
|
class MyNode(colander.SchemaNode):
|
||||||
|
@@ -68,12 +68,17 @@ class Test_FixedOffset(unittest.TestCase):
|
|||||||
def test___repr__(self):
|
def test___repr__(self):
|
||||||
inst = self._makeOne()
|
inst = self._makeOne()
|
||||||
result = inst.__repr__()
|
result = inst.__repr__()
|
||||||
self.assertEqual(result, "<FixedOffset 'oneandahalf'>")
|
self.assertEqual(result, "<FixedOffset 'oneandahalf' datetime.timedelta(0, 5400)>")
|
||||||
|
|
||||||
class Test_parse_timezone(unittest.TestCase):
|
class Test_parse_timezone(unittest.TestCase):
|
||||||
def _callFUT(self, tzstring, **kw):
|
def _callFUT(self, tzstring, **kw):
|
||||||
from ..iso8601 import parse_timezone
|
# mimic old parse_timezone() by returning a FixedOffset
|
||||||
return parse_timezone(tzstring, **kw)
|
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):
|
def test_default_Z(self):
|
||||||
from ..iso8601 import UTC
|
from ..iso8601 import UTC
|
||||||
|
@@ -171,7 +171,7 @@ The imperative style that looks like this still works, of course:
|
|||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
ranged_int = colander.SchemaNode(
|
ranged_int = colander.SchemaNode(
|
||||||
typ=colander.Int(),
|
schema_type=colander.Int,
|
||||||
validator=colander.Range(0, 10),
|
validator=colander.Range(0, 10),
|
||||||
default=10,
|
default=10,
|
||||||
title='Ranged Int'
|
title='Ranged Int'
|
||||||
@@ -182,7 +182,7 @@ But in 1.0a1+, you can alternately now do something like this:
|
|||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
class RangedInt(colander.SchemaNode):
|
class RangedInt(colander.SchemaNode):
|
||||||
typ = colander.Int()
|
schema_type = colander.Int
|
||||||
validator = colander.Range(0, 10)
|
validator = colander.Range(0, 10)
|
||||||
default = 10
|
default = 10
|
||||||
title = 'Ranged Int'
|
title = 'Ranged Int'
|
||||||
@@ -195,7 +195,7 @@ the schemanode subclass instead of plain attributes:
|
|||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
class RangedInt(colander.SchemaNode):
|
class RangedInt(colander.SchemaNode):
|
||||||
typ = colander.Int()
|
schema_type = colander.Int
|
||||||
default = 10
|
default = 10
|
||||||
title = 'Ranged Int'
|
title = 'Ranged Int'
|
||||||
|
|
||||||
@@ -220,7 +220,7 @@ example this will *not* work:
|
|||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
class RangedInt(colander.SchemaNode):
|
class RangedInt(colander.SchemaNode):
|
||||||
typ = colander.Int()
|
schema_type = colander.Int
|
||||||
default = 10
|
default = 10
|
||||||
title = 'Ranged Int'
|
title = 'Ranged Int'
|
||||||
|
|
||||||
@@ -247,7 +247,7 @@ indeed work):
|
|||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
class RangedInt(colander.SchemaNode):
|
class RangedInt(colander.SchemaNode):
|
||||||
typ = colander.Int()
|
schema_type = colander.Int
|
||||||
default = 10
|
default = 10
|
||||||
title = 'Ranged Int'
|
title = 'Ranged Int'
|
||||||
|
|
||||||
@@ -271,7 +271,7 @@ the bind parameters within values that are plain old methods:
|
|||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
class RangedInt(colander.SchemaNode):
|
class RangedInt(colander.SchemaNode):
|
||||||
typ = colander.Int()
|
schema_type = colander.Int
|
||||||
default = 10
|
default = 10
|
||||||
title = 'Ranged Int'
|
title = 'Ranged Int'
|
||||||
|
|
||||||
@@ -292,7 +292,7 @@ attributes of the schemanode that rely on binding variables:
|
|||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
class UserIdSchemaNode(colander.SchemaNode):
|
class UserIdSchemaNode(colander.SchemaNode):
|
||||||
typ = colander.String()
|
schema_type = colander.String
|
||||||
title = 'User Id'
|
title = 'User Id'
|
||||||
|
|
||||||
def after_bind(self, node, kw):
|
def after_bind(self, node, kw):
|
||||||
@@ -304,7 +304,7 @@ constructor:
|
|||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
class RangedInt(colander.SchemaNode):
|
class RangedInt(colander.SchemaNode):
|
||||||
typ = colander.Int()
|
schema_type = colander.Int
|
||||||
default = 10
|
default = 10
|
||||||
title = 'Ranged Int'
|
title = 'Ranged Int'
|
||||||
validator = colander.Range(0, 10)
|
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
|
Let's now use this schema to try to deserialize some concrete data
|
||||||
structures.
|
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
|
"cstruct" is an abbreviation of "colander structure": you can think of
|
||||||
a cstruct as a serialized representation of some application data. A
|
a cstruct as a serialized representation of some application data. A
|
||||||
"cstruct" is usually generated by the
|
"cstruct" is usually generated by the
|
||||||
@@ -1076,22 +1076,22 @@ We can imperatively construct a completely equivalent schema like so:
|
|||||||
|
|
||||||
import colander
|
import colander
|
||||||
|
|
||||||
friend = colander.SchemaNode(Tuple())
|
friend = colander.SchemaNode(colander.Tuple())
|
||||||
friend.add(colander.SchemaNode(colander.Int(),
|
friend.add(colander.SchemaNode(colander.Int(),
|
||||||
validator=colander.Range(0, 9999),
|
validator=colander.Range(0, 9999),
|
||||||
name='rank'))
|
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(),
|
phone.add(colander.SchemaNode(colander.String(),
|
||||||
validator=colander.OneOf(['home', 'work']),
|
validator=colander.OneOf(['home', 'work']),
|
||||||
name='location'))
|
name='location'))
|
||||||
phone.add(colander.SchemaNode(colander.String(), name='number'))
|
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.String(), name='name'))
|
||||||
schema.add(colander.SchemaNode(colander.Int(), name='age'),
|
schema.add(colander.SchemaNode(colander.Int(), name='age',
|
||||||
validator=colander.Range(0, 200))
|
validator=colander.Range(0, 200)))
|
||||||
schema.add(colander.SchemaNode(colander.Sequence(), friend, name='friends'))
|
schema.add(colander.SchemaNode(colander.Sequence(), friend, name='friends'))
|
||||||
schema.add(colander.SchemaNode(colander.Sequence(), phone, name='phones'))
|
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
|
.. code-block:: python
|
||||||
|
|
||||||
from colander import MappingSchema
|
from colander import SchemaNode, MappingSchema
|
||||||
from colander import Int
|
from colander import Int
|
||||||
|
|
||||||
class MySchema1(MappingSchema):
|
class MySchema1(MappingSchema):
|
||||||
|
11
setup.py
11
setup.py
@@ -24,31 +24,32 @@ def read(fname):
|
|||||||
return fp.read()
|
return fp.read()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
README = read(os.path.join(here, 'README.txt'))
|
README = read(os.path.join(here, 'README.rst'))
|
||||||
CHANGES = read(os.path.join(here, 'CHANGES.txt'))
|
CHANGES = read(os.path.join(here, 'CHANGES.rst'))
|
||||||
except:
|
except:
|
||||||
README = ''
|
README = ''
|
||||||
CHANGES = ''
|
CHANGES = ''
|
||||||
|
|
||||||
requires = ['translationstring']
|
requires = ['translationstring', 'iso8601']
|
||||||
|
|
||||||
testing_extras = ['nose', 'coverage']
|
testing_extras = ['nose', 'coverage']
|
||||||
docs_extras = ['Sphinx']
|
docs_extras = ['Sphinx']
|
||||||
|
|
||||||
setup(name='colander',
|
setup(name='colander',
|
||||||
version='1.0b1',
|
version='1.1dev',
|
||||||
description=('A simple schema-based serialization and deserialization '
|
description=('A simple schema-based serialization and deserialization '
|
||||||
'library'),
|
'library'),
|
||||||
long_description=README + '\n\n' + CHANGES,
|
long_description=README + '\n\n' + CHANGES,
|
||||||
classifiers=[
|
classifiers=[
|
||||||
"Intended Audience :: Developers",
|
"Intended Audience :: Developers",
|
||||||
"Programming Language :: Python",
|
"Programming Language :: Python",
|
||||||
"Programming Language :: Python",
|
"Programming Language :: Python :: 2",
|
||||||
"Programming Language :: Python :: 2.6",
|
"Programming Language :: Python :: 2.6",
|
||||||
"Programming Language :: Python :: 2.7",
|
"Programming Language :: Python :: 2.7",
|
||||||
"Programming Language :: Python :: 3",
|
"Programming Language :: Python :: 3",
|
||||||
"Programming Language :: Python :: 3.2",
|
"Programming Language :: Python :: 3.2",
|
||||||
"Programming Language :: Python :: 3.3",
|
"Programming Language :: Python :: 3.3",
|
||||||
|
"Programming Language :: Python :: 3.4",
|
||||||
"Programming Language :: Python :: Implementation :: CPython",
|
"Programming Language :: Python :: Implementation :: CPython",
|
||||||
"Programming Language :: Python :: Implementation :: PyPy",
|
"Programming Language :: Python :: Implementation :: PyPy",
|
||||||
],
|
],
|
||||||
|
5
tox.ini
5
tox.ini
@@ -1,6 +1,6 @@
|
|||||||
[tox]
|
[tox]
|
||||||
envlist =
|
envlist =
|
||||||
py26,py27,py32,py33,pypy,cover,docs
|
py26,py27,py32,py33,py34,pypy,pypy3,cover,docs
|
||||||
|
|
||||||
[testenv]
|
[testenv]
|
||||||
commands =
|
commands =
|
||||||
@@ -11,7 +11,7 @@ commands =
|
|||||||
basepython =
|
basepython =
|
||||||
python2.6
|
python2.6
|
||||||
commands =
|
commands =
|
||||||
python setup.py nosetests --with-xunit --with-xcoverage
|
python setup.py nosetests --with-xunit --with-xcoverage --cover-min-percentage=100
|
||||||
deps =
|
deps =
|
||||||
nosexcover
|
nosexcover
|
||||||
|
|
||||||
@@ -20,6 +20,5 @@ basepython =
|
|||||||
python2.6
|
python2.6
|
||||||
commands =
|
commands =
|
||||||
sphinx-build -b html -d docs/_build/doctrees docs docs/_build/html
|
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 =
|
deps =
|
||||||
Sphinx
|
Sphinx
|
||||||
|
Reference in New Issue
Block a user