diff --git a/README.rst b/README.rst index 3d4cc79..1ff0dd1 100644 --- a/README.rst +++ b/README.rst @@ -109,6 +109,18 @@ If ``Schema(...)`` encounters a callable (function, class, or object with ... SchemaError: (-12) should evaluate to True +.. code:: python + + >>> @Predicate(error="Zero or negative number") + ... def positive(x): + ... return x >= 0 + + >>> positive.validate(-12) + Traceback (most recent call last): + ... + SchemaError: Zero or negative number + + "Validatables" ~~~~~~~~~~~~~~ @@ -344,6 +356,7 @@ instead of a built-in one. ... SchemaError: Invalid year + 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.py b/schema.py index da395c1..b10e6c3 100644 --- a/schema.py +++ b/schema.py @@ -3,15 +3,17 @@ parsing, converted from JSON/YAML (or something else) to Python data-types.""" import re +import functools + __version__ = '0.6.7' __all__ = ['Schema', - 'And', 'Or', 'Regex', 'Optional', 'Use', 'Forbidden', 'Const', + 'And', 'Or', 'Regex', 'Optional', 'Use', 'Forbidden', 'Const', 'Predicate', 'SchemaError', 'SchemaWrongKeyError', 'SchemaMissingKeyError', 'SchemaForbiddenKeyError', - 'SchemaUnexpectedTypeError'] + 'SchemaUnexpectedTypeError',] class SchemaError(Exception): @@ -378,6 +380,15 @@ def validate(self, data): return data +def Predicate(error=None, ignore_extra_keys=False): + """ + Decorator to wrap function as Schema. + """ + def _Predicate(f): + return Schema(f, error=error, ignore_extra_keys=ignore_extra_keys) + return _Predicate + + def _callable_str(callable_): if hasattr(callable_, '__name__'): return callable_.__name__ diff --git a/test_schema.py b/test_schema.py index f58eb66..0aca39c 100644 --- a/test_schema.py +++ b/test_schema.py @@ -12,7 +12,7 @@ from schema import (Schema, Use, And, Or, Regex, Optional, Const, SchemaError, SchemaWrongKeyError, SchemaMissingKeyError, SchemaUnexpectedTypeError, - SchemaForbiddenKeyError, Forbidden) + SchemaForbiddenKeyError, Forbidden, Predicate) if sys.version_info[0] == 3: basestring = str # Python 3 does not have basestring @@ -592,3 +592,25 @@ def validate(self, data): v = {'k': 1, 'd': {'k': 2, 'l': [{'l': [3, 4, 5]}]}} d = MySchema(s).validate(v) assert d['k'] == 2 and d['d']['k'] == 3 and d['d']['l'][0]['l'] == [4, 5, 6] + + +def test_predicate(): + @Predicate() + def p1(x): + return x == 1 + assert p1._error == None and p1._ignore_extra_keys == False + + @Predicate("ERROR2", ignore_extra_keys=True) + def p2(x): + return x == 2 + assert p2._error is "ERROR2" and p2._ignore_extra_keys == True + + p2.validate(2) # success + + with SE: + try: + p2.validate(0) # failure + except SchemaError as e: + assert e.autos == ['p2(0) should evaluate to True'] + assert e.errors == ["ERROR2"] + raise