From fa5faebdb6d4c9b7bd529cb87b78a1022aebb36c Mon Sep 17 00:00:00 2001 From: Dustin Ingram Date: Wed, 20 Sep 2017 16:27:43 -0500 Subject: [PATCH] Add empty_ok kwarg to all validators (#42) * Add failing tests * Move empty_ok kwargs to Validator base class * Update docs * Version bump --- README.rst | 6 +++++ setup.py | 2 +- vladiate/test/test_validators.py | 23 +++++++++-------- vladiate/validators.py | 44 +++++++++++++++++--------------- 4 files changed, 43 insertions(+), 32 deletions(-) diff --git a/README.rst b/README.rst index f36c851..77af67e 100644 --- a/README.rst +++ b/README.rst @@ -236,6 +236,8 @@ Vladiate comes with a few common validators built-in: :``unique_with=[]``: List of field names to make the primary field unique with. + :``empty_ok=False``: + Specify whether a field which is an empty string should be ignored. *class* ``RegexValidator`` @@ -243,6 +245,8 @@ Vladiate comes with a few common validators built-in: :``pattern=r'di^'``: The regex pattern. Fails for all fields by default. + :``empty_ok=False``: + Specify whether a field which is an empty string should be ignored. *class* ``RangeValidator`` @@ -253,6 +257,8 @@ Vladiate comes with a few common validators built-in: The low value of the range. :``high``: The high value of the range. + :``empty_ok=False``: + Specify whether a field which is an empty string should be ignored. *class* ``EmptyValidator`` diff --git a/setup.py b/setup.py index 62b35f8..b5c8c72 100644 --- a/setup.py +++ b/setup.py @@ -4,7 +4,7 @@ from setuptools import setup, find_packages from setuptools.command.test import test as TestCommand -__version__ = '0.0.17' +__version__ = '0.0.18' class PyTest(TestCommand): diff --git a/vladiate/test/test_validators.py b/vladiate/test/test_validators.py index 7b80686..369e9a4 100644 --- a/vladiate/test/test_validators.py +++ b/vladiate/test/test_validators.py @@ -81,12 +81,6 @@ def test_set_validator_works(field_set, field): assert field in validator.valid_set -def test_set_validator_empty_ok(): - validator = SetValidator(['foo'], empty_ok=True) - validator.validate('') - assert '' in validator.valid_set - - @pytest.mark.parametrize('field_set, field', [ ([], 'bar'), (['foo'], 'bar'), @@ -139,10 +133,6 @@ def test_regex_validator_works(pattern, field): RegexValidator(pattern).validate(field) -def test_regex_validator_allows_empty(): - RegexValidator(r'foo.*', empty_ok=True).validate('') - - @pytest.mark.parametrize('pattern, field', [ (r'foo.*', 'afoo'), (r'^$', 'foo'), @@ -213,3 +203,16 @@ def test_base_class_raises(): with pytest.raises(NotImplementedError): validator.validate(stub(), stub()) + + +@pytest.mark.parametrize('validator_class,args', [ + (SetValidator, [['foo']]), + (RegexValidator, [r'foo.*']), + (IntValidator, []), + (FloatValidator, []), + (RangeValidator, [0, 42]), + (UniqueValidator, []), +]) +def test_all_validators_support_empty_ok(validator_class, args): + validator = validator_class(*args, empty_ok=True) + validator.validate('') diff --git a/vladiate/validators.py b/vladiate/validators.py index a425c2c..72dccdb 100644 --- a/vladiate/validators.py +++ b/vladiate/validators.py @@ -6,8 +6,9 @@ class Validator(object): ''' Generic Validator class ''' - def __init__(self): + def __init__(self, empty_ok=False): self.fail_count = 0 + self.empty_ok = empty_ok @property def bad(self): @@ -22,8 +23,8 @@ def validate(self, field, row): class CastValidator(Validator): ''' Validates that a field can be cast to a float ''' - def __init__(self): - super(CastValidator, self).__init__() + def __init__(self, **kwargs): + super(CastValidator, self).__init__(**kwargs) self.invalid_set = set([]) def validate(self, field, row={}): @@ -42,29 +43,27 @@ def bad(self): class FloatValidator(CastValidator): ''' Validates that a field can be cast to a float ''' - def __init__(self, empty_ok=False): - super(FloatValidator, self).__init__() - self.empty_ok = empty_ok + def __init__(self, **kwargs): + super(FloatValidator, self).__init__(**kwargs) self.cast = float class IntValidator(CastValidator): ''' Validates that a field can be cast to an int ''' - def __init__(self, empty_ok=False): - super(IntValidator, self).__init__() - self.empty_ok = empty_ok + def __init__(self, **kwargs): + super(IntValidator, self).__init__(**kwargs) self.cast = int class SetValidator(Validator): ''' Validates that a field is in the given set ''' - def __init__(self, valid_set=[], empty_ok=False): - super(SetValidator, self).__init__() + def __init__(self, valid_set=[], **kwargs): + super(SetValidator, self).__init__(**kwargs) self.valid_set = set(valid_set) self.invalid_set = set([]) - if empty_ok: + if self.empty_ok: self.valid_set.add('') def validate(self, field, row={}): @@ -81,8 +80,8 @@ def bad(self): class UniqueValidator(Validator): ''' Validates that a field is unique within the file ''' - def __init__(self, unique_with=[]): - super(UniqueValidator, self).__init__() + def __init__(self, unique_with=[], **kwargs): + super(UniqueValidator, self).__init__(**kwargs) self.unique_values = set([]) self.duplicates = set([]) self.unique_with = unique_with @@ -119,10 +118,9 @@ def bad(self): class RegexValidator(Validator): ''' Validates that a field matches a given regex ''' - def __init__(self, pattern=r'di^', empty_ok=False): - super(RegexValidator, self).__init__() + def __init__(self, pattern=r'di^', **kwargs): + super(RegexValidator, self).__init__(**kwargs) self.regex = re.compile(pattern) - self.empty_ok = empty_ok self.failures = set([]) def validate(self, field, row={}): @@ -137,13 +135,16 @@ def bad(self): class RangeValidator(Validator): - def __init__(self, low, high): + def __init__(self, low, high, **kwargs): + super(RangeValidator, self).__init__(**kwargs) self.fail_count = 0 self.low = low self.high = high self.outside = set() def validate(self, field, row={}): + if field == '' and self.empty_ok: + return try: value = float(field) if not self.low <= value <= self.high: @@ -164,8 +165,8 @@ def bad(self): class EmptyValidator(Validator): ''' Validates that a field is always empty ''' - def __init__(self): - super(EmptyValidator, self).__init__() + def __init__(self, **kwargs): + super(EmptyValidator, self).__init__(**kwargs) self.nonempty = set([]) def validate(self, field, row={}): @@ -183,7 +184,8 @@ def bad(self): class NotEmptyValidator(Validator): ''' Validates that a field is not empty ''' - def __init__(self): + def __init__(self, **kwargs): + super(NotEmptyValidator, self).__init__(**kwargs) self.fail_count = 0 self.failed = False