The communication framework#
Publish/Subscribe model#
Glue is built around a publish/subscribe paradigm that allows individual
components to remain synchronized without knowing about each other. The core
object that allows this is the Hub
, which listens for
messages from various parts of Glue and relays messages to other interested
objects about changes in state to the data and subsets.
You can instantiate a Hub
instance directly:
>>> from glue.core import Hub
>>> hub = Hub()
but in most cases if you are using a
DataCollection
, you can let it instantiate
the hub instead and access it via the .hub
attribute:
>>> from glue.core import DataCollection
>>> data_collection = DataCollection()
>>> data_collection.hub
<glue.core.hub.Hub at 0x102991dd8>
Messages are exchanged using Message
objects. A
message is a notice that something interesting has happened. Various
sub-classes of Message
exist, such as
DataMessage
or
SubsetMessage
, and even more specialized ones such
as DataCollectionAddMessage
.
Using the subscribe()
method, you can easily attach callback functions/methods to specific messages using the syntax:
hub.subscribe(self, subscriber, message_class, handler=..., filter=...)
where the message_class
is the type of message to listen for, such as
DataMessage
, handler
is the function/method to
be called if the message is received (the function/method should take one
argument which is the message), and filter
can be used to specify
conditions in which to pass on the message to the function/method (for more
information on this, see the subscribe()
documentation).
Subscribing to messages has to be done from a
HubListener
instance. The following simple example shows how to set up a basic HubListener
and register to listen for DataMessage
and DataCollectionAddMessage
:
>>> from glue.core import Hub, HubListener, Data, DataCollection
>>> from glue.core.message import (DataMessage,
... DataCollectionMessage)
>>>
>>> class MyListener(HubListener):
...
... def __init__(self, hub):
... hub.subscribe(self, DataCollectionMessage,
... handler=self.receive_message)
... hub.subscribe(self, DataMessage,
... handler=self.receive_message)
...
... def receive_message(self, message):
... print("Message received:")
... print("{0}".format(message))
We can then create a data collection, and create an instance of the above class:
>>> data_collection = DataCollection()
>>> hub = data_collection.hub
>>> listener = MyListener(hub)
If we create a new dataset, then add it to the data collection created above, we then trigger the receive_message
method:
>>> data = Data(x=[1,2,3])
>>> data_collection.append(data)
Message received:
DataCollectionAddMessage:
Sent from: DataCollection (1 data set)
0:
Note that DataCollectionAddMessage
is a subclass of
DataCollectionMessage
– when registering to a
message class, sub-classes of this message will also be received.
It is also possible to trigger messages manually:
>>> # We can also create messages manually
... message = DataMessage(data)
>>> hub.broadcast(message)
Message received:
DataMessage:
Sent from: Data Set: Number of dimensions: 1
Shape: 3
Components:
0) x
1) Pixel Axis 0
2) World 0
Typical workflow#
This is used in Glue to produce the following communication workflow:
An empty
DataCollection
object is created, and automatically connected to aHub
.Data are added to the data collection
Several clients register to the hub, and subscribe to particular types of messages.
Something (perhaps code, perhaps user interaction with a client) acts to change the state of a data or subset object. These changes automatically generate particular messages that get sent to the Hub. These messages communicate atomic events such as a change in the data, a change in a subset, or the fact a subset has been deleted.
Upon receiving a message, the hub relays it to all clients that have subscribed to that particular message type.
The clients react to the message however they see fit.
Here, we use the term client in the generic sense of a class that interacts with the hub.