Pub Module

This is the main entry-point to pubsub’s core functionality. The pub module supports:

  • messaging: publishing and receiving messages of a given topic

  • tracing: tracing pubsub activity in an application

  • trapping exceptions: dealing with “badly behaved” listeners (ie that leak exceptions)

  • specificatio of topic tree: defining (or just documenting) the topic tree of an application; message data specification (MDS)

The recommended usage is

from pubsub import pub

// use pub functions:
pub.sendMessage(...)

Note that this module creates a “default” instance of pubsub.core.Publisher and binds several local functions to some of its methods and those of the pubsub.core.TopicManager instance that it contains. However, an application may create as many independent instances of Publisher as required (for instance, one in each thread; with a custom queue to mediate message transfer between threads).

pubsub.pub.VERSION_API = 4

major API version

The PyPubSub API version. This is deprecated. The only valid value currently is the integer 4. Previously, versions 1, 2 and 3 API could also be activated in PyPubSub before importing pub, in which case pub.VERSION_API had the corresponding value.

Sending Messages

Sending messages is achieved via the following function:

pubsub.pub.sendMessage(topicName, **kwargs)

Send a message. :param topicName: name of message topic (dotted or tuple format) :param msgData: message data (must satisfy the topic’s MDS)

The following exception may be raised when sending a message, if the message data does not comply with the Message Data Specification for the topic:

exception pubsub.pub.SenderMissingReqdMsgDataError(topicName: str, argNames: Sequence[str], missing: Sequence[str])[source]

Bases: RuntimeError

Raised when a sendMessage() is missing arguments tagged as ‘required’ by pubsub topic of message.

exception pubsub.pub.SenderUnknownMsgDataError(topicName: str, argNames: Sequence[str], extra: Sequence[str])[source]

Bases: RuntimeError

Raised when a sendMessage() has arguments not listed among the topic’s message data specification (MDS).

Advanced use:

The following would typically only be useful in special circumstances, such as if PyPubSub’s default Publisher must be accessed, on or more separate instances of Publisher is required, and so forth.

pubsub.pub.getDefaultPublisher() Publisher[source]

Get the Publisher instance created by default when this module is imported. See the module doc for details about this instance.

class pubsub.core.Publisher(treeConfig: TreeConfig = None)[source]

Represent the class that send messages to listeners of given topics and that knows how to subscribe/unsubscribe listeners from topics.

See pubsub.core.Publisher for details.

Receiving Messages

The following functions are available for controlling what callable objects (functions, methods, or class instances with a __call__ method) will get called when messages are generated:

pubsub.pub.subscribe(listener, topicName)

Subscribe listener to named topic. Raises ListenerMismatchError if listener isn’t compatible with the topic’s MDS. Returns (pubsub.core.Listener, success), where success is False if listener was already subscribed. The pub.core.Listener wraps the callable subscribed and provides introspection-based info about the callable. Extra keyword arguments are treated as currying of listener arguments.

Example:

pub.subscribe(listener1, ‘some_topic’) pub.subscribe(listener2, ‘some_other_topic’, a=2, b=3)

In the second example, the listener2 will always receive a=2 and b=3 and pubsub treats it as though a and b were curried, i.e. as if the actual listener subscribed were a callable that did not have a or b parameters. Hence if some_other_topic has a or b as message data, subscription will raise a ListenerInadequate error.

Note that if ‘subscribe’ notification is on, the handler’s ‘notifySubscribe’ method is called after subscription.

pubsub.pub.unsubscribe(listener, topicName)

Unsubscribe from given topic. Returns the pubsub.core.Listener instance that was used to wrap listener at subscription time. Raises an TopicNameError if topicName doesn’t exist.

Note that if ‘unsubscribe’ notification is on, the handler’s notifyUnsubscribe() method will be called after unsubscribing.

pubsub.pub.unsubAll(topicName=None, listenerFilter=None, topicFilter=None)

Unsubscribe all listeners of a topic.

Parameters:
  • topicName – if none given, unsub from all topics.

  • listenerFilter – filter function to apply to listeners, unsubscribe only the listeners that satisfy listenerFilter(listener: Listener) == True

  • topicFilter – topic name, or a filter function to apply to topics; in latter case, only topics that satisfy topicFilter(topic name) == True will be affected

Returns:

list of all listeners (instances of pub.Listener) that were unsubscribed from the topic tree

Note: this method will generate one ‘unsubcribe’ notification message (see pub.setNotificationFlags()) for each listener unsubscribed.

pubsub.pub.isSubscribed(listener, topicName) bool[source]

Returns true if listener has subscribed to topicName, false otherwise. WARNING: a false return is not a guarantee that listener won’t get messages of topicName: it could receive messages of a subtopic of topicName.

The following exceptions are relevant:

