From 4710e7b1772eaeaa8e1bc1138a4d36e6cf69ef87 Mon Sep 17 00:00:00 2001 From: meejah Date: Wed, 2 Aug 2023 17:07:09 -0600 Subject: [PATCH 1/4] provide our own provides() validator --- integration/grid.py | 28 ++++++++++------ src/allmydata/storage_client.py | 3 +- src/allmydata/util/attrs_provides.py | 50 ++++++++++++++++++++++++++++ 3 files changed, 69 insertions(+), 12 deletions(-) create mode 100644 src/allmydata/util/attrs_provides.py diff --git a/integration/grid.py b/integration/grid.py index 524da730f..03c3bb6e2 100644 --- a/integration/grid.py +++ b/integration/grid.py @@ -31,9 +31,15 @@ from twisted.internet.defer import ( from twisted.internet.task import ( deferLater, ) -from twisted.internet.protocol import ProcessProtocol # see ticket 4056 +from twisted.internet.interfaces import ( + IProcessTransport, + IProcessProtocol, +) from twisted.internet.error import ProcessTerminated +from allmydata.util.attrs_provides import ( + provides, +) from allmydata.node import read_config from .util import ( _CollectOutputProtocol, @@ -68,15 +74,15 @@ class FlogGatherer(object): Flog Gatherer process. """ - # it would be best to use attr.validators.provides() here with the - # corresponding Twisted interface (IProcessTransport, - # IProcessProtocol) but that is deprecated; please replace with - # our own "provides" as part of + # it would be best to use attr.validators.provides() here but that + # is deprecated; please replace with our own "provides" as part of # https://tahoe-lafs.org/trac/tahoe-lafs/ticket/4056#ticket - # insisting on a subclass is narrower than necessary - process = attr.ib() + # for now, insisting on a subclass which is narrower than necessary + process = attr.ib( + validator=provides(IProcessTransport) + ) protocol = attr.ib( - validator=attr.validators.instance_of(ProcessProtocol) + validator=provides(IProcessProtocol) ) furl = attr.ib() @@ -156,7 +162,7 @@ class StorageServer(object): validator=attr.validators.instance_of(TahoeProcess) ) protocol = attr.ib( - validator=attr.validators.instance_of(ProcessProtocol) + validator=provides(IProcessProtocol) ) @inlineCallbacks @@ -208,7 +214,7 @@ class Client(object): validator=attr.validators.instance_of(TahoeProcess) ) protocol = attr.ib( - validator=attr.validators.instance_of(ProcessProtocol) + validator=provides(IProcessProtocol) ) request = attr.ib() # original request, for addfinalizer() @@ -336,7 +342,7 @@ class Introducer(object): validator=attr.validators.instance_of(TahoeProcess) ) protocol = attr.ib( - validator=attr.validators.instance_of(ProcessProtocol) + validator=provides(IProcessProtocol) ) furl = attr.ib() diff --git a/src/allmydata/storage_client.py b/src/allmydata/storage_client.py index d205edf08..8de3a9ca9 100644 --- a/src/allmydata/storage_client.py +++ b/src/allmydata/storage_client.py @@ -88,6 +88,7 @@ from allmydata.util.rrefutil import add_version_to_remote_reference from allmydata.util.hashutil import permute_server_hash from allmydata.util.dictutil import BytesKeyDict, UnicodeKeyDict from allmydata.util.deferredutil import async_to_deferred, race +from allmydata.util.attr_provides import provides from allmydata.storage.http_client import ( StorageClient, StorageClientImmutables, StorageClientGeneral, ClientException as HTTPClientException, StorageClientMutables, @@ -659,7 +660,7 @@ class _FoolscapStorage(object): permutation_seed = attr.ib() tubid = attr.ib() - storage_server = attr.ib(validator=attr.validators.provides(IStorageServer)) + storage_server = attr.ib(validator=provides(IStorageServer)) _furl = attr.ib() _short_description = attr.ib() diff --git a/src/allmydata/util/attrs_provides.py b/src/allmydata/util/attrs_provides.py new file mode 100644 index 000000000..4282c3d38 --- /dev/null +++ b/src/allmydata/util/attrs_provides.py @@ -0,0 +1,50 @@ +""" +Utilities related to attrs + +Handling for zope.interface is deprecated in attrs so we copy the +relevant support method here since we depend on zope.interface anyway +""" + +from attr._make import attrs, attrib + + +@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): + raise TypeError( + "'{name}' must provide {interface!r} which {value!r} " + "doesn't.".format( + name=attr.name, interface=self.interface, value=value + ), + attr, + self.interface, + value, + ) + + def __repr__(self): + return "".format( + interface=self.interface + ) + + +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. + """ + return _ProvidesValidator(interface) From cbf3eebc78aeb6a907a6e8a1d3dab84a4e5402ad Mon Sep 17 00:00:00 2001 From: meejah Date: Wed, 2 Aug 2023 17:08:28 -0600 Subject: [PATCH 2/4] news --- newsfragments/4056.bugfix | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 newsfragments/4056.bugfix diff --git a/newsfragments/4056.bugfix b/newsfragments/4056.bugfix new file mode 100644 index 000000000..1f94de0da --- /dev/null +++ b/newsfragments/4056.bugfix @@ -0,0 +1,3 @@ +Provide our own copy of attrs' "provides()" validor + +This validator is deprecated and slated for removal; that project's suggestion is to copy the code to our project. From c7f6b6484d033d0b184bb46afec2e134cc389346 Mon Sep 17 00:00:00 2001 From: meejah Date: Wed, 9 Aug 2023 15:15:20 -0600 Subject: [PATCH 3/4] spelling --- newsfragments/4056.bugfix | 2 +- src/allmydata/storage_client.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/newsfragments/4056.bugfix b/newsfragments/4056.bugfix index 1f94de0da..7e637b48c 100644 --- a/newsfragments/4056.bugfix +++ b/newsfragments/4056.bugfix @@ -1,3 +1,3 @@ -Provide our own copy of attrs' "provides()" validor +Provide our own copy of attrs' "provides()" validator This validator is deprecated and slated for removal; that project's suggestion is to copy the code to our project. diff --git a/src/allmydata/storage_client.py b/src/allmydata/storage_client.py index 8de3a9ca9..c59db0817 100644 --- a/src/allmydata/storage_client.py +++ b/src/allmydata/storage_client.py @@ -88,7 +88,7 @@ from allmydata.util.rrefutil import add_version_to_remote_reference from allmydata.util.hashutil import permute_server_hash from allmydata.util.dictutil import BytesKeyDict, UnicodeKeyDict from allmydata.util.deferredutil import async_to_deferred, race -from allmydata.util.attr_provides import provides +from allmydata.util.attrs_provides import provides from allmydata.storage.http_client import ( StorageClient, StorageClientImmutables, StorageClientGeneral, ClientException as HTTPClientException, StorageClientMutables, From 9758569cffb9f62a5597a330d653cde7e8357169 Mon Sep 17 00:00:00 2001 From: meejah Date: Wed, 9 Aug 2023 15:16:07 -0600 Subject: [PATCH 4/4] obsolete comment --- integration/grid.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/integration/grid.py b/integration/grid.py index 03c3bb6e2..b97c22bf7 100644 --- a/integration/grid.py +++ b/integration/grid.py @@ -73,11 +73,6 @@ class FlogGatherer(object): """ Flog Gatherer process. """ - - # it would be best to use attr.validators.provides() here but that - # is deprecated; please replace with our own "provides" as part of - # https://tahoe-lafs.org/trac/tahoe-lafs/ticket/4056#ticket - # for now, insisting on a subclass which is narrower than necessary process = attr.ib( validator=provides(IProcessTransport) )