[Python-modules-commits] [python-schema] 01/04: New upstream version 0.6.5

Ghislain Vaillant ghisvail-guest at moszumanska.debian.org
Tue Sep 27 06:57:21 UTC 2016


This is an automated email from the git hooks/post-receive script.

ghisvail-guest pushed a commit to branch debian/master
in repository python-schema.

commit c181b5eea829bf1de6e1eb8bd240d75358935e1f
Author: Ghislain Antony Vaillant <ghisvail at gmail.com>
Date:   Tue Sep 27 07:46:08 2016 +0100

    New upstream version 0.6.5
---
 MANIFEST.in                 |   2 +-
 PKG-INFO                    |   4 +-
 README.rst                  |   2 +-
 schema.egg-info/PKG-INFO    |   4 +-
 schema.egg-info/SOURCES.txt |   1 +
 schema.py                   | 105 ++++++---
 test_schema.py              | 513 ++++++++++++++++++++++++++++++++++++++++++++
 7 files changed, 598 insertions(+), 33 deletions(-)

diff --git a/MANIFEST.in b/MANIFEST.in
index d391d4b..c983463 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -1 +1 @@
-include README.rst LICENSE-MIT
+include README.rst LICENSE-MIT *.py
diff --git a/PKG-INFO b/PKG-INFO
index 39bddb4..de053ee 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,6 +1,6 @@
 Metadata-Version: 1.1
 Name: schema
-Version: 0.6.2
+Version: 0.6.5
 Summary: Simple data validation library
 Home-page: https://github.com/keleshev/schema
 Author: Vladimir Keleshev
@@ -302,7 +302,7 @@ Description: Schema validation just got Pythonic
             ...
             SchemaError: Invalid year
         
-        You can see all errors that occured by accessing exception's ``exc.autos``
+        You can see all errors that occurred by accessing exception's ``exc.autos``
         for auto-generated error messages, and ``exc.errors`` for errors
         which had ``error`` text passed to them.
         
diff --git a/README.rst b/README.rst
index 51a1393..9e71dc8 100644
--- a/README.rst
+++ b/README.rst
@@ -294,7 +294,7 @@ instead of a built-in one.
     ...
     SchemaError: Invalid year
 
-You can see all errors that occured by accessing exception's ``exc.autos``
+You can see all errors that occurred by accessing exception's ``exc.autos``
 for auto-generated error messages, and ``exc.errors`` for errors
 which had ``error`` text passed to them.
 
diff --git a/schema.egg-info/PKG-INFO b/schema.egg-info/PKG-INFO
index 39bddb4..de053ee 100644
--- a/schema.egg-info/PKG-INFO
+++ b/schema.egg-info/PKG-INFO
@@ -1,6 +1,6 @@
 Metadata-Version: 1.1
 Name: schema
-Version: 0.6.2
+Version: 0.6.5
 Summary: Simple data validation library
 Home-page: https://github.com/keleshev/schema
 Author: Vladimir Keleshev
@@ -302,7 +302,7 @@ Description: Schema validation just got Pythonic
             ...
             SchemaError: Invalid year
         
-        You can see all errors that occured by accessing exception's ``exc.autos``
+        You can see all errors that occurred by accessing exception's ``exc.autos``
         for auto-generated error messages, and ``exc.errors`` for errors
         which had ``error`` text passed to them.
         
diff --git a/schema.egg-info/SOURCES.txt b/schema.egg-info/SOURCES.txt
index 6d19b4e..17c9e19 100644
--- a/schema.egg-info/SOURCES.txt
+++ b/schema.egg-info/SOURCES.txt
@@ -4,6 +4,7 @@ README.rst
 schema.py
 setup.cfg
 setup.py
+test_schema.py
 schema.egg-info/PKG-INFO
 schema.egg-info/SOURCES.txt
 schema.egg-info/dependency_links.txt
diff --git a/schema.py b/schema.py
index 66ac958..779bf39 100644
--- a/schema.py
+++ b/schema.py
@@ -1,6 +1,10 @@
+"""schema is a library for validating Python data structures, such as those
+obtained from config-files, forms, external services or command-line
+parsing, converted from JSON/YAML (or something else) to Python data-types."""
+
 import re
 