exception pubsub.pub.ListenerMismatchError(msg: str, listener: Callable[[...], Any], *args)[source]

Bases: ValueError

Raised when an attempt is made to subscribe a listener to a topic, but listener does not satisfy the topic’s message data specification (MDS). This specification is inferred from the first listener subscribed to a topic, or from an imported topic tree specification (see pub.addTopicDefnProvider()).

exception pubsub.pub.MessageDataSpecError(msg: str, args: Sequence[str])[source]

Bases: RuntimeError

Raised when an attempt is made to define a topic’s Message Data Specification (MDS) to something that is not valid.

The keyword names for invalid data go in the ‘args’ list, and the msg should state the problem and contain “%s” for the args, such as MessageDataSpecError(‘duplicate args %s’, (‘arg1’, ‘arg2’)).

pubsub.pub.AUTO_TOPIC[source]

Use this as default parameter in a listener’s signature: the listener will be given the Topic object of the message.

The following additional functions may be useful during debugging:

pubsub.pub.isValid(listener, topicName, curriedArgNames=None) bool[source]

Return true only if listener can subscribe to messages of given topic. If curriedArgNames can be a list of parameters of the given listener, that should be assumed curried (i.e. actual listener signature is signature of given listener minus curried args).

pubsub.pub.validate(listener, topicName, curriedArgNames=None)[source]

Checks if listener can subscribe to topicName. If not, raises ListenerMismatchError, otherwise just returns. The curriedArgNames is same as for isValid().

exception pubsub.pub.TopicDefnError(topicNameTuple: Sequence[str])[source]

Bases: RuntimeError

Raised when an operation requires a topic have an MDS, but it doesn’t. See also pub.setTopicUnspecifiedFatal().

Advanced use:

The following are not typically required but can be useful in certain circumstances, especially during debugging:

class pubsub.core.Listener(callable_obj: Callable[[...], Any], argsInfo: CallArgsInfo, curriedArgs: Mapping[str, Any] = None, onDead: Callable[[Listener], None] = None)[source]

Wraps a callable (UserListener) so it can be stored by weak reference and introspected to verify that it adheres to a topic’s MDS.

A Listener instance has the same hash value as the callable that it wraps.

Callables that have ‘argName=pub.AUTO_TOPIC’ as a kwarg will be given the Topic object for the message sent by sendMessage(). Such a Listener will have wantsTopicObjOnCall() True.

Callables that have a ‘** kargs’ argument will receive all message data, not just that for the topic they are subscribed to. Such a listener will have wantsAllMessageData() True.

See pubsub.core.Listener for details.

pubsub.core.getListenerID(callable_obj: Callable[[...], Any]) Tuple[str, module]

Get “ID” of a callable, in the form of its name and module in which it is defined E.g. getID(Foo.bar) returns (‘Foo.bar’, ‘a.b’) if Foo.bar was defined in module a.b. :param callable_obj: a callable, ie function, bound method or callable instance

Topics

In most cases, topics are used by name in dotted string format. The following may be useful for basic PyPubSub use:

exception pubsub.pub.TopicNameError(name: str, msg: str)[source]

Bases: ValueError

Raised when the topic name is not properly formatted or no corresponding Topic object found.

Advanced use:

Some advanced uses of PyPubSub, especially (but not only) for debugging a PyPubSub-based application, could require access to the associated Topic instance, topic tree manager, special topic-related constants, or other helper functions and classes.

class pubsub.pub.TopicTreeTraverser(visitor: ITopicTreeVisitor = None)[source]

Supports taking action on every topic in the topic tree. The traverse() method traverses a topic tree and calls visitor._onTopic() for each topic in the tree that satisfies visitor._accept(). Additionally it calls visitor._startChildren() whenever it starts traversing the subtopics of a topic, and visitor._endChildren() when it is done with the subtopics. Finally, it calls visitor._doneTraversal() when traversal has been completed. The visitor must therefore adhere to the ITopicTreeVisitor interface.

pubsub.pub.ALL_TOPICS

Name of topic that is root of topic tree. Subscribe a listener to this topic to get all PyPubSub messages. Use **kwargs to receive all message data, regardless of topic.

pubsub.pub.topicTreeRoot

The topic object that is parent of all root topics. The name of this topic is pub.ALL_TOPICS.

pubsub.pub.topicsMap

The dictionary that maps topic names to Topic objects.

Advanced use:

The following are not typically required but can be useful in certain circumstances, such as during debugging:

pubsub.pub.getDefaultTopicMgr() TopicManager[source]

Get the TopicManager instance created by default when this module is imported. This function is a shortcut for pub.getDefaultPublisher().getTopicMgr().

class pubsub.core.TopicManager(treeConfig: TreeConfig = None)[source]

Manages the registry of all topics and creation/deletion of topics.

