From bfd2592ded170a5946c7e34ba36b7d3290f77231 Mon Sep 17 00:00:00 2001 From: Glyph Date: Sun, 11 Aug 2024 13:56:45 -0700 Subject: [PATCH] copy and paste code from https://gist.github.com/hynek/9eaaaeb659808f3519870dfa16d2b6b2 as advertised in https://github.com/python-attrs/attrs/pull/1265 --- requirements/tox-pin-base.txt | 2 +- src/klein/_attrs_zope.py | 47 +++++++++++++++++++++++++++++++++++ src/klein/_request.py | 3 ++- src/klein/_request_compat.py | 2 +- src/klein/_response.py | 3 ++- src/klein/_tubes.py | 4 ++- 6 files changed, 56 insertions(+), 5 deletions(-) create mode 100644 src/klein/_attrs_zope.py diff --git a/requirements/tox-pin-base.txt b/requirements/tox-pin-base.txt index 2440a65cc..b05d5d17c 100644 --- a/requirements/tox-pin-base.txt +++ b/requirements/tox-pin-base.txt @@ -1,4 +1,4 @@ -attrs==23.2.0 +attrs==24.2.0 Automat==22.10.0 characteristic==14.3.0 constantly==15.1.0 diff --git a/src/klein/_attrs_zope.py b/src/klein/_attrs_zope.py new file mode 100644 index 000000000..4eb9a0566 --- /dev/null +++ b/src/klein/_attrs_zope.py @@ -0,0 +1,47 @@ +from __future__ import annotations + +import attrs +from zope.interface import Interface + + +@attrs.define(repr=False) +class _ProvidesValidator: + interface: type[Interface] = attrs.field() + + def __call__( + self, inst: object, attr: attrs.Attribute, value: object + ) -> None: + """ + 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} " + f"which {value!r} doesn't." + ) + raise TypeError( + msg, + attr, + self.interface, + value, + ) + + def __repr__(self) -> str: + return f"" + + +def provides(interface: type[Interface]) -> _ProvidesValidator: + """ + 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. + """ + return _ProvidesValidator(interface) diff --git a/src/klein/_request.py b/src/klein/_request.py index 1a511902a..6ccb86f78 100644 --- a/src/klein/_request.py +++ b/src/klein/_request.py @@ -8,11 +8,12 @@ from typing import Union from attr import Factory, attrib, attrs -from attr.validators import instance_of, provides +from attr.validators import instance_of from hyperlink import DecodedURL from tubes.itube import IFount from zope.interface import implementer +from ._attrs_zope import provides from ._imessage import IHTTPHeaders, IHTTPRequest from ._message import MessageState, bodyAsBytes, bodyAsFount, validateBody diff --git a/src/klein/_request_compat.py b/src/klein/_request_compat.py index 5de3e35fa..6434cafbc 100644 --- a/src/klein/_request_compat.py +++ b/src/klein/_request_compat.py @@ -9,7 +9,6 @@ from typing import cast from attr import Factory, attrib, attrs -from attr.validators import provides from hyperlink import DecodedURL from tubes.itube import IFount from zope.interface import implementer @@ -17,6 +16,7 @@ from twisted.python.compat import nativeString from twisted.web.iweb import IRequest +from ._attrs_zope import provides from ._headers import IHTTPHeaders from ._headers_compat import HTTPHeadersWrappingHeaders from ._message import FountAlreadyAccessedError, MessageState diff --git a/src/klein/_response.py b/src/klein/_response.py index 353f50285..c91ddd059 100644 --- a/src/klein/_response.py +++ b/src/klein/_response.py @@ -8,10 +8,11 @@ from typing import Union from attr import Factory, attrib, attrs -from attr.validators import instance_of, provides +from attr.validators import instance_of from tubes.itube import IFount from zope.interface import implementer +from ._attrs_zope import provides from ._imessage import IHTTPHeaders, IHTTPResponse from ._message import MessageState, bodyAsBytes, bodyAsFount, validateBody diff --git a/src/klein/_tubes.py b/src/klein/_tubes.py index 59702283a..34b7756a6 100644 --- a/src/klein/_tubes.py +++ b/src/klein/_tubes.py @@ -8,7 +8,7 @@ from typing import Any, BinaryIO from attr import attrib, attrs -from attr.validators import instance_of, optional, provides +from attr.validators import instance_of, optional from tubes.itube import IDrain, IFount, ISegment from tubes.kit import Pauser, beginFlowingTo from tubes.undefer import fountToDeferred @@ -16,6 +16,8 @@ from twisted.python.failure import Failure +from ._attrs_zope import provides + __all__ = ()