mirror of
https://github.com/wxWidgets/Phoenix.git
synced 2026-01-08 04:50:07 +01:00
Update from r343 of pubsub trunk/src/pubsub on SF.net
git-svn-id: https://svn.wxwidgets.org/svn/wx/wxPython/Phoenix/trunk@75301 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
23
wx/lib/pubsub/LICENSE_BSD_Simple.txt
Normal file
23
wx/lib/pubsub/LICENSE_BSD_Simple.txt
Normal file
@@ -0,0 +1,23 @@
|
||||
Copyright (c) since 2006, Oliver Schoenborn
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
21
wx/lib/pubsub/README.txt
Normal file
21
wx/lib/pubsub/README.txt
Normal file
@@ -0,0 +1,21 @@
|
||||
PyPubSub provides a publish - subscribe API that facilitates the development of
|
||||
event-based (also known as message-based) applications. PyPubSub supports sending and
|
||||
receiving messages between objects of an application, as well as a variety of
|
||||
advanced features that facilitate debugging and maintaining topics and messages
|
||||
in larger applications.
|
||||
|
||||
See the PyPubSub website (http://pubsub.sourceforge.net/) for
|
||||
further details, and to download.
|
||||
|
||||
In order for easy_install to find the correct download links, they are listed here:
|
||||
|
||||
* `Windows Installer <http://downloads.sf.net/project/pubsub/pubsub/3.2.0/PyPubSub-3.2.0.win32.exe>`_
|
||||
* `Egg for Python 2.7 <https://downloads.sourceforge.net/project/pubsub/pubsub/3.2.0/PyPubSub-3.2.0-py2.7.egg>`_
|
||||
* `Egg for Python 2.6 <https://downloads.sourceforge.net/project/pubsub/pubsub/3.2.0/PyPubSub-3.2.0-py2.6.egg>`_
|
||||
* `Egg for Python 2.5 <https://downloads.sourceforge.net/project/pubsub/pubsub/3.2.0/PyPubSub-3.2.0-py2.5.egg>`_
|
||||
* `Egg for Python 2.4 <https://downloads.sourceforge.net/project/pubsub/pubsub/3.2.0/PyPubSub-3.2.0-py2.4.egg>`_
|
||||
* `Zip (source) <http://downloads.sf.net/project/pubsub/pubsub/3.2.0/PyPubSub-3.2.0.zip>`_
|
||||
|
||||
Note: "easy_install pypubsub" will install the egg, which is a zip file, whereas
|
||||
"easy_install -Z pypubsub" will extract the egg contents to a folder; this option will
|
||||
make importing pubsub much faster.
|
||||
71
wx/lib/pubsub/RELEASE_NOTES.txt
Normal file
71
wx/lib/pubsub/RELEASE_NOTES.txt
Normal file
@@ -0,0 +1,71 @@
|
||||
For PyPubSub v3.3.0
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
* cleanup low-level API: exception classes, moved some out of pub module that did not
|
||||
belong there (clutter), move couple modules,
|
||||
* completed the reference documentation
|
||||
* support installation via pip
|
||||
* follow some guidelines in some PEPs such as PEP 396 and PEP 8
|
||||
* support Python 2.6, 2.7, and 3.2 to 3.4a4 but drop support for Python <= 2.5
|
||||
|
||||
For PyPubSub v3.2.0
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
This is a minor release for small improvements made (see docs/CHANGELOG.txt)
|
||||
based on feedback from user community. In particular an XML reader for
|
||||
topic specification contributed by Josh English. Also cleaned up the
|
||||
documentation, updated the examples folder (available in source distribution
|
||||
as well as `online`_).
|
||||
|
||||
.. _online: https://sourceforge.net/p/pubsub/code/HEAD/tree/
|
||||
|
||||
Only 3 changes to API (function names):
|
||||
|
||||
* renamed pub.getDefaultRootAllTopics to pub.getDefaultTopicTreeRoot
|
||||
* removed pub.importTopicTree: use pub.addTopicDefnProvider(source, format)
|
||||
* renamed pub.exportTopicTree to pub.exportTopicTreeSpec
|
||||
|
||||
Oliver Schoenborn
|
||||
September 2013
|
||||
|
||||
|
||||
PyPubSub 3.1.2
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
This is a minor release for small improvements made (see docs/CHANGELOG.txt)
|
||||
based on feedback from user community. Also extended the documentation. See
|
||||
pubsub.sourceforge.net for installation and usage. See the examples folder for
|
||||
some useful examples.
|
||||
|
||||
Oliver Schoenborn
|
||||
Nov 2011
|
||||
|
||||
|
||||
PyPubSub 3.1.1b1
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Docs updated.
|
||||
|
||||
Oliver Schoenborn
|
||||
May 2010
|
||||
|
||||
|
||||
For PyPubSub v3.1.0b1
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Major cleanup of the API since 3.0 and better support
|
||||
for the legacy wxPython code. Defining a topic tree
|
||||
via a text file has been improved drastically, making it
|
||||
simpler to document topic messages and payload data
|
||||
required or optional. More examples have been added,
|
||||
and the messaging protocols clarified.
|
||||
|
||||
The included docs are not yet updated, that's what I'm
|
||||
working on now and will lead to the 3.1.1b1 release.
|
||||
I'm also working on an add-on module that would allow
|
||||
two applications to communicate over the network using
|
||||
pubsub-type messaging (with topics, etc). The design
|
||||
is almost complete.
|
||||
|
||||
Oliver Schoenborn
|
||||
Jan 2010
|
||||
@@ -1,14 +1,14 @@
|
||||
'''
|
||||
"""
|
||||
Pubsub package initialization.
|
||||
|
||||
:copyright: Copyright since 2006 by Oliver Schoenborn, all rights reserved.
|
||||
:license: BSD, see LICENSE_BSD_Simple.txt for details.
|
||||
|
||||
Last change info:
|
||||
- $Date: 2013-10-10 01:15:37 -0400 (Thu, 10 Oct 2013) $
|
||||
- $Revision: 329 $
|
||||
- $Date: 2013-11-17 13:09:24 -0500 (Sun, 17 Nov 2013) $
|
||||
- $Revision: 340 $
|
||||
|
||||
'''
|
||||
"""
|
||||
|
||||
LAST_RELEASE_DATE = "2013-09-15"
|
||||
LAST_RELEASE_VER = "3.2.1b"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
'''
|
||||
"""
|
||||
Core package of pubsub, holding the publisher, listener, and topic
|
||||
object modules. Functions defined here are used internally by
|
||||
pubsub so that the right modules can be found later, based on the
|
||||
@@ -26,13 +26,13 @@ _prependModulePath() at end of this file.
|
||||
:copyright: Copyright since 2006 by Oliver Schoenborn, all rights reserved.
|
||||
:license: BSD, see LICENSE_BSD_Simple.txt for details.
|
||||
|
||||
'''
|
||||
"""
|
||||
|
||||
|
||||
def _prependModulePath(extra):
|
||||
'''Insert extra at beginning of package's path list. Should only be
|
||||
"""Insert extra at beginning of package's path list. Should only be
|
||||
called once, at package load time, to set the folder used for
|
||||
implementation specific to the default message protocol.'''
|
||||
implementation specific to the default message protocol."""
|
||||
corepath = __path__
|
||||
initpyLoc = corepath[-1]
|
||||
import os
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
'''
|
||||
"""
|
||||
This is not really a package init file, it is only here to simplify the
|
||||
packaging and installation of pubsub.core's protocol-specific subfolders
|
||||
by setuptools. The python modules in this folder are automatically made
|
||||
@@ -9,7 +9,7 @@ protocol is "arg1" (and not usable otherwise).
|
||||
:copyright: Copyright since 2006 by Oliver Schoenborn, all rights reserved.
|
||||
:license: BSD, see LICENSE_BSD_Simple.txt for details.
|
||||
|
||||
'''
|
||||
"""
|
||||
|
||||
|
||||
msg = 'Should not import this directly, used by pubsub.core if applicable'
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
'''
|
||||
"""
|
||||
:copyright: Copyright since 2006 by Oliver Schoenborn, all rights reserved.
|
||||
:license: BSD, see LICENSE_BSD_Simple.txt for details.
|
||||
'''
|
||||
"""
|
||||
|
||||
from .listenerbase import (ListenerBase, ValidatorBase)
|
||||
from .callables import ListenerMismatchError
|
||||
@@ -25,7 +25,7 @@ class Message:
|
||||
|
||||
|
||||
class Listener(ListenerBase):
|
||||
'''
|
||||
"""
|
||||
Wraps a callable so it can be stored by weak reference and introspected
|
||||
to verify that it adheres to a topic's MDS.
|
||||
|
||||
@@ -35,12 +35,12 @@ class Listener(ListenerBase):
|
||||
protocol only one object can be sent via sendMessage, it is put in a
|
||||
Message object in its "data" field, the listener receives the Message
|
||||
object.
|
||||
'''
|
||||
"""
|
||||
|
||||
def __call__(self, actualTopic, data):
|
||||
'''Call the listener with data. Note that it raises RuntimeError
|
||||
"""Call the listener with data. Note that it raises RuntimeError
|
||||
if listener is dead. Should always return True (False would require
|
||||
the callable_ be dead but self hasn't yet been notified of it...).'''
|
||||
the callable_ be dead but self hasn't yet been notified of it...)."""
|
||||
kwargs = {}
|
||||
if self._autoTopicArgName is not None:
|
||||
kwargs[self._autoTopicArgName] = actualTopic
|
||||
@@ -53,11 +53,11 @@ class Listener(ListenerBase):
|
||||
|
||||
|
||||
class ListenerValidator(ValidatorBase):
|
||||
'''
|
||||
"""
|
||||
Accept one arg or *args; accept any **kwarg,
|
||||
and require that the Listener have at least all the kwargs (can
|
||||
have extra) of Topic.
|
||||
'''
|
||||
"""
|
||||
|
||||
def _validateArgs(self, listener, paramsInfo):
|
||||
# accept **kwargs
|
||||
|
||||
@@ -1,32 +1,32 @@
|
||||
'''
|
||||
"""
|
||||
Mixin for publishing messages to a topic's listeners. This will be
|
||||
mixed into topicobj.Topic so that a user can use a Topic object to
|
||||
send a message to the topic's listeners via a publish() method.
|
||||
|
||||
:copyright: Copyright since 2006 by Oliver Schoenborn, all rights reserved.
|
||||
:license: BSD, see LICENSE_BSD_Simple.txt for details.
|
||||
'''
|
||||
"""
|
||||
|
||||
|
||||
from .publisherbase import PublisherBase
|
||||
|
||||
|
||||
class Publisher(PublisherBase):
|
||||
'''
|
||||
"""
|
||||
Publisher that allows old-style Message.data messages to be sent
|
||||
to listeners. Listeners take one arg (required, unless there is an
|
||||
*arg), but can have kwargs (since they have default values).
|
||||
'''
|
||||
"""
|
||||
|
||||
def sendMessage(self, topicName, data=None):
|
||||
'''Send message of type topicName to all subscribed listeners,
|
||||
"""Send message of type topicName to all subscribed listeners,
|
||||
with message data. If topicName is a subtopic, listeners
|
||||
of topics more general will also get the message.
|
||||
|
||||
Note that any listener that lets a raised exception escape will
|
||||
interrupt the send operation, unless an exception handler was
|
||||
specified via pub.setListenerExcHandler().
|
||||
'''
|
||||
"""
|
||||
topicMgr = self.getTopicMgr()
|
||||
topicObj = topicMgr.getOrCreateTopic(topicName)
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
'''
|
||||
"""
|
||||
Mixin for publishing messages to a topic's listeners. This will be
|
||||
mixed into topicobj.Topic so that a user can use a Topic object to
|
||||
send a message to the topic's listeners via a publish() method.
|
||||
@@ -12,7 +12,7 @@ loop).
|
||||
:copyright: Copyright since 2006 by Oliver Schoenborn, all rights reserved.
|
||||
:license: BSD, see LICENSE_BSD_Simple.txt for details.
|
||||
|
||||
'''
|
||||
"""
|
||||
|
||||
|
||||
class PublisherMixin:
|
||||
@@ -25,10 +25,10 @@ class PublisherMixin:
|
||||
############## IMPLEMENTATION ###############
|
||||
|
||||
def _mix_prePublish(self, data, topicObj=None, iterState=None):
|
||||
'''Called just before the __sendMessage, to perform any argument
|
||||
checking, set iterState, etc'''
|
||||
"""Called just before the __sendMessage, to perform any argument
|
||||
checking, set iterState, etc"""
|
||||
return None
|
||||
|
||||
def _mix_callListener(self, listener, data, iterState):
|
||||
'''Send the data to given listener.'''
|
||||
"""Send the data to given listener."""
|
||||
listener(self, data)
|
||||
|
||||
@@ -1,29 +1,29 @@
|
||||
'''
|
||||
"""
|
||||
|
||||
:copyright: Copyright since 2006 by Oliver Schoenborn, all rights reserved.
|
||||
:license: BSD, see LICENSE_BSD_Simple.txt for details.
|
||||
|
||||
'''
|
||||
"""
|
||||
|
||||
import weakref
|
||||
from .topicutils import WeakNone
|
||||
|
||||
class SenderMissingReqdMsgDataError(RuntimeError):
|
||||
'''
|
||||
"""
|
||||
Ignore: Not used for this message protocol.
|
||||
'''
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class SenderUnknownMsgDataError(RuntimeError):
|
||||
'''
|
||||
"""
|
||||
Ignore: Not used for this message protocol.
|
||||
'''
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class ArgsInfo:
|
||||
'''
|
||||
"""
|
||||
Encode the Message Data Specification (MDS) for a given
|
||||
topic. In the arg1 protocol of pubsub, this MDS is the same for all
|
||||
topics, so this is quite a simple class, required only because
|
||||
@@ -35,7 +35,7 @@ class ArgsInfo:
|
||||
Note that the MDS is always complete because it is known:
|
||||
it consists of one required argument named 'data' and no
|
||||
optional arguments.
|
||||
'''
|
||||
"""
|
||||
|
||||
SPEC_MISSING = 10 # no args given
|
||||
SPEC_COMPLETE = 12 # all args, but not confirmed via user spec
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
'''
|
||||
"""
|
||||
The root topic of all topics is different based on messaging protocol.
|
||||
|
||||
:copyright: Copyright since 2006 by Oliver Schoenborn, all rights reserved.
|
||||
:license: BSD, see LICENSE_BSD_Simple.txt for details.
|
||||
|
||||
'''
|
||||
"""
|
||||
|
||||
from .. import policies
|
||||
|
||||
|
||||
def getRootTopicSpec():
|
||||
'''If using "arg1" messaging protocol, then root topic has one arg;
|
||||
if policies.msgDataArgName is something, then use it as arg name.'''
|
||||
"""If using "arg1" messaging protocol, then root topic has one arg;
|
||||
if policies.msgDataArgName is something, then use it as arg name."""
|
||||
argName = policies.msgDataArgName or 'data'
|
||||
argsDocs = {argName : 'data for message sent'}
|
||||
reqdArgs = (argName,)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
'''
|
||||
"""
|
||||
Low level functions and classes related to callables.
|
||||
|
||||
The AUTO_TOPIC
|
||||
@@ -10,7 +10,7 @@ CallArgsInfo regarding its autoTopicArgName data member.
|
||||
:copyright: Copyright since 2006 by Oliver Schoenborn, all rights reserved.
|
||||
:license: BSD, see LICENSE_BSD_Simple.txt for details.
|
||||
|
||||
'''
|
||||
"""
|
||||
|
||||
from inspect import getargspec, ismethod, isfunction
|
||||
|
||||
@@ -20,9 +20,9 @@ AUTO_TOPIC = '## your listener wants topic name ## (string unlikely to be use
|
||||
|
||||
|
||||
def getModule(obj):
|
||||
'''Get the module in which an object was defined. Returns '__main__'
|
||||
"""Get the module in which an object was defined. Returns '__main__'
|
||||
if no module defined (which usually indicates either a builtin, or
|
||||
a definition within main script). '''
|
||||
a definition within main script). """
|
||||
if hasattr(obj, '__module__'):
|
||||
module = obj.__module__
|
||||
else:
|
||||
@@ -31,10 +31,10 @@ def getModule(obj):
|
||||
|
||||
|
||||
def getID(callable_):
|
||||
'''Get name and module name for a callable, ie function, bound
|
||||
"""Get name and module name for a callable, ie function, bound
|
||||
method or callable instance, by inspecting the callable. E.g.
|
||||
getID(Foo.bar) returns ('Foo.bar', 'a.b') if Foo.bar was
|
||||
defined in module a.b. '''
|
||||
defined in module a.b. """
|
||||
sc = callable_
|
||||
if ismethod(sc):
|
||||
module = getModule(sc.__self__)
|
||||
@@ -50,11 +50,11 @@ def getID(callable_):
|
||||
|
||||
|
||||
def getRawFunction(callable_):
|
||||
'''Given a callable, return (offset, func) where func is the
|
||||
"""Given a callable, return (offset, func) where func is the
|
||||
function corresponding to callable, and offset is 0 or 1 to
|
||||
indicate whether the function's first argument is 'self' (1)
|
||||
or not (0). Raises ValueError if callable_ is not of a
|
||||
recognized type (function, method or has __call__ method).'''
|
||||
recognized type (function, method or has __call__ method)."""
|
||||
firstArg = 0
|
||||
if isfunction(callable_):
|
||||
#print 'Function', getID(callable_)
|
||||
@@ -77,13 +77,13 @@ def getRawFunction(callable_):
|
||||
|
||||
|
||||
class ListenerMismatchError(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()).
|
||||
'''
|
||||
"""
|
||||
|
||||
def __init__(self, msg, listener, *args):
|
||||
idStr, module = getID(listener)
|
||||
@@ -99,13 +99,13 @@ class ListenerMismatchError(ValueError):
|
||||
|
||||
|
||||
class CallArgsInfo:
|
||||
'''
|
||||
"""
|
||||
Represent the "signature" or protocol of a listener in the context of
|
||||
topics.
|
||||
'''
|
||||
"""
|
||||
|
||||
def __init__(self, func, firstArgIdx): #args, firstArgIdx, defaultVals, acceptsAllKwargs=False):
|
||||
'''Inputs:
|
||||
"""Inputs:
|
||||
- Args and defaultVals are the complete set of arguments and
|
||||
default values as obtained form inspect.getargspec();
|
||||
- The firstArgIdx points to the first item in
|
||||
@@ -130,7 +130,7 @@ class CallArgsInfo:
|
||||
self.autoTopicArgName = 'arg2', whereas
|
||||
listener(self, arg1, arg3=None) will have
|
||||
self.allParams = (arg1, arg3), self.numRequired=1, and
|
||||
self.autoTopicArgName = None.'''
|
||||
self.autoTopicArgName = None."""
|
||||
|
||||
#args, firstArgIdx, defaultVals, acceptsAllKwargs
|
||||
(allParams, varParamName, varOptParamName, defaultVals) = getargspec(func)
|
||||
@@ -160,13 +160,13 @@ class CallArgsInfo:
|
||||
return tuple( self.allParams[self.numRequired:] )
|
||||
|
||||
def getRequiredArgs(self):
|
||||
'''Return a tuple of names indicating which call arguments
|
||||
are required to be present when pub.sendMessage(...) is called. '''
|
||||
"""Return a tuple of names indicating which call arguments
|
||||
are required to be present when pub.sendMessage(...) is called. """
|
||||
return tuple( self.allParams[:self.numRequired] )
|
||||
|
||||
def __setupAutoTopic(self, defaults):
|
||||
'''Does the listener want topic of message? Returns < 0 if not,
|
||||
otherwise return index of topic kwarg within args.'''
|
||||
"""Does the listener want topic of message? Returns < 0 if not,
|
||||
otherwise return index of topic kwarg within args."""
|
||||
for indx, defaultVal in enumerate(defaults):
|
||||
if defaultVal == AUTO_TOPIC:
|
||||
#del self.defaults[indx]
|
||||
@@ -176,8 +176,8 @@ class CallArgsInfo:
|
||||
|
||||
|
||||
def getArgs(callable_):
|
||||
'''Returns an instance of CallArgsInfo for the given callable_.
|
||||
Raises ListenerMismatchError if callable_ is not a callable.'''
|
||||
"""Returns an instance of CallArgsInfo for the given callable_.
|
||||
Raises ListenerMismatchError if callable_ is not a callable."""
|
||||
# figure out what is the actual function object to inspect:
|
||||
try:
|
||||
func, firstArgIdx = getRawFunction(callable_)
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
'''
|
||||
"""
|
||||
The _resolve_name and _import_module were taken from the backport of
|
||||
importlib.import_module from 3.x to 2.7. Thanks to the Python developers
|
||||
for making this available as a standalone module. This makes it possible
|
||||
to have an import module that mimics the "import" statement more
|
||||
closely.
|
||||
'''
|
||||
"""
|
||||
|
||||
import sys
|
||||
from .. import py2and3
|
||||
|
||||
def _resolve_name(name, package, level):
|
||||
'''Return the absolute name of the module to be imported.'''
|
||||
"""Return the absolute name of the module to be imported."""
|
||||
if not hasattr(package, 'rindex'):
|
||||
raise ValueError("'package' not set to a string")
|
||||
dot = len(package)
|
||||
@@ -24,12 +24,12 @@ def _resolve_name(name, package, level):
|
||||
|
||||
|
||||
def _import_module(name, package=None):
|
||||
'''Import a module.
|
||||
"""Import a module.
|
||||
|
||||
The 'package' argument is required when performing a relative import. It
|
||||
specifies the package to use as the anchor point from which to resolve the
|
||||
relative import to an absolute import.
|
||||
'''
|
||||
"""
|
||||
if name.startswith('.'):
|
||||
if not package:
|
||||
raise TypeError("relative imports require the 'package' argument")
|
||||
@@ -44,8 +44,8 @@ def _import_module(name, package=None):
|
||||
|
||||
|
||||
def load_module(moduleName, searchPath):
|
||||
'''Try to import moduleName. If this doesn't work, use the "imp" module
|
||||
that is part of Python. '''
|
||||
"""Try to import moduleName. If this doesn't work, use the "imp" module
|
||||
that is part of Python. """
|
||||
try:
|
||||
module = _import_module(moduleName)
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
'''
|
||||
"""
|
||||
This is not really a package init file, it is only here to simplify the
|
||||
packaging and installation of pubsub.core's protocol-specific subfolders
|
||||
by setuptools. The python modules in this folder are automatically made
|
||||
@@ -9,7 +9,7 @@ protocol is "kwargs" (and not usable otherwise).
|
||||
:copyright: Copyright since 2006 by Oliver Schoenborn, all rights reserved.
|
||||
:license: BSD, see LICENSE_BSD_Simple.txt for details.
|
||||
|
||||
'''
|
||||
"""
|
||||
|
||||
|
||||
msg = 'Should not import this directly, used by pubsub.core if applicable'
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
'''
|
||||
"""
|
||||
:copyright: Copyright since 2006 by Oliver Schoenborn, all rights reserved.
|
||||
:license: BSD, see LICENSE_BSD_Simple.txt for details.
|
||||
'''
|
||||
"""
|
||||
|
||||
class Message:
|
||||
'''
|
||||
"""
|
||||
A simple container object for the two components of a message in the
|
||||
arg1 messaging protocol: the
|
||||
topic and the user data. Each listener called by sendMessage(topic, data)
|
||||
@@ -17,7 +17,7 @@ class Message:
|
||||
print msg
|
||||
|
||||
The example also shows (last line) how a message is convertible to a string.
|
||||
'''
|
||||
"""
|
||||
def __init__(self, topic, data):
|
||||
self.topic = topic
|
||||
self.data = data
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
'''
|
||||
"""
|
||||
|
||||
:copyright: Copyright since 2006 by Oliver Schoenborn, all rights reserved.
|
||||
:license: BSD, see LICENSE_BSD_Simple.txt for details.
|
||||
|
||||
'''
|
||||
"""
|
||||
|
||||
from .listenerbase import ListenerBase, ValidatorBase
|
||||
from .callables import ListenerMismatchError
|
||||
|
||||
|
||||
class Listener(ListenerBase):
|
||||
'''
|
||||
"""
|
||||
Wraps a callable so it can be stored by weak reference and introspected
|
||||
to verify that it adheres to a topic's MDS.
|
||||
|
||||
@@ -24,12 +24,12 @@ class Listener(ListenerBase):
|
||||
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.
|
||||
'''
|
||||
"""
|
||||
|
||||
def __call__(self, kwargs, actualTopic, allKwargs=None):
|
||||
'''Call the listener with **kwargs. Note that it raises RuntimeError
|
||||
"""Call the listener with **kwargs. Note that it raises RuntimeError
|
||||
if listener is dead. Should always return True (False would require
|
||||
the callable_ be dead but self hasn't yet been notified of it...).'''
|
||||
the callable_ be dead but self hasn't yet been notified of it...)."""
|
||||
if self.acceptsAllKwargs:
|
||||
kwargs = allKwargs or kwargs # if allKwargs is None then use kwargs
|
||||
|
||||
@@ -46,11 +46,11 @@ class Listener(ListenerBase):
|
||||
|
||||
|
||||
class ListenerValidator(ValidatorBase):
|
||||
'''
|
||||
"""
|
||||
Do not accept any required args or *args; accept any **kwarg,
|
||||
and require that the Listener have at least all the kwargs (can
|
||||
have extra) of Topic.
|
||||
'''
|
||||
"""
|
||||
|
||||
def _validateArgs(self, listener, paramsInfo):
|
||||
# accept **kwargs
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
'''
|
||||
"""
|
||||
:copyright: Copyright since 2006 by Oliver Schoenborn, all rights reserved.
|
||||
:license: BSD, see LICENSE_BSD_Simple.txt for details.
|
||||
'''
|
||||
"""
|
||||
|
||||
|
||||
from .publisherbase import PublisherBase
|
||||
@@ -11,17 +11,17 @@ from .. import (policies, py2and3)
|
||||
|
||||
|
||||
class Publisher(PublisherBase):
|
||||
'''
|
||||
"""
|
||||
Publisher used for kwargs protocol, ie when sending message data
|
||||
via keyword arguments.
|
||||
'''
|
||||
"""
|
||||
|
||||
def sendMessage(self, topicName, **kwargs):
|
||||
'''Send a message.
|
||||
"""Send a message.
|
||||
|
||||
:param topicName: name of message topic (dotted or tuple format)
|
||||
:param kwargs: message data (must satisfy the topic's MDS)
|
||||
'''
|
||||
"""
|
||||
topicMgr = self.getTopicMgr()
|
||||
topicObj = topicMgr.getOrCreateTopic(topicName)
|
||||
topicObj.publish(**kwargs)
|
||||
@@ -31,10 +31,10 @@ class Publisher(PublisherBase):
|
||||
|
||||
|
||||
class PublisherArg1Stage2(Publisher):
|
||||
'''
|
||||
"""
|
||||
This is used when transitioning from arg1 to kwargs
|
||||
messaging protocol.
|
||||
'''
|
||||
"""
|
||||
|
||||
_base = Publisher
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
'''
|
||||
"""
|
||||
:copyright: Copyright since 2006 by Oliver Schoenborn, all rights reserved.
|
||||
:license: BSD, see LICENSE_BSD_Simple.txt for details.
|
||||
'''
|
||||
"""
|
||||
|
||||
|
||||
class PublisherMixin:
|
||||
'''
|
||||
"""
|
||||
Mixin for publishing messages to a topic's listeners. This will be
|
||||
mixed into topicobj.Topic so that a user can use a Topic object to
|
||||
send a message to the topic's listeners via a publish() method.
|
||||
@@ -15,7 +15,7 @@ class PublisherMixin:
|
||||
happen that a listener causes another message of same topic to be
|
||||
sent (presumably, the listener has a way of preventing infinite
|
||||
loop).
|
||||
'''
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
@@ -57,9 +57,9 @@ class PublisherMixin:
|
||||
return iterState
|
||||
|
||||
def _mix_callListener(self, listener, msgKwargs, iterState):
|
||||
'''Send the message for given topic with data in msgKwargs.
|
||||
"""Send the message for given topic with data in msgKwargs.
|
||||
This sends message to listeners of parent topics as well.
|
||||
Note that at each level, msgKwargs is filtered so only those
|
||||
args that are defined for the topic are sent to listeners. '''
|
||||
args that are defined for the topic are sent to listeners. """
|
||||
listener(iterState.filteredArgs, self, msgKwargs)
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
'''
|
||||
"""
|
||||
|
||||
:copyright: Copyright since 2006 by Oliver Schoenborn, all rights reserved.
|
||||
:license: BSD, see LICENSE_BSD_Simple.txt for details.
|
||||
|
||||
'''
|
||||
"""
|
||||
|
||||
import weakref
|
||||
|
||||
@@ -14,10 +14,10 @@ from .. import py2and3
|
||||
### Exceptions raised during check() from sendMessage()
|
||||
|
||||
class SenderMissingReqdMsgDataError(RuntimeError):
|
||||
'''
|
||||
"""
|
||||
Raised when a sendMessage() is missing arguments tagged as
|
||||
'required' by pubsub topic of message.
|
||||
'''
|
||||
"""
|
||||
|
||||
def __init__(self, topicName, argNames, missing):
|
||||
argsStr = ','.join(argNames)
|
||||
@@ -28,10 +28,10 @@ class SenderMissingReqdMsgDataError(RuntimeError):
|
||||
|
||||
|
||||
class SenderUnknownMsgDataError(RuntimeError):
|
||||
'''
|
||||
"""
|
||||
Raised when a sendMessage() has arguments not listed among the topic's
|
||||
message data specification (MDS).
|
||||
'''
|
||||
"""
|
||||
|
||||
def __init__(self, topicName, argNames, extra):
|
||||
argsStr = ','.join(argNames)
|
||||
@@ -42,7 +42,7 @@ class SenderUnknownMsgDataError(RuntimeError):
|
||||
|
||||
|
||||
class ArgsInfo:
|
||||
'''
|
||||
"""
|
||||
Encode the Message Data Specification (MDS) for a given
|
||||
topic. ArgsInfos form a tree identical to that of Topics in that
|
||||
ArgInfos have a reference to their parent and children ArgInfos,
|
||||
@@ -58,7 +58,7 @@ class ArgsInfo:
|
||||
|
||||
The MDS can be created "empty", ie "incomplete", meaning it cannot
|
||||
yet be used to validate listener subscriptions to topics.
|
||||
'''
|
||||
"""
|
||||
|
||||
SPEC_MISSING = 10 # no args given
|
||||
SPEC_COMPLETE = 12 # all args, but not confirmed via user spec
|
||||
@@ -98,17 +98,17 @@ class ArgsInfo:
|
||||
return self.allDocs.copy()
|
||||
|
||||
def setArgsDocs(self, docs):
|
||||
'''docs is a mapping from arg names to their documentation'''
|
||||
"""docs is a mapping from arg names to their documentation"""
|
||||
if not self.isComplete():
|
||||
raise
|
||||
for arg, doc in py2and3.iteritems(docs):
|
||||
self.allDocs[arg] = doc
|
||||
|
||||
def check(self, msgKwargs):
|
||||
'''Check that the message arguments given satisfy the topic message
|
||||
"""Check that the message arguments given satisfy the topic message
|
||||
data specification (MDS). Raises SenderMissingReqdMsgDataError if some required
|
||||
args are missing or not known, and raises SenderUnknownMsgDataError if some
|
||||
optional args are unknown. '''
|
||||
optional args are unknown. """
|
||||
all = set(msgKwargs)
|
||||
# check that it has all required args
|
||||
needReqd = set(self.allRequired)
|
||||
@@ -125,11 +125,11 @@ class ArgsInfo:
|
||||
py2and3.keys(msgKwargs), optional - set(self.allOptional) )
|
||||
|
||||
def filterArgs(self, msgKwargs):
|
||||
'''Returns a dict which contains only those items of msgKwargs
|
||||
"""Returns a dict which contains only those items of msgKwargs
|
||||
which are defined for topic. E.g. if msgKwargs is {a:1, b:'b'}
|
||||
and topic arg spec is ('a',) then return {a:1}. The returned dict
|
||||
is valid only if check(msgKwargs) was called (or
|
||||
check(superset of msgKwargs) was called).'''
|
||||
check(superset of msgKwargs) was called)."""
|
||||
assert self.isComplete()
|
||||
if len(msgKwargs) == self.numArgs():
|
||||
return msgKwargs
|
||||
@@ -150,20 +150,20 @@ class ArgsInfo:
|
||||
return newKwargs
|
||||
|
||||
def hasSameArgs(self, *argNames):
|
||||
'''Returns true if self has all the message arguments given, no
|
||||
"""Returns true if self has all the message arguments given, no
|
||||
more and no less. Order does not matter. So if getArgs()
|
||||
returns ('arg1', 'arg2') then self.hasSameArgs('arg2', 'arg1')
|
||||
will return true. '''
|
||||
will return true. """
|
||||
return set(argNames) == set( self.getArgs() )
|
||||
|
||||
def hasParent(self, argsInfo):
|
||||
'''return True if self has argsInfo object as parent'''
|
||||
"""return True if self has argsInfo object as parent"""
|
||||
return self.parentAI() is argsInfo
|
||||
|
||||
def getCompleteAI(self):
|
||||
'''Get the closest arg spec, starting from self and moving to parent,
|
||||
"""Get the closest arg spec, starting from self and moving to parent,
|
||||
that is complete. So if self.isComplete() is True, then returns self,
|
||||
otherwise returns parent (if parent.isComplete()), etc. '''
|
||||
otherwise returns parent (if parent.isComplete()), etc. """
|
||||
AI = self
|
||||
while AI is not None:
|
||||
if AI.isComplete():
|
||||
@@ -172,8 +172,8 @@ class ArgsInfo:
|
||||
return None
|
||||
|
||||
def updateAllArgsFinal(self, topicDefn):
|
||||
'''This can only be called once, if the construction was done
|
||||
with ArgSpecGiven.SPEC_GIVEN_NONE'''
|
||||
"""This can only be called once, if the construction was done
|
||||
with ArgSpecGiven.SPEC_GIVEN_NONE"""
|
||||
assert not self.isComplete()
|
||||
assert topicDefn.isComplete()
|
||||
self.__setAllArgs(topicDefn)
|
||||
@@ -183,7 +183,7 @@ class ArgsInfo:
|
||||
self.childrenAI.append(childAI)
|
||||
|
||||
def __notifyParentCompleted(self):
|
||||
'''Parent should call this when parent ArgsInfo has been completed'''
|
||||
"""Parent should call this when parent ArgsInfo has been completed"""
|
||||
assert self.parentAI().isComplete()
|
||||
if self.isComplete():
|
||||
# verify that our spec is compatible with parent's
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
'''
|
||||
"""
|
||||
|
||||
:copyright: Copyright since 2006 by Oliver Schoenborn, all rights reserved.
|
||||
:license: BSD, see LICENSE_BSD_Simple.txt for details.
|
||||
|
||||
'''
|
||||
"""
|
||||
|
||||
def getRootTopicSpec():
|
||||
'''If using kwargs protocol, then root topic takes no args.'''
|
||||
"""If using kwargs protocol, then root topic takes no args."""
|
||||
argsDocs = {}
|
||||
reqdArgs = ()
|
||||
return argsDocs, reqdArgs
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
'''
|
||||
"""
|
||||
Top-level functionality related to message listeners.
|
||||
'''
|
||||
"""
|
||||
|
||||
'''
|
||||
"""
|
||||
:copyright: Copyright since 2006 by Oliver Schoenborn, all rights reserved.
|
||||
:license: BSD, see LICENSE_BSD_Simple.txt for details.
|
||||
'''
|
||||
"""
|
||||
|
||||
from .callables import (
|
||||
getID,
|
||||
@@ -21,7 +21,7 @@ from .listenerimpl import (
|
||||
)
|
||||
|
||||
class IListenerExcHandler:
|
||||
'''
|
||||
"""
|
||||
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::
|
||||
@@ -33,7 +33,7 @@ class IListenerExcHandler:
|
||||
... do something with listenerID ...
|
||||
|
||||
pub.setListenerExcHandler(MyHandler())
|
||||
'''
|
||||
"""
|
||||
def __call__(self, listenerID, topicObj):
|
||||
raise NotImplementedError('%s must override __call__()' % self.__class__)
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
'''
|
||||
"""
|
||||
:copyright: Copyright since 2006 by Oliver Schoenborn, all rights reserved.
|
||||
:license: BSD, see LICENSE_BSD_Simple.txt for details.
|
||||
'''
|
||||
"""
|
||||
|
||||
from . import weakmethod
|
||||
|
||||
@@ -15,18 +15,18 @@ from .callables import (
|
||||
|
||||
|
||||
class ListenerBase:
|
||||
'''
|
||||
"""
|
||||
Base class for listeners, ie. callables subscribed to pubsub.
|
||||
'''
|
||||
"""
|
||||
|
||||
AUTO_TOPIC = _AUTO_ARG
|
||||
|
||||
def __init__(self, callable_, argsInfo, onDead=None):
|
||||
'''Use callable_ as a listener of topicName. The argsInfo is the
|
||||
"""Use callable_ as a listener of topicName. The argsInfo is the
|
||||
return value from a Validator, ie an instance of callables.CallArgsInfo.
|
||||
If given, the onDead will be called with self as parameter, if/when
|
||||
callable_ gets garbage collected (callable_ is held only by weak
|
||||
reference). '''
|
||||
reference). """
|
||||
# set call policies
|
||||
self.acceptsAllKwargs = argsInfo.acceptsAllKwargs
|
||||
|
||||
@@ -45,62 +45,62 @@ class ListenerBase:
|
||||
raise NotImplementedError
|
||||
|
||||
def name(self):
|
||||
'''Return a human readable name for listener, based on the
|
||||
"""Return a human readable name for listener, based on the
|
||||
listener's type name and its id (as obtained from id(listener)). If
|
||||
caller just needs name based on type info, specify instance=False.
|
||||
Note that the listener's id() was saved at construction time (since
|
||||
it may get garbage collected at any time) so the return value of
|
||||
name() is not necessarily unique if the callable has died (because
|
||||
id's can be re-used after garbage collection).'''
|
||||
id's can be re-used after garbage collection)."""
|
||||
return '%s_%s' % (self.__nameID, self.__id)
|
||||
|
||||
def typeName(self):
|
||||
'''Get a type name for the listener. This is a class name or
|
||||
function name, as appropriate. '''
|
||||
"""Get a type name for the listener. This is a class name or
|
||||
function name, as appropriate. """
|
||||
return self.__nameID
|
||||
|
||||
def module(self):
|
||||
'''Get the module in which the callable was defined.'''
|
||||
"""Get the module in which the callable was defined."""
|
||||
return self.__module
|
||||
|
||||
def getCallable(self):
|
||||
'''Get the listener that was given at initialization. Note that
|
||||
"""Get the listener that was given at initialization. Note that
|
||||
this could be None if it has been garbage collected (e.g. if it was
|
||||
created as a wrapper of some other callable, and not stored
|
||||
locally).'''
|
||||
locally)."""
|
||||
return self._callable()
|
||||
|
||||
def isDead(self):
|
||||
'''Return True if this listener died (has been garbage collected)'''
|
||||
"""Return True if this listener died (has been garbage collected)"""
|
||||
return self._callable() is None
|
||||
|
||||
def wantsTopicObjOnCall(self):
|
||||
'''True if this listener wants topic object: it has a arg=pub.AUTO_TOPIC'''
|
||||
"""True if this listener wants topic object: it has a arg=pub.AUTO_TOPIC"""
|
||||
return self._autoTopicArgName is not None
|
||||
|
||||
def wantsAllMessageData(self):
|
||||
'''True if this listener wants all message data: it has a \**kwargs argument'''
|
||||
"""True if this listener wants all message data: it has a \**kwargs argument"""
|
||||
return self.acceptsAllKwargs
|
||||
|
||||
def _unlinkFromTopic_(self):
|
||||
'''Tell self that it is no longer used by a Topic. This allows
|
||||
to break some cyclical references.'''
|
||||
"""Tell self that it is no longer used by a Topic. This allows
|
||||
to break some cyclical references."""
|
||||
self.__onDead = None
|
||||
|
||||
def _calledWhenDead(self):
|
||||
raise RuntimeError('BUG: Dead Listener called, still subscribed!')
|
||||
|
||||
def __notifyOnDead(self, ref):
|
||||
'''This gets called when listener weak ref has died. Propagate
|
||||
info to Topic).'''
|
||||
"""This gets called when listener weak ref has died. Propagate
|
||||
info to Topic)."""
|
||||
notifyDeath = self.__onDead
|
||||
self._unlinkFromTopic_()
|
||||
if notifyDeath is not None:
|
||||
notifyDeath(self)
|
||||
|
||||
def __eq__(self, rhs):
|
||||
'''Compare for equality to rhs. This returns true if rhs has our id id(rhs) is
|
||||
same as id(self) or id(callable in self). '''
|
||||
"""Compare for equality to rhs. This returns true if rhs has our id id(rhs) is
|
||||
same as id(self) or id(callable in self). """
|
||||
if id(self) == id(rhs):
|
||||
return True
|
||||
|
||||
@@ -119,8 +119,8 @@ class ListenerBase:
|
||||
return c1 == c2
|
||||
|
||||
def __ne__(self, rhs):
|
||||
'''Counterpart to __eq__ MUST be defined... equivalent to
|
||||
'not (self == rhs)'.'''
|
||||
"""Counterpart to __eq__ MUST be defined... equivalent to
|
||||
'not (self == rhs)'."""
|
||||
return not self.__eq__(rhs)
|
||||
|
||||
def __hash__(self):
|
||||
@@ -129,28 +129,28 @@ class ListenerBase:
|
||||
return self.__hash
|
||||
|
||||
def __str__(self):
|
||||
'''String rep is the callable'''
|
||||
"""String rep is the callable"""
|
||||
return self.__nameID
|
||||
|
||||
|
||||
class ValidatorBase:
|
||||
'''
|
||||
"""
|
||||
Validates listeners. It checks whether the listener given to
|
||||
validate() method complies with required and optional arguments
|
||||
specified for topic.
|
||||
'''
|
||||
"""
|
||||
|
||||
def __init__(self, topicArgs, topicKwargs):
|
||||
'''topicArgs is a list of argument names that will be required when sending
|
||||
"""topicArgs is a list of argument names that will be required when sending
|
||||
a message to listener. Hence order of items in topicArgs matters. The topicKwargs
|
||||
is a list of argument names that will be optional, ie given as keyword arguments
|
||||
when sending a message to listener. The list is unordered. '''
|
||||
when sending a message to listener. The list is unordered. """
|
||||
self._topicArgs = set(topicArgs)
|
||||
self._topicKwargs = set(topicKwargs)
|
||||
|
||||
|
||||
def validate(self, listener):
|
||||
'''Validate that listener satisfies the requirements of
|
||||
"""Validate that listener satisfies the requirements of
|
||||
being a topic listener, if topic's kwargs keys are topicKwargKeys
|
||||
(so only the list of keyword arg names for topic are necessary).
|
||||
Raises ListenerMismatchError if listener not usable for topic.
|
||||
@@ -161,16 +161,16 @@ class ValidatorBase:
|
||||
E.g. def fn1(msgTopic=Listener.AUTO_TOPIC) would
|
||||
cause validate(fn1) to return True, whereas any other kwarg name or value
|
||||
would cause a False to be returned.
|
||||
'''
|
||||
"""
|
||||
paramsInfo = getArgs( listener )
|
||||
self._validateArgs(listener, paramsInfo)
|
||||
return paramsInfo
|
||||
|
||||
|
||||
def isValid(self, listener):
|
||||
'''Return true only if listener can subscribe to messages where
|
||||
"""Return true only if listener can subscribe to messages where
|
||||
topic has kwargs keys topicKwargKeys. Just calls validate() in
|
||||
a try-except clause.'''
|
||||
a try-except clause."""
|
||||
try:
|
||||
self.validate(listener)
|
||||
return True
|
||||
@@ -179,7 +179,7 @@ class ValidatorBase:
|
||||
|
||||
|
||||
def _validateArgs(self, listener, paramsInfo):
|
||||
'''Provide implementation in derived classes'''
|
||||
"""Provide implementation in derived classes"""
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
'''
|
||||
"""
|
||||
:copyright: Copyright since 2006 by Oliver Schoenborn, all rights reserved.
|
||||
:license: BSD, see LICENSE_BSD_Simple.txt for details.
|
||||
'''
|
||||
"""
|
||||
import sys
|
||||
|
||||
class NotificationMgr:
|
||||
'''
|
||||
"""
|
||||
Manages notifications for tracing pubsub activity. When pubsub takes a
|
||||
certain action such as sending a message or creating a topic, and
|
||||
the notification flag for that activity is True, all registered
|
||||
@@ -23,7 +23,7 @@ class NotificationMgr:
|
||||
collecting everything, which could lead to various pubsub notifications
|
||||
-- by then they should be of no interest -- such as dead
|
||||
listeners, etc.
|
||||
'''
|
||||
"""
|
||||
|
||||
def __init__(self, notificationHandler = None):
|
||||
self.__notifyOnSend = False
|
||||
@@ -82,7 +82,7 @@ class NotificationMgr:
|
||||
handler.notifyDeadListener(*args, **kwargs)
|
||||
|
||||
def getFlagStates(self):
|
||||
'''Return state of each notification flag, as a dict.'''
|
||||
"""Return state of each notification flag, as a dict."""
|
||||
return dict(
|
||||
subscribe = self.__notifyOnSubscribe,
|
||||
unsubscribe = self.__notifyOnUnsubscribe,
|
||||
@@ -95,7 +95,7 @@ class NotificationMgr:
|
||||
def setFlagStates(self, subscribe=None, unsubscribe=None,
|
||||
deadListener=None, sendMessage=None, newTopic=None,
|
||||
delTopic=None, all=None):
|
||||
'''Set the notification flag on/off for various aspects of pubsub.
|
||||
"""Set the notification flag on/off for various aspects of pubsub.
|
||||
The kwargs that are None are left at their current value. The 'all',
|
||||
if not None, is set first. E.g.
|
||||
|
||||
@@ -103,7 +103,7 @@ class NotificationMgr:
|
||||
|
||||
will toggle all notifications on, but will turn off the 'delTopic'
|
||||
notification.
|
||||
'''
|
||||
"""
|
||||
if all is not None:
|
||||
# ignore all other arg settings, and set all of them to true:
|
||||
numArgs = 7 # how many args in this method
|
||||
@@ -132,54 +132,54 @@ class NotificationMgr:
|
||||
|
||||
|
||||
class INotificationHandler:
|
||||
'''
|
||||
"""
|
||||
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).
|
||||
'''
|
||||
"""
|
||||
|
||||
def notifySubscribe(self, pubListener, topicObj, newSub):
|
||||
'''Called when a listener is subscribed to a topic.
|
||||
"""Called when a listener is subscribed to a topic.
|
||||
:param pubListener: the pubsub.core.Listener that wraps subscribed listener.
|
||||
:param topicObj: the pubsub.core.Topic object subscribed to.
|
||||
:param newSub: false if pubListener was already subscribed. '''
|
||||
:param newSub: false if pubListener was already subscribed. """
|
||||
raise NotImplementedError
|
||||
|
||||
def notifyUnsubscribe(self, pubListener, topicObj):
|
||||
'''Called when a listener is unsubscribed from given topic.
|
||||
"""Called when a listener is unsubscribed from given topic.
|
||||
:param pubListener: the pubsub.core.Listener that wraps unsubscribed listener.
|
||||
:param topicObj: the pubsub.core.Topic object unsubscribed from.'''
|
||||
:param topicObj: the pubsub.core.Topic object unsubscribed from."""
|
||||
raise NotImplementedError
|
||||
|
||||
def notifyDeadListener(self, pubListener, topicObj):
|
||||
'''Called when a listener has been garbage collected.
|
||||
"""Called when a listener has been garbage collected.
|
||||
:param pubListener: the pubsub.core.Listener that wraps GC'd listener.
|
||||
:param topicObj: the pubsub.core.Topic object it was subscribed to.'''
|
||||
:param topicObj: the pubsub.core.Topic object it was subscribed to."""
|
||||
raise NotImplementedError
|
||||
|
||||
def notifySend(self, stage, topicObj, pubListener=None):
|
||||
'''Called multiple times during a sendMessage: once before message
|
||||
"""Called multiple times during a sendMessage: once before message
|
||||
sending has started (pre), once for each listener about to be sent the
|
||||
message, and once after all listeners have received the message (post).
|
||||
:param stage: 'pre', 'post', or 'loop'.
|
||||
:param topicObj: the Topic object for the message.
|
||||
:param pubListener: None for pre and post stages; for loop, the listener
|
||||
that is about to be sent the message.'''
|
||||
that is about to be sent the message."""
|
||||
raise NotImplementedError
|
||||
|
||||
def notifyNewTopic(self, topicObj, description, required, argsDocs):
|
||||
'''Called whenever a new topic is added to the topic tree.
|
||||
"""Called whenever a new topic is added to the topic tree.
|
||||
:param topicObj: the Topic object for the message.
|
||||
:param description: docstring for the topic.
|
||||
:param required: list of message data names (keys in argsDocs) that are required.
|
||||
:param argsDocs: dictionary of all message data names, with the
|
||||
corresponding docstring. '''
|
||||
corresponding docstring. """
|
||||
raise NotImplementedError
|
||||
|
||||
def notifyDelTopic(self, topicName):
|
||||
'''Called whenever a topic is removed from topic tree.
|
||||
:param topicName: name of topic removed.'''
|
||||
"""Called whenever a topic is removed from topic tree.
|
||||
:param topicName: name of topic removed."""
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
'''
|
||||
"""
|
||||
:copyright: Copyright since 2006 by Oliver Schoenborn, all rights reserved.
|
||||
:license: BSD, see LICENSE_BSD_Simple.txt for details.
|
||||
'''
|
||||
"""
|
||||
|
||||
from .topicmgr import (
|
||||
TopicManager,
|
||||
@@ -12,45 +12,45 @@ from .. import py2and3
|
||||
|
||||
|
||||
class PublisherBase:
|
||||
'''
|
||||
"""
|
||||
Represent the class that send messages to listeners of given
|
||||
topics and that knows how to subscribe/unsubscribe listeners
|
||||
from topics.
|
||||
'''
|
||||
"""
|
||||
|
||||
def __init__(self, treeConfig = None):
|
||||
'''If treeConfig is None, a default one is created from an
|
||||
instance of TreeConfig.'''
|
||||
"""If treeConfig is None, a default one is created from an
|
||||
instance of TreeConfig."""
|
||||
self.__treeConfig = treeConfig or TreeConfig()
|
||||
self.__topicMgr = TopicManager(self.__treeConfig)
|
||||
|
||||
def getTopicMgr(self):
|
||||
'''Get the topic manager created for this publisher.'''
|
||||
"""Get the topic manager created for this publisher."""
|
||||
return self.__topicMgr
|
||||
|
||||
def getListenerExcHandler(self):
|
||||
'''Get the listener exception handler that was registered
|
||||
via setListenerExcHandler(), or None of none registered.'''
|
||||
"""Get the listener exception handler that was registered
|
||||
via setListenerExcHandler(), or None of none registered."""
|
||||
return self.__treeConfig.listenerExcHandler
|
||||
|
||||
def setListenerExcHandler(self, handler):
|
||||
'''Set the function to call when a listener raises an exception
|
||||
"""Set the function to call when a listener raises an exception
|
||||
during a sendMessage(). The handler must adhere to the
|
||||
IListenerExcHandler API. '''
|
||||
IListenerExcHandler API. """
|
||||
self.__treeConfig.listenerExcHandler = handler
|
||||
|
||||
def addNotificationHandler(self, handler):
|
||||
'''Add a handler for tracing pubsub activity. The handler should be
|
||||
a class that adheres to the API of INotificationHandler. '''
|
||||
"""Add a handler for tracing pubsub activity. The handler should be
|
||||
a class that adheres to the API of INotificationHandler. """
|
||||
self.__treeConfig.notificationMgr.addHandler(handler)
|
||||
|
||||
def clearNotificationHandlers(self):
|
||||
'''Remove all notification handlers that were added via
|
||||
self.addNotificationHandler(). '''
|
||||
"""Remove all notification handlers that were added via
|
||||
self.addNotificationHandler(). """
|
||||
self.__treeConfig.notificationMgr.clearHandlers()
|
||||
|
||||
def setNotificationFlags(self, **kwargs):
|
||||
'''Set the notification flags on or off for each type of
|
||||
"""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;
|
||||
@@ -69,15 +69,15 @@ class PublisherBase:
|
||||
|
||||
will toggle all notifications on, but will turn off the 'delTopic'
|
||||
notification.
|
||||
'''
|
||||
"""
|
||||
self.__treeConfig.notificationMgr.setFlagStates(**kwargs)
|
||||
|
||||
def getNotificationFlags(self):
|
||||
'''Return a dictionary with the notification flag states.'''
|
||||
"""Return a dictionary with the notification flag states."""
|
||||
return self.__treeConfig.notificationMgr.getFlagStates()
|
||||
|
||||
def setTopicUnspecifiedFatal(self, newVal=True, checkExisting=True):
|
||||
'''Changes the creation policy for topics.
|
||||
"""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
|
||||
@@ -114,7 +114,7 @@ class PublisherBase:
|
||||
3. Use it as in #1 during app development, and once stable, use
|
||||
#2. This is easiest to do in combination with
|
||||
pub.exportTopicTreeSpec().
|
||||
'''
|
||||
"""
|
||||
oldVal = self.__treeConfig.raiseOnTopicUnspecified
|
||||
self.__treeConfig.raiseOnTopicUnspecified = newVal
|
||||
|
||||
@@ -124,14 +124,14 @@ class PublisherBase:
|
||||
return oldVal
|
||||
|
||||
def sendMessage(self, topicName, *args, **kwargs):
|
||||
'''Send a message for topic name with given data (args and kwargs).
|
||||
"""Send a message for topic name with given data (args and kwargs).
|
||||
This will be overridden by derived classes that implement
|
||||
message-sending for different messaging protocols; not all
|
||||
parameters may be accepted.'''
|
||||
parameters may be accepted."""
|
||||
raise NotImplementedError
|
||||
|
||||
def subscribe(self, listener, topicName):
|
||||
'''Subscribe listener to named topic. Raises ListenerMismatchError
|
||||
"""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
|
||||
@@ -139,18 +139,18 @@ class PublisherBase:
|
||||
the callable.
|
||||
|
||||
Note that if 'subscribe' notification is on, the handler's
|
||||
'notifySubscribe' method is called after subscription.'''
|
||||
'notifySubscribe' method is called after subscription."""
|
||||
topicObj = self.__topicMgr.getOrCreateTopic(topicName)
|
||||
subscribedListener, success = topicObj.subscribe(listener)
|
||||
return subscribedListener, success
|
||||
|
||||
def unsubscribe(self, listener, topicName):
|
||||
'''Unsubscribe from given topic. Returns the pubsub.core.Listener
|
||||
"""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. '''
|
||||
notifyUnsubscribe() method will be called after unsubscribing. """
|
||||
topicObj = self.__topicMgr.getTopic(topicName)
|
||||
unsubdLisnr = topicObj.unsubscribe(listener)
|
||||
|
||||
@@ -158,7 +158,7 @@ class PublisherBase:
|
||||
|
||||
def unsubAll(self, topicName = None,
|
||||
listenerFilter = None, topicFilter = None):
|
||||
'''By default (no args given), unsubscribe all listeners from all
|
||||
"""By default (no args given), unsubscribe all listeners from all
|
||||
topics. A listenerFilter can be given so that only the listeners
|
||||
that satisfy listenerFilter(listener) == True will be unsubscribed
|
||||
(with listener being a pub.Listener wrapper instance for each listener
|
||||
@@ -171,7 +171,7 @@ class PublisherBase:
|
||||
were unsubscribed from the topic tree).
|
||||
|
||||
Note: this method will generate one 'unsubcribe' notification message
|
||||
(see pub.setNotificationFlags()) for each listener unsubscribed.'''
|
||||
(see pub.setNotificationFlags()) for each listener unsubscribed."""
|
||||
unsubdListeners = []
|
||||
|
||||
if topicName is None:
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
'''
|
||||
"""
|
||||
Definitions related to message data specification.
|
||||
|
||||
:copyright: Copyright since 2006 by Oliver Schoenborn, all rights reserved.
|
||||
:license: BSD, see LICENSE_BSD_Simple.txt for details.
|
||||
|
||||
'''
|
||||
"""
|
||||
|
||||
|
||||
from .listener import getArgs as getListenerArgs
|
||||
@@ -17,10 +17,10 @@ from .topicargspecimpl import (
|
||||
|
||||
|
||||
def topicArgsFromCallable(_callable):
|
||||
'''Get the topic message data names and list of those that are required,
|
||||
"""Get the topic message data names and list of those that are required,
|
||||
by introspecting given callable. Returns a pair, (args, required)
|
||||
where args is a dictionary of allowed message data names vs docstring,
|
||||
and required states which ones are required rather than optional.'''
|
||||
and required states which ones are required rather than optional."""
|
||||
argsInfo = getListenerArgs(_callable)
|
||||
required = argsInfo.getRequiredArgs()
|
||||
defaultDoc = 'UNDOCUMENTED'
|
||||
@@ -29,7 +29,7 @@ def topicArgsFromCallable(_callable):
|
||||
|
||||
|
||||
class ArgSpecGiven:
|
||||
'''
|
||||
"""
|
||||
The message data specification (MDS) for a topic.
|
||||
This consists of each argument name that listener should have in its
|
||||
call protocol, plus which ones are required in any sendMessage(), and a
|
||||
@@ -37,7 +37,7 @@ class ArgSpecGiven:
|
||||
into an ArgsInfo object which is basically a superset of that information,
|
||||
needed to ensure that the arguments specifications satisfy
|
||||
pubsub policies for chosen API version.
|
||||
'''
|
||||
"""
|
||||
|
||||
SPEC_GIVEN_NONE = 1 # specification not given
|
||||
SPEC_GIVEN_ALL = 3 # all args specified
|
||||
@@ -64,7 +64,7 @@ class ArgSpecGiven:
|
||||
self.argsSpecType = ArgSpecGiven.SPEC_GIVEN_ALL
|
||||
|
||||
def isComplete(self):
|
||||
'''Returns True if the definition is usable, false otherwise.'''
|
||||
"""Returns True if the definition is usable, false otherwise."""
|
||||
return self.argsSpecType == ArgSpecGiven.SPEC_GIVEN_ALL
|
||||
|
||||
def getOptional(self):
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
'''
|
||||
"""
|
||||
:copyright: Copyright since 2006 by Oliver Schoenborn, all rights reserved.
|
||||
:license: BSD, see LICENSE_BSD_Simple.txt for details.
|
||||
'''
|
||||
"""
|
||||
|
||||
|
||||
import os, re, inspect
|
||||
@@ -20,34 +20,34 @@ from .topicexc import UnrecognizedSourceFormatError
|
||||
|
||||
|
||||
class ITopicDefnProvider:
|
||||
'''
|
||||
"""
|
||||
All topic definition providers added via pub.addTopicDefnProvider()
|
||||
must have this interface. Derived classes must override the getDefn(),
|
||||
getTreeDoc() and topicNames() methods.
|
||||
'''
|
||||
"""
|
||||
|
||||
def getDefn(self, topicNameTuple):
|
||||
'''Must return a pair (string, ArgSpecGiven) for given topic.
|
||||
"""Must return a pair (string, ArgSpecGiven) for given topic.
|
||||
The first item is a description for topic, the second item
|
||||
contains the message data specification (MDS). Note topic name
|
||||
is in tuple format ('a', 'b', 'c') rather than 'a.b.c'. '''
|
||||
is in tuple format ('a', 'b', 'c') rather than 'a.b.c'. """
|
||||
msg = 'Must return (string, ArgSpecGiven), or (None, None)'
|
||||
raise NotImplementedError(msg)
|
||||
|
||||
def topicNames(self):
|
||||
'''Return an iterator over topic names available from this provider.
|
||||
"""Return an iterator over topic names available from this provider.
|
||||
Note that the topic names should be in tuple rather than dotted-string
|
||||
format so as to be compatible with getDefn().'''
|
||||
format so as to be compatible with getDefn()."""
|
||||
msg = 'Must return a list of topic names available from this provider'
|
||||
raise NotImplementedError(msg)
|
||||
|
||||
def getTreeDoc(self):
|
||||
'''Get the docstring for the topic tree.'''
|
||||
"""Get the docstring for the topic tree."""
|
||||
msg = 'Must return documentation string for root topic (tree)'
|
||||
raise NotImplementedError(msg)
|
||||
|
||||
def __iter__(self):
|
||||
'''Same as self.topicNames(), do NOT override.'''
|
||||
"""Same as self.topicNames(), do NOT override."""
|
||||
return self.topicNames()
|
||||
|
||||
|
||||
@@ -57,15 +57,15 @@ SPEC_METHOD_NAME = 'msgDataSpec'
|
||||
|
||||
|
||||
class ITopicDefnDeserializer:
|
||||
'''
|
||||
"""
|
||||
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 TopicDefn:
|
||||
'''Encapsulate date for a topic definition. Used by
|
||||
getNextTopic().'''
|
||||
"""Encapsulate date for a topic definition. Used by
|
||||
getNextTopic()."""
|
||||
|
||||
def __init__(self, nameTuple, description, argsDocs, required):
|
||||
self.nameTuple = nameTuple
|
||||
@@ -77,41 +77,41 @@ class ITopicDefnDeserializer:
|
||||
return (self.description is not None) and (self.argsDocs is not None)
|
||||
|
||||
def getTreeDoc(self):
|
||||
'''Get the docstring for the topic tree.'''
|
||||
"""Get the docstring for the topic tree."""
|
||||
raise NotImplementedError
|
||||
|
||||
def getNextTopic(self):
|
||||
'''Get the next topic definition available from the data. The return
|
||||
"""Get the next topic definition available from the data. The return
|
||||
must be an instance of TopicDefn. Must return None when no topics
|
||||
are left.'''
|
||||
are left."""
|
||||
raise NotImplementedError
|
||||
|
||||
def doneIter(self):
|
||||
'''Called automatically by TopicDefnProvider once
|
||||
"""Called automatically by TopicDefnProvider once
|
||||
it considers the iteration completed. Override this only if
|
||||
deserializer needs to take action, such as closing a file.'''
|
||||
deserializer needs to take action, such as closing a file."""
|
||||
pass
|
||||
|
||||
def resetIter(self):
|
||||
'''Called by the TopicDefnProvider if it needs to
|
||||
"""Called by the TopicDefnProvider if it needs to
|
||||
restart the topic iteration. Override this only if special action needed,
|
||||
such as resetting a file pointer to beginning of file.'''
|
||||
such as resetting a file pointer to beginning of file."""
|
||||
pass
|
||||
|
||||
|
||||
class TopicDefnDeserialClass(ITopicDefnDeserializer):
|
||||
'''
|
||||
"""
|
||||
Convert a nested class tree as a topic definition tree. Format: the class
|
||||
name is the topic name, its doc string is its description. The topic's
|
||||
message data specification is determined by inspecting a class method called
|
||||
the same as SPEC_METHOD_NAME. The doc string of that method is parsed to
|
||||
extract the description for each message data.
|
||||
'''
|
||||
"""
|
||||
|
||||
def __init__(self, pyClassObj=None):
|
||||
'''If pyClassObj is given, it is an object that contains nested
|
||||
"""If pyClassObj is given, it is an object that contains nested
|
||||
classes defining root topics; the root topics contain nested
|
||||
classes defining subtopics; etc.'''
|
||||
classes defining subtopics; etc."""
|
||||
self.__rootTopics = []
|
||||
self.__iterStarted = False
|
||||
self.__nextTopic = iter(self.__rootTopics)
|
||||
@@ -156,10 +156,10 @@ class TopicDefnDeserialClass(ITopicDefnDeserializer):
|
||||
return [nt for (nt, defn) in self.__rootTopics]
|
||||
|
||||
def __addDefnFromClassObj(self, pyClassObj):
|
||||
'''Extract a topic definition from a Python class: topic name,
|
||||
"""Extract a topic definition from a Python class: topic name,
|
||||
docstring, and MDS, and docstring for each message data.
|
||||
The class name is the topic name, assumed to be a root topic, and
|
||||
descends recursively into nested classes to define subtopic etc. '''
|
||||
descends recursively into nested classes to define subtopic etc. """
|
||||
if self.__iterStarted:
|
||||
raise RuntimeError('addDefnFromClassObj must be called before iteration started!')
|
||||
|
||||
@@ -193,7 +193,7 @@ class TopicDefnDeserialClass(ITopicDefnDeserializer):
|
||||
self.__findTopics(topicClassObj, parentNameTuple2)
|
||||
|
||||
def __getTopicClasses(self, pyClassObj, parentNameTuple=()):
|
||||
'''Returns a list of pairs, (topicNameTuple, memberClassObj)'''
|
||||
"""Returns a list of pairs, (topicNameTuple, memberClassObj)"""
|
||||
memberNames = dir(pyClassObj)
|
||||
topicClasses = []
|
||||
for memberName in memberNames:
|
||||
@@ -232,15 +232,14 @@ class TopicDefnDeserialClass(ITopicDefnDeserializer):
|
||||
|
||||
|
||||
class TopicDefnDeserialModule(ITopicDefnDeserializer):
|
||||
'''
|
||||
"""
|
||||
Deserialize a module containing Python source code defining a topic tree.
|
||||
This loads the module and gives it to an instance of TopicDefnDeserialClass.
|
||||
'''
|
||||
"""
|
||||
|
||||
def __init__(self, moduleName, searchPath=None):
|
||||
'''Load the given named module, searched for in searchPath or, if not
|
||||
specified, in sys.path. Give it to a TopicDefnDeserialClass.
|
||||
'''
|
||||
"""Load the given named module, searched for in searchPath or, if not
|
||||
specified, in sys.path. Give it to a TopicDefnDeserialClass."""
|
||||
from . import imp2
|
||||
module = imp2.load_module(moduleName, searchPath)
|
||||
self.__classDeserial = TopicDefnDeserialClass(module)
|
||||
@@ -262,16 +261,16 @@ class TopicDefnDeserialModule(ITopicDefnDeserializer):
|
||||
|
||||
|
||||
class TopicDefnDeserialString(ITopicDefnDeserializer):
|
||||
'''
|
||||
"""
|
||||
Deserialize a string containing Python source code defining a topic tree.
|
||||
The string has the same format as expected by TopicDefnDeserialModule.
|
||||
'''
|
||||
"""
|
||||
|
||||
def __init__(self, source):
|
||||
'''This just saves the string into a temporary file created in
|
||||
"""This just saves the string into a temporary file created in
|
||||
os.getcwd(), and the rest is delegated to TopicDefnDeserialModule.
|
||||
The temporary file (module -- as well as its byte-compiled
|
||||
version) will be deleted when the doneIter() method is called.'''
|
||||
version) will be deleted when the doneIter() method is called."""
|
||||
|
||||
def createTmpModule():
|
||||
moduleNamePre = 'tmp_export_topics_'
|
||||
@@ -316,7 +315,7 @@ TOPIC_TREE_FROM_CLASS = 'class'
|
||||
|
||||
|
||||
class TopicDefnProvider(ITopicDefnProvider):
|
||||
'''
|
||||
"""
|
||||
Default implementation of the ITopicDefnProvider API. This
|
||||
implementation accepts several formats for the topic tree
|
||||
source data and delegates to a registered ITopicDefnDeserializer
|
||||
@@ -327,14 +326,14 @@ class TopicDefnProvider(ITopicDefnProvider):
|
||||
when source is *not* an ITopicDefnProvider.
|
||||
|
||||
Additional de-serializers can be registered via registerTypeForImport().
|
||||
'''
|
||||
"""
|
||||
|
||||
_typeRegistry = {}
|
||||
|
||||
def __init__(self, source, format, **providerKwargs):
|
||||
'''Find the correct de-serializer class from registry for the given
|
||||
"""Find the correct de-serializer class from registry for the given
|
||||
format; instantiate it with given source and providerKwargs; get
|
||||
all available topic definitions.'''
|
||||
all available topic definitions."""
|
||||
if format not in self._typeRegistry:
|
||||
raise UnrecognizedSourceFormatError()
|
||||
providerClassObj = self._typeRegistry[format]
|
||||
@@ -366,7 +365,7 @@ class TopicDefnProvider(ITopicDefnProvider):
|
||||
|
||||
@classmethod
|
||||
def registerTypeForImport(cls, typeName, providerClassObj):
|
||||
'''If a new type of importer is defined for topic definitions, it
|
||||
"""If a new type of importer is defined for topic definitions, it
|
||||
can be registered with pubsub by providing a name for the new
|
||||
importer (typeName), and the class to instantiate when
|
||||
pub.addTopicDefnProvider(obj, typeName) is called. For instance, ::
|
||||
@@ -377,7 +376,7 @@ class TopicDefnProvider(ITopicDefnProvider):
|
||||
TopicDefnProvider.registerTypeForImport('some name', SomeNewImporter)
|
||||
# will instantiate SomeNewImporter(source)
|
||||
pub.addTopicDefnProvider(source, 'some name')
|
||||
'''
|
||||
"""
|
||||
assert issubclass(providerClassObj, ITopicDefnDeserializer)
|
||||
cls._typeRegistry[typeName] = providerClassObj
|
||||
|
||||
@@ -412,7 +411,7 @@ defaultTopicTreeSpecFooter = \
|
||||
|
||||
|
||||
def exportTopicTreeSpec(moduleName = None, rootTopic=None, bak='bak', moduleDoc=None):
|
||||
'''Using TopicTreeSpecPrinter, exports the topic tree rooted at rootTopic to a
|
||||
"""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:
|
||||
@@ -425,7 +424,7 @@ def exportTopicTreeSpec(moduleName = None, rootTopic=None, bak='bak', moduleDoc=
|
||||
corresponding topic. Otherwise, complete tree, using
|
||||
pub.getDefaultTopicTreeRoot() as starting point.
|
||||
- The moduleDoc is the doc string for the module ie topic tree.
|
||||
'''
|
||||
"""
|
||||
|
||||
if rootTopic is None:
|
||||
from .. import pub
|
||||
@@ -453,24 +452,24 @@ def exportTopicTreeSpec(moduleName = None, rootTopic=None, bak='bak', moduleDoc=
|
||||
##############################################################
|
||||
|
||||
class TopicTreeSpecPrinter:
|
||||
'''
|
||||
"""
|
||||
Helper class to print the topic tree using the Python class
|
||||
syntax. The "printout" can be sent to any file object (object that has a
|
||||
write() method). If printed to a module, the module can be imported and
|
||||
given to pub.addTopicDefnProvider(module, 'module'). Importing the module
|
||||
also provides code completion of topic names (rootTopic.subTopic can be
|
||||
given to any pubsub function requiring a topic name).
|
||||
'''
|
||||
"""
|
||||
|
||||
INDENT_CH = ' '
|
||||
#INDENT_CH = '.'
|
||||
|
||||
def __init__(self, rootTopic=None, fileObj=None, width=70, indentStep=4,
|
||||
treeDoc = defaultTopicTreeSpecHeader, footer = defaultTopicTreeSpecFooter):
|
||||
'''For formatting, can specify the width of output, the indent step, the
|
||||
"""For formatting, can specify the width of output, the indent step, the
|
||||
header and footer to print to override defaults. The destination is fileObj;
|
||||
if none is given, then sys.stdout is used. If rootTopic is given, calls
|
||||
writeAll(rootTopic) at end of __init__.'''
|
||||
writeAll(rootTopic) at end of __init__."""
|
||||
self.__traverser = TopicTreeTraverser(self)
|
||||
|
||||
import sys
|
||||
@@ -510,13 +509,13 @@ class TopicTreeSpecPrinter:
|
||||
self.writeAll(rootTopic)
|
||||
|
||||
def getOutput(self):
|
||||
'''Each line that was sent to fileObj was saved in a list; returns a
|
||||
string which is ``'\\n'.join(list)``.'''
|
||||
"""Each line that was sent to fileObj was saved in a list; returns a
|
||||
string which is ``'\\n'.join(list)``."""
|
||||
return '\n'.join( self.__output )
|
||||
|
||||
def writeAll(self, topicObj):
|
||||
'''Traverse each topic of topic tree, starting at topicObj, printing
|
||||
each topic definition as the tree gets traversed. '''
|
||||
"""Traverse each topic of topic tree, starting at topicObj, printing
|
||||
each topic definition as the tree gets traversed. """
|
||||
self.__traverser.traverse(topicObj)
|
||||
|
||||
def _accept(self, topicObj):
|
||||
@@ -545,7 +544,7 @@ class TopicTreeSpecPrinter:
|
||||
self.__destination.write(self.getOutput())
|
||||
|
||||
def _onTopic(self, topicObj):
|
||||
'''This gets called for each topic. Print as per specified content.'''
|
||||
"""This gets called for each topic. Print as per specified content."""
|
||||
# don't print root of tree, it is the ALL_TOPICS builtin topic
|
||||
if topicObj.isAll():
|
||||
self.__lastWasAll = True
|
||||
@@ -564,12 +563,12 @@ class TopicTreeSpecPrinter:
|
||||
self.__printTopicArgSpec(topicObj)
|
||||
|
||||
def _startChildren(self):
|
||||
'''Increase the indent'''
|
||||
"""Increase the indent"""
|
||||
if not self.__lastWasAll:
|
||||
self.__indent += self.__indentStep
|
||||
|
||||
def _endChildren(self):
|
||||
'''Decrease the indent'''
|
||||
"""Decrease the indent"""
|
||||
if not self.__lastWasAll:
|
||||
self.__indent -= self.__indentStep
|
||||
|
||||
@@ -578,14 +577,14 @@ class TopicTreeSpecPrinter:
|
||||
return msg
|
||||
if msg.startswith("'''") or msg.startswith('"""'):
|
||||
return msg
|
||||
return "'''\n%s\n'''" % msg.strip()
|
||||
return '"""\n%s\n"""' % msg.strip()
|
||||
|
||||
def __printTopicDescription(self, topicObj):
|
||||
if topicObj.getDescription():
|
||||
extraIndent = self.__indentStep
|
||||
self.__formatItem("'''", extraIndent)
|
||||
self.__formatItem('"""', extraIndent)
|
||||
self.__formatItem( topicObj.getDescription(), extraIndent )
|
||||
self.__formatItem("'''", extraIndent)
|
||||
self.__formatItem('"""', extraIndent)
|
||||
|
||||
def __printTopicArgSpec(self, topicObj):
|
||||
extraIndent = self.__indentStep
|
||||
@@ -609,7 +608,7 @@ class TopicTreeSpecPrinter:
|
||||
|
||||
# and finally, the args docs
|
||||
extraIndent += self.__indentStep
|
||||
self.__formatItem("'''", extraIndent)
|
||||
self.__formatItem('"""', extraIndent)
|
||||
# but ignore the arg keys that are in parent args docs:
|
||||
parentMsgKeys = ()
|
||||
if topicObj.getParent() is not None:
|
||||
@@ -620,7 +619,7 @@ class TopicTreeSpecPrinter:
|
||||
argDesc = argsDocs[key]
|
||||
msg = "- %s: %s" % (key, argDesc)
|
||||
self.__formatItem(msg, extraIndent)
|
||||
self.__formatItem("'''", extraIndent)
|
||||
self.__formatItem('"""', extraIndent)
|
||||
|
||||
def __formatItem(self, item, extraIndent=0):
|
||||
indent = extraIndent + self.__indent
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
'''
|
||||
"""
|
||||
:copyright: Copyright since 2006 by Oliver Schoenborn, all rights reserved.
|
||||
:license: BSD, see LICENSE_BSD_Simple.txt for details.
|
||||
'''
|
||||
"""
|
||||
|
||||
|
||||
|
||||
class TopicNameError(ValueError):
|
||||
'''Raised when the topic name is not properly formatted or
|
||||
no corresponding Topic object found. '''
|
||||
"""Raised when the topic name is not properly formatted or
|
||||
no corresponding Topic object found. """
|
||||
def __init__(self, name, msg):
|
||||
ValueError.__init__(self, 'Topic name "%s": %s' % (name, msg))
|
||||
|
||||
|
||||
class TopicDefnError(RuntimeError):
|
||||
'''
|
||||
"""
|
||||
Raised when an operation requires a topic have an MDS, but it doesn't.
|
||||
See also pub.setTopicUnspecifiedFatal().
|
||||
'''
|
||||
"""
|
||||
def __init__(self, topicNameTuple):
|
||||
msg = "No topic specification for topic '%s'." \
|
||||
% '.'.join(topicNameTuple)
|
||||
@@ -25,14 +25,14 @@ class TopicDefnError(RuntimeError):
|
||||
|
||||
|
||||
class MessageDataSpecError(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')).
|
||||
'''
|
||||
"""
|
||||
|
||||
def __init__(self, msg, args):
|
||||
argsMsg = msg % ','.join(args)
|
||||
@@ -40,17 +40,17 @@ class MessageDataSpecError(RuntimeError):
|
||||
|
||||
|
||||
class ExcHandlerError(RuntimeError):
|
||||
'''
|
||||
"""
|
||||
Raised when a listener exception handler (see pub.setListenerExcHandler())
|
||||
raises an exception. The original exception is contained.
|
||||
'''
|
||||
"""
|
||||
|
||||
def __init__(self, badExcListenerID, topicObj, origExc=None):
|
||||
'''The badExcListenerID is the name of the listener that raised
|
||||
"""The badExcListenerID is the name of the listener that raised
|
||||
the original exception that handler was attempting to handle.
|
||||
The topicObj is the Topic object for the topic of the
|
||||
sendMessage that had an exception raised.
|
||||
The origExc is currently not used. '''
|
||||
The origExc is currently not used. """
|
||||
self.badExcListenerID = badExcListenerID
|
||||
import traceback
|
||||
self.exc = traceback.format_exc()
|
||||
@@ -62,10 +62,10 @@ class ExcHandlerError(RuntimeError):
|
||||
|
||||
|
||||
class UnrecognizedSourceFormatError(ValueError):
|
||||
'''
|
||||
"""
|
||||
Raised when a topic definition provider doesn't recognize the format
|
||||
of source input it was given.
|
||||
'''
|
||||
"""
|
||||
def __init__(self):
|
||||
ValueError.__init__(self, 'Source format not recognized')
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
'''
|
||||
"""
|
||||
Code related to the concept of topic tree and its management: creating
|
||||
and removing topics, getting info about a particular topic, etc.
|
||||
|
||||
:copyright: Copyright since 2006 by Oliver Schoenborn, all rights reserved.
|
||||
:license: BSD, see LICENSE_BSD_Simple.txt for details.
|
||||
'''
|
||||
"""
|
||||
|
||||
__all__ = [
|
||||
'TopicManager',
|
||||
@@ -52,7 +52,7 @@ ARGS_SPEC_NONE = ArgSpecGiven.SPEC_GIVEN_NONE
|
||||
# ---------------------------------------------------------
|
||||
|
||||
class TopicManager:
|
||||
'''
|
||||
"""
|
||||
Manages the registry of all topics and creation/deletion
|
||||
of topics.
|
||||
|
||||
@@ -60,7 +60,7 @@ class TopicManager:
|
||||
'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).
|
||||
'''
|
||||
"""
|
||||
|
||||
# Allowed return values for isTopicSpecified()
|
||||
TOPIC_SPEC_NOT_SPECIFIED = 0 # false
|
||||
@@ -69,10 +69,10 @@ class TopicManager:
|
||||
|
||||
|
||||
def __init__(self, treeConfig=None):
|
||||
'''The optional treeConfig is an instance of TreeConfig, used to
|
||||
"""The optional treeConfig is an instance of TreeConfig, used to
|
||||
configure the topic tree such as notification settings, etc. A
|
||||
default config is created if not given. This method should only be
|
||||
called by an instance of Publisher (see Publisher.getTopicManager()).'''
|
||||
called by an instance of Publisher (see Publisher.getTopicManager())."""
|
||||
self.__allTopics = None # root of topic tree
|
||||
self._topicsMap = {} # registry of all topics
|
||||
self.__treeConfig = treeConfig or TreeConfig()
|
||||
@@ -86,7 +86,7 @@ class TopicManager:
|
||||
self.__allTopics = self.__createTopic((ALL_TOPICS,), desc, specGiven=specGiven)
|
||||
|
||||
def getRootAllTopics(self):
|
||||
'''Get the topic that is parent of all root (ie top-level) topics,
|
||||
"""Get the topic that is parent of all root (ie top-level) topics,
|
||||
for default TopicManager instance created when this module is imported.
|
||||
Some notes:
|
||||
|
||||
@@ -94,11 +94,11 @@ class TopicManager:
|
||||
getParent() is None;
|
||||
- all root-level topics satisfy isAll()==False, isRoot()==True, and
|
||||
getParent() is getDefaultTopicTreeRoot();
|
||||
- all other topics satisfy neither. '''
|
||||
- all other topics satisfy neither. """
|
||||
return self.__allTopics
|
||||
|
||||
def addDefnProvider(self, providerOrSource, format=None):
|
||||
'''Register a topic definition provider. After this method is called, whenever a topic must be created,
|
||||
"""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.
|
||||
|
||||
@@ -107,7 +107,7 @@ class TopicManager:
|
||||
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.
|
||||
'''
|
||||
"""
|
||||
if isinstance(providerOrSource, ITopicDefnProvider):
|
||||
provider = providerOrSource
|
||||
else:
|
||||
@@ -118,17 +118,17 @@ class TopicManager:
|
||||
return provider
|
||||
|
||||
def clearDefnProviders(self):
|
||||
'''Remove all registered topic definition providers'''
|
||||
"""Remove all registered topic definition providers"""
|
||||
self.__defnProvider.clear()
|
||||
|
||||
def getNumDefnProviders(self):
|
||||
'''Get how many topic definitions providers are registered.'''
|
||||
"""Get how many topic definitions providers are registered."""
|
||||
return self.__defnProvider.getNumProviders()
|
||||
|
||||
def getTopic(self, name, okIfNone=False):
|
||||
'''Get the Topic instance for the given topic name. By default, raises
|
||||
"""Get the Topic instance for the given topic name. By default, raises
|
||||
an TopicNameError exception if a topic with given name doesn't exist. If
|
||||
okIfNone=True, returns None instead of raising an exception.'''
|
||||
okIfNone=True, returns None instead of raising an exception."""
|
||||
topicNameDotted = stringize(name)
|
||||
#if not name:
|
||||
# raise TopicNameError(name, 'Empty topic name not allowed')
|
||||
@@ -152,11 +152,11 @@ class TopicManager:
|
||||
raise TopicNameError(name, msg)
|
||||
|
||||
def newTopic(self, _name, _desc, _required=(), **_argDocs):
|
||||
'''Deprecated legacy method.
|
||||
"""Deprecated legacy method.
|
||||
If topic _name already exists, just returns it and does nothing else.
|
||||
Otherwise, uses getOrCreateTopic() to create it, then sets its
|
||||
description (_desc) and its message data specification (_argDocs
|
||||
and _required). Replaced by getOrCreateTopic().'''
|
||||
and _required). Replaced by getOrCreateTopic()."""
|
||||
topic = self.getTopic(_name, True)
|
||||
if topic is None:
|
||||
topic = self.getOrCreateTopic(_name)
|
||||
@@ -165,7 +165,7 @@ class TopicManager:
|
||||
return topic
|
||||
|
||||
def getOrCreateTopic(self, name, protoListener=None):
|
||||
'''Get the Topic instance for topic of given name, creating it
|
||||
"""Get the Topic instance for topic of given name, creating it
|
||||
(and any of its missing parent topics) as necessary. Pubsub
|
||||
functions such as subscribe() use this to obtain the Topic object
|
||||
corresponding to a topic name.
|
||||
@@ -201,7 +201,7 @@ class TopicManager:
|
||||
|
||||
The MDS can also be defined via a call to subscribe(listener, topicName),
|
||||
which indirectly calls getOrCreateTopic(topicName, listener).
|
||||
'''
|
||||
"""
|
||||
obj = self.getTopic(name, okIfNone=True)
|
||||
if obj:
|
||||
# if object is not sendable but a proto listener was given,
|
||||
@@ -229,17 +229,17 @@ class TopicManager:
|
||||
return self.__createTopic(nameTuple, desc, parent = parentObj, specGiven = specGiven)
|
||||
|
||||
def isTopicInUse(self, name):
|
||||
'''Determine if topic 'name' is in use. True if a Topic object exists
|
||||
"""Determine if topic 'name' is in use. True if a Topic object exists
|
||||
for topic name (i.e. message has already been sent for that topic, or a
|
||||
least one listener subscribed), false otherwise. Note: a topic may be in use
|
||||
but not have a definition (MDS and docstring); or a topic may have a
|
||||
definition, but not be in use.'''
|
||||
definition, but not be in use."""
|
||||
return self.getTopic(name, okIfNone=True) is not None
|
||||
|
||||
def hasTopicDefinition(self, name):
|
||||
'''Determine if there is a definition avaiable for topic 'name'. Return
|
||||
"""Determine if there is a definition avaiable for topic 'name'. Return
|
||||
true if there is, false otherwise. Note: a topic may have a
|
||||
definition without being in use, and vice versa.'''
|
||||
definition without being in use, and vice versa."""
|
||||
# in already existing Topic object:
|
||||
alreadyCreated = self.getTopic(name, okIfNone=True)
|
||||
if alreadyCreated is not None and alreadyCreated.hasMDS():
|
||||
@@ -253,16 +253,16 @@ class TopicManager:
|
||||
return False
|
||||
|
||||
def checkAllTopicsHaveMDS(self):
|
||||
'''Check that all topics that have been created for their MDS.
|
||||
Raise a TopicDefnError if one is found that does not have one.'''
|
||||
"""Check that all topics that have been created for their MDS.
|
||||
Raise a TopicDefnError if one is found that does not have one."""
|
||||
for topic in py2and3.itervalues(self._topicsMap):
|
||||
if not topic.hasMDS():
|
||||
raise TopicDefnError(topic.getNameTuple())
|
||||
|
||||
def delTopic(self, name):
|
||||
'''Delete the named topic, including all sub-topics. Returns False
|
||||
"""Delete the named topic, including all sub-topics. Returns False
|
||||
if topic does not exist; True otherwise. Also unsubscribe any listeners
|
||||
of topic and all subtopics. '''
|
||||
of topic and all subtopics. """
|
||||
# find from which parent the topic object should be removed
|
||||
dottedName = stringize(name)
|
||||
try:
|
||||
@@ -283,9 +283,9 @@ class TopicManager:
|
||||
return True
|
||||
|
||||
def getTopicsSubscribed(self, listener):
|
||||
'''Get the list of Topic objects that have given listener
|
||||
"""Get the list of Topic objects that have given listener
|
||||
subscribed. Note: the listener can also get messages from any
|
||||
sub-topic of returned list.'''
|
||||
sub-topic of returned list."""
|
||||
assocTopics = []
|
||||
for topicObj in py2and3.itervalues(self._topicsMap):
|
||||
if topicObj.hasListener(listener):
|
||||
@@ -293,7 +293,7 @@ class TopicManager:
|
||||
return assocTopics
|
||||
|
||||
def __getClosestParent(self, topicNameDotted):
|
||||
'''Returns a pair, (closest parent, tuple path from parent). The
|
||||
"""Returns a pair, (closest parent, tuple path from parent). The
|
||||
first item is the closest parent Topic that exists.
|
||||
The second one is the list of topic name elements that have to be
|
||||
created to create the given topic.
|
||||
@@ -303,7 +303,7 @@ class TopicManager:
|
||||
Note that if none of the branch exists (not even A), then return
|
||||
will be [root topic, ['A',B','C','D']). Note also that if A.B.C
|
||||
exists, the return will be (A.B.C, ['D']) regardless of whether
|
||||
A.B.C.D exists. '''
|
||||
A.B.C.D exists. """
|
||||
subtopicNames = []
|
||||
headTail = topicNameDotted.rsplit('.', 1)
|
||||
while len(headTail) > 1:
|
||||
@@ -319,9 +319,9 @@ class TopicManager:
|
||||
return self.__allTopics, subtopicNames
|
||||
|
||||
def __createParentTopics(self, topicName):
|
||||
'''This will find which parents need to be created such that
|
||||
"""This will find which parents need to be created such that
|
||||
topicName can be created (but doesn't create given topic),
|
||||
and creates them. Returns the parent object.'''
|
||||
and creates them. Returns the parent object."""
|
||||
assert self.getTopic(topicName, okIfNone=True) is None
|
||||
parentObj, subtopicNames = self.__getClosestParent(stringize(topicName))
|
||||
|
||||
@@ -341,9 +341,9 @@ class TopicManager:
|
||||
return parentObj
|
||||
|
||||
def __createTopic(self, nameTuple, desc, specGiven, parent=None):
|
||||
'''Actual topic creation step. Adds new Topic instance to topic map,
|
||||
"""Actual topic creation step. Adds new Topic instance to topic map,
|
||||
and sends notification message (see ``Publisher.addNotificationMgr()``)
|
||||
regarding topic creation.'''
|
||||
regarding topic creation."""
|
||||
if specGiven is None:
|
||||
specGiven = ArgSpecGiven()
|
||||
parentAI = None
|
||||
@@ -373,9 +373,9 @@ class TopicManager:
|
||||
|
||||
|
||||
def validateNameHierarchy(topicTuple):
|
||||
'''Check that names in topicTuple are valid: no spaces, not empty.
|
||||
"""Check that names in topicTuple are valid: no spaces, not empty.
|
||||
Raise ValueError if fails check. E.g. ('',) and ('a',' ') would
|
||||
both fail, but ('a','b') would be ok. '''
|
||||
both fail, but ('a','b') would be ok. """
|
||||
if not topicTuple:
|
||||
topicName = stringize(topicTuple)
|
||||
errMsg = 'empty topic name'
|
||||
@@ -401,38 +401,38 @@ def validateNameHierarchy(topicTuple):
|
||||
|
||||
|
||||
class _MasterTopicDefnProvider:
|
||||
'''
|
||||
"""
|
||||
Stores a list of topic definition providers. When queried for a topic
|
||||
definition, queries each provider (registered via addProvider()) and
|
||||
returns the first complete definition provided, or (None,None).
|
||||
|
||||
The providers must follow the ITopicDefnProvider protocol.
|
||||
'''
|
||||
"""
|
||||
|
||||
def __init__(self, treeConfig):
|
||||
self.__providers = []
|
||||
self.__treeConfig = treeConfig
|
||||
|
||||
def addProvider(self, provider):
|
||||
'''Add given provider IF not already added. '''
|
||||
"""Add given provider IF not already added. """
|
||||
assert(isinstance(provider, ITopicDefnProvider))
|
||||
if provider not in self.__providers:
|
||||
self.__providers.append(provider)
|
||||
|
||||
def clear(self):
|
||||
'''Remove all providers added.'''
|
||||
"""Remove all providers added."""
|
||||
self.__providers = []
|
||||
|
||||
def getNumProviders(self):
|
||||
'''Return how many providers added.'''
|
||||
"""Return how many providers added."""
|
||||
return len(self.__providers)
|
||||
|
||||
def getDefn(self, topicNameTuple):
|
||||
'''Returns a pair (docstring, MDS) for the topic. The first item is
|
||||
"""Returns a pair (docstring, MDS) for the topic. The first item is
|
||||
a string containing the topic's "docstring", i.e. a description string
|
||||
for the topic, or None if no docstring available for the topic. The
|
||||
second item is None or an instance of ArgSpecGiven specifying the
|
||||
required and optional message data for listeners of this topic. '''
|
||||
required and optional message data for listeners of this topic. """
|
||||
desc, defn = None, None
|
||||
for provider in self.__providers:
|
||||
tmpDesc, tmpDefn = provider.getDefn(topicNameTuple)
|
||||
@@ -444,8 +444,8 @@ class _MasterTopicDefnProvider:
|
||||
return desc, defn
|
||||
|
||||
def isDefined(self, topicNameTuple):
|
||||
'''Returns True only if a complete definition exists, ie topic
|
||||
has a description and a complete message data specification (MDS).'''
|
||||
"""Returns True only if a complete definition exists, ie topic
|
||||
has a description and a complete message data specification (MDS)."""
|
||||
desc, defn = self.getDefn(topicNameTuple)
|
||||
if desc is None or defn is None:
|
||||
return False
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
'''
|
||||
"""
|
||||
Provide the Topic class.
|
||||
|
||||
:copyright: Copyright since 2006 by Oliver Schoenborn, all rights reserved.
|
||||
:license: BSD, see LICENSE_BSD_Simple.txt for details.
|
||||
'''
|
||||
"""
|
||||
|
||||
|
||||
from weakref import ref as weakref
|
||||
@@ -42,16 +42,16 @@ from .. import py2and3
|
||||
|
||||
|
||||
class Topic(PublisherMixin):
|
||||
'''
|
||||
"""
|
||||
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).
|
||||
'''
|
||||
"""
|
||||
|
||||
def __init__(self, treeConfig, nameTuple, description,
|
||||
msgArgsInfo, parent=None):
|
||||
'''Create a topic. Should only be called by TopicManager via its
|
||||
"""Create a topic. Should only be called by TopicManager via its
|
||||
getOrCreateTopic() method (which gets called in several places
|
||||
in pubsub, such as sendMessage, subscribe, and newTopic).
|
||||
|
||||
@@ -62,7 +62,7 @@ class Topic(PublisherMixin):
|
||||
:param parent: parent of topic
|
||||
|
||||
:raises ValueError: invalid topic name
|
||||
'''
|
||||
"""
|
||||
if parent is None:
|
||||
if nameTuple != (ALL_TOPICS,):
|
||||
msg = 'Only one topic, named %s, can be root of topic tree'
|
||||
@@ -111,24 +111,24 @@ class Topic(PublisherMixin):
|
||||
parent.__adoptSubtopic( self )
|
||||
|
||||
def setDescription(self, desc):
|
||||
'''Set the 'docstring' of topic'''
|
||||
"""Set the 'docstring' of topic"""
|
||||
self.__description = desc
|
||||
|
||||
def getDescription(self):
|
||||
'''Return the 'docstring' of topic'''
|
||||
"""Return the 'docstring' of topic"""
|
||||
if self.__description is None:
|
||||
return None
|
||||
return smartDedent(self.__description)
|
||||
|
||||
def setMsgArgSpec(self, argsDocs, required=()):
|
||||
'''Specify the message data for topic messages.
|
||||
"""Specify the message data for topic messages.
|
||||
:param argsDocs: a dictionary of keyword names (message data name) and data 'docstring'; cannot be None
|
||||
:param required: a list of those keyword names, appearing in argsDocs,
|
||||
which are required (all others are assumed optional)
|
||||
|
||||
Can only be called if this info has not been already set at construction
|
||||
or in a previous call.
|
||||
:raise RuntimeError: if MDS already set at construction or previous call.'''
|
||||
:raise RuntimeError: if MDS already set at construction or previous call."""
|
||||
assert self.__parentTopic is not None # for root of tree, this method never called!
|
||||
if argsDocs is None:
|
||||
raise ValueError('Cannot set listener spec to None')
|
||||
@@ -148,10 +148,10 @@ class Topic(PublisherMixin):
|
||||
raise RuntimeError('Not allowed to call this: msg spec already set!')
|
||||
|
||||
def getArgs(self):
|
||||
'''Returns a pair (reqdArgs, optArgs) where reqdArgs is tuple
|
||||
"""Returns a pair (reqdArgs, optArgs) where reqdArgs is tuple
|
||||
of names of required message arguments, optArgs is tuple
|
||||
of names for optional arguments. If topic args not specified
|
||||
yet, returns (None, None).'''
|
||||
yet, returns (None, None)."""
|
||||
sendable = self.__msgArgs.isComplete()
|
||||
assert sendable == self.hasMDS()
|
||||
if sendable:
|
||||
@@ -160,19 +160,19 @@ class Topic(PublisherMixin):
|
||||
return None, None
|
||||
|
||||
def getArgDescriptions(self):
|
||||
'''Get a map of keyword names to docstrings: documents each MDS element. '''
|
||||
"""Get a map of keyword names to docstrings: documents each MDS element. """
|
||||
return self.__msgArgs.getArgsDocs()
|
||||
|
||||
def setArgDescriptions(self, **docs):
|
||||
'''Set the docstring for each MDS datum.'''
|
||||
"""Set the docstring for each MDS datum."""
|
||||
self.__msgArgs.setArgsDocs(docs)
|
||||
|
||||
def hasMDS(self):
|
||||
'''Return true if this topic has a message data specification (MDS).'''
|
||||
"""Return true if this topic has a message data specification (MDS)."""
|
||||
return self.__validator is not None
|
||||
|
||||
def filterMsgArgs(self, msgKwargs, check=False):
|
||||
'''Get the MDS docstrings for each of the spedified kwargs.'''
|
||||
"""Get the MDS docstrings for each of the spedified kwargs."""
|
||||
filteredArgs = self.__msgArgs.filterArgs(msgKwargs)
|
||||
# if no check of args yet, do it now:
|
||||
if check:
|
||||
@@ -180,14 +180,14 @@ class Topic(PublisherMixin):
|
||||
return filteredArgs
|
||||
|
||||
def isAll(self):
|
||||
'''Returns true if this topic is the 'all topics' topic. All root
|
||||
topics behave as though they are child of that topic. '''
|
||||
"""Returns true if this topic is the 'all topics' topic. All root
|
||||
topics behave as though they are child of that topic. """
|
||||
return self.__tupleName == (ALL_TOPICS,)
|
||||
|
||||
def isRoot(self):
|
||||
'''Returns true if this is a "root" topic, false otherwise. A
|
||||
"""Returns true if this is a "root" topic, false otherwise. A
|
||||
root topic is a topic whose name contains no dots and which
|
||||
has pub.ALL_TOPICS as parent.'''
|
||||
has pub.ALL_TOPICS as parent."""
|
||||
parent = self.getParent()
|
||||
if parent:
|
||||
return parent.isAll()
|
||||
@@ -195,36 +195,36 @@ class Topic(PublisherMixin):
|
||||
return False
|
||||
|
||||
def getName(self):
|
||||
'''Return dotted form of full topic name'''
|
||||
"""Return dotted form of full topic name"""
|
||||
return stringize(self.__tupleName)
|
||||
|
||||
def getNameTuple(self):
|
||||
'''Return tuple form of full topic name'''
|
||||
"""Return tuple form of full topic name"""
|
||||
return self.__tupleName
|
||||
|
||||
def getNodeName(self):
|
||||
'''Return the last part of the topic name (has no dots)'''
|
||||
"""Return the last part of the topic name (has no dots)"""
|
||||
name = self.__tupleName[-1]
|
||||
return name
|
||||
|
||||
def getParent(self):
|
||||
'''Get Topic object that is parent of self (i.e. self is a subtopic
|
||||
of parent). Return none if self is the "all topics" topic.'''
|
||||
"""Get Topic object that is parent of self (i.e. self is a subtopic
|
||||
of parent). Return none if self is the "all topics" topic."""
|
||||
if self.__parentTopic is None:
|
||||
return None
|
||||
return self.__parentTopic()
|
||||
|
||||
def hasSubtopic(self, name=None):
|
||||
'''Return true only if name is a subtopic of self. If name not
|
||||
specified, return true only if self has at least one subtopic.'''
|
||||
"""Return true only if name is a subtopic of self. If name not
|
||||
specified, return true only if self has at least one subtopic."""
|
||||
if name is None:
|
||||
return len(self.__subTopics) > 0
|
||||
|
||||
return name in self.__subTopics
|
||||
|
||||
def getSubtopic(self, relName):
|
||||
'''Get the specified subtopic object. The relName can be a valid
|
||||
subtopic name, a dotted-name string, or a tuple. '''
|
||||
"""Get the specified subtopic object. The relName can be a valid
|
||||
subtopic name, a dotted-name string, or a tuple. """
|
||||
if not relName:
|
||||
raise ValueError("getSubtopic() arg can't be empty")
|
||||
topicTuple = tupleize(relName)
|
||||
@@ -241,50 +241,49 @@ class Topic(PublisherMixin):
|
||||
return topicObj
|
||||
|
||||
def getSubtopics(self):
|
||||
'''Get a list of Topic instances that are subtopics of self.'''
|
||||
"""Get a list of Topic instances that are subtopics of self."""
|
||||
return py2and3.values(self.__subTopics)
|
||||
|
||||
def getNumListeners(self):
|
||||
'''Return number of listeners currently subscribed to topic. This is
|
||||
"""Return number of listeners currently subscribed to topic. This is
|
||||
different from number of listeners that will get notified since more
|
||||
general topics up the topic tree may have listeners.'''
|
||||
general topics up the topic tree may have listeners."""
|
||||
return len(self.__listeners)
|
||||
|
||||
def hasListener(self, listener):
|
||||
'''Return true if listener is subscribed to this topic.'''
|
||||
"""Return true if listener is subscribed to this topic."""
|
||||
return listener in self.__listeners
|
||||
|
||||
def hasListeners(self):
|
||||
'''Return true if there are any listeners subscribed to
|
||||
this topic, false otherwise.'''
|
||||
"""Return true if there are any listeners subscribed to
|
||||
this topic, false otherwise."""
|
||||
return bool(self.__listeners)
|
||||
|
||||
def getListeners(self):
|
||||
'''Get a list of Listener objects for listeners
|
||||
subscribed to this topic.'''
|
||||
return py2and3.keys(self.__listeners)
|
||||
"""Get an iterator of listeners subscribed to this topic."""
|
||||
return py2and3.iterkeys(self.__listeners)
|
||||
|
||||
def validate(self, listener):
|
||||
'''Checks whether listener could be subscribed to this topic:
|
||||
"""Checks whether listener could be subscribed to this topic:
|
||||
if yes, just returns; if not, raises ListenerMismatchError.
|
||||
Note that method raises TopicDefnError if self not
|
||||
hasMDS().'''
|
||||
hasMDS()."""
|
||||
if not self.hasMDS():
|
||||
raise TopicDefnError(self.__tupleName)
|
||||
return self.__validator.validate(listener)
|
||||
|
||||
def isValid(self, listener):
|
||||
'''Return True only if listener could be subscribed to this topic,
|
||||
"""Return True only if listener could be subscribed to this topic,
|
||||
otherwise returns False. Note that method raises TopicDefnError
|
||||
if self not hasMDS().'''
|
||||
if self not hasMDS()."""
|
||||
if not self.hasMDS():
|
||||
raise TopicDefnError(self.__tupleName)
|
||||
return self.__validator.isValid(listener)
|
||||
|
||||
def subscribe(self, listener):
|
||||
'''Subscribe listener to this topic. Returns a pair
|
||||
"""Subscribe listener to this topic. Returns a pair
|
||||
(pub.Listener, success). The success is true only if listener
|
||||
was not already subscribed and is now subscribed. '''
|
||||
was not already subscribed and is now subscribed. """
|
||||
if listener in self.__listeners:
|
||||
assert self.hasMDS()
|
||||
subdLisnr, newSub = self.__listeners[listener], False
|
||||
@@ -305,12 +304,12 @@ class Topic(PublisherMixin):
|
||||
return subdLisnr, newSub
|
||||
|
||||
def unsubscribe(self, listener):
|
||||
'''Unsubscribe the specified listener from this topic. Returns
|
||||
"""Unsubscribe the specified listener from this topic. Returns
|
||||
the pub.Listener object associated with the listener that was
|
||||
unsubscribed, or None if the specified listener was not
|
||||
subscribed to this topic. Note that this method calls
|
||||
``notifyUnsubscribe(listener, self)`` on all registered notification
|
||||
handlers (see pub.addNotificationHandler).'''
|
||||
handlers (see pub.addNotificationHandler)."""
|
||||
unsubdLisnr = self.__listeners.pop(listener, None)
|
||||
if unsubdLisnr is None:
|
||||
return None
|
||||
@@ -324,10 +323,10 @@ class Topic(PublisherMixin):
|
||||
return unsubdLisnr
|
||||
|
||||
def unsubscribeAllListeners(self, filter=None):
|
||||
'''Clears list of subscribed listeners. If filter is given, it must
|
||||
"""Clears list of subscribed listeners. If filter is given, it must
|
||||
be a function that takes a listener and returns true if the listener
|
||||
should be unsubscribed. Returns the list of Listener for listeners
|
||||
that were unsubscribed.'''
|
||||
that were unsubscribed."""
|
||||
unsubd = []
|
||||
if filter is None:
|
||||
for listener in self.__listeners:
|
||||
@@ -335,10 +334,12 @@ class Topic(PublisherMixin):
|
||||
unsubd = py2and3.keys(self.__listeners)
|
||||
self.__listeners = {}
|
||||
else:
|
||||
unsubd = [listener for listener in self.__listeners if filter(listener)]
|
||||
for listener in unsubd:
|
||||
listener._unlinkFromTopic_()
|
||||
del self.__listeners[listener]
|
||||
unsubd = []
|
||||
for listener in py2and3.keys(self.__listeners):
|
||||
if filter(listener):
|
||||
unsubd.append(listener)
|
||||
listener._unlinkFromTopic_()
|
||||
del self.__listeners[listener]
|
||||
|
||||
# send notification regarding all listeners actually unsubscribed
|
||||
notificationMgr = self._treeConfig.notificationMgr
|
||||
@@ -354,14 +355,14 @@ class Topic(PublisherMixin):
|
||||
#############################################################
|
||||
|
||||
def _getListenerSpec(self):
|
||||
'''Only to be called by pubsub package'''
|
||||
"""Only to be called by pubsub package"""
|
||||
return self.__msgArgs
|
||||
|
||||
def _publish(self, data):
|
||||
'''This sends message to listeners of parent topics as well.
|
||||
"""This sends message to listeners of parent topics as well.
|
||||
If an exception is raised in a listener, the publish is
|
||||
aborted, except if there is a handler (see
|
||||
pub.setListenerExcHandler).'''
|
||||
pub.setListenerExcHandler)."""
|
||||
self._treeConfig.notificationMgr.notifySend('pre', self)
|
||||
|
||||
# send to ourself
|
||||
@@ -405,10 +406,10 @@ class Topic(PublisherMixin):
|
||||
raise ExcHandlerError(listener.name(), topicObj, exc)
|
||||
|
||||
def __finalize(self):
|
||||
'''Finalize the topic specification, which currently means
|
||||
"""Finalize the topic specification, which currently means
|
||||
creating the listener validator for this topic. This allows
|
||||
calls to subscribe() to validate that listener adheres to
|
||||
topic's message data specification (MDS).'''
|
||||
topic's message data specification (MDS)."""
|
||||
assert self.__msgArgs.isComplete()
|
||||
assert not self.hasMDS()
|
||||
|
||||
@@ -419,15 +420,15 @@ class Topic(PublisherMixin):
|
||||
assert not self.__listeners
|
||||
|
||||
def _undefineSelf_(self, topicsMap):
|
||||
'''Called by topic manager when deleting a topic.'''
|
||||
"""Called by topic manager when deleting a topic."""
|
||||
if self.__parentTopic is not None:
|
||||
self.__parentTopic().__abandonSubtopic(self.__tupleName[-1])
|
||||
self.__undefineBranch(topicsMap)
|
||||
|
||||
def __undefineBranch(self, topicsMap):
|
||||
'''Unsubscribe all our listeners, remove all subtopics from self,
|
||||
"""Unsubscribe all our listeners, remove all subtopics from self,
|
||||
then detach from parent. Parent is not notified, because method
|
||||
assumes it has been called by parent'''
|
||||
assumes it has been called by parent"""
|
||||
#print 'Remove %s listeners (%s)' % (self.getName(), self.getNumListeners())
|
||||
self.unsubscribeAllListeners()
|
||||
self.__parentTopic = None
|
||||
@@ -441,18 +442,18 @@ class Topic(PublisherMixin):
|
||||
del topicsMap[self.getName()]
|
||||
|
||||
def __adoptSubtopic(self, topicObj):
|
||||
'''Add topicObj as child topic.'''
|
||||
"""Add topicObj as child topic."""
|
||||
assert topicObj.__parentTopic() is self
|
||||
attrName = topicObj.getNodeName()
|
||||
self.__subTopics[attrName] = topicObj
|
||||
|
||||
def __abandonSubtopic(self, name):
|
||||
'''The given subtopic becomes orphan (no parent).'''
|
||||
"""The given subtopic becomes orphan (no parent)."""
|
||||
topicObj = self.__subTopics.pop(name)
|
||||
assert topicObj.__parentTopic() is self
|
||||
|
||||
def __onDeadListener(self, weakListener):
|
||||
'''One of our subscribed listeners has died, so remove it and notify'''
|
||||
"""One of our subscribed listeners has died, so remove it and notify"""
|
||||
pubListener = self.__listeners.pop(weakListener)
|
||||
# notify:
|
||||
self._treeConfig.notificationMgr.notifyDeadListener(pubListener, self)
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
'''
|
||||
"""
|
||||
|
||||
:copyright: Copyright since 2006 by Oliver Schoenborn, all rights reserved.
|
||||
:license: BSD, see LICENSE_BSD_Simple.txt for details.
|
||||
|
||||
'''
|
||||
"""
|
||||
|
||||
class TopicTreeTraverser:
|
||||
'''
|
||||
"""
|
||||
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()
|
||||
@@ -14,29 +14,29 @@ class TopicTreeTraverser:
|
||||
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.
|
||||
'''
|
||||
"""
|
||||
DEPTH = 'Depth first through topic tree'
|
||||
BREADTH = 'Breadth first through topic tree'
|
||||
MAP = 'Sequential through topic manager\'s topics map'
|
||||
|
||||
def __init__(self, visitor = None):
|
||||
'''The visitor, if given, must adhere to API of
|
||||
"""The visitor, if given, must adhere to API of
|
||||
ITopicTreeVisitor. The visitor can be changed or
|
||||
set via setVisitor(visitor) before calling traverse().'''
|
||||
set via setVisitor(visitor) before calling traverse()."""
|
||||
self.__handler = visitor
|
||||
|
||||
def setVisitor(self, visitor):
|
||||
'''The visitor must adhere to API of ITopicTreeVisitor. '''
|
||||
"""The visitor must adhere to API of ITopicTreeVisitor. """
|
||||
self.__handler = visitor
|
||||
|
||||
def traverse(self, topicObj, how=DEPTH, onlyFiltered=True):
|
||||
'''Start traversing tree at topicObj. Note that topicObj is a
|
||||
"""Start traversing tree at topicObj. Note that topicObj is a
|
||||
Topic object, not a topic name. The how defines if tree should
|
||||
be traversed breadth or depth first. If onlyFiltered is
|
||||
False, then all nodes are accepted (_accept(node) not called).
|
||||
|
||||
This method can be called multiple times.
|
||||
'''
|
||||
"""
|
||||
if how == self.MAP:
|
||||
raise NotImplementedError('not yet available')
|
||||
|
||||
@@ -104,40 +104,40 @@ class TopicTreeTraverser:
|
||||
|
||||
|
||||
class ITopicTreeVisitor:
|
||||
'''
|
||||
"""
|
||||
Derive from ITopicTreeVisitor and override one or more of the
|
||||
self._*() methods. Give an instance to an instance of
|
||||
TopicTreeTraverser.
|
||||
'''
|
||||
"""
|
||||
|
||||
def _accept(self, topicObj):
|
||||
'''Override this to filter nodes of topic tree. Must return
|
||||
"""Override this to filter nodes of topic tree. Must return
|
||||
True (accept node) of False (reject node). Note that rejected
|
||||
nodes cause traversal to move to next branch (no children
|
||||
traversed).'''
|
||||
traversed)."""
|
||||
return True
|
||||
|
||||
def _startTraversal(self):
|
||||
'''Override this to define what to do when traversal() starts.'''
|
||||
"""Override this to define what to do when traversal() starts."""
|
||||
pass
|
||||
|
||||
def _onTopic(self, topicObj):
|
||||
'''Override this to define what to do for each node.'''
|
||||
"""Override this to define what to do for each node."""
|
||||
pass
|
||||
|
||||
def _startChildren(self):
|
||||
'''Override this to take special action whenever a
|
||||
"""Override this to take special action whenever a
|
||||
new level of the topic hierarchy is started (e.g., indent
|
||||
some output). '''
|
||||
some output). """
|
||||
pass
|
||||
|
||||
def _endChildren(self):
|
||||
'''Override this to take special action whenever a
|
||||
"""Override this to take special action whenever a
|
||||
level of the topic hierarchy is completed (e.g., dedent
|
||||
some output). '''
|
||||
some output). """
|
||||
pass
|
||||
|
||||
def _doneTraversal(self):
|
||||
'''Override this to take special action when traversal done.'''
|
||||
"""Override this to take special action when traversal done."""
|
||||
pass
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
'''
|
||||
"""
|
||||
Various utilities used by topic-related modules.
|
||||
|
||||
:copyright: Copyright since 2006 by Oliver Schoenborn, all rights reserved.
|
||||
:license: BSD, see LICENSE_BSD_Simple.txt for details.
|
||||
|
||||
'''
|
||||
"""
|
||||
|
||||
from textwrap import TextWrapper, dedent
|
||||
|
||||
@@ -21,19 +21,19 @@ ALL_TOPICS = 'ALL_TOPICS'
|
||||
|
||||
|
||||
class WeakNone:
|
||||
'''Pretend to be a weak reference to nothing. Used by ArgsInfos to
|
||||
refer to parent when None so no if-else blocks needed. '''
|
||||
"""Pretend to be a weak reference to nothing. Used by ArgsInfos to
|
||||
refer to parent when None so no if-else blocks needed. """
|
||||
def __call__(self):
|
||||
return None
|
||||
|
||||
|
||||
def smartDedent(paragraph):
|
||||
'''Dedent paragraph using textwrap.dedent(), but properly dedents
|
||||
"""Dedent paragraph using textwrap.dedent(), but properly dedents
|
||||
even if the first line of paragraph does not contain blanks.
|
||||
This handles the case where a user types a documentation string as
|
||||
"""A long string spanning
|
||||
several lines."""
|
||||
'''
|
||||
'''A long string spanning
|
||||
several lines.'''
|
||||
"""
|
||||
if paragraph.startswith(' '):
|
||||
para = dedent(paragraph)
|
||||
else:
|
||||
@@ -48,7 +48,7 @@ _validNameRE = re.compile(r'[-0-9a-zA-Z]\w*')
|
||||
|
||||
|
||||
def validateName(topicName):
|
||||
'''Raise TopicNameError if nameTuple not valid as topic name.'''
|
||||
"""Raise TopicNameError if nameTuple not valid as topic name."""
|
||||
topicNameTuple = tupleize(topicName)
|
||||
if not topicNameTuple:
|
||||
reason = 'name tuple must have at least one item!'
|
||||
@@ -75,12 +75,12 @@ def validateName(topicName):
|
||||
|
||||
|
||||
def stringize(topicName):
|
||||
'''If topicName is a string, just return it
|
||||
"""If topicName is a string, just return it
|
||||
as is. If it is a topic definition object (ie an object that has
|
||||
'msgDataSpec' as data member), return the dotted name of corresponding
|
||||
topic. Otherwise, assume topicName is a tuple and convert it to to a
|
||||
dotted name i.e. ('a','b','c') => 'a.b.c'. Empty name is not allowed
|
||||
(ValueError). The reverse operation is tupleize(topicName).'''
|
||||
(ValueError). The reverse operation is tupleize(topicName)."""
|
||||
if py2and3.isstring(topicName):
|
||||
return topicName
|
||||
|
||||
@@ -97,10 +97,10 @@ def stringize(topicName):
|
||||
|
||||
|
||||
def tupleize(topicName):
|
||||
'''If topicName is a tuple of strings, just return it as is. Otherwise,
|
||||
"""If topicName is a tuple of strings, just return it as is. Otherwise,
|
||||
convert it to tuple, assuming dotted notation used for topicName. I.e.
|
||||
'a.b.c' => ('a','b','c'). Empty topicName is not allowed (ValueError).
|
||||
The reverse operation is stringize(topicNameTuple).'''
|
||||
The reverse operation is stringize(topicNameTuple)."""
|
||||
# assume name is most often str; if more often tuple,
|
||||
# then better use isinstance(name, tuple)
|
||||
if hasattr(topicName, "msgDataSpec"):
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
'''
|
||||
"""
|
||||
|
||||
:copyright: Copyright since 2006 by Oliver Schoenborn, all rights reserved.
|
||||
:license: BSD, see LICENSE_BSD_Simple.txt for details.
|
||||
'''
|
||||
"""
|
||||
|
||||
from .notificationmgr import NotificationMgr
|
||||
|
||||
|
||||
class TreeConfig:
|
||||
'''
|
||||
"""
|
||||
Each topic tree has its own topic manager and configuration,
|
||||
such as notification and exception handling.
|
||||
'''
|
||||
"""
|
||||
|
||||
def __init__(self, notificationHandler=None, listenerExcHandler=None):
|
||||
self.notificationMgr = NotificationMgr(notificationHandler)
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
'''
|
||||
"""
|
||||
Some topic definition validation functions.
|
||||
|
||||
:copyright: Copyright since 2006 by Oliver Schoenborn, all rights reserved.
|
||||
:license: BSD, see LICENSE_BSD_Simple.txt for details.
|
||||
'''
|
||||
"""
|
||||
|
||||
from .topicexc import MessageDataSpecError
|
||||
|
||||
|
||||
def verifyArgsDifferent(allArgs, allParentArgs, topicName):
|
||||
'''Verify that allArgs does not contain any of allParentArgs. Raise
|
||||
MessageDataSpecError if fail. '''
|
||||
"""Verify that allArgs does not contain any of allParentArgs. Raise
|
||||
MessageDataSpecError if fail. """
|
||||
extra = set(allArgs).intersection(allParentArgs)
|
||||
if extra:
|
||||
msg = 'Args %%s already used in parent of "%s"' % topicName
|
||||
@@ -18,8 +18,8 @@ def verifyArgsDifferent(allArgs, allParentArgs, topicName):
|
||||
|
||||
|
||||
def verifySubset(all, sub, topicName, extraMsg=''):
|
||||
'''Verify that sub is a subset of all for topicName. Raise
|
||||
MessageDataSpecError if fail. '''
|
||||
"""Verify that sub is a subset of all for topicName. Raise
|
||||
MessageDataSpecError if fail. """
|
||||
notInAll = set(sub).difference(all)
|
||||
if notInAll:
|
||||
args = ','.join(all)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
'''
|
||||
"""
|
||||
This module provides a basic "weak method" implementation, WeakMethod. It uses
|
||||
weakref.WeakRef which, used on its own, produces weak methods that are dead on
|
||||
creation, not very useful. Use the getWeakRef(object) module function to create the
|
||||
@@ -7,7 +7,7 @@ proper type of weak reference (weakref.WeakRef or WeakMethod) for given object.
|
||||
:copyright: Copyright since 2006 by Oliver Schoenborn, all rights reserved.
|
||||
:license: BSD, see LICENSE_BSD_Simple.txt for details.
|
||||
|
||||
'''
|
||||
"""
|
||||
|
||||
# for function and method parameter counting:
|
||||
from inspect import ismethod
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
'''
|
||||
"""
|
||||
Aggregates policies for pubsub. Mainly, related to messaging protocol.
|
||||
|
||||
:copyright: Copyright since 2006 by Oliver Schoenborn, all rights reserved.
|
||||
:license: BSD, see LICENSE_BSD_Simple.txt for details.
|
||||
'''
|
||||
"""
|
||||
|
||||
msgProtocolTransStage = None
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
'''
|
||||
"""
|
||||
This is the main entry-point to pubsub's core functionality. The :mod:`~pubsub.pub`
|
||||
module supports:
|
||||
|
||||
@@ -21,14 +21,14 @@ 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).
|
||||
'''
|
||||
"""
|
||||
|
||||
'''
|
||||
"""
|
||||
:copyright: Copyright since 2006 by Oliver Schoenborn, all rights reserved.
|
||||
:license: BSD, see LICENSE_BSD_Simple.txt for details.
|
||||
'''
|
||||
"""
|
||||
|
||||
VERSION_API = 3 #: major API version
|
||||
VERSION_API = 3 #: major API version
|
||||
|
||||
VERSION_SVN = "$Rev: 243 $".split()[1] # DO NOT CHANGE: automatically updated by VCS
|
||||
|
||||
@@ -119,7 +119,7 @@ __all__ = [
|
||||
|
||||
'TopicTreeTraverser',
|
||||
|
||||
]
|
||||
]
|
||||
|
||||
|
||||
# --------- Publisher singleton and bound methods ------------------------------------
|
||||
@@ -144,8 +144,8 @@ setTopicUnspecifiedFatal = _publisher.setTopicUnspecifiedFatal
|
||||
getMsgProtocol = _publisher.getMsgProtocol
|
||||
|
||||
def getDefaultPublisher():
|
||||
'''Get the Publisher instance created by default when this module
|
||||
is imported. See the module doc for details about this instance.'''
|
||||
"""Get the Publisher instance created by default when this module
|
||||
is imported. See the module doc for details about this instance."""
|
||||
return _publisher
|
||||
|
||||
|
||||
@@ -158,28 +158,28 @@ topicsMap = _topicMgr._topicsMap
|
||||
|
||||
|
||||
def isValid(listener, topicName):
|
||||
'''Return true only if listener can subscribe to messages of given topic.'''
|
||||
"""Return true only if listener can subscribe to messages of given topic."""
|
||||
return _topicMgr.getTopic(topicName).isValid(listener)
|
||||
|
||||
|
||||
def validate(listener, topicName):
|
||||
'''Checks if listener can subscribe to topicName. If not, raises
|
||||
ListenerMismatchError, otherwise just returns.'''
|
||||
"""Checks if listener can subscribe to topicName. If not, raises
|
||||
ListenerMismatchError, otherwise just returns."""
|
||||
_topicMgr.getTopic(topicName).validate(listener)
|
||||
|
||||
|
||||
def isSubscribed(listener, topicName):
|
||||
'''Returns true if listener has subscribed to topicName, false otherwise.
|
||||
"""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. '''
|
||||
topicName. """
|
||||
return _topicMgr.getTopic(topicName).hasListener(listener)
|
||||
|
||||
|
||||
def getDefaultTopicMgr():
|
||||
'''Get the TopicManager instance created by default when this
|
||||
"""Get the TopicManager instance created by default when this
|
||||
module is imported. This function is a shortcut for
|
||||
``pub.getDefaultPublisher().getTopicMgr()``.'''
|
||||
``pub.getDefaultPublisher().getTopicMgr()``."""
|
||||
return _topicMgr
|
||||
|
||||
|
||||
@@ -188,11 +188,11 @@ clearTopicDefnProviders = _topicMgr.clearDefnProviders
|
||||
getNumTopicDefnProviders = _topicMgr.getNumDefnProviders
|
||||
|
||||
def instantiateAllDefinedTopics(provider):
|
||||
'''Loop over all topics of given provider and "instantiate" each topic, thus
|
||||
"""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. '''
|
||||
listener is registered on it. """
|
||||
for topicName in provider:
|
||||
_topicMgr.getOrCreateTopic(topicName)
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
'''
|
||||
"""
|
||||
Setup pubsub for the *arg1* message protocol. In a default pubsub installation
|
||||
the default protocol is *kargs*.
|
||||
|
||||
@@ -14,20 +14,20 @@ protocol cannot be changed (i.e., importing it after the first
|
||||
The *arg1* protocol is identical to the legacy messaging protocol from
|
||||
first version of pubsub (when it was still part of wxPython) and
|
||||
is *deprecated*. This module is therefore *deprecated*.
|
||||
'''
|
||||
"""
|
||||
|
||||
'''
|
||||
"""
|
||||
:copyright: Copyright since 2006 by Oliver Schoenborn, all rights reserved.
|
||||
:license: BSD, see LICENSE_BSD_Simple.txt for details.
|
||||
'''
|
||||
"""
|
||||
|
||||
from . import policies
|
||||
policies.msgDataProtocol = 'arg1'
|
||||
|
||||
|
||||
def enforceArgName(commonName):
|
||||
'''This will configure pubsub to require that all listeners use
|
||||
"""This will configure pubsub to require that all listeners use
|
||||
the same argument name (*commonName*) as first parameter. This
|
||||
is a ueful first step in migrating an application that has been
|
||||
using *arg1* protocol to the more powerful *kwargs* protocol. '''
|
||||
using *arg1* protocol to the more powerful *kwargs* protocol. """
|
||||
policies.setMsgDataArgName(1, commonName)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
'''
|
||||
"""
|
||||
Setup pubsub for the kwargs message protocol. In a default installation
|
||||
this is the default protocol so this module is only needed if setupkargs
|
||||
utility functions are used, or in a custom installation where kwargs
|
||||
@@ -9,21 +9,21 @@ This module must be imported before the first ``from pubsub import pub``
|
||||
statement in the application. Once :mod:pub has been imported, the messaging
|
||||
protocol cannot be changed (i.e., importing it after the first
|
||||
``from pubsub import pub`` statement has undefined behavior).
|
||||
'''
|
||||
"""
|
||||
|
||||
'''
|
||||
"""
|
||||
:copyright: Copyright since 2006 by Oliver Schoenborn, all rights reserved.
|
||||
:license: BSD, see LICENSE_BSD_Simple.txt for details.
|
||||
'''
|
||||
"""
|
||||
|
||||
from . import policies
|
||||
policies.msgDataProtocol = 'kwargs'
|
||||
|
||||
|
||||
def transitionFromArg1(commonName):
|
||||
'''Utility function to assist migrating an application from using
|
||||
"""Utility function to assist migrating an application from using
|
||||
the arg1 messaging protocol to using the kwargs protocol. Call this
|
||||
after having run and debugged your application with ``setuparg1.enforceArgName(commonName)``. See the migration docs
|
||||
for more detais.
|
||||
'''
|
||||
"""
|
||||
policies.setMsgDataArgName(2, commonName)
|
||||
|
||||
@@ -1,16 +1,12 @@
|
||||
'''
|
||||
"""
|
||||
Provides utility functions and classes that are not required for using
|
||||
pubsub but are likely to be very useful.
|
||||
'''
|
||||
"""
|
||||
|
||||
'''
|
||||
"""
|
||||
:copyright: Copyright since 2006 by Oliver Schoenborn, all rights reserved.
|
||||
:license: BSD, see LICENSE_BSD_Simple.txt for details.
|
||||
'''
|
||||
|
||||
from .intraimport import intraImport
|
||||
intraImport(__path__)
|
||||
|
||||
"""
|
||||
|
||||
from .topictreeprinter import printTreeDocs
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
'''
|
||||
"""
|
||||
Some utility classes for exception handling of exceptions raised
|
||||
within listeners:
|
||||
|
||||
@@ -11,7 +11,7 @@ within listeners:
|
||||
:copyright: Copyright since 2006 by Oliver Schoenborn, all rights reserved.
|
||||
:license: BSD, see LICENSE_BSD_Simple.txt for details.
|
||||
|
||||
'''
|
||||
"""
|
||||
|
||||
|
||||
import sys, traceback
|
||||
@@ -20,7 +20,7 @@ from ..core.listener import IListenerExcHandler
|
||||
|
||||
|
||||
class TracebackInfo:
|
||||
'''
|
||||
"""
|
||||
Represent the traceback information for when an exception is
|
||||
raised -- but not caught -- in a listener. The complete
|
||||
traceback cannot be stored since this leads to circular
|
||||
@@ -38,7 +38,7 @@ class TracebackInfo:
|
||||
* self.traceback: list of quadruples as returned by traceback.extract_tb()
|
||||
|
||||
Normally you just need to call one of the two getFormatted() methods.
|
||||
'''
|
||||
"""
|
||||
def __init__(self):
|
||||
tmpInfo = sys.exc_info()
|
||||
self.ExcClass = tmpInfo[0]
|
||||
@@ -50,15 +50,15 @@ class TracebackInfo:
|
||||
del tmpInfo
|
||||
|
||||
def getFormattedList(self):
|
||||
'''Get a list of strings as returned by the traceback module's
|
||||
format_list() and format_exception_only() functions.'''
|
||||
"""Get a list of strings as returned by the traceback module's
|
||||
format_list() and format_exception_only() functions."""
|
||||
tmp = traceback.format_list(self.traceback)
|
||||
tmp.extend( traceback.format_exception_only(self.ExcClass, self.excArg) )
|
||||
return tmp
|
||||
|
||||
def getFormattedString(self):
|
||||
'''Get a string similar to the stack trace that gets printed
|
||||
to stdout by Python interpreter when an exception is not caught.'''
|
||||
"""Get a string similar to the stack trace that gets printed
|
||||
to stdout by Python interpreter when an exception is not caught."""
|
||||
return ''.join(self.getFormattedList())
|
||||
|
||||
def __str__(self):
|
||||
@@ -66,24 +66,24 @@ class TracebackInfo:
|
||||
|
||||
|
||||
class ExcPublisher(IListenerExcHandler):
|
||||
'''
|
||||
"""
|
||||
Example exception handler that simply publishes the exception traceback
|
||||
as a message of topic name given by topicUncaughtExc.
|
||||
'''
|
||||
"""
|
||||
|
||||
# name of the topic
|
||||
topicUncaughtExc = 'uncaughtExcInListener'
|
||||
|
||||
def __init__(self, topicMgr=None):
|
||||
'''If topic manager is specified, will automatically call init().
|
||||
"""If topic manager is specified, will automatically call init().
|
||||
Otherwise, caller must call init() after pubsub imported. See
|
||||
pub.setListenerExcHandler().'''
|
||||
pub.setListenerExcHandler()."""
|
||||
if topicMgr is not None:
|
||||
self.init(topicMgr)
|
||||
|
||||
def init(self, topicMgr):
|
||||
'''Must be called only after pubsub has been imported since this
|
||||
handler creates a pubsub topic.'''
|
||||
"""Must be called only after pubsub has been imported since this
|
||||
handler creates a pubsub topic."""
|
||||
obj = topicMgr.getOrCreateTopic(self.topicUncaughtExc)
|
||||
obj.setDescription('generated when a listener raises an exception')
|
||||
obj.setMsgArgSpec( dict(
|
||||
@@ -92,8 +92,8 @@ class ExcPublisher(IListenerExcHandler):
|
||||
self.__topicObj = obj
|
||||
|
||||
def __call__(self, listenerID, topicObj):
|
||||
'''Handle the exception raised by given listener. Send the
|
||||
Traceback to all subscribers of topic self.topicUncaughtExc. '''
|
||||
"""Handle the exception raised by given listener. Send the
|
||||
Traceback to all subscribers of topic self.topicUncaughtExc. """
|
||||
tbInfo = TracebackInfo()
|
||||
self.__topicObj.publish(listenerStr=listenerID, excTraceback=tbInfo)
|
||||
|
||||
|
||||
@@ -13,21 +13,21 @@ __all__ = ('printImported', 'StructMsg', 'Callback', 'Enum' )
|
||||
|
||||
|
||||
def printImported():
|
||||
'''Output a list of pubsub modules imported so far'''
|
||||
"""Output a list of pubsub modules imported so far"""
|
||||
ll = [mod for mod in sys.modules.keys() if mod.find('pubsub') >= 0] # iter keys ok
|
||||
ll.sort()
|
||||
py2and3.print_('\n'.join(ll))
|
||||
|
||||
|
||||
class StructMsg:
|
||||
'''
|
||||
"""
|
||||
This *can* be used to package message data. Each of the keyword
|
||||
args given at construction will be stored as a member of the 'data'
|
||||
member of instance. E.g. "m=Message2(a=1, b='b')" would succeed
|
||||
"assert m.data.a==1" and "assert m.data.b=='b'". However, use of
|
||||
Message2 makes your messaging code less documented and harder to
|
||||
debug.
|
||||
'''
|
||||
"""
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
class Data: pass
|
||||
@@ -36,7 +36,7 @@ class StructMsg:
|
||||
|
||||
|
||||
class Callback:
|
||||
'''This can be used to wrap functions that are referenced by class
|
||||
"""This can be used to wrap functions that are referenced by class
|
||||
data if the data should be called as a function. E.g. given
|
||||
>>> def func(): pass
|
||||
>>> class A:
|
||||
@@ -46,7 +46,7 @@ class Callback:
|
||||
will fail since Python will try to call a() as a method of boo,
|
||||
whereas a() is a free function. But if you have instead
|
||||
"self.a = Callback(func)", then "boo.a()" works as expected.
|
||||
'''
|
||||
"""
|
||||
def __init__(self, callable_):
|
||||
self.__callable = callable_
|
||||
def __call__(self, *args, **kwargs):
|
||||
@@ -54,7 +54,7 @@ class Callback:
|
||||
|
||||
|
||||
class Enum:
|
||||
'''Used only internally. Represent one value out of an enumeration
|
||||
"""Used only internally. Represent one value out of an enumeration
|
||||
set. It is meant to be used as::
|
||||
|
||||
class YourAllowedValues:
|
||||
@@ -70,12 +70,12 @@ class Enum:
|
||||
...
|
||||
if val is YourAllowedValues.enum1:
|
||||
...
|
||||
'''
|
||||
"""
|
||||
nextValue = 0
|
||||
values = set()
|
||||
|
||||
def __init__(self, value=None, *desc):
|
||||
'''Use value if given, otherwise use next integer.'''
|
||||
"""Use value if given, otherwise use next integer."""
|
||||
self.desc = '\n'.join(desc)
|
||||
if value is None:
|
||||
assert Enum.nextValue not in Enum.values
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
'''
|
||||
"""
|
||||
Provide an interface class for handling pubsub notification messages,
|
||||
and an example class (though very useful in practice) showing how to
|
||||
use it.
|
||||
@@ -17,19 +17,19 @@ relevant information.
|
||||
|
||||
:copyright: Copyright since 2006 by Oliver Schoenborn, all rights reserved.
|
||||
:license: BSD, see LICENSE_BSD_Simple.txt for details.
|
||||
'''
|
||||
"""
|
||||
|
||||
from ..core import callables
|
||||
from ..core.notificationmgr import INotificationHandler
|
||||
|
||||
|
||||
class IgnoreNotificationsMixin(INotificationHandler):
|
||||
'''
|
||||
"""
|
||||
Derive your Notifications handler from this class if your handler
|
||||
just wants to be notified of one or two types of pubsub events.
|
||||
Then just override the desired methods. The rest of the notifications
|
||||
will automatically be ignored.
|
||||
'''
|
||||
"""
|
||||
|
||||
def notifySubscribe(self, pubListener, topicObj, newSub):
|
||||
pass
|
||||
@@ -47,16 +47,16 @@ class IgnoreNotificationsMixin(INotificationHandler):
|
||||
|
||||
|
||||
class NotifyByWriteFile(INotificationHandler):
|
||||
'''
|
||||
"""
|
||||
Print a message to stdout when a notification is received.
|
||||
'''
|
||||
"""
|
||||
|
||||
defaultPrefix = 'PUBSUB:'
|
||||
|
||||
def __init__(self, fileObj = None, prefix = None):
|
||||
'''Will write to stdout unless fileObj given. Will use
|
||||
"""Will write to stdout unless fileObj given. Will use
|
||||
defaultPrefix as prefix for each line output, unless prefix
|
||||
specified. '''
|
||||
specified. """
|
||||
self.__pre = prefix or self.defaultPrefix
|
||||
|
||||
if fileObj is None:
|
||||
@@ -107,7 +107,7 @@ class NotifyByWriteFile(INotificationHandler):
|
||||
|
||||
|
||||
class NotifyByPubsubMessage(INotificationHandler):
|
||||
'''
|
||||
"""
|
||||
Handle pubsub notification messages by generating
|
||||
messages of a 'pubsub.' subtopic. Also provides
|
||||
an example of how to create a notification handler.
|
||||
@@ -123,7 +123,7 @@ class NotifyByPubsubMessage(INotificationHandler):
|
||||
message is generated. If you have subscribed a listener of
|
||||
this topic, your listener will be notified of what listener
|
||||
unsubscribed from what topic.
|
||||
'''
|
||||
"""
|
||||
|
||||
topicRoot = 'pubsub'
|
||||
|
||||
@@ -142,8 +142,8 @@ class NotifyByPubsubMessage(INotificationHandler):
|
||||
self.createNotificationTopics(topicMgr)
|
||||
|
||||
def createNotificationTopics(self, topicMgr):
|
||||
'''Create the notification topics. The root of the topics created
|
||||
is self.topicRoot. The topicMgr is (usually) pub.topicMgr.'''
|
||||
"""Create the notification topics. The root of the topics created
|
||||
is self.topicRoot. The topicMgr is (usually) pub.topicMgr."""
|
||||
# see if the special topics have already been defined
|
||||
try:
|
||||
topicMgr.getTopic(self.topicRoot)
|
||||
@@ -185,10 +185,10 @@ class NotifyByPubsubMessage(INotificationHandler):
|
||||
self.__doNotification(pubTopic, kwargs)
|
||||
|
||||
def notifySend(self, stage, topicObj, pubListener=None):
|
||||
'''Stage must be 'pre' or 'post'. Note that any pubsub sendMessage
|
||||
"""Stage must be 'pre' or 'post'. Note that any pubsub sendMessage
|
||||
operation resulting from this notification (which sends a message;
|
||||
listener could handle by sending another message!) will NOT themselves
|
||||
lead to a send notification. '''
|
||||
lead to a send notification. """
|
||||
if (self._pubTopic is None) or self.__sending:
|
||||
return
|
||||
|
||||
@@ -223,14 +223,14 @@ class NotifyByPubsubMessage(INotificationHandler):
|
||||
|
||||
|
||||
def _createTopics(topicMap, topicMgr):
|
||||
'''
|
||||
"""
|
||||
Create notification topics. These are used when
|
||||
some of the notification flags have been set to True (see
|
||||
pub.setNotificationFlags(). The topicMap is a dict where key is
|
||||
the notification type, and value is the topic name to create.
|
||||
Notification type is a string in ('send', 'subscribe',
|
||||
'unsubscribe', 'newTopic', 'delTopic', 'deadListener'.
|
||||
'''
|
||||
"""
|
||||
def newTopic(_name, _desc, _required=None, **argsDocs):
|
||||
topic = topicMgr.getOrCreateTopic(_name)
|
||||
topic.setDescription(_desc)
|
||||
@@ -278,7 +278,7 @@ def _createTopics(topicMap, topicMgr):
|
||||
|
||||
|
||||
def useNotifyByPubsubMessage(publisher=None, all=True, **kwargs):
|
||||
'''Will cause all of pubsub's notifications of pubsub "actions" (such as
|
||||
"""Will cause all of pubsub's notifications of pubsub "actions" (such as
|
||||
new topic created, message sent, listener subscribed, etc) to be sent
|
||||
out as messages. Topic will be 'pubsub' subtopics, such as
|
||||
'pubsub.newTopic', 'pubsub.delTopic', 'pubsub.sendMessage', etc.
|
||||
@@ -297,7 +297,7 @@ def useNotifyByPubsubMessage(publisher=None, all=True, **kwargs):
|
||||
from wx.lib.pubsub.utils import notification
|
||||
notification.useNotifyByPubsubMessage()
|
||||
|
||||
'''
|
||||
"""
|
||||
if publisher is None:
|
||||
from .. import pub
|
||||
publisher = pub.getDefaultPublisher()
|
||||
@@ -310,7 +310,7 @@ def useNotifyByPubsubMessage(publisher=None, all=True, **kwargs):
|
||||
|
||||
def useNotifyByWriteFile(fileObj=None, prefix=None,
|
||||
publisher=None, all=True, **kwargs):
|
||||
'''Will cause all pubsub notifications of pubsub "actions" (such as
|
||||
"""Will cause all pubsub notifications of pubsub "actions" (such as
|
||||
new topic created, message sent, listener died etc) to be written to
|
||||
specified file (or stdout if none given). The fileObj need only
|
||||
provide a 'write(string)' method.
|
||||
@@ -319,7 +319,7 @@ def useNotifyByWriteFile(fileObj=None, prefix=None,
|
||||
constructor. The 'all' and kwargs arguments are those of pubsub's
|
||||
setNotificationFlags(), except that 'all' defaults to True. See
|
||||
useNotifyByPubsubMessage() for an explanation of pubModule (typically
|
||||
only if pubsub inside wxPython's wx.lib)'''
|
||||
only if pubsub inside wxPython's wx.lib)"""
|
||||
notifHandler = NotifyByWriteFile(fileObj, prefix)
|
||||
|
||||
if publisher is None:
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
'''
|
||||
"""
|
||||
Output various aspects of topic tree to string or file.
|
||||
|
||||
:copyright: Copyright since 2006 by Oliver Schoenborn, all rights reserved.
|
||||
:license: BSD, see LICENSE_BSD_Simple.txt for details.
|
||||
'''
|
||||
"""
|
||||
|
||||
from textwrap import TextWrapper
|
||||
|
||||
@@ -11,7 +11,7 @@ from ..core.topictreetraverser import (ITopicTreeVisitor, TopicTreeTraverser)
|
||||
|
||||
|
||||
class TopicTreePrinter(ITopicTreeVisitor):
|
||||
'''
|
||||
"""
|
||||
Example topic tree visitor that prints a prettified representation
|
||||
of topic tree by doing a depth-first traversal of topic tree and
|
||||
print information at each (topic) node of tree. Extra info to be
|
||||
@@ -38,19 +38,19 @@ class TopicTreePrinter(ITopicTreeVisitor):
|
||||
> arg1: (required) its description
|
||||
> arg2: some other description
|
||||
|
||||
'''
|
||||
"""
|
||||
|
||||
allowedExtras = frozenset('DAaL') # must NOT change
|
||||
ALL_TOPICS_NAME = 'ALL_TOPICS' # output for name of 'all topics' topic
|
||||
|
||||
def __init__(self, extra=None, width=70, indentStep=4,
|
||||
bulletTopic='\\--', bulletTopicItem='|==', bulletTopicArg='-', fileObj=None):
|
||||
'''Topic tree printer will print listeners for each topic only
|
||||
"""Topic tree printer will print listeners for each topic only
|
||||
if printListeners is True. The width will be used to limit
|
||||
the width of text output, while indentStep is the number of
|
||||
spaces added each time the text is indented further. The
|
||||
three bullet parameters define the strings used for each
|
||||
item (topic, topic items, and kwargs). '''
|
||||
item (topic, topic items, and kwargs). """
|
||||
self.__contentMeth = dict(
|
||||
D = self.__printTopicDescription,
|
||||
A = self.__printTopicArgsAll,
|
||||
@@ -83,7 +83,7 @@ class TopicTreePrinter(ITopicTreeVisitor):
|
||||
self.__destination.write(self.getOutput())
|
||||
|
||||
def _onTopic(self, topicObj):
|
||||
'''This gets called for each topic. Print as per specified content.'''
|
||||
"""This gets called for each topic. Print as per specified content."""
|
||||
|
||||
# topic name
|
||||
self.__wrapper.width = self.__width
|
||||
@@ -102,17 +102,17 @@ class TopicTreePrinter(ITopicTreeVisitor):
|
||||
function(indent, topicObj)
|
||||
|
||||
def _startChildren(self):
|
||||
'''Increase the indent'''
|
||||
"""Increase the indent"""
|
||||
self.__indent += self.__indentStep
|
||||
|
||||
def _endChildren(self):
|
||||
'''Decrease the indent'''
|
||||
"""Decrease the indent"""
|
||||
self.__indent -= self.__indentStep
|
||||
|
||||
def __formatDefn(self, indent, item, defn='', sep=': '):
|
||||
'''Print a definition: a block of text at a certain indent,
|
||||
"""Print a definition: a block of text at a certain indent,
|
||||
has item name, and an optional definition separated from
|
||||
item by sep. '''
|
||||
item by sep. """
|
||||
if defn:
|
||||
prefix = '%s%s%s' % (' '*indent, item, sep)
|
||||
self.__wrapper.initial_indent = prefix
|
||||
@@ -162,7 +162,7 @@ class TopicTreePrinter(ITopicTreeVisitor):
|
||||
|
||||
|
||||
def printTreeDocs(rootTopic=None, topicMgr=None, **kwargs):
|
||||
'''Print out the topic tree to a file (or file-like object like a
|
||||
"""Print out the topic tree to a file (or file-like object like a
|
||||
StringIO), starting at rootTopic. If root topic should be root of
|
||||
whole tree, get it from pub.getDefaultTopicTreeRoot().
|
||||
The treeVisitor is an instance of pub.TopicTreeTraverser.
|
||||
@@ -182,7 +182,7 @@ def printTreeDocs(rootTopic=None, topicMgr=None, **kwargs):
|
||||
|
||||
The kwargs are the same as for TopicTreePrinter constructor:
|
||||
extra(None), width(70), indentStep(4), bulletTopic, bulletTopicItem,
|
||||
bulletTopicArg, fileObj(stdout). If fileObj not given, stdout is used.'''
|
||||
bulletTopicArg, fileObj(stdout). If fileObj not given, stdout is used."""
|
||||
if rootTopic is None:
|
||||
if topicMgr is None:
|
||||
from .. import pub
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
'''
|
||||
"""
|
||||
Contributed by Joshua R English, adapted by Oliver Schoenborn to be
|
||||
consistent with pubsub API.
|
||||
|
||||
@@ -8,7 +8,7 @@ Python nested class format.
|
||||
|
||||
To use:
|
||||
|
||||
xml = """
|
||||
xml = '''
|
||||
<topicdefntree>
|
||||
<description>Test showing topic hierarchy and inheritance</description>
|
||||
<topic id="parent">
|
||||
@@ -26,7 +26,7 @@ To use:
|
||||
</topic>
|
||||
</topic>
|
||||
</topicdefntree>
|
||||
"""
|
||||
'''
|
||||
|
||||
These topic definitions are loaded through an XmlTopicDefnProvider:
|
||||
|
||||
@@ -43,7 +43,7 @@ of the XML tree.
|
||||
|
||||
:copyright: Copyright since 2013 by Oliver Schoenborn, all rights reserved.
|
||||
:license: BSD, see LICENSE_BSD_Simple.txt for details.
|
||||
'''
|
||||
"""
|
||||
|
||||
__author__ = 'Joshua R English'
|
||||
__revision__ = 6
|
||||
@@ -249,9 +249,9 @@ def indent(elem, level=0):
|
||||
|
||||
|
||||
def exportTopicTreeSpecXml(moduleName=None, rootTopic=None, bak='bak', moduleDoc=None):
|
||||
'''
|
||||
"""
|
||||
If rootTopic is None, then pub.getDefaultTopicTreeRoot() is assumed.
|
||||
'''
|
||||
"""
|
||||
|
||||
if rootTopic is None:
|
||||
from .. import pub
|
||||
|
||||
Reference in New Issue
Block a user