Note that any method that accepts a topic name can accept it in the ‘dotted’ format such as 'a.b.c.' or in tuple format such as ('a', 'b', 'c'). Any such method will raise a ValueError if name not valid (empty, invalid characters, etc).

See pubsub.core.TopicManager for details.

class pubsub.core.Topic(treeConfig: TreeConfig, nameTuple: Tuple[str, ...], description: str, msgArgsInfo: ArgsInfo, parent: Topic = None)[source]

Represent topics in pubsub. Contains information about a topic, including topic’s message data specification (MDS), the list of subscribed listeners, docstring for the topic. It allows Python-like access to subtopics (e.g. A.B is subtopic B of topic A).

See pubsub.core.Topic for details.

Listener Exception Handling

Listeners that leak exceptions are typically burried deep into the stacktrace, and can cause an application to abort. The following may simplify the task of providing useful error messages from misbehaved listeners, without interrupting the application or even the PyPubSub send-message:

pubsub.pub.getListenerExcHandler()

Get the listener exception handler that was registered via setListenerExcHandler(), or None of none registered.

pubsub.pub.setListenerExcHandler(handler)

Set the function to call when a listener raises an exception during a sendMessage().

class pubsub.pub.IListenerExcHandler[source]

Interface class base class for any handler given to pub.setListenerExcHandler() Such handler is called whenever a listener raises an exception during a pub.sendMessage(). Example:

from pubsub import pub

class MyHandler(pub.IListenerExcHandler):
    def __call__(self, listenerID, topicObj):
        ... do something with listenerID ...

pub.setListenerExcHandler(MyHandler())

Without an exception handler, the sendMessage() will fail.

exception pubsub.pub.ExcHandlerError(badExcListenerID: str, topicObj: Topic, origExc: Exception = None)[source]

Bases: RuntimeError

Raised when a listener exception handler (see pub.setListenerExcHandler()) raises an exception. The original exception is contained.

See pubsub.utils.exchandling for ready-made exception handlers which may fit your requirements.

PyPubSub Tracing (aka Notification)

While debugging an application it may be useful to trap some of PyPubSub’s activity:

class pubsub.pub.INotificationHandler[source]

Defines the interface expected by pubsub for pubsub activity notifications. Any instance that supports the same methods, or derives from this class, will work as a notification handler for pubsub events (see pub.addNotificationHandler).

pubsub.pub.addNotificationHandler(handler)

Add a handler for tracing pubsub activity.

pubsub.pub.clearNotificationHandlers()

Remove all notification handlers that were added via self.addNotificationHandler().

pubsub.pub.setNotificationFlags(**kwargs)

Set the notification flags on or off for each type of pubsub activity. The kwargs keys can be any of the following:

  • subscribe: if True, get notified whenever a listener subscribes to a topic;

  • unsubscribe: if True, get notified whenever a listener unsubscribes from a topic;

  • deadListener: if True, get notified whenever a subscribed listener has been garbage-collected;

  • sendMessage: if True, get notified whenever sendMessage() is called;

  • newTopic: if True, get notified whenever a new topic is created;

  • delTopic: if True, get notified whenever a topic is “deleted” from topic tree;

  • all: set all of the above to the given value (True or False).

The kwargs that are not given or None are left at their current value. Those that are False will cause corresponding notification to be silenced. The ‘all’ is set first, then the others. E.g.

mgr.setFlagStates(all=True, delTopic=False)

will toggle all notifications on, but will turn off the ‘delTopic’ notification.

pubsub.pub.getNotificationFlags()

Return a dictionary with the notification flag states.

See pubsub.utils for some ready-made notification handlers which may fit your requirements.

Topic Specification

Topic definition, documentation, and message data specification (MDS):

exception pubsub.pub.TopicDefnError(topicNameTuple: Sequence[str])[source]

Bases: RuntimeError

Raised when an operation requires a topic have an MDS, but it doesn’t. See also pub.setTopicUnspecifiedFatal().

pubsub.pub.exportTopicTreeSpec(moduleName: str = None, rootTopic: Topic | str = None, bak: str = 'bak', moduleDoc: str = None)[source]

Using TopicTreeSpecPrinter, exports the topic tree rooted at rootTopic to a Python module (.py) file. This module will define module-level classes representing root topics, nested classes for subtopics etc. Returns a string representing the contents of the file. Parameters:

  • If moduleName is given, the topic tree is written to moduleName.py in os.getcwd(). By default, it is first backed up, it it already exists, using bak as the filename extension. If bak is None, existing module file gets overwritten.

  • If rootTopic is specified, the export only traverses tree from corresponding topic. Otherwise, complete tree, using pub.getDefaultTopicTreeRoot() as starting point.

  • The moduleDoc is the doc string for the module ie topic tree.

pubsub.pub.setTopicUnspecifiedFatal(newVal=True, checkExisting=True)

Changes the creation policy for topics.

