i18n-ize colander.

This commit is contained in:
Chris McDonough
2010-04-26 01:06:29 +00:00
parent 54aadac413
commit c46a7bc3ce
5 changed files with 257 additions and 101 deletions

View File

@@ -3,10 +3,20 @@ import itertools
import iso8601 import iso8601
import pprint import pprint
import re import re
import translationstring
_ = translationstring.TranslationStringFactory('colander')
class _missing(object): class _missing(object):
pass pass
def interpolate(msgs):
for s in msgs:
if hasattr(s, 'interpolate'):
yield s.interpolate()
else:
yield s
class Invalid(Exception): class Invalid(Exception):
""" """
An exception raised by data types and validators indicating that An exception raised by data types and validators indicating that
@@ -113,7 +123,7 @@ class Invalid(Exception):
exc.msg and msgs.append(exc.msg) exc.msg and msgs.append(exc.msg)
keyname = exc._keyname() keyname = exc._keyname()
keyname and keyparts.append(keyname) keyname and keyparts.append(keyname)
errors['.'.join(keyparts)] = '; '.join(msgs) errors['.'.join(keyparts)] = '; '.join(interpolate(msgs))
return errors return errors
def __str__(self): def __str__(self):
@@ -164,7 +174,7 @@ class Function(object):
The default value for the ``message`` when not provided via the The default value for the ``message`` when not provided via the
constructor is ``Invalid value``. constructor is ``Invalid value``.
""" """
def __init__(self, function, message='Invalid value'): def __init__(self, function, message=_('Invalid value')):
self.function = function self.function = function
self.message = message self.message = message
@@ -189,7 +199,7 @@ class Regex(object):
def __init__(self, regex, msg=None): def __init__(self, regex, msg=None):
self.match_object = re.compile(regex) self.match_object = re.compile(regex)
if msg is None: if msg is None:
self.msg = "String does not match expected pattern" self.msg = _("String does not match expected pattern")
else: else:
self.msg = msg self.msg = msg
@@ -204,7 +214,7 @@ class Email(Regex):
""" """
def __init__(self, msg=None): def __init__(self, msg=None):
if msg is None: if msg is None:
msg = "Invalid email address" msg = _("Invalid email address")
super(Email, self).__init__( super(Email, self).__init__(
u'(?i)^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$', msg=msg) u'(?i)^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$', msg=msg)
@@ -219,23 +229,22 @@ class Range(object):
:exc:`colander.Invalid` error when reporting a validation failure :exc:`colander.Invalid` error when reporting a validation failure
caused by a value not meeting the minimum. If ``min_err`` is caused by a value not meeting the minimum. If ``min_err`` is
specified, it must be a string. The string may contain the specified, it must be a string. The string may contain the
replacement targets ``%(min)s`` and ``%(val)s``, representing the replacement targets ``${min}`` and ``${val}``, representing the
minimum value and the provided value respectively. If it is not minimum value and the provided value respectively. If it is not
provided, it defaults to ``'%(val)s is less than minimum value provided, it defaults to ``'${val} is less than minimum value
%(min)s'``. ${min}'``.
``max_err`` is used to form the ``msg`` of the ``max_err`` is used to form the ``msg`` of the
:exc:`colander.Invalid` error when reporting a validation failure :exc:`colander.Invalid` error when reporting a validation failure
caused by a value exceeding the maximum. If ``max_err`` is caused by a value exceeding the maximum. If ``max_err`` is
specified, it must be a string. The string may contain the specified, it must be a string. The string may contain the
replacement targets ``%(max)s`` and ``%(val)s``, representing the replacement targets ``${max}`` and ``${val}``, representing the
maximum value and the provided value respectively. If it is not maximum value and the provided value respectively. If it is not
provided, it defaults to ``'%(val)s is greater than maximum value provided, it defaults to ``'${val} is greater than maximum value
%(max)s'``. ${max}'``.
""" """
min_err = _('${val} is less than minimum value ${min}')
min_err = '%(val)s is less than minimum value %(min)s' max_err = _('${val} is greater than maximum value ${max}')
max_err = '%(val)s is greater than maximum value %(max)s'
def __init__(self, min=None, max=None, min_err=None, max_err=None): def __init__(self, min=None, max=None, min_err=None, max_err=None):
self.min = min self.min = min
@@ -248,11 +257,13 @@ class Range(object):
def __call__(self, node, value): def __call__(self, node, value):
if self.min is not None: if self.min is not None:
if value < self.min: if value < self.min:
raise Invalid(node,self.min_err % {'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 self.max is not None:
if value > self.max: if value > self.max:
raise Invalid(node,self.max_err % {'val':value, 'max':self.max}) max_err = _(self.max_err, mapping={'val':value, 'max':self.max})
raise Invalid(node, max_err)
class Length(object): class Length(object):
""" Validator which succeeds if the value passed to it has a """ Validator which succeeds if the value passed to it has a
@@ -265,26 +276,28 @@ class Length(object):
def __call__(self, node, value): def __call__(self, node, value):
if self.min is not None: if self.min is not None:
if len(value) < self.min: if len(value) < self.min:
raise Invalid( min_err = _('Shorter than minimum length ${min}',
node, mapping={'min':self.min})
'Shorter than minimum length %s' % self.min) raise Invalid(node, min_err)
if self.max is not None: if self.max is not None:
if len(value) > self.max: if len(value) > self.max:
raise Invalid( max_err = _('Longer than maximum length ${max}',
node, mapping={'max':self.max})
'Longer than maximum length %s' % self.max) raise Invalid(node, max_err)
class OneOf(object): class OneOf(object):
""" Validator which succeeds if the value passed to it is one of """ Validator which succeeds if the value passed to it is one of
a fixed set of values """ a fixed set of values """
def __init__(self, values): def __init__(self, choices):
self.values = values self.choices = choices
def __call__(self, node, value): def __call__(self, node, value):
if not value in self.values: if not value in self.choices:
raise Invalid(node, '"%s" is not one of %s' % ( choices = ', '.join(['%s' % x for x in self.choices])
value, ', '.join(['%s' % x for x in self.values]))) err = _('"${val}" is not one of ${choices}',
mapping={'val':value, 'choices':choices})
raise Invalid(node, err)
class Mapping(object): class Mapping(object):
""" A type which represents a mapping of names to nodes. """ A type which represents a mapping of names to nodes.
@@ -350,7 +363,10 @@ class Mapping(object):
try: try:
return dict(value) return dict(value)
except Exception, e: except Exception, e:
raise Invalid(node, '%r is not a mapping type: %s' % (value, e)) raise Invalid(node,
_('"${val}" is not a mapping type: ${err}',
mapping = {'val':value, 'err':e})
)
def _impl(self, node, value, callback, default_callback, unknown=None, def _impl(self, node, value, callback, default_callback, unknown=None,
partial=None): partial=None):
@@ -375,7 +391,9 @@ class Mapping(object):
if not partial: if not partial:
raise Invalid( raise Invalid(
subnode, subnode,
'"%s" is required but missing' % subnode.name) _('"${val}" is required but missing',
mapping={'val':subnode.name})
)
else: else:
continue continue
result[name] = default_callback(subnode) result[name] = default_callback(subnode)
@@ -388,8 +406,11 @@ class Mapping(object):
if unknown == 'raise': if unknown == 'raise':
if value: if value:
raise Invalid(node, raise Invalid(
'Unrecognized keys in mapping: %r' % value) node,
_('Unrecognized keys in mapping: "${val}"',
mapping={'val':value})
)
elif unknown == 'preserve': elif unknown == 'preserve':
result.update(value) result.update(value)
@@ -435,15 +456,20 @@ class Tuple(Positional):
""" """
def _validate(self, node, value): def _validate(self, node, value):
if not hasattr(value, '__iter__'): if not hasattr(value, '__iter__'):
raise Invalid(node, '%r is not iterable' % value) raise Invalid(
node,
_('"${val}" is not iterable', mapping={'val':value})
)
valuelen, nodelen = len(value), len(node.children) valuelen, nodelen = len(value), len(node.children)
if valuelen != nodelen: if valuelen != nodelen:
raise Invalid( raise Invalid(
node, node,
('%s has an incorrect number of elements ' _('"${val}" has an incorrect number of elements '
'(expected %s, was %s)' % (value, nodelen, valuelen))) '(expected ${exp}, was ${was})',
mapping={'val':value, 'exp':nodelen, 'was':valuelen})
)
return list(value) return list(value)
@@ -510,7 +536,9 @@ class Sequence(Positional):
if accept_scalar: if accept_scalar:
return [value] return [value]
else: else:
raise Invalid(node, '%r is not iterable' % value) raise Invalid(node, _('"${val}" is not iterable',
mapping={'val':value})
)
def _impl(self, node, value, callback, accept_scalar): def _impl(self, node, value, callback, accept_scalar):
if accept_scalar is None: if accept_scalar is None:
@@ -612,10 +640,12 @@ class String(object):
if not isinstance(value, unicode): if not isinstance(value, unicode):
value = unicode(str(value), self.encoding) value = unicode(str(value), self.encoding)
except Exception, e: except Exception, e:
raise Invalid(node, '%r is not a string: %s' % (value, e)) raise Invalid(node,
_('${val} is not a string: %{err}',
mapping={'val':value, 'err':e}))
if not value: if not value:
if node.required: if node.required:
raise Invalid(node, 'Required') raise Invalid(node, _('Required'))
value = node.default value = node.default
return value return value
@@ -629,7 +659,9 @@ class String(object):
return result return result
except Exception, e: except Exception, e:
raise Invalid(node, raise Invalid(node,
'%r cannot be serialized to str: %s' % (value, e)) _('"${val} cannot be serialized to str: ${err}',
mapping={'val':value, 'err':e})
)
Str = String Str = String
@@ -645,15 +677,21 @@ class Integer(object):
except Exception: except Exception:
if value == '': if value == '':
if node.required: if node.required:
raise Invalid(node, 'Required') raise Invalid(node, _('Required'))
return node.default return node.default
raise Invalid(node, '"%s" is not a number' % value) raise Invalid(node,
_('"${val}" is not a number',
mapping={'val':value})
)
def serialize(self, node, value): def serialize(self, node, value):
try: try:
return str(int(value)) return str(int(value))
except Exception: except Exception:
raise Invalid(node, '"%s" is not a number' % value) raise Invalid(node,
_('"${val}" is not a number',
mapping={'val':value}),
)
Int = Integer Int = Integer
@@ -669,15 +707,21 @@ class Float(object):
except Exception: except Exception:
if value == '': if value == '':
if node.required: if node.required:
raise Invalid(node, 'Required') raise Invalid(node, _('Required'))
return node.default return node.default
raise Invalid(node, '"%s" is not a number' % value) raise Invalid(node,
_('"${val}" is not a number',
mapping={'val':value})
)
def serialize(self, node, value): def serialize(self, node, value):
try: try:
return str(float(value)) return str(float(value))
except Exception: except Exception:
raise Invalid(node, '"%s" is not a number' % value) raise Invalid(node,
_('"${val}" is not a number',
mapping={'val':value}),
)
Int = Integer Int = Integer
@@ -699,10 +743,12 @@ class Boolean(object):
try: try:
value = str(value) value = str(value)
except: except:
raise Invalid(node, '%r is not a string' % value) raise Invalid(node,
_('${val} is not a string', mapping={'val':value})
)
if not value: if not value:
if node.required: if node.required:
raise Invalid(node, 'Required') raise Invalid(node, _('Required'))
value = node.default value = node.default
value = value.lower() value = value.lower()
if value in ('false', '0'): if value in ('false', '0'):
@@ -759,7 +805,9 @@ class GlobalObject(object):
if not self.package: if not self.package:
raise Invalid( raise Invalid(
node, node,
'relative name "%s" irresolveable without package' % value) _('relative name "${val}" irresolveable without package',
mapping={'val':value})
)
if value in ['.', ':']: if value in ['.', ':']:
value = self.package.__name__ value = self.package.__name__
else: else:
@@ -774,7 +822,9 @@ class GlobalObject(object):
if self.package is None: if self.package is None:
raise Invalid( raise Invalid(
node, node,
'relative name "%s" irresolveable without package' % value) _('relative name "${val}" irresolveable without package',
mapping={'val':value})
)
name = module.split('.') name = module.split('.')
else: else:
name = value.split('.') name = value.split('.')
@@ -782,8 +832,9 @@ class GlobalObject(object):
if module is None: if module is None:
raise Invalid( raise Invalid(
node, node,
'relative name "%s" irresolveable without package' % _('relative name "${val}" irresolveable without '
value) 'package', mapping={'val':value})
)
module = module.split('.') module = module.split('.')
name.pop(0) name.pop(0)
while not name[0]: while not name[0]:
@@ -805,7 +856,8 @@ class GlobalObject(object):
def deserialize(self, node, value): def deserialize(self, node, value):
if not isinstance(value, basestring): if not isinstance(value, basestring):
raise Invalid(node, '"%s" is not a string' % value) raise Invalid(node,
_('"${val}" is not a string', mapping={'val':value}))
try: try:
if ':' in value: if ':' in value:
return self._pkg_resources_style(node, value) return self._pkg_resources_style(node, value)
@@ -813,13 +865,17 @@ class GlobalObject(object):
return self._zope_dottedname_style(node, value) return self._zope_dottedname_style(node, value)
except ImportError: except ImportError:
raise Invalid(node, raise Invalid(node,
'The dotted name "%s" cannot be imported' % value) _('The dotted name "${name}" cannot be imported',
mapping={'name':value}))
def serialize(self, node, value): def serialize(self, node, value):
try: try:
return value.__name__ return value.__name__
except AttributeError: except AttributeError:
raise Invalid(node, '%r has no __name__' % value) raise Invalid(node,
_('"${val}" has no __name__',
mapping={'val':value})
)
class DateTime(object): class DateTime(object):
""" A type representing a Python ``datetime.datetime`` object. """ A type representing a Python ``datetime.datetime`` object.
@@ -839,10 +895,10 @@ class DateTime(object):
You can adjust the error message reported by this class by You can adjust the error message reported by this class by
changing its ``err_template`` attribute in a subclass on an changing its ``err_template`` attribute in a subclass on an
instance of this class. By default, the ``err_template`` instance of this class. By default, the ``err_template``
attribute is the string ``%(value)s cannot be parsed as an iso8601 attribute is the string ``${value} cannot be parsed as an iso8601
date: %(exc)s``. This string is used as the interpolation subject date: ${exc}``. This string is used as the interpolation subject
of a dictionary composed of ``value`` and ``exc``. ``value`` and of a dictionary composed of ``val`` and ``err``. ``val`` and
``exc`` are the unvalidatable value and the exception caused ``err`` are the unvalidatable value and the exception caused
trying to convert the value, respectively. trying to convert the value, respectively.
For convenience, this type is also willing to coerce For convenience, this type is also willing to coerce
@@ -859,7 +915,7 @@ class DateTime(object):
The subnodes of the :class:`colander.SchemaNode` that wraps The subnodes of the :class:`colander.SchemaNode` that wraps
this type are ignored. this type are ignored.
""" """
err_template = '%(value)s cannot be parsed as an iso8601 date: %(exc)s' err_template = _('${val} cannot be parsed as an iso8601 date: ${err}')
def __init__(self, default_tzinfo=None): def __init__(self, default_tzinfo=None):
if default_tzinfo is None: if default_tzinfo is None:
default_tzinfo = iso8601.iso8601.Utc() default_tzinfo = iso8601.iso8601.Utc()
@@ -869,7 +925,10 @@ class DateTime(object):
if type(value) is datetime.date: # cant use isinstance; dt subs date if type(value) is datetime.date: # cant use isinstance; dt subs date
value = datetime.datetime.combine(value, datetime.time()) value = datetime.datetime.combine(value, datetime.time())
if not isinstance(value, datetime.datetime): if not isinstance(value, datetime.datetime):
raise Invalid(node, '%r is not a datetime object' % value) raise Invalid(node,
_('"${val}" is not a datetime object',
mapping={'val':value})
)
if value.tzinfo is None: if value.tzinfo is None:
value = value.replace(tzinfo=self.default_tzinfo) value = value.replace(tzinfo=self.default_tzinfo)
return value.isoformat() return value.isoformat()
@@ -883,8 +942,8 @@ class DateTime(object):
result = datetime.datetime(year, month, day, result = datetime.datetime(year, month, day,
tzinfo=self.default_tzinfo) tzinfo=self.default_tzinfo)
except Exception, e: except Exception, e:
raise Invalid(node, self.err_template % {'value':value, raise Invalid(node, _(self.err_template,
'exc':e}) mapping={'val':value, 'err':e}))
return result return result
class Date(object): class Date(object):
@@ -899,9 +958,9 @@ class Date(object):
You can adjust the error message reported by this class by You can adjust the error message reported by this class by
changing its ``err_template`` attribute in a subclass on an changing its ``err_template`` attribute in a subclass on an
instance of this class. By default, the ``err_template`` instance of this class. By default, the ``err_template``
attribute is the string ``%(value)s cannot be parsed as an iso8601 attribute is the string ``${val} cannot be parsed as an iso8601
date: %(exc)s``. This string is used as the interpolation subject date: ${err}``. This string is used as the interpolation subject
of a dictionary composed of ``value`` and ``exc``. ``value`` and of a dictionary composed of ``val`` and ``err``. ``val`` and
``exc`` are the unvalidatable value and the exception caused ``exc`` are the unvalidatable value and the exception caused
trying to convert the value, respectively. trying to convert the value, respectively.
@@ -920,18 +979,21 @@ class Date(object):
The subnodes of the :class:`colander.SchemaNode` that wraps The subnodes of the :class:`colander.SchemaNode` that wraps
this type are ignored. this type are ignored.
""" """
err_template = '%(value)s cannot be parsed as an iso8601 date: %(exc)s' err_template = _('${val} cannot be parsed as an iso8601 date: ${err}')
def serialize(self, node, value): def serialize(self, node, value):
if isinstance(value, datetime.datetime): if isinstance(value, datetime.datetime):
value = value.date() value = value.date()
if not isinstance(value, datetime.date): if not isinstance(value, datetime.date):
raise Invalid(node, '%r is not a date object' % value) raise Invalid(node,
_('"${val}" is not a date object',
mapping={'val':value})
)
return value.isoformat() return value.isoformat()
def deserialize(self, node, value): def deserialize(self, node, value):
if not value: if not value:
if node.required: if node.required:
raise Invalid(node, 'Required') raise Invalid(node, _('Required'))
return node.default return node.default
try: try:
result = iso8601.parse_date(value) result = iso8601.parse_date(value)
@@ -941,8 +1003,10 @@ class Date(object):
year, month, day = map(int, value.split('-', 2)) year, month, day = map(int, value.split('-', 2))
result = datetime.date(year, month, day) result = datetime.date(year, month, day)
except Exception, e: except Exception, e:
raise Invalid(node, self.err_template % {'value':value, raise Invalid(node,
'exc':e}) _(self.err_template,
mapping={'val':value, 'err':e})
)
return result return result
class SchemaNode(object): class SchemaNode(object):

View File

@@ -0,0 +1,60 @@
# Translations template for colander.
# Copyright (C) 2010 ORGANIZATION
# This file is distributed under the same license as the colander project.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2010.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: colander 0.5.2\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2010-04-25 21:06-0400\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 0.9.5\n"
#: colander/__init__.py:177
msgid "Invalid value"
msgstr ""
#: colander/__init__.py:202
msgid "String does not match expected pattern"
msgstr ""
#: colander/__init__.py:217
msgid "Invalid email address"
msgstr ""
#: colander/__init__.py:246
msgid "${val} is less than minimum value ${min}"
msgstr ""
#: colander/__init__.py:247
msgid "${val} is greater than maximum value ${max}"
msgstr ""
#: colander/__init__.py:461
msgid "\"${val}\" is not iterable"
msgstr ""
#: colander/__init__.py:648 colander/__init__.py:680 colander/__init__.py:710
#: colander/__init__.py:751 colander/__init__.py:996
msgid "Required"
msgstr ""
#: colander/__init__.py:747
msgid "${val} is not a string"
msgstr ""
#: colander/__init__.py:860
msgid "\"${val}\" is not a string"
msgstr ""
#: colander/__init__.py:918 colander/__init__.py:982
msgid "${val} cannot be parsed as an iso8601 date: ${err}"
msgstr ""

View File

@@ -189,7 +189,7 @@ class TestRange(unittest.TestCase):
def test_min_failure(self): def test_min_failure(self):
validator = self._makeOne(min=1) validator = self._makeOne(min=1)
e = invalid_exc(validator, None, 0) e = invalid_exc(validator, None, 0)
self.assertEqual(e.msg, '0 is less than minimum value 1') self.assertEqual(e.msg.interpolate(), '0 is less than minimum value 1')
def test_min_failure_msg_override(self): def test_min_failure_msg_override(self):
validator = self._makeOne(min=1, min_err='wrong') validator = self._makeOne(min=1, min_err='wrong')
@@ -199,7 +199,8 @@ class TestRange(unittest.TestCase):
def test_max_failure(self): def test_max_failure(self):
validator = self._makeOne(max=1) validator = self._makeOne(max=1)
e = invalid_exc(validator, None, 2) e = invalid_exc(validator, None, 2)
self.assertEqual(e.msg, '2 is greater than maximum value 1') self.assertEqual(e.msg.interpolate(),
'2 is greater than maximum value 1')
def test_max_failure_msg_override(self): def test_max_failure_msg_override(self):
validator = self._makeOne(max=1, max_err='wrong') validator = self._makeOne(max=1, max_err='wrong')
@@ -272,12 +273,12 @@ class TestLength(unittest.TestCase):
def test_min_failure(self): def test_min_failure(self):
validator = self._makeOne(min=1) validator = self._makeOne(min=1)
e = invalid_exc(validator, None, '') e = invalid_exc(validator, None, '')
self.assertEqual(e.msg, 'Shorter than minimum length 1') self.assertEqual(e.msg.interpolate(), 'Shorter than minimum length 1')
def test_max_failure(self): def test_max_failure(self):
validator = self._makeOne(max=1) validator = self._makeOne(max=1)
e = invalid_exc(validator, None, 'ab') e = invalid_exc(validator, None, 'ab')
self.assertEqual(e.msg, 'Longer than maximum length 1') self.assertEqual(e.msg.interpolate(), 'Longer than maximum length 1')
class TestOneOf(unittest.TestCase): class TestOneOf(unittest.TestCase):
def _makeOne(self, values): def _makeOne(self, values):
@@ -291,7 +292,8 @@ class TestOneOf(unittest.TestCase):
def test_failure(self): def test_failure(self):
validator = self._makeOne([1, 2]) validator = self._makeOne([1, 2])
e = invalid_exc(validator, None, None) e = invalid_exc(validator, None, None)
self.assertEqual(e.msg, '"None" is not one of 1, 2') self.assertEqual(e.msg.interpolate(), '"None" is not one of 1, 2')
class TestMapping(unittest.TestCase): class TestMapping(unittest.TestCase):
def _makeOne(self, *arg, **kw): def _makeOne(self, *arg, **kw):
@@ -314,7 +316,7 @@ class TestMapping(unittest.TestCase):
typ = self._makeOne() typ = self._makeOne()
e = invalid_exc(typ.deserialize, node, None) e = invalid_exc(typ.deserialize, node, None)
self.failUnless( self.failUnless(
e.msg.startswith('None is not a mapping type')) e.msg.interpolate().startswith('"None" is not a mapping type'))
def test_deserialize_no_subnodes(self): def test_deserialize_no_subnodes(self):
node = DummySchemaNode(None) node = DummySchemaNode(None)
@@ -334,7 +336,9 @@ class TestMapping(unittest.TestCase):
node.children = [DummySchemaNode(None, name='a')] node.children = [DummySchemaNode(None, name='a')]
typ = self._makeOne(unknown='raise') typ = self._makeOne(unknown='raise')
e = invalid_exc(typ.deserialize, node, {'a':1, 'b':2}) e = invalid_exc(typ.deserialize, node, {'a':1, 'b':2})
self.assertEqual(e.msg, "Unrecognized keys in mapping: {'b': 2}") self.assertEqual(e.msg.interpolate(),
"Unrecognized keys in mapping: \"{'b': 2}\"")
def test_deserialize_unknown_preserve(self): def test_deserialize_unknown_preserve(self):
node = DummySchemaNode(None) node = DummySchemaNode(None)
@@ -372,7 +376,8 @@ class TestMapping(unittest.TestCase):
] ]
typ = self._makeOne() typ = self._makeOne()
e = invalid_exc(typ.deserialize, node, {'a':1}) e = invalid_exc(typ.deserialize, node, {'a':1})
self.assertEqual(e.children[0].msg, '"b" is required but missing') self.assertEqual(e.children[0].msg.interpolate(),
'"b" is required but missing')
def test_deserialize_subnode_partial(self): def test_deserialize_subnode_partial(self):
node = DummySchemaNode(None) node = DummySchemaNode(None)
@@ -389,7 +394,7 @@ class TestMapping(unittest.TestCase):
typ = self._makeOne() typ = self._makeOne()
e = invalid_exc(typ.serialize, node, None) e = invalid_exc(typ.serialize, node, None)
self.failUnless( self.failUnless(
e.msg.startswith('None is not a mapping type')) e.msg.interpolate().startswith('"None" is not a mapping type'))
def test_serialize_no_subnodes(self): def test_serialize_no_subnodes(self):
node = DummySchemaNode(None) node = DummySchemaNode(None)
@@ -443,8 +448,8 @@ class TestTuple(unittest.TestCase):
typ = self._makeOne() typ = self._makeOne()
e = invalid_exc(typ.deserialize, node, None) e = invalid_exc(typ.deserialize, node, None)
self.assertEqual( self.assertEqual(
e.msg, e.msg.interpolate(),
'None is not iterable') '"None" is not iterable')
self.assertEqual(e.node, node) self.assertEqual(e.node, node)
def test_deserialize_no_subnodes(self): def test_deserialize_no_subnodes(self):
@@ -465,16 +470,16 @@ class TestTuple(unittest.TestCase):
node.children = [DummySchemaNode(None, name='a')] node.children = [DummySchemaNode(None, name='a')]
typ = self._makeOne() typ = self._makeOne()
e = invalid_exc(typ.deserialize, node, ('a','b')) e = invalid_exc(typ.deserialize, node, ('a','b'))
self.assertEqual(e.msg, self.assertEqual(e.msg.interpolate(),
"('a', 'b') has an incorrect number of elements (expected 1, was 2)") "\"('a', 'b')\" has an incorrect number of elements (expected 1, was 2)")
def test_deserialize_toosmall(self): def test_deserialize_toosmall(self):
node = DummySchemaNode(None) node = DummySchemaNode(None)
node.children = [DummySchemaNode(None, name='a')] node.children = [DummySchemaNode(None, name='a')]
typ = self._makeOne() typ = self._makeOne()
e = invalid_exc(typ.deserialize, node, ()) e = invalid_exc(typ.deserialize, node, ())
self.assertEqual(e.msg, self.assertEqual(e.msg.interpolate(),
"() has an incorrect number of elements (expected 1, was 0)") '"()" has an incorrect number of elements (expected 1, was 0)')
def test_deserialize_subnodes_raise(self): def test_deserialize_subnodes_raise(self):
node = DummySchemaNode(None) node = DummySchemaNode(None)
@@ -492,8 +497,8 @@ class TestTuple(unittest.TestCase):
typ = self._makeOne() typ = self._makeOne()
e = invalid_exc(typ.serialize, node, None) e = invalid_exc(typ.serialize, node, None)
self.assertEqual( self.assertEqual(
e.msg, e.msg.interpolate(),
'None is not iterable') '"None" is not iterable')
self.assertEqual(e.node, node) self.assertEqual(e.node, node)
def test_serialize_no_subnodes(self): def test_serialize_no_subnodes(self):
@@ -514,16 +519,17 @@ class TestTuple(unittest.TestCase):
node.children = [DummySchemaNode(None, name='a')] node.children = [DummySchemaNode(None, name='a')]
typ = self._makeOne() typ = self._makeOne()
e = invalid_exc(typ.serialize, node, ('a','b')) e = invalid_exc(typ.serialize, node, ('a','b'))
self.assertEqual(e.msg, self.assertEqual(e.msg.interpolate(),
"('a', 'b') has an incorrect number of elements (expected 1, was 2)") "\"('a', 'b')\" has an incorrect number of elements (expected 1, was 2)")
def test_serialize_toosmall(self): def test_serialize_toosmall(self):
node = DummySchemaNode(None) node = DummySchemaNode(None)
node.children = [DummySchemaNode(None, name='a')] node.children = [DummySchemaNode(None, name='a')]
typ = self._makeOne() typ = self._makeOne()
e = invalid_exc(typ.serialize, node, ()) e = invalid_exc(typ.serialize, node, ())
self.assertEqual(e.msg, self.assertEqual(e.msg.interpolate(),
"() has an incorrect number of elements (expected 1, was 0)") '"()" has an incorrect number of elements (expected 1, was 0)'
)
def test_serialize_subnodes_raise(self): def test_serialize_subnodes_raise(self):
node = DummySchemaNode(None) node = DummySchemaNode(None)
@@ -552,8 +558,8 @@ class TestSequence(unittest.TestCase):
node.children = [node] node.children = [node]
e = invalid_exc(typ.deserialize, node, None) e = invalid_exc(typ.deserialize, node, None)
self.assertEqual( self.assertEqual(
e.msg, e.msg.interpolate(),
'None is not iterable') '"None" is not iterable')
self.assertEqual(e.node, node) self.assertEqual(e.node, node)
def test_deserialize_not_iterable_accept_scalar(self): def test_deserialize_not_iterable_accept_scalar(self):
@@ -592,8 +598,8 @@ class TestSequence(unittest.TestCase):
node.children = [node] node.children = [node]
e = invalid_exc(typ.serialize, node, None) e = invalid_exc(typ.serialize, node, None)
self.assertEqual( self.assertEqual(
e.msg, e.msg.interpolate(),
'None is not iterable') '"None" is not iterable')
self.assertEqual(e.node, node) self.assertEqual(e.node, node)
def test_serialize_not_iterable_accept_scalar(self): def test_serialize_not_iterable_accept_scalar(self):
@@ -894,14 +900,15 @@ class TestGlobalObject(unittest.TestCase):
typ = self._makeOne() typ = self._makeOne()
e = invalid_exc(typ._zope_dottedname_style, None, '.') e = invalid_exc(typ._zope_dottedname_style, None, '.')
self.assertEqual( self.assertEqual(
e.msg, e.msg.interpolate(),
'relative name "." irresolveable without package') 'relative name "." irresolveable without package')
def test_zope_dottedname_style_resolve_relative_nocurrentpackage(self): def test_zope_dottedname_style_resolve_relative_nocurrentpackage(self):
typ = self._makeOne() typ = self._makeOne()
e = invalid_exc(typ._zope_dottedname_style, None, '.whatever') e = invalid_exc(typ._zope_dottedname_style, None, '.whatever')
self.assertEqual( self.assertEqual(
e.msg, 'relative name ".whatever" irresolveable without package') e.msg.interpolate(),
'relative name ".whatever" irresolveable without package')
def test_zope_dottedname_style_irrresolveable_relative(self): def test_zope_dottedname_style_irrresolveable_relative(self):
import colander.tests import colander.tests
@@ -971,7 +978,7 @@ class TestGlobalObject(unittest.TestCase):
def test_deserialize_not_a_string(self): def test_deserialize_not_a_string(self):
typ = self._makeOne() typ = self._makeOne()
e = invalid_exc(typ.deserialize, None, None) e = invalid_exc(typ.deserialize, None, None)
self.assertEqual(e.msg, '"None" is not a string') self.assertEqual(e.msg.interpolate(), '"None" is not a string')
def test_deserialize_using_pkgresources_style(self): def test_deserialize_using_pkgresources_style(self):
typ = self._makeOne() typ = self._makeOne()
@@ -986,7 +993,7 @@ class TestGlobalObject(unittest.TestCase):
def test_deserialize_style_raises(self): def test_deserialize_style_raises(self):
typ = self._makeOne() typ = self._makeOne()
e = invalid_exc(typ.deserialize, None, 'cant.be.found') e = invalid_exc(typ.deserialize, None, 'cant.be.found')
self.assertEqual(e.msg, self.assertEqual(e.msg.interpolate(),
'The dotted name "cant.be.found" cannot be imported') 'The dotted name "cant.be.found" cannot be imported')
def test_serialize_ok(self): def test_serialize_ok(self):
@@ -998,7 +1005,7 @@ class TestGlobalObject(unittest.TestCase):
def test_serialize_fail(self): def test_serialize_fail(self):
typ = self._makeOne() typ = self._makeOne()
e = invalid_exc(typ.serialize, None, None) e = invalid_exc(typ.serialize, None, None)
self.assertEqual(e.msg, 'None has no __name__') self.assertEqual(e.msg.interpolate(), '"None" has no __name__')
class TestDateTime(unittest.TestCase): class TestDateTime(unittest.TestCase):
def _makeOne(self, *arg, **kw): def _makeOne(self, *arg, **kw):
@@ -1020,7 +1027,8 @@ class TestDateTime(unittest.TestCase):
typ = self._makeOne() typ = self._makeOne()
node = DummySchemaNode(None) node = DummySchemaNode(None)
e = invalid_exc(typ.serialize, node, 'garbage') e = invalid_exc(typ.serialize, node, 'garbage')
self.assertEqual(e.msg, "'garbage' is not a datetime object") self.assertEqual(e.msg.interpolate(),
'"garbage" is not a datetime object')
def test_serialize_with_date(self): def test_serialize_with_date(self):
import datetime import datetime
@@ -1093,7 +1101,7 @@ class TestDate(unittest.TestCase):
typ = self._makeOne() typ = self._makeOne()
node = DummySchemaNode(None) node = DummySchemaNode(None)
e = invalid_exc(typ.serialize, node, 'garbage') e = invalid_exc(typ.serialize, node, 'garbage')
self.assertEqual(e.msg, "'garbage' is not a date object") self.assertEqual(e.msg.interpolate(), '"garbage" is not a date object')
def test_serialize_with_date(self): def test_serialize_with_date(self):
import datetime import datetime

View File

@@ -8,3 +8,23 @@ nocapture=1
cover-package=colander cover-package=colander
cover-erase=1 cover-erase=1
[compile_catalog]
directory = colander/locale
domain = colander
statistics = true
[extract_messages]
add_comments = TRANSLATORS:
output_file = colander/locale/colander.pot
width = 80
[init_catalog]
domain = colander
input_file = colander/locale/colander.pot
output_dir = colander/locale
[update_catalog]
domain = colander
input_file = colander/locale/colander.pot
output_dir = colander/locale
previous = true

View File

@@ -21,7 +21,7 @@ here = os.path.abspath(os.path.dirname(__file__))
README = open(os.path.join(here, 'README.txt')).read() README = open(os.path.join(here, 'README.txt')).read()
CHANGES = open(os.path.join(here, 'CHANGES.txt')).read() CHANGES = open(os.path.join(here, 'CHANGES.txt')).read()
requires = ['iso8601'] requires = ['iso8601', 'translationstring']
setup(name='colander', setup(name='colander',
version='0.5.2', version='0.5.2',
@@ -43,5 +43,9 @@ setup(name='colander',
tests_require = requires, tests_require = requires,
install_requires = requires, install_requires = requires,
test_suite="colander", test_suite="colander",
message_extractors = { ".": [
("**.py", "chameleon_python", None ),
("**.pt", "chameleon_xml", None ),
]},
) )