Skip to content

Commit 08c59ef

Browse files
refactor: Event connector UX (#200)
# Summary This PR addresses #57 by refactoring the code involved in creating `Connector`s for `Event`s in event-driven models in an attempt to simplify the user experience. Previously multiple lines of code were required involving several library classes putting some burden of understanding low level details on the user. Now these lines can be replaced with a single line of code involving a concrete `Connector` class which will be familiar to users. # Changes - `EventConnectorBuilder` becomes a utility class `EventConnectorSpecBuilder` with its responsibility changed to producing a mapping of `event.type` -> `ConnectorSpec`. - `ConnectorBuilder` exposes a new method `build_event_connectors(components: list[Component]) -> list[Connector]` which consumes the `EventConnectorSpecBuilder` to produce the required `Event` `Connector`s. - `Connector` ABC has a new class method `builder` which returns a `ConnectorBuilder` for a given `Connector` concrete class. - Updates tests, examples, and docs with this streamlined approach. - Also corrects the logic related to restricting attempted use of event based models with `RayConnector` (previously restriction was applied to `RayProcess` which actually can be used with different `Connector` classes). --------- Co-authored-by: Toby Coleman <toby@tobycoleman.com>
1 parent 68cfe9e commit 08c59ef

15 files changed

Lines changed: 103 additions & 96 deletions

File tree

docs/api/events/events.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,4 @@
44
- Event
55
- SystemEvent
66
- StopEvent
7-
- EventConnectorBuilder
87
- EventHandlers

docs/examples/tutorials/event-driven-models.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ Finally, we need components to subscribe to these events and process them. Use t
6666
Now we can create a [`Process`][plugboard.process.Process] from all these components. The outputs from `CollectLow` and `CollectHigh` are connected to separate [`FileWriter`][plugboard.library.FileWriter] components so that we'll get a CSV file containing the latest high and low values at each step of the simulation.
6767

6868
!!! info
69-
We need a few extra lines of code to create connectors for the event-based parts of the model. If you define your process in YAML this will be done automatically for you, but if you are defining the process in code then you will need to use the [`EventConnectorBuilder`][plugboard.events.EventConnectorBuilder] to do this.
69+
We need a few extra lines of code to create connectors for the event-based parts of the model. If you define your process in YAML this will be done automatically for you, but if you are defining the process in code then you will need to use the [`ConnectorBuilder`][plugboard.connector.ConnectorBuilder] to do this.
7070

7171
```python hl_lines="15-17"
7272
--8<-- "examples/tutorials/005_events/hello_events.py:main"

examples/demos/finance/001_momentum_signal/momentum-signal.ipynb

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,6 @@
6969
"import typing as _t\n",
7070
"\n",
7171
"from plugboard.connector import AsyncioConnector, ConnectorBuilder\n",
72-
"from plugboard.events import EventConnectorBuilder\n",
7372
"from plugboard.process import LocalProcess\n",
7473
"from plugboard.schemas import ConnectorSpec\n",
7574
"\n",
@@ -480,9 +479,7 @@
480479
"]\n",
481480
"\n",
482481
"# Event connectors\n",
483-
"builder = ConnectorBuilder(connector_cls=AsyncioConnector)\n",
484-
"event_builder = EventConnectorBuilder(connector_builder=builder)\n",
485-
"event_connectors = list(event_builder.build(components).values())\n",
482+
"event_connectors = AsyncioConnector.builder().build_event_connectors(components)\n",
486483
"\n",
487484
"process = LocalProcess(components=components, connectors=connectors + event_connectors)"
488485
]

examples/demos/llm/004_image_processing/local-and-remote-image-processing.ipynb

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@
7373
"from plugboard.schemas import ComponentArgsDict, ConnectorSpec\n",
7474
"from plugboard.process import LocalProcess\n",
7575
"from plugboard.library import FileReader, FileWriter, LLMImageProcessor\n",
76-
"from plugboard.events import Event, EventConnectorBuilder"
76+
"from plugboard.events import Event"
7777
]
7878
},
7979
{
@@ -390,9 +390,7 @@
390390
"]\n",
391391
"\n",
392392
"# Event Connectors\n",
393-
"builder = ConnectorBuilder(connector_cls=AsyncioConnector)\n",
394-
"event_builder = EventConnectorBuilder(connector_builder=builder)\n",
395-
"event_connectors = list(event_builder.build(components).values())\n",
393+
"event_connectors = AsyncioConnector.builder().build_event_connectors(components)\n",
396394
"\n",
397395
"# Define Process\n",
398396
"process = LocalProcess(components=components, connectors=connectors + event_connectors)\n",

examples/tutorials/005_events/hello_events.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
from plugboard.component import Component, IOController
1111
from plugboard.connector import AsyncioConnector, ConnectorBuilder
12-
from plugboard.events import Event, EventConnectorBuilder, StopEvent
12+
from plugboard.events import Event, StopEvent
1313
from plugboard.library import FileWriter
1414
from plugboard.process import LocalProcess
1515
from plugboard.schemas import ConnectorSpec, ComponentArgsDict
@@ -139,9 +139,7 @@ async def main() -> None:
139139
connect("collect-high.value", "save-high.value"),
140140
connect("collect-low.value", "save-low.value"),
141141
]
142-
connector_builder = ConnectorBuilder(connector_cls=AsyncioConnector) # (2)!
143-
event_connector_builder = EventConnectorBuilder(connector_builder=connector_builder)
144-
event_connectors = list(event_connector_builder.build(components).values())
142+
event_connectors = AsyncioConnector.builder().build_event_connectors(components) # (3)!
145143

146144
process = LocalProcess(
147145
components=components,

plugboard/connector/connector.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import typing as _t
77

88
from plugboard.connector.channel import Channel
9+
from plugboard.connector.connector_builder import ConnectorBuilder
910
from plugboard.schemas import ConnectorSpec
1011
from plugboard.utils import ExportMixin
1112

@@ -39,3 +40,8 @@ def dict(self) -> dict[str, _t.Any]: # noqa: D102
3940
"id": self.id,
4041
"spec": self.spec.model_dump(),
4142
}
43+
44+
@classmethod
45+
def builder(cls, *args: _t.Any, **kwargs: _t.Any) -> ConnectorBuilder:
46+
"""Returns a `ConnectorBuilder` for this `Connector` class."""
47+
return ConnectorBuilder(cls, *args, **kwargs)

plugboard/connector/connector_builder.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,14 @@
11
"""Provides `ConnectorBuilder` to build `Connector` objects."""
22

3+
from __future__ import annotations
4+
35
import typing as _t
46

5-
from plugboard.connector.connector import Connector
7+
8+
if _t.TYPE_CHECKING: # pragma: no cover
9+
from plugboard.connector.connector import Connector
10+
11+
from plugboard.connector.event_connector_spec_builder import EventConnectorSpecBuilder
612
from plugboard.schemas import ConnectorSpec
713

814

@@ -17,3 +23,8 @@ def __init__(self, connector_cls: type[Connector], *args: _t.Any, **kwargs: _t.A
1723
def build(self, spec: ConnectorSpec) -> Connector:
1824
"""Builds a `Connector` object."""
1925
return self._connector_cls(spec, *self._args, **self._kwargs)
26+
27+
def build_event_connectors(self, components: _t.Iterable[_t.Any]) -> list[Connector]:
28+
"""Builds event connectors for the given components."""
29+
evt_conn_map = EventConnectorSpecBuilder.build(components)
30+
return [self.build(spec=spec) for spec in evt_conn_map.values()]
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
"""Provides `EventConnectorSpecBuilder` utility class which builds event connector specs for components.""" # noqa: E501,W505
2+
3+
from __future__ import annotations
4+
5+
import typing as _t
6+
7+
from plugboard.events.event import Event
8+
from plugboard.schemas import ConnectorMode, ConnectorSocket, ConnectorSpec
9+
10+
11+
if _t.TYPE_CHECKING:
12+
from plugboard.component import Component
13+
14+
15+
class EventConnectorSpecBuilder: # pragma: no cover
16+
"""`EventConnectorSpecBuilder` constructs connector specs for component event handlers."""
17+
18+
_source_descriptor: str = "publishers"
19+
_target_descriptor: str = "subscribers"
20+
21+
@staticmethod
22+
def build(components: _t.Iterable[Component]) -> dict[str, ConnectorSpec]:
23+
"""Returns mapping of connector specs for events handled by components."""
24+
evt_conn_map: dict[str, ConnectorSpec] = {}
25+
for component in components:
26+
comp_evt_conn_map = EventConnectorSpecBuilder._build_for_component(
27+
evt_conn_map, component
28+
)
29+
evt_conn_map.update(comp_evt_conn_map)
30+
return evt_conn_map
31+
32+
@staticmethod
33+
def _build_for_component(
34+
evt_conn_map: dict[str, ConnectorSpec], component: Component
35+
) -> dict[str, ConnectorSpec]:
36+
component_evts = set(component.io.input_events + component.io.output_events)
37+
return {
38+
evt.type: EventConnectorSpecBuilder._build_for_event(evt.type)
39+
for evt in component_evts
40+
if evt.type not in evt_conn_map
41+
}
42+
43+
@staticmethod
44+
def _build_for_event(evt_type: str) -> ConnectorSpec:
45+
evt_type_safe = Event.safe_type(evt_type)
46+
source = ConnectorSocket(
47+
entity=evt_type_safe, descriptor=EventConnectorSpecBuilder._source_descriptor
48+
)
49+
target = ConnectorSocket(
50+
entity=evt_type_safe, descriptor=EventConnectorSpecBuilder._target_descriptor
51+
)
52+
spec = ConnectorSpec(source=source, target=target, mode=ConnectorMode.PUBSUB)
53+
return spec

plugboard/events/__init__.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
11
"""Provides models and utilities for handling events."""
22

33
from plugboard.events.event import Event, StopEvent, SystemEvent
4-
from plugboard.events.event_connector_builder import EventConnectorBuilder
54
from plugboard.events.event_handlers import EventHandlers
65

76

87
__all__ = [
98
"Event",
10-
"EventConnectorBuilder",
119
"EventHandlers",
1210
"StopEvent",
1311
"SystemEvent",

plugboard/events/event_connector_builder.py

Lines changed: 0 additions & 49 deletions
This file was deleted.

0 commit comments

Comments
 (0)