Skip to content

Commit 3b9ee75

Browse files
authored
Merge pull request #30 from aditbiswas1/csv-validator
feat: generic validator for seperator and type filter
2 parents 7afb681 + 2511e7b commit 3b9ee75

File tree

2 files changed

+91
-0
lines changed

2 files changed

+91
-0
lines changed

filters/tests/test_validations.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import unittest
22
from nose2.tools.such import helper
33
from voluptuous import Invalid
4+
from django.core.exceptions import ImproperlyConfigured
45
from filters import validations
56

67
INT = 1
@@ -75,3 +76,27 @@ class CSVofIntegersTestCase(BaseValidationTestCase, unittest.TestCase):
7576
NON_INT_UNICODE, NON_ALNUM_STR, NON_ALNUM_UNICODE, NON_INT_CSV,
7677
INT, LONG
7778
]
79+
80+
class GenericSeparatedValidatorTestCase(unittest.TestCase):
81+
def test_default_separator(self):
82+
validator = validations.GenericSeparatedValidator(int)
83+
self.assertEqual(validator('1,2,3'), [1,2,3])
84+
self.assertRaises(Invalid, validator, 'a,b,c')
85+
self.assertEqual(validator('1', [1]))
86+
87+
def test_custom_separator(self):
88+
validator = validations.GenericSeparatedValidator(int, 'mm')
89+
self.assertEqual(validator('1mm2mm3'), [1,2,3])
90+
self.assertRaises(Invalid, validator, 'ammbmmc')
91+
self.assertEqual(validator('1', [1]))
92+
93+
def test_custom_type(self):
94+
validator = validations.GenericSeparatedValidator(
95+
validations.IntegerLike())
96+
self.assertEqual(validator('1,2,3'), ['1','2','3'])
97+
self.assertRaises(Invalid, validator, 'a,b,c')
98+
self.assertEqual(validator('1', ['1']))
99+
100+
def test_invalid_separator(self):
101+
self.assertRaises(ImproperlyConfigured,
102+
validations.GenericSeparatedValidator, 12)

filters/validations.py

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import numbers
44
from voluptuous import Invalid
55
from django.utils.dateparse import parse_datetime, parse_date
6+
from django.core.exceptions import ImproperlyConfigured
67

78
# Forward compatibility with Python 3.x
89
if sys.version_info.major == 3:
@@ -118,3 +119,68 @@ def fn(value):
118119
'<{0}> is not a valid csv of integers'.format(value)
119120
)
120121
return fn
122+
123+
124+
125+
class GenericSeparatedValidator(object):
126+
'''
127+
Creates list like validator for any voluptuous validation function
128+
and any custom string separator.
129+
Instance of the class should be passed to the schema like this:
130+
>>> CSVofIntegers = GenericSeparatedValidator(int, ',')
131+
>>> Schema({ "field": CSVofIntegers() })
132+
133+
>>> CSVofIntegers = GenericSeparatedValidator(int, ',')
134+
>>> CSVofIntegers('1,2,3')
135+
[1,2,3]
136+
137+
>>> WeirdSeparatedValidation = GenericSeparatedValidator(int, '^^')
138+
>>> WeirdSeparatedValidation('1^^2^^3')
139+
[1, 2, 3]
140+
141+
>>> CSVofIntegerLike = GenericSeparatedValidator(IntegerLike(), ',')
142+
>>> CSVofIntegerLike('a,b,c')
143+
Traceback (most recent call last):
144+
...
145+
voluptuous.error.Invalid: <a,b,c> is not valid set of <IntegerLike>,\
146+
Invalid input <a>; expected an integer
147+
'''
148+
149+
def __init__(self, validation_function, separator=',', msg=None):
150+
if not isinstance(separator, basestring):
151+
raise ImproperlyConfigured(
152+
'GenericSeparatedValidator separator \
153+
must be of type basestring'
154+
)
155+
self.separator = separator
156+
self.validation_function = validation_function
157+
self.msg = msg
158+
if sys.version_info.major == 3:
159+
self.value_type = validation_function.__qualname__.split('.')[0]
160+
else:
161+
self.value_type = validation_function.__name__
162+
163+
def __call__(self, value):
164+
'''
165+
Checks whether a value is list of given validation_function.
166+
Returns list of validated values or just one valid value in
167+
list if there is only one element in given CSV string.
168+
'''
169+
try:
170+
if isinstance(value, basestring):
171+
if self.separator in value:
172+
seperated_string_values =[item.strip() for item
173+
in value.split(self.separator)]
174+
values = [self.validation_function(item) for item
175+
in seperated_string_values]
176+
else:
177+
values = [self.validation_function(value)]
178+
return values
179+
else:
180+
raise ValueError
181+
except (Invalid, ValueError) as e:
182+
raise Invalid(self.msg or
183+
('<{0}> is not valid set of <{1}>, {2}'.format(
184+
value,
185+
self.value_type,
186+
e)))

0 commit comments

Comments
 (0)