-__version__ = '0.6.2'
+__version__ = '0.6.5'
 __all__ = ['Schema',
            'And', 'Or', 'Regex', 'Optional',
            'SchemaError',
@@ -10,42 +14,56 @@ __all__ = ['Schema',
 
 
 class SchemaError(Exception):
-
     """Error during Schema validation."""
 
-    def __init__(self, autos, errors):
+    def __init__(self, autos, errors=None):
         self.autos = autos if type(autos) is list else [autos]
         self.errors = errors if type(errors) is list else [errors]
         Exception.__init__(self, self.code)
 
     @property
     def code(self):
+        """
+        Removes duplicates values in auto and error list.
+        parameters.
+        """
         def uniq(seq):
+            """
+            Utility function that removes duplicate.
+            """
             seen = set()
             seen_add = seen.add
             # This way removes duplicates while preserving the order.
             return [x for x in seq if x not in seen and not seen_add(x)]
-        a = uniq(i for i in self.autos if i is not None)
-        e = uniq(i for i in self.errors if i is not None)
-        if e:
-            return '\n'.join(e)
-        return '\n'.join(a)
+        data_set = uniq(i for i in self.autos if i is not None)
+        error_list = uniq(i for i in self.errors if i is not None)
+        if error_list:
+            return '\n'.join(error_list)
+        return '\n'.join(data_set)
 
 
 class SchemaWrongKeyError(SchemaError):
+    """Error Should be raised when an unexpected key is detected within the
+    data set being."""
     pass
 
 
 class SchemaMissingKeyError(SchemaError):
+    """Error should be raised when a mandatory key is not found within the
+    data set being vaidated"""
     pass
 
 
 class SchemaUnexpectedTypeError(SchemaError):
+    """Error should be raised when a type mismatch is detected within the
+    data set being validated."""
     pass
 
 
 class And(object):
-
+    """
+    Utility function to combine validation directives in AND Boolean fashion.
+    """
     def __init__(self, *args, **kw):
         self._args = args
         assert list(kw) in (['error'], [])
@@ -56,14 +74,27 @@ class And(object):
                            ', '.join(repr(a) for a in self._args))
 
     def validate(self, data):
+        """
+        Validate data using defined sub schema/expressions ensuring all
+        values are valid.
+        :param data: to be validated with sub defined schemas.
+        :return: returns validated data
+        """
         for s in [Schema(s, error=self._error) for s in self._args]:
             data = s.validate(data)
         return data
 
 
 class Or(And):
-
+    """Utility function to combine validation directives in a OR Boolean
+    fashion."""
     def validate(self, data):
+        """
+        Validate data using sub defined schema/expressions ensuring at least
+        one value is valid.
+        :param data: data to be validated by provided schema.
+        :return: return validated data if not validation
+        """
         x = SchemaError([], [])
         for s in [Schema(s, error=self._error) for s in self._args]:
             try:
@@ -71,10 +102,14 @@ class Or(And):
             except SchemaError as _x:
                 x = _x
         raise SchemaError(['%r did not validate %r' % (self, data)] + x.autos,
-                          [self._error.format(data) if self._error else None] + x.errors)
+                          [self._error.format(data) if self._error else None] +
+                          x.errors)
 
 
 class Regex(object):
+    """
+    Enables schema.py to validate string using regular expressions.
+    """
     # Map all flags bits to a more readable description
     NAMES = ['re.ASCII', 're.DEBUG', 're.VERBOSE', 're.UNICODE', 're.DOTALL',
              're.MULTILINE', 're.LOCALE', 're.IGNORECASE', 're.TEMPLATE']
@@ -98,6 +133,11 @@ class Regex(object):
         )
 
     def validate(self, data):
+        """
+        Validated data using defined regex.
+        :param data: data to be validated
+        :return: return validated data.
+        """
         e = self._error
 
         try:
@@ -110,7 +150,10 @@ class Regex(object):
 
 
 class Use(object):
