The topic of a pubsub message can be understood in several ways:
A topic is uniquely identified by its full name, represented as a character string. Example of some topic names are
As explained in another section, topics form a hierarchy. Every topic therefore has a “node name”. Some of the node names from the previous examples are
The “full name” consists of the node-names from the root of the hierarchy, separated by dots (as the first examples showed).
For this reason, topic names can also be given to pubsub functions as tuples of strings rather than a dotted string. The tuple equivalent of the topic names from the previous examples are (“sports”,), (“sports”, “hockey”), (“app”,”options”,”editor”).
Most pub functions and methods that require a topic name accept either forms of topic name.
A topic specifies what data can be transported as part of messages of that topic. This specification is called Message Data Specification, or MDS for short. The specification covers the data name and whether the data is required or optional.
Assume the following MDS for “topic_A”:
messages carry the data:arg1: requiredarg2: optional
Then the following messages could be sent:
pub.sendMessage("topic_A", arg1=123)
pub.sendMessage("topic_A", arg1=123, arg2=456)
but the following would raise an exception:
pub.sendMessage("topic_A", arg2=456)
By default, the MDS is created for you at runtime by introspection of the first listener subscribed to a topic. This makes it simple to use pubsub “out of the box” (since you don’t have to write the MDS yourself), but has the disadvantage of making your MDS specification dependant on the order of subscription of listeners. If this is problematic, the MDS can be imported, as explained next.
Pubsub allows the MDS to be imported from a Python file and referenced at runtime. This will make the MDS independent of the order of subscription of listeners and even allow you to document your topics and MDS.
You can output the MDS of each pubsub topic defined in an application via the pub.exportTopicTree() function:
# init:
from pubsub import pb
# main:
... subscribe listeners ...
... send messages ...
# upon exit:
exportModuleDoc = '''...'''
pub.exportTopicTree(
'filename.py', moduleDoc=exportModuleDoc)
A technique that you may find useful when developing a new application is to start by using the runtime introspection-based MDS of pubsub; when your application core is sufficiently developed, use pub.exportTopicTree() to export it, clean it up, save it, and add a pub.importTopicTree() early in the startup of you application. Also call pub.setTopicUnspecifiedFatal(True)() so that new topics can’t be created inadvertently.
Topics form a hierarchy:
Root of topic tree:
Subtopics: a subtopic is to its parent topic as a derived class is to its parent class in Python, ie something more specific, involving more data. A subtopic “inherits” its parent’s MDS and may “extend” it by specifying additional message data permitted for the subtopic.
Messages are delivered to the listeners of the specified topic, as well as listeners of topics up the topic branch:
pub.sendMessage('a.b.c', argA=1, argB=2, argC=3)
The message, of topic “a.b.c”, will be received by listeners of “a.b.c”. Parts of the message will be received by listeners of “a.b”, by listeners of “a”, and by listeners of ALL_TOPICS.
The part of the message received by a parent topic is determined by the parent topic’s MDS. IE only the message data that pertains to a topic’s parent is sent to the listeners of the parent topic. In the above example, if topic “a.b” specifies argA and argB only, then only those two items will be sent to listeners of “a.b”. Similarly, if topic “a” specifies argA only as MDS, then only argA will be sent to listeners of “a”.
The order of delivery of a message to listeners is unspecified. Your application must not depend on it.
A subtopic “inherits” its parent’s MDS and may “extend” it by specifying additional message data permitted for the subtopic. For instance if topic “a” has message data arg1 only, then its subtopics (such as “a.b”) must support at least arg1:
def listen( arg1): pass
def listenSubB(arg1): pass
def listenSubC(arg1, arg2): pass
pub.subscribe(listen, "a")
pub.subscribe(listenSubB, "a.b")
pub.subscribe(listenSubC, "a.c")
Assuming the above, the following would cause pubsub to raise an exception:
def listenSubD(arg2): pass
pub.subscribe(listenSubD, "a.d") // raises: arg1 missing
Note
Because the root topic has an empty MDS, no data is transmitted to listeners of the root topic, except if a listener uses the catch-all **kwargs in its signature.
Note
A subtopic can make one or more of its parent data items required for the subtopic if it was optional for the topic, but cannot do the reverse. In
def listen( arg1=1): pass
def listenSub(arg1): pass
pub.subscribe(listen, "a")
pub.subscribe(listenSub, "a.b")
the subtopic “a.b” is valid: arg1 is required, so a sender will always provide it, so eventhough it is optional for topic 1 listeners, those listeners will always receive it too.
topic definition tree and its syntax, topic tree printing
The topic hierarchy can be created on the fly by the application (default), or can be specified and documented via one or more Topic Definition Providers (TDP). TDP’s are objects that state
Pubsub can be setup to deny (via an exception) any attempt to use a topic that is not specified by a TDP. This is very useful in larger applications.
Every topic has a message arguments specification, i.e. a prescribed set of argument names that are allowed when sending a message of that topic. This is the TMAS, for topic message arguments specification. For instance, topic “sports.baseball” might have following TMAS:
but only location and playersA are required: playersB and dateTime are only used during a match (vs a practice). Then every message of topic “sports.baseball” must be given at least a location and list for playersA, and every recipient of the message must accept all four parameters (since some messages will have all four parameters). I.e.:
pub.sendMessage("sports.baseball", playersA=[], location="Montreal")
pub.sendMessage("sports.baseball", playersA=[], playersB=[], location="Montreal")
pub.sendMessage("sports.baseball", playersA=[], location="Montreal", dateTime=today() )
pub.sendMessage("sports.baseball", playersA=[], playersB=[], location="Montreal", dateTime=today() )
pub.sendMessage("sports.baseball", playersA=[]) # ERROR: required location missing
The TMAS of each topic: