Skip to content

Commit 40f99d7

Browse files
committed
Issue #4.
1 parent 9590f1e commit 40f99d7

File tree

3 files changed

+57
-44
lines changed

3 files changed

+57
-44
lines changed

pyvalid/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
accepts = Accepts
88
returns = Returns
9-
version = '0.8'
9+
version = '0.9'
1010

1111

1212
__all__ = [

pyvalid/validators.py

+37-43
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import sys
2+
import re
23
from abc import ABCMeta, abstractmethod
34
from pyvalid import accepts
4-
from collections import Iterable, Container, Callable
5+
from collections import Iterable, Container, Callable, Sized
56
from six import with_metaclass
67

78

@@ -26,30 +27,24 @@ class AbstractValidator(with_metaclass(ABCMeta, Validator)):
2627
def checkers(self):
2728
raise NotImplementedError
2829

29-
@property
30-
@abstractmethod
31-
def settings(self):
32-
raise NotImplementedError
33-
34-
@settings.setter
35-
@abstractmethod
36-
def settings(self, value):
37-
raise NotImplementedError
38-
3930
@abstractmethod
4031
def __call__(self, val):
4132
raise NotImplementedError
4233

4334
def __init__(self):
44-
for key, val in list(self.settings.items()):
45-
if (key not in self.checkers) or (val is None):
46-
del self.settings[key]
35+
Validator.__init__(self, self)
36+
for checker_func, checker_args in list(self.checkers.items()):
37+
try:
38+
to_del = checker_args[0] is None
39+
except:
40+
to_del = True
41+
if to_del:
42+
del self.checkers[checker_func]
4743

4844
def _check(self, val):
4945
valid = True
50-
for checker_name, checker_arg in self.settings.items():
51-
checker = self.checkers[checker_name]
52-
valid = checker(val, checker_arg)
46+
for checker_func, checker_args in self.checkers.items():
47+
valid = checker_func(val, *checker_args)
5348
if not valid:
5449
break
5550
return valid
@@ -85,14 +80,6 @@ def in_range_checker(cls, val, in_range):
8580
def not_in_range_checker(cls, val, not_in_range):
8681
return not cls.in_range_checker(val, not_in_range)
8782

88-
@property
89-
def settings(self):
90-
return self.__settings
91-
92-
@settings.setter
93-
def settings(self, value):
94-
self.__settings = value
95-
9683
@property
9784
def checkers(self):
9885
return self.__checkers
@@ -102,12 +89,13 @@ def checkers(self):
10289
in_range=[Iterable, Container], not_in_range=[Iterable, Container]
10390
)
10491
def __init__(self, **kwargs):
105-
self.__settings = kwargs
10692
self.__checkers = {
107-
'min_val': NumberValidator.min_val_checker,
108-
'max_val': NumberValidator.max_val_checker,
109-
'in_range': NumberValidator.in_range_checker,
110-
'not_in_range': NumberValidator.not_in_range_checker
93+
NumberValidator.min_val_checker: [kwargs.get('min_val', None)],
94+
NumberValidator.max_val_checker: [kwargs.get('max_val', None)],
95+
NumberValidator.in_range_checker: [kwargs.get('in_range', None)],
96+
NumberValidator.not_in_range_checker: [
97+
kwargs.get('not_in_range', None)
98+
]
11199
}
112100
AbstractValidator.__init__(self)
113101

@@ -143,29 +131,35 @@ def in_range_checker(cls, val, in_range):
143131
def not_in_range_checker(cls, val, not_in_range):
144132
return not cls.in_range_checker(val, not_in_range)
145133

146-
@property
147-
def settings(self):
148-
return self.__settings
149-
150-
@settings.setter
151-
def settings(self, value):
152-
self.__settings = value
134+
@classmethod
135+
def re_checker(cls, val, pattern, flags=0):
136+
try:
137+
match_obj = re.match(pattern, val, flags)
138+
is_valid = match_obj is not None
139+
except re.error:
140+
is_valid = False
141+
return is_valid
153142

154143
@property
155144
def checkers(self):
156145
return self.__checkers
157146

158147
@accepts(
159148
object, min_len=int, max_len=int,
160-
in_range=[Iterable, Container], not_in_range=[Iterable, Container]
149+
in_range=[Iterable, Container], not_in_range=[Iterable, Container],
150+
re_pattern=str, re_flags=int
161151
)
162152
def __init__(self, **kwargs):
163-
self.__settings = kwargs
164153
self.__checkers = {
165-
'min_len': StringValidator.min_len_checker,
166-
'max_len': StringValidator.max_len_checker,
167-
'in_range': StringValidator.in_range_checker,
168-
'not_in_range': StringValidator.not_in_range_checker
154+
StringValidator.min_len_checker: [kwargs.get('min_len', None)],
155+
StringValidator.max_len_checker: [kwargs.get('max_len', None)],
156+
StringValidator.in_range_checker: [kwargs.get('in_range', None)],
157+
StringValidator.not_in_range_checker: [
158+
kwargs.get('not_in_range', None)
159+
],
160+
StringValidator.re_checker: [
161+
kwargs.get('re_pattern', None), kwargs.get('re_flags', 0)
162+
]
169163
}
170164
AbstractValidator.__init__(self)
171165

tests/test_string_validator.py

+19
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import unittest
22
from pyvalid.validators import StringValidator
3+
import re
34

45

56
class StringValidatorTestCase(unittest.TestCase):
@@ -32,6 +33,24 @@ def test_not_in_range(self):
3233
self.assertTrue(validator('Java'))
3334
self.assertFalse(validator('CPython'))
3435

36+
def test_re(self):
37+
# Allowed characters are: latin alphabet letters and digits
38+
validator = StringValidator(re_pattern='^[a-zA-Z0-9]+$')
39+
self.assertTrue(validator('pyvalid'))
40+
self.assertTrue(validator('42'))
41+
self.assertFalse(validator('__pyvalid__'))
42+
# Regular expression is broken
43+
validator = StringValidator(re_pattern=':)')
44+
self.assertFalse(validator('pyvalid'))
45+
self.assertFalse(validator(':)'))
46+
# Try to use regular expression with flag
47+
validator = StringValidator(
48+
re_pattern='^pyvalid$', re_flags=re.IGNORECASE
49+
)
50+
self.assertTrue(validator('pyvalid'))
51+
self.assertTrue(validator('PyValid'))
52+
self.assertFalse(validator('42'))
53+
3554
def test_mixed(self):
3655
validator = StringValidator(
3756
min_len=6, max_len=64,

0 commit comments

Comments
 (0)