-
+    """
+    For more general use cases, you can use the Use class to transform
+    the data while it is being validate.
+    """
     def __init__(self, callable_, error=None):
         assert callable(callable_)
         self._callable = callable_
@@ -129,7 +172,8 @@ class Use(object):
         except BaseException as x:
             f = _callable_str(self._callable)
             raise SchemaError('%s(%r) raised %r' % (f, data, x),
-                              self._error.format(data) if self._error else None)
+                              self._error.format(data)
+                              if self._error else None)
 
 
 COMPARABLE, CALLABLE, VALIDATOR, TYPE, DICT, ITERABLE = range(6)
@@ -152,7 +196,10 @@ def _priority(s):
 
 
 class Schema(object):
-
+    """
+    Entry point of the library, use this class to instantiate validation
+    schema for the data that will be validated.
+    """
     def __init__(self, schema, error=None, ignore_extra_keys=False):
         self._schema = schema
         self._error = error
@@ -197,14 +244,18 @@ class Schema(object):
             required = set(k for k in s if type(k) is not Optional)
             if not required.issubset(coverage):
                 missing_keys = required - coverage
-                s_missing_keys = ", ".join(repr(k) for k in missing_keys)
-                raise SchemaMissingKeyError('Missing keys: ' + s_missing_keys, e)
+                s_missing_keys = \
+                    ', '.join(repr(k) for k in sorted(missing_keys, key=repr))
+                raise \
+                    SchemaMissingKeyError('Missing keys: ' + s_missing_keys, e)
             if not self._ignore_extra_keys and (len(new) != len(data)):
                 wrong_keys = set(data.keys()) - set(new.keys())
