├── .gitignore ├── .travis.yml ├── LICENSE ├── MANIFEST.in ├── README.md ├── pyschemes ├── __init__.py ├── base.py └── validators.py ├── requirements.txt ├── setup.cfg ├── setup.py ├── tests ├── conftest.py ├── test_base.py └── test_validators.py └── tox.ini /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *,cover 47 | .hypothesis/ 48 | 49 | # Translations 50 | *.mo 51 | *.pot 52 | 53 | # Django stuff: 54 | *.log 55 | local_settings.py 56 | 57 | # Flask stuff: 58 | instance/ 59 | .webassets-cache 60 | 61 | # Scrapy stuff: 62 | .scrapy 63 | 64 | # Sphinx documentation 65 | docs/_build/ 66 | 67 | # PyBuilder 68 | target/ 69 | 70 | # Jupyter Notebook 71 | .ipynb_checkpoints 72 | 73 | # pyenv 74 | .python-version 75 | 76 | # celery beat schedule file 77 | celerybeat-schedule 78 | 79 | # SageMath parsed files 80 | *.sage.py 81 | 82 | # dotenv 83 | .env 84 | 85 | # virtualenv 86 | .venv 87 | venv/ 88 | ENV/ 89 | 90 | # Spyder project settings 91 | .spyderproject 92 | 93 | # Rope project settings 94 | .ropeproject 95 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | python: 3 | - "2.6" 4 | - "2.7" 5 | - "3.2" 6 | - "3.3" 7 | - "3.4" 8 | # PyPy versions 9 | - "pypy" # PyPy2 2.5.0 10 | - "pypy3" # Pypy3 2.4.0 11 | - "pypy-5.3.1" 12 | # command to install dependencies 13 | install: 14 | - pip install . 15 | - pip install -r requirements.txt 16 | # command to run tests 17 | script: pytest 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 2 | Version 2, December 2004 3 | 4 | Copyright (C) 2004 Sam Hocevar 5 | 6 | Everyone is permitted to copy and distribute verbatim or modified 7 | copies of this license document, and changing it is allowed as long 8 | as the name is changed. 9 | 10 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 11 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 12 | 13 | 0. You just DO WHAT THE FUCK YOU WANT TO. 14 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README.rst LICENSE-MIT *.py 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PySchemes 2 | PySchemes is a library for validating data structures in Python. PySchemes is designed to be simple and Pythonic. 3 | 4 | ![https://travis-ci.org/shivylp/pyschemes](https://secure.travis-ci.org/shivylp/pyschemes.svg?branch=master) 5 | 6 | 7 | 8 | ## Features 9 | * Simple representation of schema using primitive Python types (Or Complex types as well) 10 | * Sane Schema Structures 11 | * Sane errors 12 | * Power of PySchemes lies in its validators (take a look at tests to understand usage of various validators) 13 | * Lambda functions or any callable can be easily used as a validator 14 | 15 | ## Examples 16 | 17 | ```python 18 | # Execute this before executing any of the examples 19 | >>> from pyschemes import Scheme, validators 20 | ``` 21 | 22 | 1. Simple TypeChecking 23 | ```python 24 | >>> Scheme(int).validate(10) 25 | 10 26 | 27 | >>> Scheme(int).validate(10.3) 28 | Traceback (most recent call last): 29 | File "", line 1, in 30 | TypeError: expected type: 'int', got 'float' 31 | 32 | >>> from collections import Iterable 33 | >>> Scheme(Iterable).validate([1, 2]) 34 | [1, 2] 35 | 36 | >>> Scheme(Iterable).validate(("hello", )) 37 | ("hello", ) 38 | 39 | >>> Scheme(Iterable).validate({"a": "b", "c": "d"}) 40 | {"a": "b", "c": "d"} 41 | 42 | >>> Scheme(Iterable).validate(range(100)) 43 | range(0, 100) 44 | 45 | >>> Scheme(Iterable).validate(lambda x: x) 46 | Traceback (most recent call last): 47 | File "", line 1, in 48 | TypeError: expected type: 'Iterable', got 'function' 49 | ``` 50 | 51 | 2. Simple Value Validation 52 | ```python 53 | >>> Scheme(10).validate(10) 54 | 10 55 | 56 | >>> Scheme(10).validate(10.3) 57 | Traceback (most recent call last): 58 | File "", line 1, in 59 | ValueError: expected value '10', got '10.3' 60 | ``` 61 | 62 | 3. Simple Choices Validation 63 | ```python 64 | >>> choices = validators.Any(["choiceA", "choiceB", "choiceC"]) 65 | 66 | >>> Scheme(choices).validate("choiceA") 67 | 'choiceA' 68 | 69 | >>> Scheme(choices).validate("hello") 70 | Traceback (most recent call last): 71 | File "", line 1, in 72 | ValueError: value did not pass any validation 73 | ``` 74 | 75 | 4. Validating a List/Iterable Scheme 76 | 77 | ```python 78 | >>> Scheme([str, 2, int]).validate(["hello", 2, 15]) 79 | ["hello", 2, 15] 80 | 81 | >>> Scheme((str, 2, int)).validate(("hello", 2, 15)) 82 | ("hello", 2, 15) 83 | 84 | >>> Scheme((str, 2, int)).validate(["hello", 2, 15]) 85 | Traceback (most recent call last): 86 | File "", line 1, in 87 | TypeError: expected type: 'tuple', got 'list' 88 | 89 | >>> Scheme([str, 2, int]).validate(["hello", 3, 130]) 90 | Traceback (most recent call last): 91 | File "", line 1, in 92 | ValueError: element at index 1 (expected value '2', got '3') 93 | 94 | >>> Scheme([str, 2, int]).validate(["hello", 2, 4.5]) 95 | Traceback (most recent call last): 96 | File "", line 1, in 97 | TypeError: element at index 2 (expected type: 'int', got 'float') 98 | ``` 99 | 100 | 101 | 5. Validating a Dictionary/Mapping Scheme 102 | ```python 103 | >>> Scheme({str: int}).validate({"a": 1, "b": 2}) 104 | {'a': 1, 'b': 2} 105 | 106 | >>> Scheme({str: int}).validate({"a": 1, "b": 3.5}) 107 | Traceback (most recent call last): 108 | File "", line 1, in 109 | TypeError: at key 'b' (expected type: 'int', got 'float') 110 | 111 | >>> Scheme({"hello": 10, str: object}).validate({"hello": 10, "world": 12}) 112 | {"hello": 10, "world": 12} 113 | 114 | >>> Scheme({"required-key": int}).validate({}) 115 | Traceback (most recent call last): 116 | File "", line 1, in 117 | ValueError: missing key 'required-key' 118 | 119 | >>> from pyschemes.validators import Optional 120 | >>> Scheme({"k": Optional(str, "hello")}).validate({}) 121 | {'k': 'hello'} 122 | 123 | >>> Scheme({"k": Optional(str, "hello")}).validate({"k": "world"}) 124 | {'k': 'world'} 125 | 126 | >>> Scheme({"k": Optional(int, 10)}).validate({"k": "world"}) 127 | Traceback (most recent call last): 128 | File "", line 1, in 129 | TypeError: at key 'k' (expected type: 'int', got 'str') 130 | 131 | >>> Scheme({"only-key": str}).validate({"only-key": "hello", "b": "not-allowed"}) 132 | Traceback (most recent call last): 133 | File "", line 1, in 134 | ValueError: at key 'b' (not allowed) 135 | ``` 136 | 137 | 138 | 6. Lambda/Callable Scheme 139 | ```python 140 | >>> Scheme(lambda x: x < 100).validate(10) 141 | 10 142 | 143 | >>> Scheme(lambda x: x < 100).validate(101) 144 | Traceback (most recent call last): 145 | File "", line 1, in 146 | ValueError: validator '' failed for value '101' 147 | 148 | >>> def CustomValidator(value): 149 | ... if value == "foo" or value in range(100): 150 | ... return value 151 | ... else: 152 | ... return False 153 | ... 154 | >>> Scheme(CustomValidator).validate(101) 155 | Traceback (most recent call last): 156 | File "", line 1, in 157 | ValueError: validator 'CustomValidator' failed for value '10' 158 | 159 | >>> Scheme(CustomValidator).validate(9) 160 | 9 161 | >>> Scheme(CustomValidator).validate("foo") 162 | 'foo' 163 | ``` 164 | 165 | 166 | 7. Compund Schemes 167 | ```python 168 | >>> Scheme({"test": lambda x: x < 100}).validate({"test": 10}) 169 | {'test': 10} 170 | 171 | >>> Scheme({"test": lambda x: x < 100}).validate({"test": 101}) 172 | Traceback (most recent call last): 173 | File "", line 1, in 174 | ValueError: at key 'test' (validator '' failed for value '101') 175 | 176 | >>> Scheme({"test": Scheme({"a": str, "b": int})}).validate({"test": {}}) 177 | Traceback (most recent call last): 178 | File "", line 1, in 179 | ValueError: at key 'test' (missing key 'a') 180 | 181 | >>> Scheme({"test": Scheme({"b": int})}).validate({"test": {"b": 1.5}}) 182 | Traceback (most recent call last): 183 | File "", line 1, in 184 | TypeError: at key 'test' (at key 'b' (expected type: 'int', got 'float')) 185 | 186 | >>> Scheme({"t": {str: int}}).validate({"t": {"a": 1}}) 187 | {'t': {'a': 1}} 188 | 189 | >>> Scheme({"t": (str, int)}).validate({"t": ("a", 1)}) 190 | {'t': ('a', 1)} 191 | ``` 192 | 193 | And more to come in tests! 194 | -------------------------------------------------------------------------------- /pyschemes/__init__.py: -------------------------------------------------------------------------------- 1 | """Schemer is a Schema Validation package for Python.""" 2 | 3 | 4 | from .base import Scheme 5 | 6 | 7 | __author__ = "Shivaprasad" 8 | __version__ = "1.0.0" 9 | __all__ = ["Scheme"] 10 | -------------------------------------------------------------------------------- /pyschemes/base.py: -------------------------------------------------------------------------------- 1 | """Schema Validation.""" 2 | 3 | from .validators import Validator 4 | 5 | 6 | class Scheme(object): 7 | """Scheme Validator.""" 8 | 9 | def __init__(self, scheme_spec): 10 | self.validator = Validator.create_validator(scheme_spec) 11 | 12 | def validate(self, value): 13 | return self.validator.validate(value) 14 | -------------------------------------------------------------------------------- /pyschemes/validators.py: -------------------------------------------------------------------------------- 1 | import abc 2 | import re 3 | import collections.abc 4 | 5 | 6 | COMPARABLE, CALLABLE, VALIDATOR, TYPE, DICT, ITERABLE = range(6) 7 | 8 | 9 | def get_entry_type(s): 10 | """Return entry type for a given entry.""" 11 | if type(s) in (list, tuple, set, frozenset): 12 | return ITERABLE 13 | if type(s) is dict: 14 | return DICT 15 | if issubclass(type(s), type): 16 | return TYPE 17 | if hasattr(s, 'validate'): 18 | return VALIDATOR 19 | if callable(s): 20 | return CALLABLE 21 | else: 22 | return COMPARABLE 23 | 24 | 25 | class Validator(object): 26 | 27 | def __init__(self, scheme): 28 | self.scheme = scheme 29 | 30 | def __call__(self, value): 31 | return self.validate(value) 32 | 33 | def validate(self, value): 34 | raise NotImplementedError("{}.{}(value)".format( 35 | self.__class__.__name__, "validate")) 36 | 37 | @staticmethod 38 | def create_validator(spec): 39 | _type = get_entry_type(spec) 40 | if _type is ITERABLE: 41 | return IterableValidator(spec) 42 | elif _type is VALIDATOR: 43 | return spec 44 | elif _type is DICT: 45 | return MappingValidator(spec) 46 | elif _type is TYPE: 47 | return TypeValidator(spec) 48 | elif _type is CALLABLE: 49 | return CallableValidator(spec) 50 | elif _type is COMPARABLE: 51 | return ValueValidator(spec) 52 | 53 | 54 | class CallableValidator(Validator): 55 | 56 | def __init__(self, _callable, fail_flag=False): 57 | self._callable = _callable 58 | self.fail_flag = fail_flag 59 | 60 | def validate(self, value): 61 | result = self._callable(value) 62 | if result == self.fail_flag: 63 | msg = "validator '{}' failed for value '{}'" 64 | raise ValueError(msg.format(self._callable.__name__, value)) 65 | return value 66 | 67 | 68 | class TypeValidator(Validator): 69 | 70 | def __init__(self, _type): 71 | if type(_type) not in [type, abc.ABCMeta]: 72 | raise TypeError("'_type' must be a type") 73 | self._type = _type 74 | 75 | def validate(self, value): 76 | if isinstance(value, self._type): 77 | return value 78 | else: 79 | msg = "expected type: '{}', got '{}'" 80 | expected = self._type.__name__ 81 | actual = type(value).__name__ 82 | raise TypeError(msg.format(expected, actual)) 83 | 84 | 85 | class LengthValidator(Validator): 86 | 87 | def __init__(self, length): 88 | self.length = length 89 | 90 | def validate(self, value): 91 | actual = len(value) 92 | if actual != self.length: 93 | raise ValueError("expected length '{}', got '{}'".format( 94 | self.length, actual 95 | )) 96 | return value 97 | 98 | 99 | class ValueValidator(Validator): 100 | 101 | def validate(self, value): 102 | if value != self.scheme: 103 | raise ValueError("expected value '{}', got '{}'".format( 104 | self.scheme, value)) 105 | return value 106 | 107 | 108 | class All(Validator): 109 | """Check that all validators pass.""" 110 | 111 | def __init__(self, schemes): 112 | TypeValidator(collections.abc.Iterable).validate(schemes) 113 | self.__schemes = [] 114 | for scheme in schemes: 115 | self.__schemes.append(Validator.create_validator(scheme)) 116 | 117 | def validate(self, value): 118 | for scheme in self.__schemes: 119 | scheme.validate(value) 120 | return value 121 | 122 | 123 | class Any(Validator): 124 | """Check if any value is valid as per any one scheme""" 125 | 126 | def __init__(self, schemes): 127 | TypeValidator(collections.abc.Iterable).validate(schemes) 128 | self.__schemes = [] 129 | for scheme in schemes: 130 | self.__schemes.append(Validator.create_validator(scheme)) 131 | 132 | def validate(self, value): 133 | for scheme in self.__schemes: 134 | try: 135 | scheme.validate(value) 136 | return value 137 | except Exception: 138 | pass 139 | raise ValueError("value did not pass 'Any' validation") 140 | 141 | 142 | class IterableValidator(Validator): 143 | 144 | def __init__(self, iterable, ignore_type=False): 145 | TypeValidator(collections.abc.Iterable).validate(iterable) 146 | self.__type = object 147 | if not ignore_type: 148 | self.__type = type(iterable) 149 | self.__iterable = [] 150 | for itm in iterable: 151 | self.__iterable.append(Validator.create_validator(itm)) 152 | 153 | def validate(self, value): 154 | validated = [] 155 | TypeValidator(self.__type).validate(value) 156 | LengthValidator(len(self.__iterable)).validate(value) 157 | for i in range(len(self.__iterable)): 158 | item = self.__iterable[i] 159 | try: 160 | validated_value = item.validate(value[i]) 161 | except Exception as ex: 162 | raise type(ex)("element at index {} ({})".format(i, ex)) 163 | validated.append(validated_value) 164 | return type(value)(validated) # return value of sample type 165 | 166 | 167 | class Optional(Validator): 168 | """Optional Scheme.""" 169 | 170 | def __init__(self, scheme, default): 171 | self.__scheme = Validator.create_validator(scheme) 172 | self.default = default 173 | 174 | def validate(self, *value): 175 | if len(value) > 0: 176 | val = value[0] 177 | return self.__scheme.validate(val) 178 | return self.default 179 | 180 | 181 | class MappingValidator(Validator): 182 | """Validator for dicts and key-value maps.""" 183 | 184 | def __init__(self, mapping, ignore_unknown=False): 185 | TypeValidator(collections.abc.Mapping).validate(mapping) 186 | self.__mapping = dict(mapping) 187 | self.ignore_unknown = ignore_unknown 188 | 189 | def validate(self, value): 190 | TypeValidator(collections.abc.Mapping).validate(value) 191 | for k, v in value.items(): 192 | try: 193 | key_type = type(k) 194 | value_rule = self.__mapping.get(k, None) 195 | if value_rule: 196 | validator = Validator.create_validator(value_rule) 197 | else: 198 | type_rule = self.__mapping.get(key_type, None) 199 | if type_rule is None: 200 | if self.ignore_unknown is False: 201 | raise ValueError("not allowed") 202 | else: 203 | validator = Validator.create_validator(type_rule) 204 | validator.validate(v) 205 | except Exception as ex: 206 | raise type(ex)("at key '{}' ({})".format( 207 | k, ex 208 | )) 209 | for k in self.__mapping: 210 | _type = get_entry_type(k) 211 | if _type is COMPARABLE: 212 | if k not in value: 213 | expected = self.__mapping.get(k, None) 214 | if isinstance(expected, Optional): 215 | value[k] = expected.default 216 | else: 217 | raise ValueError("missing key '{}'".format(k)) 218 | return value 219 | 220 | 221 | class RegexValidator(object): 222 | """Validator for regex patterns.""" 223 | 224 | def __init__(self, pattern): 225 | self.pattern = re.compile(pattern) 226 | 227 | def validate(self, value): 228 | TypeValidator(str).validate(value) 229 | if self.pattern.match(value): 230 | return value 231 | else: 232 | raise ValueError("value '{}' did not match regex r'{}'".format( 233 | value, str(self.pattern) 234 | )) 235 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | pytest 2 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [wheel] 2 | universal = 1 3 | 4 | [semantic_release] 5 | version_variable = pyschemes:__version__ 6 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | """A setuptools based setup module.""" 2 | 3 | 4 | from setuptools import setup, find_packages 5 | from codecs import open 6 | from os import path 7 | 8 | here = path.abspath(path.dirname(__file__)) 9 | 10 | # Get the long description from the README file 11 | try: 12 | f = open(path.join(here, 'README.md'), encoding='utf-8') 13 | long_description = f.read() 14 | except: 15 | pass 16 | finally: 17 | long_description = "(n/a)" 18 | 19 | 20 | setup( 21 | name='pyschemes', 22 | version='1.0.0', 23 | description='A Pythonic data-structure validator', 24 | long_description=long_description, 25 | url='https://github.com/shivylp/pyschemes', 26 | author='Shivaprasad', 27 | author_email='shiv.ylp@gmail.com', 28 | license='MIT', 29 | keywords='validator schema', 30 | packages=find_packages(exclude=['tests']), 31 | install_requires=[] 32 | ) 33 | -------------------------------------------------------------------------------- /tests/conftest.py: -------------------------------------------------------------------------------- 1 | """Setup testing environment.""" 2 | 3 | import sys 4 | import os 5 | 6 | root_dir = os.path.dirname(os.path.dirname(__file__)) 7 | sys.path.insert(0, root_dir) 8 | -------------------------------------------------------------------------------- /tests/test_base.py: -------------------------------------------------------------------------------- 1 | """Test base.py module.""" 2 | 3 | from pyschemes import Scheme 4 | 5 | 6 | def test_scheme(): 7 | Scheme(int).validate(10) 8 | -------------------------------------------------------------------------------- /tests/test_validators.py: -------------------------------------------------------------------------------- 1 | """Test Validators.""" 2 | 3 | 4 | import pytest 5 | from collections.abc import Mapping, Iterable 6 | from types import FunctionType 7 | from pyschemes.validators import TypeValidator, LengthValidator, ValueValidator 8 | from pyschemes.validators import CallableValidator, All, Any, Optional 9 | from pyschemes.validators import IterableValidator, MappingValidator 10 | from pyschemes.validators import IterableValidator, RegexValidator 11 | 12 | 13 | def test_type_validator(): 14 | TypeValidator(int).validate(10) 15 | TypeValidator(float).validate(10.3) 16 | TypeValidator(str).validate("hello world") 17 | TypeValidator(dict).validate({}) 18 | TypeValidator(FunctionType).validate(lambda x: x) 19 | TypeValidator(Iterable).validate([]) 20 | TypeValidator(Mapping).validate({}) 21 | with pytest.raises(TypeError): 22 | TypeValidator(FunctionType).validate(None) 23 | TypeValidator(int).validate(10.3) 24 | 25 | 26 | def test_length_validator(): 27 | LengthValidator(10).validate(range(10)) 28 | with pytest.raises(ValueError): 29 | LengthValidator(10).validate(range(20)) 30 | 31 | 32 | def test_value_validator(): 33 | ValueValidator({}).validate({}) 34 | ValueValidator(10).validate(10) 35 | ValueValidator(["hello", "world"]).validate(["hello", "world"]) 36 | with pytest.raises(ValueError): 37 | ValueValidator(10).validate(100) 38 | 39 | 40 | def test_callable_validator(): 41 | CallableValidator(lambda x: x < 10).validate(9) 42 | with pytest.raises(ValueError): 43 | CallableValidator(lambda x: x < 10).validate(10) 44 | 45 | 46 | def test_All(): 47 | tests = [int, 6] 48 | All(tests).validate(6) 49 | with pytest.raises(ValueError): 50 | All(tests).validate(10) 51 | with pytest.raises(TypeError): 52 | All(tests).validate(10.5) 53 | 54 | 55 | def test_Any(): 56 | must_be_number = [int, float, complex] 57 | Any(must_be_number).validate(1+2j) 58 | Any(must_be_number).validate(1) 59 | Any(must_be_number).validate(1.6) 60 | with pytest.raises(ValueError): 61 | Any(must_be_number).validate("hello") 62 | 63 | 64 | def test_Iterable_validator(): 65 | schema = [str, int, Any([2, 3, 4])] 66 | IterableValidator(schema).validate(["hello", 2, 4]) 67 | with pytest.raises(ValueError): 68 | IterableValidator(schema).validate(["hello", 2, 5]) 69 | with pytest.raises(TypeError): 70 | IterableValidator(schema).validate([10, 2, 4]) 71 | 72 | 73 | def test_optional_validator(): 74 | optional = Optional(str, "hello") 75 | assert optional.validate("world") == "world" 76 | assert optional.validate() == "hello" 77 | with pytest.raises(TypeError): 78 | optional.validate(10) 79 | 80 | 81 | def test_mapping_validator(): 82 | map_scheme = { 83 | "height": All([Any([int, float]), lambda x: x < 8]), 84 | "name": str, 85 | "phone": All([str, lambda x: len(x) == 10]), 86 | "likes": Optional({ 87 | str: str 88 | }, {}) 89 | } 90 | MappingValidator(map_scheme).validate({ 91 | "height": 6.7, 92 | "name": "bob", 93 | "phone": "0123456789", 94 | "likes": { 95 | "movie": "Inglourius Basterds" 96 | } 97 | }) 98 | with pytest.raises(ValueError): 99 | MappingValidator(map_scheme).validate({ 100 | "name": "bob", 101 | "phone": "0123456789" 102 | }) 103 | with pytest.raises(TypeError): 104 | MappingValidator(map_scheme).validate({ 105 | "name": "bob", 106 | "phone": 1234567890 107 | }) 108 | with pytest.raises(ValueError): 109 | MappingValidator(map_scheme).validate({ 110 | "name": "bob", 111 | "phone": "1234567890", 112 | "balh-not-allowed": "hello" 113 | }) 114 | 115 | 116 | 117 | def test_regex_validator(): 118 | pattern = r"\w+\d+\w+" # e.g. hello4world 119 | RegexValidator(pattern).validate("hello4world") 120 | with pytest.raises(ValueError): 121 | RegexValidator(pattern).validate("helloworld") 122 | with pytest.raises(TypeError): 123 | RegexValidator(pattern).validate([1,2,3]) 124 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | # Tox (http://tox.testrun.org/) is a tool for running tests in 2 | # multiple virtualenvs. This configuration file will run the 3 | # test suite on all supported python versions. To use it, "pip 4 | # install tox" and then run "tox" from this directory. 5 | 6 | [tox] 7 | envlist = py2, py3 8 | 9 | [testenv] 10 | commands = py.test 11 | deps = pytest 12 | --------------------------------------------------------------------------------