Maintainability
===============


.. _label-topic_tree_def:

Specify topic tree def
----------------------

*Topic Specification* can be used to have better control over your topic hierarchy. 
If you don't specify your application's topics, PyPubSub infers them from the first
subscribed listener of each topic. E.g.::

    def listener1(arg1,      arg2=None): pass
    def listener2(arg1=None, arg2=None): pass
    
    pub.subscribe(listener1, 'topic.sub') 
    pub.subscribe(listener2, 'topic.sub')
    
Because listener1 is the first to be subscribed to 'topic.sub' topic, PyPubSub uses it to
infer the specification of 'topic.sub': the specification is "messages of that topic 
*must* provide data for arg1, and *may* provide data for arg2". The second listener 
subscribed, listener2, is allowed to subscribe because it is compatible with the 
topic's specification created at the previous call. What if your intent was that arg1 is 
optional as well, i.e. the signature of listener1 is wrong (it should provide a default 
value for arg1)? Or what if per chance listener2 gets subscribed first (could happen if 
both are subscribed in different modules whose load order changes)?

The only way to not depend on the order of subscription of listeners is to use
*Topic definition providers* (TDP). This is described below.


Topic Definition Providers
^^^^^^^^^^^^^^^^^^^^^^^^^^

The easiest way to understand a topic tree definition is to get PyPubSub to output one
for your application via ``pub.exportTopicTreeSpec()``. Here is an example, taken from 
the file examples/advanced/kwargs_topics.py generated by that function, assuming 
two root topics 'topic_1' and 'topic_2' and the call 
``pub.exportTopicTreeSpec('kwargs_topics')``:

.. literalinclude:: ../../examples/advanced/kwargs_topics.py

This shows how the topic definition tree is defined using a Python module with 
a nested class tree that represents the topics, and msgDataSpec() functions 
that represent the listener signatures for the given topics. This also shows 
how it is possible to document each topic and message datum. 

An application uses the above module via the following::

    import kwargs_topics
    pub.addTopicDefnProvider( kwargs_topics, pub.TOPIC_TREE_FROM_CLASS )

The format type is ``pub.TOPIC_TREE_FROM_CLASS`` because once imported, the 
kwargs_topics object is a module containing topic definitions as classes; based
on that setting, PyPubSub will look for all classes in the kwargs_topics object,
and instantiate one topic definition for each one. 

See examples/advanced/main_kwargs.py for an example of using a topic tree
definition in an application. 

It is possible to support other formats for topic tree definition. For example, 
``pubsub.utils.XmlTopicDefnProvider`` was contributed to PyPubSub by one of its
devoted users. A new type of provider need only adhere to 
the ``pub.ITopicTreeDefnProvider`` interface; ``pub.addTopicDefnProvider()`` 
accepts any instance that implements from that interface::

    xmlString = open('xml_topics.xml', 'r').read()
    provider = XmlTopicDefnProvider(xmlString)
    pub.addTopicDefnProvider( provider )

It is typically useful to combine topic tree definition with the following 
call, placed once at the beginning of an application::

    pub.setTopicUnspecifiedFatal(True)

Then any attempt to use a topic that is not defined in the topic tree definition
will raise an ``pub.TopicUnspecifiedError``.

Note that any topic that does not have a docstring is not considered 
to be defined. This may allow for some temporary "undefining" of topics. 
