1""" 2Tests for `attr.validators`. 3""" 4 5from __future__ import absolute_import, division, print_function 6 7import pytest 8import zope.interface 9 10import attr 11 12from attr import has 13from attr import validators as validator_module 14from attr._compat import TYPE 15from attr.validators import and_, in_, instance_of, optional, provides 16 17from .utils import simple_attr 18 19 20class TestInstanceOf(object): 21 """ 22 Tests for `instance_of`. 23 """ 24 def test_success(self): 25 """ 26 Nothing happens if types match. 27 """ 28 v = instance_of(int) 29 v(None, simple_attr("test"), 42) 30 31 def test_subclass(self): 32 """ 33 Subclasses are accepted too. 34 """ 35 v = instance_of(int) 36 # yep, bools are a subclass of int :( 37 v(None, simple_attr("test"), True) 38 39 def test_fail(self): 40 """ 41 Raises `TypeError` on wrong types. 42 """ 43 v = instance_of(int) 44 a = simple_attr("test") 45 with pytest.raises(TypeError) as e: 46 v(None, a, "42") 47 assert ( 48 "'test' must be <{type} 'int'> (got '42' that is a <{type} " 49 "'str'>).".format(type=TYPE), 50 a, int, "42", 51 52 ) == e.value.args 53 54 def test_repr(self): 55 """ 56 Returned validator has a useful `__repr__`. 57 """ 58 v = instance_of(int) 59 assert ( 60 "<instance_of validator for type <{type} 'int'>>" 61 .format(type=TYPE) 62 ) == repr(v) 63 64 65def always_pass(_, __, ___): 66 """ 67 Toy validator that always passses. 68 """ 69 70 71def always_fail(_, __, ___): 72 """ 73 Toy validator that always fails. 74 """ 75 0/0 76 77 78class TestAnd(object): 79 def test_success(self): 80 """ 81 Succeeds if all wrapped validators succeed. 82 """ 83 v = and_(instance_of(int), always_pass) 84 85 v(None, simple_attr("test"), 42) 86 87 def test_fail(self): 88 """ 89 Fails if any wrapped validator fails. 90 """ 91 v = and_(instance_of(int), always_fail) 92 93 with pytest.raises(ZeroDivisionError): 94 v(None, simple_attr("test"), 42) 95 96 def test_sugar(self): 97 """ 98 `and_(v1, v2, v3)` and `[v1, v2, v3]` are equivalent. 99 """ 100 @attr.s 101 class C(object): 102 a1 = attr.ib("a1", validator=and_( 103 instance_of(int), 104 )) 105 a2 = attr.ib("a2", validator=[ 106 instance_of(int), 107 ]) 108 109 assert C.__attrs_attrs__[0].validator == C.__attrs_attrs__[1].validator 110 111 112class IFoo(zope.interface.Interface): 113 """ 114 An interface. 115 """ 116 def f(): 117 """ 118 A function called f. 119 """ 120 121 122class TestProvides(object): 123 """ 124 Tests for `provides`. 125 """ 126 def test_success(self): 127 """ 128 Nothing happens if value provides requested interface. 129 """ 130 @zope.interface.implementer(IFoo) 131 class C(object): 132 def f(self): 133 pass 134 135 v = provides(IFoo) 136 v(None, simple_attr("x"), C()) 137 138 def test_fail(self): 139 """ 140 Raises `TypeError` if interfaces isn't provided by value. 141 """ 142 value = object() 143 a = simple_attr("x") 144 145 v = provides(IFoo) 146 with pytest.raises(TypeError) as e: 147 v(None, a, value) 148 assert ( 149 "'x' must provide {interface!r} which {value!r} doesn't." 150 .format(interface=IFoo, value=value), 151 a, IFoo, value, 152 ) == e.value.args 153 154 def test_repr(self): 155 """ 156 Returned validator has a useful `__repr__`. 157 """ 158 v = provides(IFoo) 159 assert ( 160 "<provides validator for interface {interface!r}>" 161 .format(interface=IFoo) 162 ) == repr(v) 163 164 165@pytest.mark.parametrize("validator", [ 166 instance_of(int), 167 [always_pass, instance_of(int)], 168]) 169class TestOptional(object): 170 """ 171 Tests for `optional`. 172 """ 173 def test_success(self, validator): 174 """ 175 Nothing happens if validator succeeds. 176 """ 177 v = optional(validator) 178 v(None, simple_attr("test"), 42) 179 180 def test_success_with_none(self, validator): 181 """ 182 Nothing happens if None. 183 """ 184 v = optional(validator) 185 v(None, simple_attr("test"), None) 186 187 def test_fail(self, validator): 188 """ 189 Raises `TypeError` on wrong types. 190 """ 191 v = optional(validator) 192 a = simple_attr("test") 193 with pytest.raises(TypeError) as e: 194 v(None, a, "42") 195 assert ( 196 "'test' must be <{type} 'int'> (got '42' that is a <{type} " 197 "'str'>).".format(type=TYPE), 198 a, int, "42", 199 200 ) == e.value.args 201 202 def test_repr(self, validator): 203 """ 204 Returned validator has a useful `__repr__`. 205 """ 206 v = optional(validator) 207 208 if isinstance(validator, list): 209 assert ( 210 ("<optional validator for _AndValidator(_validators=[{func}, " 211 "<instance_of validator for type <{type} 'int'>>]) or None>") 212 .format(func=repr(always_pass), type=TYPE) 213 ) == repr(v) 214 else: 215 assert ( 216 ("<optional validator for <instance_of validator for type " 217 "<{type} 'int'>> or None>") 218 .format(type=TYPE) 219 ) == repr(v) 220 221 222class TestIn_(object): 223 """ 224 Tests for `in_`. 225 """ 226 def test_success_with_value(self): 227 """ 228 If the value is in our options, nothing happens. 229 """ 230 v = in_([1, 2, 3]) 231 a = simple_attr("test") 232 v(1, a, 3) 233 234 def test_fail(self): 235 """ 236 Raise ValueError if the value is outside our options. 237 """ 238 v = in_([1, 2, 3]) 239 a = simple_attr("test") 240 with pytest.raises(ValueError) as e: 241 v(None, a, None) 242 assert ( 243 "'test' must be in [1, 2, 3] (got None)", 244 ) == e.value.args 245 246 def test_repr(self): 247 """ 248 Returned validator has a useful `__repr__`. 249 """ 250 v = in_([3, 4, 5]) 251 assert( 252 ("<in_ validator with options [3, 4, 5]>") 253 ) == repr(v) 254 255 256def test_hashability(): 257 """ 258 Validator classes are hashable. 259 """ 260 for obj_name in dir(validator_module): 261 obj = getattr(validator_module, obj_name) 262 if not has(obj): 263 continue 264 hash_func = getattr(obj, '__hash__', None) 265 assert hash_func is not None 266 assert hash_func is not object.__hash__ 267