busline 0.3.1__py3-none-any.whl → 0.5.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- busline/client/client.py +27 -0
- busline/{eventbus_client → client}/eventbus_connector.py +5 -10
- busline/client/multiclient.py +41 -0
- busline/client/publisher/publisher.py +60 -0
- busline/client/pubsub_client.py +151 -0
- busline/client/subscriber/event_handler/closure_event_handler.py +18 -0
- busline/client/subscriber/event_handler/event_handler.py +15 -0
- busline/client/subscriber/event_handler/multi_handler.py +30 -0
- busline/client/subscriber/subscriber.py +109 -0
- busline/client/subscriber/topic_subscriber.py +79 -0
- busline/event/event.py +43 -21
- busline/event/registry.py +67 -0
- busline/event/test.py +47 -0
- busline/exceptions.py +8 -0
- busline/local/__init__.py +3 -0
- busline/local/eventbus/__init__.py +0 -0
- busline/local/eventbus/async_local_eventbus.py +26 -0
- busline/local/eventbus/eventbus.py +86 -0
- busline/local/eventbus/local_eventbus.py +23 -0
- busline/local/local_pubsub_client.py +54 -0
- busline/local/publisher/__init__.py +0 -0
- busline/local/publisher/local_publisher.py +38 -0
- busline/local/subscriber/__init__.py +0 -0
- busline/local/subscriber/local_subscriber.py +43 -0
- busline/local/test.py +156 -0
- busline-0.5.1.dist-info/METADATA +215 -0
- busline-0.5.1.dist-info/RECORD +36 -0
- {busline-0.3.1.dist-info → busline-0.5.1.dist-info}/WHEEL +1 -1
- busline/event/event_content.py +0 -18
- busline/event/event_metadata.py +0 -26
- busline/eventbus/async_local_eventbus.py +0 -32
- busline/eventbus/eventbus.py +0 -112
- busline/eventbus/exceptions.py +0 -2
- busline/eventbus/queued_local_eventbus.py +0 -50
- busline/eventbus/topic.py +0 -35
- busline/eventbus_client/eventbus_client.py +0 -99
- busline/eventbus_client/exceptions.py +0 -4
- busline/eventbus_client/local_eventbus_client.py +0 -25
- busline/eventbus_client/publisher/local_eventbus_publisher.py +0 -35
- busline/eventbus_client/publisher/publisher.py +0 -59
- busline/eventbus_client/subscriber/closure_event_listener.py +0 -19
- busline/eventbus_client/subscriber/event_listener.py +0 -15
- busline/eventbus_client/subscriber/local_eventbus_closure_subscriber.py +0 -17
- busline/eventbus_client/subscriber/local_eventbus_subscriber.py +0 -40
- busline/eventbus_client/subscriber/subscriber.py +0 -93
- busline-0.3.1.dist-info/METADATA +0 -111
- busline-0.3.1.dist-info/RECORD +0 -30
- /busline/{eventbus → client}/__init__.py +0 -0
- /busline/{eventbus_client → client/publisher}/__init__.py +0 -0
- /busline/{eventbus_client/publisher → client/subscriber}/__init__.py +0 -0
- /busline/{eventbus_client/subscriber → client/subscriber/event_handler}/__init__.py +0 -0
- {busline-0.3.1.dist-info → busline-0.5.1.dist-info/licenses}/LICENSE +0 -0
- {busline-0.3.1.dist-info → busline-0.5.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,67 @@
|
|
1
|
+
from dataclasses import dataclass, field
|
2
|
+
from typing import Dict, Type
|
3
|
+
|
4
|
+
from busline.event.event import Event
|
5
|
+
|
6
|
+
class _Singleton(type):
|
7
|
+
_instances = {}
|
8
|
+
|
9
|
+
def __call__(cls, *args, **kwargs):
|
10
|
+
if cls not in cls._instances:
|
11
|
+
cls._instances[cls] = super().__call__(*args, **kwargs)
|
12
|
+
return cls._instances[cls]
|
13
|
+
|
14
|
+
|
15
|
+
@dataclass
|
16
|
+
class EventRegistry(metaclass=_Singleton):
|
17
|
+
"""
|
18
|
+
Registry to manage different event types
|
19
|
+
|
20
|
+
Author: Nicola Ricciardi
|
21
|
+
"""
|
22
|
+
|
23
|
+
associations: Dict[str, Type[Event]] = field(default_factory=dict)
|
24
|
+
|
25
|
+
def unregister(self, event_type: str):
|
26
|
+
"""
|
27
|
+
Remove an event type association
|
28
|
+
"""
|
29
|
+
|
30
|
+
self.associations.pop(event_type)
|
31
|
+
|
32
|
+
def register(self, event_type: str, event_class: Type[Event]):
|
33
|
+
"""
|
34
|
+
Add a new association between an event type and an event class
|
35
|
+
"""
|
36
|
+
|
37
|
+
self.associations[event_type] = event_class
|
38
|
+
|
39
|
+
def retrive_class(self, event) -> Type[Event]:
|
40
|
+
"""
|
41
|
+
Retrive event class of event input based on saved associations and given event type
|
42
|
+
|
43
|
+
KeyError is raised if no association is found
|
44
|
+
"""
|
45
|
+
|
46
|
+
return self.associations[event.event_type]
|
47
|
+
|
48
|
+
def convert(self, event: Event, raise_on_miss: bool = True) -> Event:
|
49
|
+
"""
|
50
|
+
Convert a generic event, auto-building the right event class based on event type.
|
51
|
+
|
52
|
+
If raise_on_miss=True, a KeyError exception is raised. Otherwise, input is returned in output.
|
53
|
+
"""
|
54
|
+
|
55
|
+
if event.event_type not in self.associations and not raise_on_miss:
|
56
|
+
return event
|
57
|
+
|
58
|
+
event_class: Type[Event] = self.retrive_class(event)
|
59
|
+
|
60
|
+
return event_class.from_event(event)
|
61
|
+
|
62
|
+
|
63
|
+
|
64
|
+
|
65
|
+
|
66
|
+
|
67
|
+
|
busline/event/test.py
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
import unittest
|
2
|
+
from typing import Type
|
3
|
+
|
4
|
+
from busline.event.event import Event
|
5
|
+
from busline.event.registry import EventRegistry
|
6
|
+
|
7
|
+
|
8
|
+
class Event1(Event):
|
9
|
+
def my_value1(self) -> int:
|
10
|
+
return self.content
|
11
|
+
|
12
|
+
class Event2(Event):
|
13
|
+
def my_value2(self) -> int:
|
14
|
+
return self.content
|
15
|
+
|
16
|
+
class TestEventRegistry(unittest.TestCase):
|
17
|
+
|
18
|
+
def test(self):
|
19
|
+
|
20
|
+
event_registry = EventRegistry() # singleton
|
21
|
+
|
22
|
+
event_registry.register("event1", Event1)
|
23
|
+
event_registry.register("event2", Event2)
|
24
|
+
|
25
|
+
event_registry = EventRegistry() # singleton
|
26
|
+
|
27
|
+
generic_event1 = Event(content=1, event_type="event1")
|
28
|
+
generic_event2 = Event(content=2, event_type="event2")
|
29
|
+
generic_unknown_event = Event(content=2, event_type="unknown")
|
30
|
+
|
31
|
+
event1: Event1 = event_registry.convert(generic_event1)
|
32
|
+
|
33
|
+
self.assertEqual(event1.event_type, "event1")
|
34
|
+
self.assertEqual(event1.my_value1(), 1)
|
35
|
+
|
36
|
+
event2_class = event_registry.retrive_class(generic_event2)
|
37
|
+
|
38
|
+
self.assertIs(event2_class, Event2)
|
39
|
+
|
40
|
+
event2_class: Type[Event2] = event2_class
|
41
|
+
|
42
|
+
event2 = event2_class.from_event(generic_event2)
|
43
|
+
|
44
|
+
self.assertEqual(event2.event_type, "event2")
|
45
|
+
self.assertEqual(event2.my_value2(), 2)
|
46
|
+
|
47
|
+
self.assertRaises(KeyError, lambda: event_registry.retrive_class(generic_unknown_event))
|
busline/exceptions.py
ADDED
File without changes
|
@@ -0,0 +1,26 @@
|
|
1
|
+
import logging
|
2
|
+
import asyncio
|
3
|
+
from dataclasses import dataclass
|
4
|
+
from busline.event.event import Event
|
5
|
+
from busline.local.eventbus.eventbus import EventBus
|
6
|
+
|
7
|
+
|
8
|
+
@dataclass
|
9
|
+
class AsyncLocalEventBus(EventBus):
|
10
|
+
"""
|
11
|
+
Async local eventbus
|
12
|
+
|
13
|
+
Author: Nicola Ricciardi
|
14
|
+
"""
|
15
|
+
|
16
|
+
async def put_event(self, topic: str, event: Event):
|
17
|
+
|
18
|
+
topic_subscriptions = self._get_topic_subscriptions(topic)
|
19
|
+
|
20
|
+
logging.debug(f"new event {event} on topic {topic}, notify subscribers: {topic_subscriptions}")
|
21
|
+
|
22
|
+
tasks = [subscriber.on_event(topic, event) for subscriber in topic_subscriptions]
|
23
|
+
|
24
|
+
await asyncio.gather(*tasks)
|
25
|
+
|
26
|
+
|
@@ -0,0 +1,86 @@
|
|
1
|
+
from abc import ABC, abstractmethod
|
2
|
+
from dataclasses import dataclass, field
|
3
|
+
from typing import Dict, List, Optional
|
4
|
+
from busline.exceptions import TopicNotFound
|
5
|
+
from busline.client.subscriber.subscriber import Subscriber
|
6
|
+
from busline.event.event import Event
|
7
|
+
|
8
|
+
|
9
|
+
@dataclass
|
10
|
+
class EventBus(ABC):
|
11
|
+
"""
|
12
|
+
Abstract class used as base for new eventbus implemented in local projects.
|
13
|
+
|
14
|
+
Author: Nicola Ricciardi
|
15
|
+
"""
|
16
|
+
|
17
|
+
subscriptions: Dict[str, List[Subscriber]] = field(default_factory=dict)
|
18
|
+
|
19
|
+
def __post_init__(self):
|
20
|
+
|
21
|
+
self.reset_subscriptions()
|
22
|
+
|
23
|
+
def reset_subscriptions(self):
|
24
|
+
self.subscriptions = {}
|
25
|
+
|
26
|
+
@property
|
27
|
+
def topics(self) -> List[str]:
|
28
|
+
return list(self.subscriptions.keys())
|
29
|
+
|
30
|
+
def add_subscriber(self, topic: str, subscriber: Subscriber):
|
31
|
+
"""
|
32
|
+
Add subscriber to topic
|
33
|
+
|
34
|
+
:param topic:
|
35
|
+
:param subscriber:
|
36
|
+
:return:
|
37
|
+
"""
|
38
|
+
|
39
|
+
self.subscriptions.setdefault(topic, [])
|
40
|
+
self.subscriptions[topic].append(subscriber)
|
41
|
+
|
42
|
+
def remove_subscriber(self, subscriber: Subscriber, topic: Optional[str] = None, raise_if_topic_missed: bool = False):
|
43
|
+
"""
|
44
|
+
Remove subscriber from topic selected or from all if topic is None
|
45
|
+
|
46
|
+
:param raise_if_topic_missed:
|
47
|
+
:param subscriber:
|
48
|
+
:param topic:
|
49
|
+
:return:
|
50
|
+
"""
|
51
|
+
|
52
|
+
if raise_if_topic_missed and topic is not None and topic not in self.subscriptions.keys():
|
53
|
+
raise TopicNotFound(f"topic '{topic}' not found")
|
54
|
+
|
55
|
+
for name in self.subscriptions.keys():
|
56
|
+
|
57
|
+
if topic is None or self._topic_names_match(topic, name):
|
58
|
+
if subscriber in self.subscriptions[name]:
|
59
|
+
self.subscriptions[name].remove(subscriber)
|
60
|
+
|
61
|
+
|
62
|
+
def _topic_names_match(self, t1: str, t2: str):
|
63
|
+
return t1 == t2
|
64
|
+
|
65
|
+
def _get_topic_subscriptions(self, topic: str) -> List[Subscriber]:
|
66
|
+
|
67
|
+
topic_subscriptions: List[Subscriber] = []
|
68
|
+
for t, subs in self.subscriptions.items():
|
69
|
+
if self._topic_names_match(t, topic):
|
70
|
+
topic_subscriptions.extend(subs)
|
71
|
+
|
72
|
+
return topic_subscriptions
|
73
|
+
|
74
|
+
@abstractmethod
|
75
|
+
async def put_event(self, topic: str, event: Event):
|
76
|
+
"""
|
77
|
+
Put a new event in the bus and notify subscribers of corresponding
|
78
|
+
event's topic
|
79
|
+
|
80
|
+
:param topic:
|
81
|
+
:param event:
|
82
|
+
:return:
|
83
|
+
"""
|
84
|
+
|
85
|
+
raise NotImplemented()
|
86
|
+
|
@@ -0,0 +1,23 @@
|
|
1
|
+
from busline.event.event import Event
|
2
|
+
from busline.local import DEFAULT_EVENT_BUS_INSTANCE
|
3
|
+
from busline.local.eventbus.eventbus import EventBus
|
4
|
+
|
5
|
+
|
6
|
+
class LocalEventBus(EventBus):
|
7
|
+
"""
|
8
|
+
Local *singleton* event bus instance
|
9
|
+
|
10
|
+
Author: Nicola Ricciardi
|
11
|
+
"""
|
12
|
+
|
13
|
+
# === SINGLETON pattern ===
|
14
|
+
_instance = None
|
15
|
+
|
16
|
+
def __new__(cls, *args, **kwargs):
|
17
|
+
if cls._instance is None:
|
18
|
+
cls._instance = DEFAULT_EVENT_BUS_INSTANCE # super().__new__(cls)
|
19
|
+
|
20
|
+
return cls._instance
|
21
|
+
|
22
|
+
async def put_event(self, topic: str, event: Event):
|
23
|
+
return self._instance.put_event(topic, event)
|
@@ -0,0 +1,54 @@
|
|
1
|
+
from typing import Callable, Optional, Self, override
|
2
|
+
from dataclasses import dataclass, field
|
3
|
+
from busline.client.publisher.publisher import Publisher
|
4
|
+
from busline.client.subscriber.event_handler.closure_event_handler import ClosureEventHandler
|
5
|
+
from busline.event.event import Event
|
6
|
+
from busline.client.pubsub_client import PubSubClient, PubSubClientBuilder
|
7
|
+
from busline.local.eventbus.eventbus import EventBus
|
8
|
+
from busline.local.eventbus.local_eventbus import LocalEventBus
|
9
|
+
from busline.local.publisher.local_publisher import LocalEventBusPublisher
|
10
|
+
from busline.local.subscriber.local_subscriber import LocalEventBusSubscriber
|
11
|
+
|
12
|
+
|
13
|
+
@dataclass
|
14
|
+
class LocalPubSubClient(PubSubClient):
|
15
|
+
pass
|
16
|
+
|
17
|
+
|
18
|
+
@dataclass
|
19
|
+
class LocalPubSubClientBuilder(PubSubClientBuilder):
|
20
|
+
"""
|
21
|
+
Builder for a local pub/sub client.
|
22
|
+
|
23
|
+
EventBus fed in init will be used to build publishers and subscribers
|
24
|
+
|
25
|
+
Author: Nicola Ricciardi
|
26
|
+
"""
|
27
|
+
|
28
|
+
eventbus: EventBus = field(default_factory=LocalEventBus)
|
29
|
+
|
30
|
+
def with_default_publisher(self) -> Self:
|
31
|
+
""""
|
32
|
+
LocalEventBusPublisher coupled with eventbus is used
|
33
|
+
"""
|
34
|
+
|
35
|
+
self.base_client.publishers.append(
|
36
|
+
LocalEventBusPublisher(eventbus=self.eventbus)
|
37
|
+
)
|
38
|
+
|
39
|
+
return self
|
40
|
+
|
41
|
+
def with_closure_subscriber(self, closure: Callable[[str, Event], None]) -> Self:
|
42
|
+
self.base_client.subscribers.append(
|
43
|
+
LocalEventBusSubscriber(
|
44
|
+
eventbus=self.eventbus,
|
45
|
+
fallback_event_handler=ClosureEventHandler(closure)
|
46
|
+
)
|
47
|
+
)
|
48
|
+
|
49
|
+
return self
|
50
|
+
|
51
|
+
@override
|
52
|
+
def build(self) -> LocalPubSubClient:
|
53
|
+
return LocalPubSubClient.from_pubsub_client(self.base_client)
|
54
|
+
|
File without changes
|
@@ -0,0 +1,38 @@
|
|
1
|
+
import logging
|
2
|
+
from typing import override
|
3
|
+
|
4
|
+
from busline.client.publisher.publisher import Publisher
|
5
|
+
from busline.event.event import Event
|
6
|
+
from busline.local.eventbus.eventbus import EventBus
|
7
|
+
from busline.exceptions import EventBusClientNotConnected
|
8
|
+
from dataclasses import dataclass, field
|
9
|
+
|
10
|
+
|
11
|
+
@dataclass
|
12
|
+
class LocalEventBusPublisher(Publisher):
|
13
|
+
"""
|
14
|
+
Publisher which works with local eventbus, this class can be initialized and used stand-alone
|
15
|
+
|
16
|
+
Author: Nicola Ricciardi
|
17
|
+
"""
|
18
|
+
|
19
|
+
eventbus: EventBus
|
20
|
+
connected: bool = field(default=False)
|
21
|
+
|
22
|
+
@override
|
23
|
+
async def connect(self):
|
24
|
+
logging.info(f"publisher {self.identifier} connecting...")
|
25
|
+
self.connected = True
|
26
|
+
|
27
|
+
@override
|
28
|
+
async def disconnect(self):
|
29
|
+
logging.info(f"publisher {self.identifier} disconnecting...")
|
30
|
+
self.connected = False
|
31
|
+
|
32
|
+
@override
|
33
|
+
async def _internal_publish(self, topic_name: str, event: Event, **kwargs):
|
34
|
+
|
35
|
+
if not self.connected:
|
36
|
+
raise EventBusClientNotConnected()
|
37
|
+
|
38
|
+
await self.eventbus.put_event(topic_name, event)
|
File without changes
|
@@ -0,0 +1,43 @@
|
|
1
|
+
import logging
|
2
|
+
from dataclasses import dataclass, field
|
3
|
+
from typing import Optional, override
|
4
|
+
|
5
|
+
from busline.client.subscriber.topic_subscriber import TopicSubscriber
|
6
|
+
from busline.local.eventbus.eventbus import EventBus
|
7
|
+
from busline.exceptions import EventBusClientNotConnected
|
8
|
+
|
9
|
+
|
10
|
+
@dataclass
|
11
|
+
class LocalEventBusSubscriber(TopicSubscriber):
|
12
|
+
"""
|
13
|
+
Subscriber topic-based which works with local eventbus
|
14
|
+
|
15
|
+
Author: Nicola Ricciardi
|
16
|
+
"""
|
17
|
+
|
18
|
+
eventbus: EventBus
|
19
|
+
connected: bool = field(default=False)
|
20
|
+
|
21
|
+
@override
|
22
|
+
async def connect(self):
|
23
|
+
logging.info(f"subscriber {self.identifier} connecting...")
|
24
|
+
self.connected = True
|
25
|
+
|
26
|
+
@override
|
27
|
+
async def disconnect(self):
|
28
|
+
logging.info(f"subscriber {self.identifier} disconnecting...")
|
29
|
+
self.connected = False
|
30
|
+
|
31
|
+
@override
|
32
|
+
async def _internal_subscribe(self, topic: str, **kwargs):
|
33
|
+
if not self.connected:
|
34
|
+
raise EventBusClientNotConnected()
|
35
|
+
|
36
|
+
self.eventbus.add_subscriber(topic, self)
|
37
|
+
|
38
|
+
@override
|
39
|
+
async def _internal_unsubscribe(self, topic: Optional[str] = None, **kwargs):
|
40
|
+
if not self.connected:
|
41
|
+
raise EventBusClientNotConnected()
|
42
|
+
|
43
|
+
self.eventbus.remove_subscriber(self, topic)
|
busline/local/test.py
ADDED
@@ -0,0 +1,156 @@
|
|
1
|
+
import unittest
|
2
|
+
|
3
|
+
from busline.client.multiclient import EventBusMultiClient
|
4
|
+
from busline.client.subscriber.event_handler.closure_event_handler import ClosureEventHandler
|
5
|
+
from busline.local.eventbus.async_local_eventbus import AsyncLocalEventBus
|
6
|
+
from busline.local.eventbus.local_eventbus import LocalEventBus
|
7
|
+
from busline.local.local_pubsub_client import LocalPubSubClientBuilder
|
8
|
+
from busline.local.publisher.local_publisher import LocalEventBusPublisher
|
9
|
+
from busline.event.event import Event
|
10
|
+
from busline.local.subscriber.local_subscriber import LocalEventBusSubscriber
|
11
|
+
|
12
|
+
|
13
|
+
class TestLocalEventBus(unittest.IsolatedAsyncioTestCase):
|
14
|
+
|
15
|
+
async def test_async_eventbus(self):
|
16
|
+
|
17
|
+
local_eventbus_instance1 = LocalEventBus() # singleton
|
18
|
+
local_eventbus_instance2 = LocalEventBus() # singleton
|
19
|
+
|
20
|
+
self.assertIs(local_eventbus_instance1, local_eventbus_instance2) # check singleton
|
21
|
+
|
22
|
+
event = Event()
|
23
|
+
received_event = None
|
24
|
+
|
25
|
+
def callback(t: str, e: Event):
|
26
|
+
nonlocal received_event
|
27
|
+
|
28
|
+
received_event = e
|
29
|
+
|
30
|
+
subscriber = LocalEventBusSubscriber(
|
31
|
+
eventbus=local_eventbus_instance1,
|
32
|
+
fallback_event_handler=ClosureEventHandler(callback)
|
33
|
+
)
|
34
|
+
publisher = LocalEventBusPublisher(eventbus=local_eventbus_instance2)
|
35
|
+
|
36
|
+
await subscriber.connect()
|
37
|
+
await publisher.connect()
|
38
|
+
|
39
|
+
await subscriber.subscribe("test")
|
40
|
+
|
41
|
+
await publisher.publish("test", event)
|
42
|
+
|
43
|
+
self.assertIs(event, received_event)
|
44
|
+
|
45
|
+
await subscriber.unsubscribe()
|
46
|
+
received_event = None
|
47
|
+
|
48
|
+
await publisher.publish("test", event)
|
49
|
+
|
50
|
+
self.assertIs(received_event, None)
|
51
|
+
|
52
|
+
|
53
|
+
async def test_local_client(self):
|
54
|
+
received_event = None
|
55
|
+
event = Event()
|
56
|
+
|
57
|
+
def client_callback(topic_name: str, e: Event):
|
58
|
+
nonlocal received_event
|
59
|
+
|
60
|
+
received_event = e
|
61
|
+
|
62
|
+
client = LocalPubSubClientBuilder()\
|
63
|
+
.with_default_publisher()\
|
64
|
+
.with_closure_subscriber(client_callback)\
|
65
|
+
.build()
|
66
|
+
|
67
|
+
await client.connect()
|
68
|
+
|
69
|
+
await client.subscribe("test")
|
70
|
+
|
71
|
+
await client.publish("test", event)
|
72
|
+
|
73
|
+
self.assertIs(event, received_event)
|
74
|
+
|
75
|
+
await client.unsubscribe()
|
76
|
+
received_event = None
|
77
|
+
|
78
|
+
await client.publish("test", event)
|
79
|
+
|
80
|
+
self.assertIs(received_event, None)
|
81
|
+
|
82
|
+
async def test_mhs(self):
|
83
|
+
|
84
|
+
received_event = 0
|
85
|
+
|
86
|
+
def callback(t: str, e: Event):
|
87
|
+
nonlocal received_event
|
88
|
+
|
89
|
+
received_event += 1
|
90
|
+
|
91
|
+
subscriber = LocalEventBusSubscriber(
|
92
|
+
fallback_event_handler=ClosureEventHandler(callback),
|
93
|
+
eventbus=LocalEventBus()
|
94
|
+
)
|
95
|
+
|
96
|
+
await subscriber.connect()
|
97
|
+
|
98
|
+
await subscriber.subscribe("t1")
|
99
|
+
await subscriber.subscribe("t2", handler=ClosureEventHandler(callback))
|
100
|
+
|
101
|
+
await subscriber.notify("t1", Event())
|
102
|
+
|
103
|
+
self.assertEqual(received_event, 1)
|
104
|
+
|
105
|
+
await subscriber.notify("t2", Event())
|
106
|
+
|
107
|
+
self.assertEqual(received_event, 2)
|
108
|
+
|
109
|
+
await subscriber.unsubscribe()
|
110
|
+
|
111
|
+
await subscriber.notify("t1", Event())
|
112
|
+
await subscriber.notify("t2", Event())
|
113
|
+
|
114
|
+
self.assertEqual(received_event, 2)
|
115
|
+
|
116
|
+
async def test_multi_client(self):
|
117
|
+
local_eventbus_instance1 = AsyncLocalEventBus() # not singleton
|
118
|
+
local_eventbus_instance2 = AsyncLocalEventBus() # not singleton
|
119
|
+
|
120
|
+
n_events: int = 0
|
121
|
+
|
122
|
+
def on_event_callback(topic_name: str, e: Event):
|
123
|
+
nonlocal n_events
|
124
|
+
|
125
|
+
n_events += 1
|
126
|
+
|
127
|
+
client1 = LocalPubSubClientBuilder(local_eventbus_instance1)\
|
128
|
+
.with_default_publisher()\
|
129
|
+
.with_closure_subscriber(lambda t, e: ...)\
|
130
|
+
.build()
|
131
|
+
|
132
|
+
client2 = LocalPubSubClientBuilder(local_eventbus_instance2)\
|
133
|
+
.with_default_publisher()\
|
134
|
+
.with_closure_subscriber(lambda t, e: ...)\
|
135
|
+
.build()
|
136
|
+
|
137
|
+
multi_client = EventBusMultiClient([
|
138
|
+
client1,
|
139
|
+
client2
|
140
|
+
])
|
141
|
+
|
142
|
+
await multi_client.connect()
|
143
|
+
|
144
|
+
await multi_client.subscribe("topic", handler=ClosureEventHandler(on_event_callback))
|
145
|
+
|
146
|
+
await multi_client.publish("topic", Event())
|
147
|
+
|
148
|
+
await multi_client.disconnect()
|
149
|
+
|
150
|
+
self.assertEqual(n_events, 2)
|
151
|
+
|
152
|
+
|
153
|
+
|
154
|
+
|
155
|
+
if __name__ == '__main__':
|
156
|
+
unittest.main()
|