Skip to content

Commit

Permalink
Remove attrs.validators.provides (#1265)
Browse files Browse the repository at this point in the history
* Remove attrs.validators.provides

* Add news fragment
  • Loading branch information
hynek committed Mar 16, 2024
1 parent 1e21449 commit 8460190
Show file tree
Hide file tree
Showing 6 changed files with 3 additions and 142 deletions.
2 changes: 2 additions & 0 deletions changelog.d/1265.breaking.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
`attrs.validators.provides()` has been removed.
The removed code is available as a [gist](https://gist.github.com/hynek/9eaaaeb659808f3519870dfa16d2b6b2) for convenient copy and pasting.
2 changes: 0 additions & 2 deletions docs/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -451,8 +451,6 @@ All objects from ``attrs.validators`` are also available from ``attr.validators`
...
ValueError: 'val' must be in [1, 2, 3] (got 4), Attribute(name='val', default=NOTHING, validator=<in_ validator with options [1, 2, 3]>, repr=True, eq=True, eq_key=None, order=True, order_key=None, hash=None, init=True, metadata=mappingproxy({}), type=None, converter=None, kw_only=False, inherited=False, on_setattr=None), [1, 2, 3], 4)

.. autofunction:: attrs.validators.provides

.. autofunction:: attrs.validators.and_

For convenience, it's also possible to pass a list to `attrs.field`'s validator argument.
Expand Down
4 changes: 1 addition & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ tests-mypy = [
# pin.
'mypy>=1.6; platform_python_implementation == "CPython" and python_version >= "3.8"',
]
tests-no-zope = [
tests = [
# For regression test to ensure cloudpickle compat doesn't break.
'cloudpickle; platform_python_implementation == "CPython"',
"hypothesis",
Expand All @@ -46,7 +46,6 @@ tests-no-zope = [
"pytest-xdist[psutil]",
"attrs[tests-mypy]",
]
tests = ["attrs[tests-no-zope]", "zope.interface"]
cov = [
"attrs[tests]",
# Ensure coverage is new enough for `source_pkgs`.
Expand All @@ -56,7 +55,6 @@ docs = [
"furo",
"myst-parser",
"sphinx",
"zope.interface",
"sphinx-notfound-page",
"sphinxcontrib-towncrier",
"towncrier",
Expand Down
49 changes: 0 additions & 49 deletions src/attr/validators.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@
"min_len",
"not_",
"optional",
"provides",
"set_disabled",
]

Expand Down Expand Up @@ -190,54 +189,6 @@ def matches_re(regex, flags=0, func=None):
return _MatchesReValidator(pattern, match_func)


@attrs(repr=False, slots=True, hash=True)
class _ProvidesValidator:
interface = attrib()

def __call__(self, inst, attr, value):
"""
We use a callable class to be able to change the ``__repr__``.
"""
if not self.interface.providedBy(value):
msg = f"'{attr.name}' must provide {self.interface!r} which {value!r} doesn't."
raise TypeError(
msg,
attr,
self.interface,
value,
)

def __repr__(self):
return f"<provides validator for interface {self.interface!r}>"


def provides(interface):
"""
A validator that raises a `TypeError` if the initializer is called
with an object that does not provide the requested *interface* (checks are
performed using ``interface.providedBy(value)`` (see `zope.interface
<https://zopeinterface.readthedocs.io/en/latest/>`_).
:param interface: The interface to check for.
:type interface: ``zope.interface.Interface``
:raises TypeError: With a human readable error message, the attribute
(of type `attrs.Attribute`), the expected interface, and the
value it got.
.. deprecated:: 23.1.0
"""
import warnings

warnings.warn(
"attrs's zope-interface support is deprecated and will be removed in, "
"or after, April 2024.",
DeprecationWarning,
stacklevel=2,
)
return _ProvidesValidator(interface)


@attrs(repr=False, slots=True, hash=True)
class _OptionalValidator:
validator = attrib()
Expand Down
1 change: 0 additions & 1 deletion src/attr/validators.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ def instance_of(
) -> _ValidatorType[_T1 | _T2 | _T3]: ...
@overload
def instance_of(type: tuple[type, ...]) -> _ValidatorType[Any]: ...
def provides(interface: Any) -> _ValidatorType[Any]: ...
def optional(
validator: (
_ValidatorType[_T]
Expand Down
87 changes: 0 additions & 87 deletions tests/test_validators.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,25 +30,11 @@
min_len,
not_,
optional,
provides,
)

from .utils import simple_attr


@pytest.fixture(scope="module")
def zope_interface():
"""Provides ``zope.interface`` if available, skipping the test if not."""
try:
import zope.interface
except ImportError:
raise pytest.skip(
"zope-related tests skipped when zope.interface is not installed"
)

return zope.interface


class TestDisableValidators:
@pytest.fixture(autouse=True)
def _reset_default(self):
Expand Down Expand Up @@ -314,79 +300,6 @@ class C:
assert C.__attrs_attrs__[0].validator == C.__attrs_attrs__[1].validator


@pytest.fixture(scope="module")
def ifoo(zope_interface):
"""Provides a test ``zope.interface.Interface`` in ``zope`` tests."""

class IFoo(zope_interface.Interface):
"""
An interface.
"""

def f():
"""
A function called f.
"""

return IFoo


class TestProvides:
"""
Tests for `provides`.
"""

def test_in_all(self):
"""
Verify that this validator is in ``__all__``.
"""
assert provides.__name__ in validator_module.__all__

def test_success(self, zope_interface, ifoo):
"""
Nothing happens if value provides requested interface.
"""

@zope_interface.implementer(ifoo)
class C:
def f(self):
pass

with pytest.deprecated_call():
v = provides(ifoo)

v(None, simple_attr("x"), C())

def test_fail(self, ifoo):
"""
Raises `TypeError` if interfaces isn't provided by value.
"""
value = object()
a = simple_attr("x")

with pytest.deprecated_call():
v = provides(ifoo)

with pytest.raises(TypeError) as e:
v(None, a, value)

assert (
f"'x' must provide {ifoo!r} which {value!r} doesn't.",
a,
ifoo,
value,
) == e.value.args

def test_repr(self, ifoo):
"""
Returned validator has a useful `__repr__`.
"""
with pytest.deprecated_call():
v = provides(ifoo)

assert (f"<provides validator for interface {ifoo!r}>") == repr(v)


@pytest.mark.parametrize(
"validator",
[
Expand Down

0 comments on commit 8460190

Please sign in to comment.