diff --git a/jsonmodels/validators.py b/jsonmodels/validators.py index ef9ff80..a77a033 100644 --- a/jsonmodels/validators.py +++ b/jsonmodels/validators.py @@ -77,7 +77,7 @@ def modify_schema(self, field_schema): field_schema['exclusiveMaximum'] = True -class Regex(object): +class ECMA262Regex(object): """Validator for regular expressions.""" @@ -133,6 +133,45 @@ def modify_schema(self, field_schema): self.pattern, self.flags) +class Regex(ECMA262Regex): + """ + Deprecated. Use the more explicit `ECMA262Regex` + """ + pass + + +class PythonRegex(object): + + def __init__(self, pattern, flags=0): + """ + Initialize a new `PythonRegex` validator. + + :param pattern: The regex pattern to validate against. + :param flags: Bitwise OR of valid `re` flags. + Notice that these flags WILL NOT be written to a schema generated + by this validator. + """ + self.regex = re.compile(pattern, flags=flags) + + def validate(self, value): + try: + match = self.regex.search(value) + except TypeError as e: + raise ValidationError(*e.args) + if match is None: + raise ValidationError( + 'Value "{value}" did not match pattern "{pattern}".'.format( + value=value, pattern=self.regex.pattern + )) + + def modify_schema(self, schema): + """ + Adds the "pattern" keyword to the given schema. + Note that all `re` flags are discarded and are not added to the schema. + """ + schema["pattern"] = self.regex.pattern + + class Length(object): """Validator for length.""" diff --git a/tests/test_schema.py b/tests/test_schema.py index ff25af4..590c0cf 100644 --- a/tests/test_schema.py +++ b/tests/test_schema.py @@ -1,3 +1,4 @@ +import re import pytest from jsonmodels import models, fields, validators, errors, builders @@ -270,6 +271,22 @@ class Person(models.Base): assert compare_schemas(pattern, schema) +def test_python_regex_validator(): + pattern = "some pattern" + schema = {} + validator = validators.PythonRegex(pattern) + validator.modify_schema(schema) + assert schema["pattern"] == pattern + + +def test_python_regex_validator_ignore_flags(): + pattern = "some pattern" + schema = {} + validator = validators.PythonRegex(pattern, re.DOTALL | re.VERBOSE) + validator.modify_schema(schema) + assert schema["pattern"] == pattern + + def test_regex_validator_when_ecma_regex_given(): class Person(models.Base): diff --git a/tests/test_validation.py b/tests/test_validation.py index e22c81f..6624b36 100644 --- a/tests/test_validation.py +++ b/tests/test_validation.py @@ -133,18 +133,22 @@ def test_max_exclusive_validation(): def test_regex_validation(): - validator = validators.Regex('some') - assert 'some' == validator.pattern + v1 = validators.Regex('some') + v2 = validators.PythonRegex('some') + assert 'some' == v1.pattern == v2.regex.pattern - validator.validate('some string') - validator.validate('get some chips') - with pytest.raises(errors.ValidationError): - validator.validate('asdf') - with pytest.raises(errors.ValidationError): - validator.validate('trololo') + for s in ('some string', 'get some chips'): + v1.validate(s) + v2.validate(s) + + for v in (v1, v2): + with pytest.raises(errors.ValidationError): + v.validate('asdf') + with pytest.raises(errors.ValidationError): + v.validate('trololo') -def test_regex_validation_flags(): +def test_ecma_regex_validation_flags(): # Invalid flags ignored validator = validators.Regex("foo", bla=True, ble=False, ignorecase=True) assert validator.flags == [validators.Regex.FLAGS["ignorecase"]] @@ -167,11 +171,13 @@ def test_regex_validation_flags(): def test_regex_validation_for_wrong_type(): - validator = validators.Regex('some') - assert 'some' == validator.pattern + v1 = validators.Regex('some') + v2 = validators.PythonRegex('some') + assert 'some' == v1.pattern == v2.regex.pattern - with pytest.raises(errors.ValidationError): - validator.validate(1) + for v in (v1, v2): + with pytest.raises(errors.ValidationError): + v.validate(1) def test_validation_2():