diff --git a/changelog.d/1265.breaking.md b/changelog.d/1265.breaking.md new file mode 100644 index 000000000..1edd7d10a --- /dev/null +++ b/changelog.d/1265.breaking.md @@ -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. diff --git a/docs/api.rst b/docs/api.rst index 116dd2fb4..ce146171a 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -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=, 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. diff --git a/pyproject.toml b/pyproject.toml index ff7bc227d..c7ec20704 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -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", @@ -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`. @@ -56,7 +55,6 @@ docs = [ "furo", "myst-parser", "sphinx", - "zope.interface", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", diff --git a/src/attr/validators.py b/src/attr/validators.py index 0cc8c118c..952060b7b 100644 --- a/src/attr/validators.py +++ b/src/attr/validators.py @@ -35,7 +35,6 @@ "min_len", "not_", "optional", - "provides", "set_disabled", ] @@ -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"" - - -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 - `_). - - :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() diff --git a/src/attr/validators.pyi b/src/attr/validators.pyi index caca56c68..8b2617adb 100644 --- a/src/attr/validators.pyi +++ b/src/attr/validators.pyi @@ -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] diff --git a/tests/test_validators.py b/tests/test_validators.py index 856da499e..e0ee3131d 100644 --- a/tests/test_validators.py +++ b/tests/test_validators.py @@ -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): @@ -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"") == repr(v) - - @pytest.mark.parametrize( "validator", [