We have a large codebase which uses threads. Many - but not all - of these
threads implement a method named 'stop()' which sets a flag, triggers an
event, closes a connection, or what-have-you in order to command the thread
in question to terminate.
I was writing a thread manager, intended to automatically terminate threads
in an organized way at shutdown. It could accept any thread which
implemented a 'stop()' method, so how could I type-hint it correctly?
'Aha!' said I, 'This is what those newfangled Protocol things are for! I
shall use one of them!' (We only recently updated from 3.7 to 3.11, so
quite a lot of features are still 'newfangled' to me.)
However, I then encountered an issue: I could define a Protocol that
specified the 'stop()' method easily enough, but if I annotated the manager
as taking that, it would accept *any* class which implemented a method
named 'stop()', which was not correct; the manager should only accept
*threads* which implement such a method. I couldn't add 'threading.Thread'
as a parent of the protocol; protocols aren't allowed to inherit from
normal classes. And there's no syntax for marking an argument as needing to
be *both* a given type and a given protocol.
My proposal is this: Currently, a Protocol is forbidden from inheriting
from a normal class, on the basis that it would break transitivity of
subtyping.
Instead, allow Protocols to inherit normal classes, with the rule that a
class is only considered to implement that protocol if it also inherits the
same normal classes. E.g.:
```python
import typing as _tp
class Base:
...
class MyProtocol(Base, _tp.Protocol):
def proto_method(self, string: str) -> bool:
raise NotImplementedError()
class Foo:
def proto_method(self, string: str) -> bool:
...
class Bar(Base):
def proto_method(self, string: str) -> str:
...
class Baz(Base):
def proto_method(self, string: str) -> bool:
...
class Zap(MyProtocol):
def proto_method(self, string: str) -> bool:
...
def my_func(proto: MyProtocol):
...
my_func(Foo()) # Invalid; `Foo` does not inherit `Base` and therefore does
not implement `MyProtocol` despite having the necessary method
my_func(Bar()) # Invalid; `Bar` does not implement the method with the
correct signature for `MyProtocol`
my_func(Baz()) # Valid; `baz` inherits `Base` explicitly
my_func(Zap()) # Valid; `Zap` inherits `Base` indirectly, via inheriting
`MyProtocol` explicitly
```
--
So many books, so little time... - Anon.
You haven't *lived*
'Till you've heard the floor ring
To the whoop and the call
Of 'Balance and swing!'
_______________________________________________
Python-ideas mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at
https://mail.python.org/archives/list/[email protected]/message/7YCLH2CUZAZEXSZXUKP2LQLG7P4RHMIV/
Code of Conduct: http://python.org/psf/codeofconduct/