I am trying to use type hinting to specify the API to follow when implementing a connector class (to a broker, in this case).
I want to specify that such class(es) should be context manager(s)
How do I do that?
Let me reword it more clearly: how can I define the Broker
class so that it indicates that its concrete implementations, e.g. the Rabbit
class, must be context managers?
Is there a practical way? Do I have to specify __enter__
and __exit__
and just inherit from Protocol
?
Is it enough to inherit from ContextManager
?
By the way, should I use @runtime
or @runtime_checkable
? (My VScode linter seems to have problems finding those in typing
. I am using python 3 7.5)
I know how to do it with ABC's, but I would like to learn how to do it with protocol definitions (which I have used fine already, but they weren't context managers).
I cannot make out how to use the ContextManager
type. So far I haven't been able to find good examples from the official docs.
At present I came up with
from typing import Protocol, ContextManager, runtime, Dict, List
@runtime
class Broker(ContextManager):
"""
Basic interface to a broker.
It must be a context manager
"""
def publish(self, data: str) -> None:
"""
Publish data to the topic/queue
"""
...
def subscribe(self) -> None:
"""
Subscribe to the topic/queue passed to constructor
"""
...
def read(self) -> str:
"""
Read data from the topic/queue
"""
...
and the implementation is
@implements(Broker)
class Rabbit:
def __init__(self,
url: str,
queue: str = 'default'):
"""
url: where to connect, i.e. where the broker is
queue: the topic queue, one only
"""
# self.url = url
self.queue = queue
self.params = pika.URLParameters(url)
self.params.socket_timeout = 5
def __enter__(self):
self.connection = pika.BlockingConnection(self.params) # Connect to CloudAMQP
self.channel = self.connection.channel() # start a channel
self.channel.queue_declare(queue=self.queue) # Declare a queue
return self
def __exit__(self, exc_type, exc_value, traceback):
self.connection.close()
def publish(self, data: str):
pass # TBD
def subscribe(self):
pass # TBD
def read(self):
pass # TBD
Note: the implements
decorator works fine (it comes form a previous project), it checks the class is a subclass of the given protocol