By default, pubsub will accept topic names for topics that don’t have a message data specification (MDS). This default behavior makes pubsub easier to use initially, but allows topic names with typos to go uncaught in common operations such as sendMessage() and subscribe(). In a large application, this can lead to nasty bugs. Pubsub’s default behavior is equivalent to setTopicUnspecifiedFatal(false).

When called with newVal=True, any future pubsub operation that requires a topic (such as subscribe and sendMessage) will require an MDS; if none is available, pubsub will raise a TopicDefnError exception.

If checkExisting is not given or True, all existing topics are validated. A TopicDefnError exception is raised if one is found to be incomplete (has hasMDS() false).

Returns previous value of newVal.

Note that this method can be used in several ways:

  1. Only use it in your application when something is not working as expected: just add a call at the beginning of your app when you have a problem with topic messages not being received (for instance), and remove it when you have fixed the problem.

  2. Use it from the beginning of your app and never use newVal=False: add a call at the beginning of your app and you leave it in (forever), and use Topic Definition Providers to provide the listener specifications. These are easy to use via the pub.addTopicDefnProvider().

  3. Use it as in #1 during app development, and once stable, use #2. This is easiest to do in combination with pub.exportTopicTreeSpec().

pubsub.pub.addTopicDefnProvider(providerOrSource, format=None)

Register a topic definition provider. After this method is called, whenever a topic must be created, the first definition provider that has a definition for the required topic is used to instantiate the topic.

If providerOrSource is an instance of ITopicDefnProvider, register it as a provider of topic definitions. Otherwise, register a new instance of TopicDefnProvider(providerOrSource, format). In that case, if format is not given, it defaults to TOPIC_TREE_FROM_MODULE. Either way, returns the instance of ITopicDefnProvider registered.

pubsub.pub.getNumTopicDefnProviders()

Get how many topic definitions providers are registered.

pubsub.pub.clearTopicDefnProviders()

Remove all registered topic definition providers

pubsub.pub.instantiateAllDefinedTopics(provider)[source]

Loop over all topics of given provider and “instantiate” each topic, thus forcing a parse of the topics documentation, message data specification (MDS), comparison with parent MDS, and MDS documentation. Without this function call, an error among any of those characteristics will manifest only if the a listener is registered on it.

exception pubsub.pub.UnrecognizedSourceFormatError[source]

Bases: ValueError

Raised when a topic definition provider doesn’t recognize the format of source input it was given.

pubsub.pub.TOPIC_TREE_FROM_MODULE = 'module'

str(object=’’) -> str str(bytes_or_buffer[, encoding[, errors]]) -> str

Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.__str__() (if defined) or repr(object). encoding defaults to sys.getdefaultencoding(). errors defaults to ‘strict’.

Provide to pub.addTopicDefnProvider() as value for format parameter when the source is a module which has been imported. The module can contain any number of classes, the names of which correspond to root topics.

pubsub.pub.TOPIC_TREE_FROM_CLASS = 'class'

str(object=’’) -> str str(bytes_or_buffer[, encoding[, errors]]) -> str

Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.__str__() (if defined) or repr(object). encoding defaults to sys.getdefaultencoding(). errors defaults to ‘strict’.

Provide to pub.addTopicDefnProvider() as value for format parameter when the source is a class. The class contains, as nested classes, the root topics (and those contain nested classes for subtopics, etc).

pubsub.pub.TOPIC_TREE_FROM_STRING = 'string'

str(object=’’) -> str str(bytes_or_buffer[, encoding[, errors]]) -> str

Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.__str__() (if defined) or repr(object). encoding defaults to sys.getdefaultencoding(). errors defaults to ‘strict’.

Provide to pub.addTopicDefnProvider() as value for format parameter when the source is a string. The string contains Python code that defines one class for each root topic (and those contain nested classes for subtopics, etc).

Developer:

The following are useful to extend the capabilities of PyPubSub to support more topic definition providers or serialization formats for the builtin provider:

class pubsub.core.ITopicDefnProvider[source]

All topic definition providers added via pub.addTopicDefnProvider() must have this interface. Derived classes must override the getDefn(), getTreeDoc() and topicNames() methods.

class pubsub.core.ITopicDefnDeserializer[source]

Interface class for all topic definition de-serializers that can be accepted by TopicDefnProvider. A deserializer creates a topic tree from something such as file, module, or string.

class pubsub.core.TopicDefnProvider(source: Any, format: str, **providerKwargs)[source]

Bases: ITopicDefnProvider

Default implementation of the ITopicDefnProvider API. This implementation accepts several formats for the topic tree source data and delegates to a registered ITopicDefnDeserializer that converts source data into topic definitions.

This provider is instantiated automatically by pub.addTopicDefnProvider(source, format) when source is not an ITopicDefnProvider.

Additional de-serializers can be registered via registerTypeForImport().