-                s_wrong_keys = ', '.join(repr(k) for k in sorted(wrong_keys,
-                                                                 key=repr))
-                raise SchemaWrongKeyError('Wrong keys %s in %r' % (s_wrong_keys, data),
-                                          e.format(data) if e else None)
+                s_wrong_keys = \
+                    ', '.join(repr(k) for k in sorted(wrong_keys, key=repr))
+                raise \
+                    SchemaWrongKeyError(
+                        'Wrong keys %s in %r' % (s_wrong_keys, data),
+                        e.format(data) if e else None)
 
             # Apply default-having optionals that haven't been used:
             defaults = set(k for k in s if type(k) is Optional and
@@ -226,8 +277,9 @@ class Schema(object):
             except SchemaError as x:
                 raise SchemaError([None] + x.autos, [e] + x.errors)
             except BaseException as x:
-                raise SchemaError('%r.validate(%r) raised %r' % (s, data, x),
-                                  self._error.format(data) if self._error else None)
+                raise SchemaError(
+                    '%r.validate(%r) raised %r' % (s, data, x),
+                    self._error.format(data) if self._error else None)
         if flavor == CALLABLE:
             f = _callable_str(s)
             try:
@@ -236,8 +288,9 @@ class Schema(object):
             except SchemaError as x:
                 raise SchemaError([None] + x.autos, [e] + x.errors)
             except BaseException as x:
-                raise SchemaError('%s(%r) raised %r' % (f, data, x),
-                                  self._error.format(data) if self._error else None)
+                raise SchemaError(
+                    '%s(%r) raised %r' % (f, data, x),
+                    self._error.format(data) if self._error else None)
             raise SchemaError('%s(%r) should evaluate to True' % (f, data), e)
         if s == data:
             return data
@@ -247,9 +300,7 @@ class Schema(object):
 
 
 class Optional(Schema):
-
-    """Marker for an optional part of Schema."""
-
+    """Marker for an optional part of the validation Schema."""
     _MARKER = object()
 
     def __init__(self, *args, **kwargs):
diff --git a/test_schema.py b/test_schema.py
new file mode 100644
index 0000000..f994a00
--- /dev/null
+++ b/test_schema.py
@@ -0,0 +1,513 @@
+from __future__ import with_statement
+from collections import defaultdict, namedtuple
+from operator import methodcaller
+import os
+import re
+import sys
+import copy
+
+from pytest import raises
+
+from schema import (Schema, Use, And, Or, Regex, Optional,
+                    SchemaError, SchemaWrongKeyError,
+                    SchemaMissingKeyError, SchemaUnexpectedTypeError)
+
+if sys.version_info[0] == 3:
+    basestring = str  # Python 3 does not have basestring
+    unicode = str  # Python 3 does not have unicode
+
+
+SE = raises(SchemaError)
+
+
+def ve(_):
+    raise ValueError()
+
+
+def se(_):
+    raise SchemaError('first auto', 'first error')
+
+
+def test_schema():
+
+    assert Schema(1).validate(1) == 1
+    with SE: Schema(1).validate(9)
+
+    assert Schema(int).validate(1) == 1
+    with SE: Schema(int).validate('1')
+    assert Schema(Use(int)).validate('1') == 1
+    with SE: Schema(int).validate(int)
+
+    assert Schema(str).validate('hai') == 'hai'
+    with SE: Schema(str).validate(1)
+    assert Schema(Use(str)).validate(1) == '1'
+
+    assert Schema(list).validate(['a', 1]) == ['a', 1]
+    assert Schema(dict).validate({'a': 1}) == {'a': 1}
+    with SE: Schema(dict).validate(['a', 1])
+
+    assert Schema(lambda n: 0 < n < 5).validate(3) == 3
+    with SE: Schema(lambda n: 0 < n < 5).validate(-1)
+
+
+def test_validate_file():
+    assert Schema(
+            Use(open)).validate('LICENSE-MIT').read().startswith('Copyright')
+    with SE: Schema(Use(open)).validate('NON-EXISTENT')
+    assert Schema(os.path.exists).validate('.') == '.'
+    with SE: Schema(os.path.exists).validate('./non-existent/')
+    assert Schema(os.path.isfile).validate('LICENSE-MIT') == 'LICENSE-MIT'
+    with SE: Schema(os.path.isfile).validate('NON-EXISTENT')
+
+
+def test_and():
+    assert And(int, lambda n: 0 < n < 5).validate(3) == 3
+    with SE: And(int, lambda n: 0 < n < 5).validate(3.33)
+    assert And(Use(int), lambda n: 0 < n < 5).validate(3.33) == 3
+    with SE: And(Use(int), lambda n: 0 < n < 5).validate('3.33')
+
+
+def test_or():
+    assert Or(int, dict).validate(5) == 5
+    assert Or(int, dict).validate({}) == {}
+    with SE: Or(int, dict).validate('hai')
+    assert Or(int).validate(4)
+    with SE: Or().validate(2)
+
+
+def test_regex():
+    # Simple case: validate string
+    assert Regex(r'foo').validate('afoot') == 'afoot'
+    with SE: Regex(r'bar').validate('afoot')
+
+    # More complex case: validate string
+    assert Regex(r'^[a-z]+$').validate('letters') == 'letters'
+    with SE:
+        Regex(r'^[a-z]+$').validate('letters + spaces') == 'letters + spaces'
+
+    # Validate dict key
+    assert (Schema({Regex(r'^foo'): str})
+            .validate({'fookey': 'value'}) == {'fookey': 'value'})
+    with SE: Schema({Regex(r'^foo'): str}).validate({'barkey': 'value'})
+
+    # Validate dict value
+    assert (Schema({str: Regex(r'^foo')}).validate({'key': 'foovalue'}) ==
+            {'key': 'foovalue'})
+    with SE: Schema({str: Regex(r'^foo')}).validate({'key': 'barvalue'})
+
+    # Error if the value does not have a buffer interface
+    with SE: Regex(r'bar').validate(1)
+    with SE: Regex(r'bar').validate({})
+    with SE: Regex(r'bar').validate([])
+    with SE: Regex(r'bar').validate(None)
+
+    # Validate that the pattern has a buffer interface
+    assert Regex(re.compile(r'foo')).validate('foo') == 'foo'
+    assert Regex(unicode('foo')).validate('foo') == 'foo'
+    with raises(TypeError): Regex(1).validate('bar')
+    with raises(TypeError): Regex({}).validate('bar')
+    with raises(TypeError): Regex([]).validate('bar')
+    with raises(TypeError): Regex(None).validate('bar')
+
+
+def test_validate_list():
+    assert Schema([1, 0]).validate([1, 0, 1, 1]) == [1, 0, 1, 1]
+    assert Schema([1, 0]).validate([]) == []
+    with SE: Schema([1, 0]).validate(0)
+    with SE: Schema([1, 0]).validate([2])
+    assert And([1, 0], lambda l: len(l) > 2).validate([0, 1, 0]) == [0, 1, 0]
+    with SE: And([1, 0], lambda l: len(l) > 2).validate([0, 1])
+
+
+def test_list_tuple_set_frozenset():
+    assert Schema([int]).validate([1, 2])
+    with SE: Schema([int]).validate(['1', 2])
+    assert Schema(set([int])).validate(set([1, 2])) == set([1, 2])
+    with SE: Schema(set([int])).validate([1, 2])  # not a set
+    with SE: Schema(set([int])).validate(['1', 2])
+    assert Schema(tuple([int])).validate(tuple([1, 2])) == tuple([1, 2])
+    with SE: Schema(tuple([int])).validate([1, 2])  # not a set
+
+
+def test_strictly():
+    assert Schema(int).validate(1) == 1
+    with SE: Schema(int).validate('1')
+
+
+def test_dict():
+    assert Schema({'key': 5}).validate({'key': 5}) == {'key': 5}
+    with SE: Schema({'key': 5}).validate({'key': 'x'})
+    with SE: Schema({'key': 5}).validate(['key', 5])
+    assert Schema({'key': int}).validate({'key': 5}) == {'key': 5}
+    assert Schema({'n': int, 'f': float}).validate(
+            {'n': 5, 'f': 3.14}) == {'n': 5, 'f': 3.14}
+    with SE: Schema({'n': int, 'f': float}).validate(
+            {'n': 3.14, 'f': 5})
+    with SE:
+        try:
+            Schema({}).validate({'abc': None, 1: None})
+        except SchemaWrongKeyError as e:
+            assert e.args[0].startswith("Wrong keys 'abc', 1 in")
+            raise
+    with SE:
+        try:
+            Schema({'key': 5}).validate({})
+        except SchemaMissingKeyError as e:
+            assert e.args[0] == "Missing keys: 'key'"
+            raise
+    with SE:
+        try:
+            Schema({'key': 5}).validate({'n': 5})
+        except SchemaMissingKeyError as e:
+            assert e.args[0] == "Missing keys: 'key'"
+            raise
+    with SE:
+        try:
+            Schema({}).validate({'n': 5})
+        except SchemaWrongKeyError as e:
+            assert e.args[0] == "Wrong keys 'n' in {'n': 5}"
+            raise
+    with SE:
+        try:
+            Schema({'key': 5}).validate({'key': 5, 'bad': 5})
+        except SchemaWrongKeyError as e:
+            assert e.args[0] in ["Wrong keys 'bad' in {'key': 5, 'bad': 5}",
+                                 "Wrong keys 'bad' in {'bad': 5, 'key': 5}"]
+            raise
+    with SE:
+        try:
+            Schema({}).validate({'a': 5, 'b': 5})
+        except SchemaError as e:
+            assert e.args[0] in ["Wrong keys 'a', 'b' in {'a': 5, 'b': 5}",
+                                 "Wrong keys 'a', 'b' in {'b': 5, 'a': 5}"]
+            raise
+
+    with SE:
+        try:
+            Schema({int: int}).validate({'': ''})
+        except SchemaUnexpectedTypeError as e:
+            assert e.args[0] in ["'' should be instance of 'int'"]
+
+
+def test_dict_keys():
+    assert Schema({str: int}).validate(
+            {'a': 1, 'b': 2}) == {'a': 1, 'b': 2}
+    with SE: Schema({str: int}).validate({1: 1, 'b': 2})
+    assert Schema({Use(str): Use(int)}).validate(
+            {1: 3.14, 3.14: 1}) == {'1': 3, '3.14': 1}
+
+
+def test_ignore_extra_keys():
+    assert Schema({'key': 5}, ignore_extra_keys=True).validate(
+            {'key': 5, 'bad': 4}) == {'key': 5}
+
+
+def test_ignore_extra_keys_validation_and_return_keys():
+    assert Schema({'key': 5, object: object}, ignore_extra_keys=True).validate(
+            {'key': 5, 'bad': 4}) == {'key': 5, 'bad': 4}
+
+
+def test_dict_optional_keys():
+    with SE: Schema({'a': 1, 'b': 2}).validate({'a': 1})
+    assert Schema({'a': 1, Optional('b'): 2}).validate({'a': 1}) == {'a': 1}
+    assert Schema({'a': 1, Optional('b'): 2}).validate(
+            {'a': 1, 'b': 2}) == {'a': 1, 'b': 2}
+    # Make sure Optionals are favored over types:
+    assert Schema({basestring: 1,
+                   Optional('b'): 2}).validate({'a': 1, 'b': 2}) == {'a': 1, 'b': 2}
+
+
+def test_dict_optional_defaults():
+    # Optionals fill out their defaults:
+    assert Schema({Optional('a', default=1): 11,
+                   Optional('b', default=2): 22}).validate({'a': 11}) == {'a': 11, 'b': 2}
+
+    # Optionals take precedence over types. Here, the "a" is served by the
+    # Optional:
+    assert Schema({Optional('a', default=1): 11,
+                   basestring: 22}).validate({'b': 22}) == {'a': 1, 'b': 22}
+
+    with raises(TypeError):
+        Optional(And(str, Use(int)), default=7)
+
+
+def test_dict_subtypes():
+    d = defaultdict(int, key=1)
+    v = Schema({'key': 1}).validate(d)
+    assert v == d
+    assert isinstance(v, defaultdict)
+    # Please add tests for Counter and OrderedDict once support for Python2.6
+    # is dropped!
+
+
+def test_complex():
+    s = Schema({'<file>': And([Use(open)], lambda l: len(l)),
+                '<path>': os.path.exists,
+                Optional('--count'): And(int, lambda n: 0 <= n <= 5)})
+    data = s.validate({'<file>': ['./LICENSE-MIT'], '<path>': './'})
+    assert len(data) == 2
+    assert len(data['<file>']) == 1
+    assert data['<file>'][0].read().startswith('Copyright')
+    assert data['<path>'] == './'
+
+
+def test_nice_errors():
+    try:
+        Schema(int, error='should be integer').validate('x')
+    except SchemaError as e:
+        assert e.errors == ['should be integer']
+    try:
+        Schema(Use(float), error='should be a number').validate('x')
+    except SchemaError as e:
+        assert e.code == 'should be a number'
+    try:
+        Schema({Optional('i'): Use(int, error='should be a number')}).validate({'i': 'x'})
+    except SchemaError as e:
+        assert e.code == 'should be a number'
+
+
+def test_use_error_handling():
+    try:
+        Use(ve).validate('x')
+    except SchemaError as e:
+        assert e.autos == ["ve('x') raised ValueError()"]
+        assert e.errors == [None]
+    try:
+        Use(ve, error='should not raise').validate('x')
+    except SchemaError as e:
+        assert e.autos == ["ve('x') raised ValueError()"]
+        assert e.errors == ['should not raise']
+    try:
+        Use(se).validate('x')
+    except SchemaError as e:
+        assert e.autos == [None, 'first auto']
+        assert e.errors == [None, 'first error']
+    try:
+        Use(se, error='second error').validate('x')
+    except SchemaError as e:
+        assert e.autos == [None, 'first auto']
+        assert e.errors == ['second error', 'first error']
+
+
+def test_or_error_handling():
+    try:
+        Or(ve).validate('x')
+    except SchemaError as e:
+        assert e.autos[0].startswith('Or(')
+        assert e.autos[0].endswith(") did not validate 'x'")
+        assert e.autos[1] == "ve('x') raised ValueError()"
+        assert len(e.autos) == 2
+        assert e.errors == [None, None]
+    try:
+        Or(ve, error='should not raise').validate('x')
+    except SchemaError as e:
+        assert e.autos[0].startswith('Or(')
+        assert e.autos[0].endswith(") did not validate 'x'")
+        assert e.autos[1] == "ve('x') raised ValueError()"
+        assert len(e.autos) == 2
+        assert e.errors == ['should not raise', 'should not raise']
+    try:
+        Or('o').validate('x')
+    except SchemaError as e:
+        assert e.autos == ["Or('o') did not validate 'x'",
+                           "'o' does not match 'x'"]
+        assert e.errors == [None, None]
+    try:
+        Or('o', error='second error').validate('x')
+    except SchemaError as e:
+        assert e.autos == ["Or('o') did not validate 'x'",
+                           "'o' does not match 'x'"]
+        assert e.errors == ['second error', 'second error']
+
+
+def test_and_error_handling():
+    try:
+        And(ve).validate('x')
+    except SchemaError as e:
+        assert e.autos == ["ve('x') raised ValueError()"]
+        assert e.errors == [None]
+    try:
+        And(ve, error='should not raise').validate('x')
+    except SchemaError as e:
+        assert e.autos == ["ve('x') raised ValueError()"]
+        assert e.errors == ['should not raise']
+    try:
+        And(str, se).validate('x')
+    except SchemaError as e:
+        assert e.autos == [None, 'first auto']
+        assert e.errors == [None, 'first error']
+    try:
+        And(str, se, error='second error').validate('x')
+    except SchemaError as e:
+        assert e.autos == [None, 'first auto']
+        assert e.errors == ['second error', 'first error']
+
+
+def test_schema_error_handling():
+    try:
+        Schema(Use(ve)).validate('x')
+    except SchemaError as e:
+        assert e.autos == [None, "ve('x') raised ValueError()"]
+        assert e.errors == [None, None]
+    try:
+        Schema(Use(ve), error='should not raise').validate('x')
+    except SchemaError as e:
+        assert e.autos == [None, "ve('x') raised ValueError()"]
+        assert e.errors == ['should not raise', None]
+    try:
+        Schema(Use(se)).validate('x')
+    except SchemaError as e:
+        assert e.autos == [None, None, 'first auto']
+        assert e.errors == [None, None, 'first error']
+    try:
+        Schema(Use(se), error='second error').validate('x')
+    except SchemaError as e:
+        assert e.autos == [None, None, 'first auto']
+        assert e.errors == ['second error', None, 'first error']
+
+
+def test_use_json():
+    import json
+    gist_schema = Schema(And(Use(json.loads),  # first convert from JSON
+                             {Optional('description'): basestring,
+                              'public': bool,
+                              'files': {basestring: {'content': basestring}}}))
+    gist = '''{"description": "the description for this gist",
+               "public": true,
+               "files": {
+                   "file1.txt": {"content": "String file contents"},
+                   "other.txt": {"content": "Another file contents"}}}'''
+    assert gist_schema.validate(gist)
+
+
+def test_error_reporting():
+    s = Schema({'<files>': [Use(open, error='<files> should be readable')],
+                '<path>': And(os.path.exists, error='<path> should exist'),
+                '--count': Or(None, And(Use(int), lambda n: 0 < n < 5),
+                              error='--count should be integer 0 < n < 5')},
+               error='Error:')
+    s.validate({'<files>': [], '<path>': './', '--count': 3})
+
+    try:
+        s.validate({'<files>': [], '<path>': './', '--count': '10'})
+    except SchemaError as e:
+        assert e.code == 'Error:\n--count should be integer 0 < n < 5'
+    try:
+        s.validate({'<files>': [], '<path>': './hai', '--count': '2'})
+    except SchemaError as e:
+        assert e.code == 'Error:\n<path> should exist'
+    try:
+        s.validate({'<files>': ['hai'], '<path>': './', '--count': '2'})
+    except SchemaError as e:
+        assert e.code == 'Error:\n<files> should be readable'
+
+
+def test_schema_repr():  # what about repr with `error`s?
+    schema = Schema([Or(None, And(str, Use(float)))])
+    repr_ = "Schema([Or(None, And(<type 'str'>, Use(<type 'float'>)))])"
+    # in Python 3 repr contains <class 'str'>, not <type 'str'>
+    assert repr(schema).replace('class', 'type') == repr_
+
+
+def test_validate_object():
+    schema = Schema({object: str})
+    assert schema.validate({42: 'str'}) == {42: 'str'}
+    with SE: schema.validate({42: 777})
+
+
+def test_issue_9_prioritized_key_comparison():
+    validate = Schema({'key': 42, object: 42}).validate
+    assert validate({'key': 42, 777: 42}) == {'key': 42, 777: 42}
+
+
+def test_issue_9_prioritized_key_comparison_in_dicts():
+    # http://stackoverflow.com/questions/14588098/docopt-schema-validation
+    s = Schema({'ID': Use(int, error='ID should be an int'),
+                'FILE': Or(None, Use(open, error='FILE should be readable')),
+                Optional(str): object})
+    data = {'ID': 10, 'FILE': None, 'other': 'other', 'other2': 'other2'}
+    assert s.validate(data) == data
+    data = {'ID': 10, 'FILE': None}
+    assert s.validate(data) == data
+
+
+def test_missing_keys_exception_with_non_str_dict_keys():
+    s = Schema({And(str, Use(str.lower), 'name'): And(str, len)})
+    with SE: s.validate(dict())
+    with SE:
+        try:
+            Schema({1: 'x'}).validate(dict())
+        except SchemaMissingKeyError as e:
+            assert e.args[0] == "Missing keys: 1"
+            raise
+
+
+def test_issue_56_cant_rely_on_callables_to_have_name():
+    s = Schema(methodcaller('endswith', '.csv'))
+    assert s.validate('test.csv') == 'test.csv'
+    with SE:
+        try:
+            s.validate('test.py')
+        except SchemaError as e:
+            assert "operator.methodcaller" in e.args[0]
+            raise
+
+
+def test_exception_handling_with_bad_validators():
+    BadValidator = namedtuple("BadValidator", ["validate"])
+    s = Schema(BadValidator("haha"))
+    with SE:
+        try:
+            s.validate("test")
+        except SchemaError as e:
+            assert "TypeError" in e.args[0]
+            raise
+
+
+def test_issue_83_iterable_validation_return_type():
+    TestSetType = type("TestSetType", (set,), dict())
+    data = TestSetType(["test", "strings"])
+    s = Schema(set([str]))
+    assert isinstance(s.validate(data), TestSetType)
+
+
+def test_optional_key_convert_failed_randomly_while_with_another_optional_object():
+    """
+    In this test, created_at string "2015-10-10 00:00:00" is expected to be converted
+    to a datetime instance.
+        - it works when the schema is
+
+            s = Schema({
+                    'created_at': _datetime_validator,
+                    Optional(basestring): object,
+                })
+
+        - but when wrapping the key 'created_at' with Optional, it fails randomly
+    :return:
+    """
+    import datetime
+    fmt = '%Y-%m-%d %H:%M:%S'
+    _datetime_validator = Or(None, Use(lambda i: datetime.datetime.strptime(i, fmt)))
+    # FIXME given tests enough
+    for i in range(1024):
+        s = Schema({
+            Optional('created_at'): _datetime_validator,
+            Optional('updated_at'): _datetime_validator,
+            Optional('birth'): _datetime_validator,
+            Optional(basestring): object,
+        })
+        data = {
+            'created_at': '2015-10-10 00:00:00'
+        }
+        validated_data = s.validate(data)
+        # is expected to be converted to a datetime instance, but fails randomly
+        # (most of the time)
+        assert isinstance(validated_data['created_at'], datetime.datetime)
+        # assert isinstance(validated_data['created_at'], basestring)
+
+
+def test_copy():
+    s1 = SchemaError('a', None)
+    s2 = copy.deepcopy(s1)
+    assert s1 is not s2
+    assert type(s1) is type(s2)

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/python-schema.git



More information about the Python-modules-commits mailing list