pubsub
the pubsub module contains a number of base classes for the publish/subscribe pattern as used in pydsol. The package is based on the Java implementation of DSOL (Distributed Simulation Object Library), first documented in 2002. Information about the Java and Python DSOL libraries can be found at https://simulation.tudelft.nl. Specific information about the publish/subscribe implementation in Java can be found in the djutils project (Delft Java Utilities) at https://djutils.org
Typically, implemented listener classes inherit from the abstract base class EventListener (possibly through mixin), and classes that produce events inherit from the abstract base class EventProducer (possibly using multiple inheritance).
The whole idea of publish/subscribe is that you decouple the producer of information and the possible receiver(s), which otherwise has to be hardcoded, making information exchange between objects very inflexible. With pub/sub, the producer will notify only subscribed objects of the content in the event. The receivers can change dynamically over time, by subscribing or unsubscribing for the receipt of certain EventTypes. We call the process of producing an event by the publisher “firing” of an event with the fire() method; receipt by the receiver is done in the notify() method.
Note that Events are completely different from SimEvents. Events are used in publish/subscribe; SimEvents contain information about code to be executed at a later point in time.
- exception pydsol.core.pubsub.EventError[source]
Bases:
Exception
General Exception for working with Events and publish/subscribe
- with_traceback()
Exception.with_traceback(tb) – set self.__traceback__ to tb and return self.
- class pydsol.core.pubsub.EventType(name: str, metadata: Dict[str, Type] | None = None)[source]
Bases:
object
EventType is a strongly typed identifier for an event, which can contain additional information about the event itself such as metadata. The EventType is typically instantiated as a static variable in a class.
The implementation of the EventType takes care that there will not be a problem with duplicate names for EventTypes. As part of the EventType, the defining class is coded as well to make the EventType unique. So, in the above example, the name “Producer.PRODUCTION” is stored internally as the unique name for the EventType.
Example
class Producer PRODUCTION_EVENT = EventType("PRODUCTION")
The event type can then be used anywhere in the code as Producer.PRODUCTION_EVENT. Note that an Event and a SimEvent are two completely different concepts: Events are for the publish/subscribe pattern, SimEvents are to store deferred method information.
- __init__(name: str, metadata: Dict[str, Type] | None = None)[source]
Instantiate a new EventType, usually in a static manner.
Example
class Producer PRODUCTION_EVENT = EventType("PRODUCTION")
The event type can then be used anywhere in the code as Producer.PRODUCTION_EVENT.
- Parameters:
name (str) – the human readable name of the event
metadata (dict[str, Type], optional) – a dict with the metadata, defining the structure of the payload in the event, as pairs of the name to be used in the dict and the expected type of the data field. When metadata is None or undefined, the payload of the event can be anything. When metadata IS defined, the payload has to be a dict.
- Raises:
EventError – when name is not a str, or defining_class is not a type
EventError – when there was already an event defined with this name in the defining_class
EventError – when the metadata does not consist of [str, Type] pairs
- property name
Return the human readable name of the event type.
- property defining_class
Return the name of the defining class.
The defining class is the class in which the event type has been defined
- property metadata: Dict[str, Type] | None
Return the metadata dict or None.
The metadata dict contains the expected structure of the payload dict of the event in case it is defined. metadata can be None, in which case the payload does not have to be a dict, and can have any structure.
- class pydsol.core.pubsub.Event(event_type: EventType, content, check: bool = True)[source]
Bases:
object
An event is an object with a payload that is to be sent between a producer and zero or more listeners. In a sense, the Event is the “envelope” of the content.
- __init__(event_type: EventType, content, check: bool = True)[source]
Instantiate an event with content. Events are strongly typed using a (usually static) instance of the EventType class, to distinguish different types of events from each other.
- Parameters:
event_type (EventType) – a reference to a (usually static) event type for identification
content – the payload of the event, which can be of any type; common types are list, dict, or simple types
check (bool, optional) – whether to check the fields in the content in case the event_type has metadata; the check whether the content is a dict is always checked when there is metadata
- Raises:
EventError – if event_type is not an EventType
EventError – if the specified metadata and the content is not a dict
EventError – if the dict content is not consistent with the EventType metadata
- property content: Any
Returns the payload of the event; can be of any type.
- class pydsol.core.pubsub.TimedEvent(timestamp: float | int, event_type: EventType, content, check: bool = True)[source]
Bases:
Event
A TimedEvent is an event with a time identifier (int, float).
The time identifier is not specified as an extra element in the payload, but rather as an extra attribute of the Event. The constructor explicitly demands for the specification of a timestamp, typically the simulator time. Like an event, it is an object with a payload that is to be sent between a producer and zero or more listeners. In a sense, the TimedEvent is the timestamped “envelope” of the content.
- __init__(timestamp: float | int, event_type: EventType, content, check: bool = True)[source]
Instantiate a timed event with content.
TimedEvents are strongly typed using a (usually static) instance of the EventType class, to distinguish different types of events from each other. Furthermore, the timestamp indicates when the event was fired. Typically the value of the timestamp is the simulator time.
- Parameters:
timestamp (int or float) – the timestamp of the event, typically the simulator time
event_type (EventType) – a reference to a (usually static) event type for identification
content – the payload of the event, which can be of any type; common types are list, dict, or simple types
check (bool, optional) – whether to check the fields in the content in case the event_type has metadata; the check whether the content is a dict is always checked when there is metadata
- Raises:
EventError – if timestamp is not of type int or float
EventError – if event_type is not an EventType
EventError – if the EventType specified metadata and the content is not a dict
EventError – if the dict content is not consistent with the EventType metadata
- property timestamp: int | float
Returns the timestamp of the event; typically the simulator time.
- property content: Any
Returns the payload of the event; can be of any type.
- class pydsol.core.pubsub.EventListener[source]
Bases:
ABC
The EventListener abstract class defines the behavior of an event subscriber.
The EventListener is an interface for a class that needs to be able to receive events from one or more EventProducers. In order to receive events, a listener has to implement the notify() method. In the notify() method, events can be tested for their EventType with if-elif statements, and then the corresponding content can be acted upon.
Its most important method is notify(event) that is called from the EventProducer (using the fier(event) method) to handle the Event.
- class pydsol.core.pubsub.EventProducer[source]
Bases:
object
EventProducer is an abstract class defining the producer behavior.
The EventProducer class acts as a superclass for classes that need to fire events to an unknown and possibly varying number of subscribers (also called listeners). The main methods that can be called on the EventProducer are: add_listener and remove_listener. In addition, the logic of the class that extends the base EventProducer class calls the fire(event_type, content) method to notify the listener(s) (if any).
The most important private attribute of the EventProducer is
_listeners: dict[EventType, list[EventListener]]
. This structure Maps the EventType to a list of listeners for that EventType. Note that this is a list to make behavior reproducible: we want events to subscribers to be fired in the same order when replicating the model run. The dictionary is ordered (unsorted) in Python 3.7+, and the list is reproducible. Adict[EventType, set[EventListener]]
would not be reproducible, since the set is unordered.- add_listener(event_type: EventType, listener: EventListener)[source]
Add an EventListener to this EventProducer for a given EventType. If the listener already is registered for this EventType, this will be ignored.
- Parameters:
event_type (EventType) – the EventType for which this listener subscribes
listener (EventListener) – the subscriber to register for the provided Eventtype
- Raises:
EventError – if any of the arguments is of the wrong type
- remove_listener(event_type: EventType, listener: EventListener)[source]
Remove an EventListener of this EventProducer for a given EventType. If the listener is not registered for this EventType, this will be ignored.
- Parameters:
event_type (EventType) – the EventType for which this listener unsubscribes
listener (EventListener) – the subscriber to remove for the provided Eventtype
- Raises:
EventError – if any of the arguments is of the wrong type
- remove_all_listeners(event_type: EventType | None = None, listener: EventListener | None = None)[source]
Remove an EventListener (if given) for a provided EventType (if given) for this EventProducer. It is no problem if there are no matches. There are four situations:
- event_type == None and listener == None
all listeners for all event types are removed
- event_type == None and listener is specified
the listener is removed for any event for which it was registered
- event_type is specified and listener == None
all listeners are removed for the given event_type
- event_type and listener are both specified
the listener for the given event type is removed, if it was registered; in essence this is the same as remove_listener
- Parameters:
event_type (EventType, optional) – the EventType for which this listener unsubscribes
listener (EventListener, optional) – the subscriber to remove for the provided EventType
- Raises:
EventError – if any of the arguments is of the wrong type
- fire_event(event: Event)[source]
fire this event to the subscribed listeners for the EventType of the event.
- Parameters:
event (Event) – the event to fire to the subscribed listeners
- Raises:
EventError – if the event is not of the right type
- fire(event_type: EventType, content, check: bool = True)[source]
construct an event based on the arguments and fire this event to the subscribed listeners for the event_type
- Parameters:
event_type (EventType) – a reference to a (usually static) event type for identification
content – the payload of the event, which can be of any type; common types are list, dict, or simple types
check (bool, optional) – whether to check the fields in the content in case the event_type has metadata; the check whether the content is a dict is always checked when there is metadata
- Raises:
EventError – if event_type is not an EventType
EventError – if the EventType specified metadata and the content is not a dict
EventError – if the dict content is not consistent with the EventType metadata
- fire_timed_event(timed_event: TimedEvent)[source]
fire this timed_event to the subscribed listeners for the EventType of the event.
- Parameters:
event (TimedEvent) – the timed_event to fire to the subscribed listeners
- Raises:
EventError – if the timed_event is not of the right type
- fire_timed(time: int | float, event_type: EventType, content, check: bool = True)[source]
construct a timed event based on the arguments and fire this event to the subscribed listeners for the event_type
- Parameters:
timestamp (int or float) – the timestamp of the event, typically the simulator time
event_type (EventType) – a reference to a (usually static) event type for identification
content – the payload of the event, which can be of any type; common types are list, dict, or simple types
check (bool, optional) – whether to check the fields in the content in case the event_type has metadata; the check whether the content is a dict is always checked when there is metadata
- Raises:
EventError – if timestamp is not of type int or float
EventError – if event_type is not an EventType
EventError – if the EventType specified metadata and the content is not a dict
EventError – if the dict content is not consistent with the EventType metadata