[RFC] Enhancement/one of validator (#181)
This commit is contained in:
parent
a328e67169
commit
461e1b2aab
|
@ -52,6 +52,9 @@ Changes:
|
|||
`#128 <https://github.com/python-attrs/attrs/pull/128>`_
|
||||
- ``__attrs_post_init__()`` is now run if validation is disabled.
|
||||
`#130 <https://github.com/python-attrs/attrs/pull/130>`_
|
||||
- Added `attr.validators.in_(options)`` that, given the allowed `options`, checks whether the attribute value is in it.
|
||||
This can be used to check constants, enums, mappings, etc.
|
||||
`#181 <https://github.com/python-attrs/attrs/pull/181>`_
|
||||
- Added ``attr.validators.and_()`` that composes multiple validators into one.
|
||||
`#161 <https://github.com/python-attrs/attrs/issues/161>`_
|
||||
- For convenience, the ``validator`` argument of ``@attr.s`` now can take a ``list`` of validators that are wrapped using ``and_()``.
|
||||
|
|
29
docs/api.rst
29
docs/api.rst
|
@ -270,6 +270,33 @@ Validators
|
|||
...
|
||||
TypeError: ("'x' must be <type 'int'> (got None that is a <type 'NoneType'>).", Attribute(name='x', default=NOTHING, validator=<instance_of validator for type <type 'int'>>, repr=True, cmp=True, hash=None, init=True), <type 'int'>, None)
|
||||
|
||||
.. autofunction:: attr.validators.in_
|
||||
|
||||
For example:
|
||||
|
||||
.. doctest::
|
||||
|
||||
>>> import enum
|
||||
>>> class State(enum.Enum):
|
||||
... ON = "on"
|
||||
... OFF = "off"
|
||||
>>> @attr.s
|
||||
... class C(object):
|
||||
... state = attr.ib(validator=attr.validators.in_(State))
|
||||
... val = attr.ib(validator=attr.validators.in_([1, 2, 3]))
|
||||
>>> C(State.ON, 1)
|
||||
C(state=<State.ON: 'on'>, val=1)
|
||||
>>> C("on", 1)
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ValueError: 'state' must be in <enum 'State'> (got 'on')
|
||||
>>> C(State.ON, 4)
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ValueError: 'val' must be in [1, 2, 3] (got 4)
|
||||
|
||||
.. autofunction:: attr.validators.provides
|
||||
|
||||
.. autofunction:: attr.validators.and_
|
||||
|
||||
For convenience, it's also possible to pass a list to :func:`attr.ib`'s validator argument.
|
||||
|
@ -279,8 +306,6 @@ Validators
|
|||
x = attr.ib(validator=attr.validators.and_(v1, v2, v3))
|
||||
x = attr.ib(validator=[v1, v2, v3])
|
||||
|
||||
.. autofunction:: attr.validators.provides
|
||||
|
||||
.. autofunction:: attr.validators.optional
|
||||
|
||||
For example:
|
||||
|
|
|
@ -48,9 +48,9 @@ def instance_of(type):
|
|||
:param type: The type to check for.
|
||||
:type type: type or tuple of types
|
||||
|
||||
The :exc:`TypeError` is raised with a human readable error message, the
|
||||
attribute (of type :class:`attr.Attribute`), the expected type, and the
|
||||
value it got.
|
||||
:raises TypeError: With a human readable error message, the attribute
|
||||
(of type :class:`attr.Attribute`), the expected type, and the value it
|
||||
got.
|
||||
"""
|
||||
return _InstanceOfValidator(type)
|
||||
|
||||
|
@ -87,9 +87,9 @@ def provides(interface):
|
|||
|
||||
:param zope.interface.Interface interface: The interface to check for.
|
||||
|
||||
The :exc:`TypeError` is raised with a human readable error message, the
|
||||
attribute (of type :class:`attr.Attribute`), the expected interface, and
|
||||
the value it got.
|
||||
:raises TypeError: With a human readable error message, the attribute
|
||||
(of type :class:`attr.Attribute`), the expected interface, and the
|
||||
value it got.
|
||||
"""
|
||||
return _ProvidesValidator(interface)
|
||||
|
||||
|
@ -127,3 +127,39 @@ def optional(validator):
|
|||
if isinstance(validator, list):
|
||||
return _OptionalValidator(_AndValidator(validator))
|
||||
return _OptionalValidator(validator)
|
||||
|
||||
|
||||
@attributes(repr=False, slots=True)
|
||||
class _InValidator(object):
|
||||
options = attr()
|
||||
|
||||
def __call__(self, inst, attr, value):
|
||||
if value not in self.options:
|
||||
raise ValueError(
|
||||
"'{name}' must be in {options!r} (got {value!r})"
|
||||
.format(name=attr.name, options=self.options, value=value)
|
||||
)
|
||||
|
||||
def __repr__(self):
|
||||
return (
|
||||
"<in_ validator with options {options!r}>"
|
||||
.format(options=self.options)
|
||||
)
|
||||
|
||||
|
||||
def in_(options):
|
||||
"""
|
||||
A validator that raises a :exc:`ValueError` if the initializer is called
|
||||
with a value that does not belong in the options provided. The check is
|
||||
performed using ``value in options``.
|
||||
|
||||
:param options: Allowed options.
|
||||
:type options: list, tuple, :class:`enum.Enum`, ...
|
||||
|
||||
:raises ValueError: With a human readable error message, the attribute (of
|
||||
type :class:`attr.Attribute`), the expected options, and the value it
|
||||
got.
|
||||
|
||||
.. versionadded:: 17.1.0
|
||||
"""
|
||||
return _InValidator(options)
|
||||
|
|
|
@ -7,7 +7,7 @@ from __future__ import absolute_import, division, print_function
|
|||
import pytest
|
||||
import zope.interface
|
||||
|
||||
from attr.validators import and_, instance_of, provides, optional
|
||||
from attr.validators import and_, instance_of, provides, optional, in_
|
||||
from attr._compat import TYPE
|
||||
from attr._make import attributes, attr
|
||||
|
||||
|
@ -214,3 +214,37 @@ class TestOptional(object):
|
|||
"<{type} 'int'>> or None>")
|
||||
.format(type=TYPE)
|
||||
) == repr(v)
|
||||
|
||||
|
||||
class TestIn_(object):
|
||||
"""
|
||||
Tests for `in_`.
|
||||
"""
|
||||
def test_success_with_value(self):
|
||||
"""
|
||||
If the value is in our options, nothing happens.
|
||||
"""
|
||||
v = in_([1, 2, 3])
|
||||
a = simple_attr("test")
|
||||
v(1, a, 3)
|
||||
|
||||
def test_fail(self):
|
||||
"""
|
||||
Raise ValueError if the value is outside our options.
|
||||
"""
|
||||
v = in_([1, 2, 3])
|
||||
a = simple_attr("test")
|
||||
with pytest.raises(ValueError) as e:
|
||||
v(None, a, None)
|
||||
assert (
|
||||
"'test' must be in [1, 2, 3] (got None)",
|
||||
) == e.value.args
|
||||
|
||||
def test_repr(self):
|
||||
"""
|
||||
Returned validator has a useful `__repr__`.
|
||||
"""
|
||||
v = in_([3, 4, 5])
|
||||
assert(
|
||||
("<in_ validator with options [3, 4, 5]>")
|
||||
) == repr(v)
|
||||
|
|
Loading…
Reference in New Issue