diff --git a/wx/lib/pubsub/README.txt b/wx/lib/pubsub/README.txt deleted file mode 100644 index 0ad681a8..00000000 --- a/wx/lib/pubsub/README.txt +++ /dev/null @@ -1,9 +0,0 @@ -Release Notes -============= - -* 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 diff --git a/wx/lib/pubsub/README_WxPython.txt b/wx/lib/pubsub/README_WxPython.txt deleted file mode 100644 index a96fe49b..00000000 --- a/wx/lib/pubsub/README_WxPython.txt +++ /dev/null @@ -1,22 +0,0 @@ -# this file gets copied to wx/lib/pubsub folder when release to wxPython - -For wxPython users: - -The code in this wx/lib/pubsub folder is taken verbatim from the PyPubSub -project on SourceForge.net. Pubsub originated as a wxPython lib, but it is now -a standalone project on SourceForge. It is included as part of wxPython -distribution for convenience to wxPython users, but pubsub can also be installed -standalone (see installation notes at http://pypubsub.sourceforge.net), or you -can also overwrite the version in this folder with the standalone version or -put an SVN checkout of pubsub in this folder, etc. - -Note that the source distribution on SF.net tends to be updated more often than -the copy in wx/lib/pubsub. If you wish to install pubsub standalone, there are -instructions on the Install page of http://pypubsub.sourceforge.net. - -There is extensive documentation for pubsub at http://pypubsub.sourceforge.net, -and some examples are in wx/lib/pubsub/examples. The WxPython wiki also discusses -usage of pubsub in wxPython. - -Oliver Schoenborn -December 2013 diff --git a/wx/lib/pubsub/RELEASE_NOTES.txt b/wx/lib/pubsub/RELEASE_NOTES.txt deleted file mode 100644 index d931911a..00000000 --- a/wx/lib/pubsub/RELEASE_NOTES.txt +++ /dev/null @@ -1,71 +0,0 @@ -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 \ No newline at end of file diff --git a/wx/lib/pubsub/__init__.py b/wx/lib/pubsub/__init__.py index 058e1363..49d8524b 100644 --- a/wx/lib/pubsub/__init__.py +++ b/wx/lib/pubsub/__init__.py @@ -5,16 +5,17 @@ Pubsub package initialization. :license: BSD, see LICENSE_BSD_Simple.txt for details. """ -_PREVIOUS_RELEASE_DATE = "2013-09-15" -_PREVIOUS_RELEASE_VER = "3.2.1b" +# Import pubsub (Version 4.0.0) from site-packages/pubsub -__version__ = "3.3.0" - -__all__ = [ - 'pub', - 'utils', - 'setupkwargs', - 'setuparg1', - '__version__' - ] +# Import all items from the pubsub package so they appear +# in the wx.lib.pubsub package namespace. This to maintain +# its appearance as wx.lib.pubsub where it was originally +# created but subsequently published as a stand-alone package +# that is currently sourced from https://github.com/schollii/pypubsub +try: + from pubsub import * +except ImportError: + msg = "Stand-alone pubsub not found. Use pip install Pypubsub" + raise ImportError(msg) + diff --git a/wx/lib/pubsub/core/__init__.py b/wx/lib/pubsub/core/__init__.py deleted file mode 100644 index 0e5e6cef..00000000 --- a/wx/lib/pubsub/core/__init__.py +++ /dev/null @@ -1,92 +0,0 @@ -""" -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 -selected messaging protocol. - -Indeed some of the API depends on the messaging -protocol used. For instance sendMessage(), defined in publisher.py, -has a different signature (and hence implementation) for the kwargs -protocol than for the arg1 protocol. - -The most convenient way to -support this is to put the parts of the package that differ based -on protocol in separate folder, and add one of those folders to -the package's __path__ variable (defined automatically by the Python -interpreter when __init__.py is executed). For instance, code -specific to the kwargs protocol goes in the kwargs folder, and code -specific to the arg1 protocol in the arg1 folder. Then when doing -"from pubsub.core import listener", the correct listener.py will be -found for the specified protocol. The default protocol is kwargs. - -Only one protocol can be used in an application. The default protocol, -if none is chosen by user, is kwargs, as selected by the call to -_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 - called once, at package load time, to set the folder used for - implementation specific to the default message protocol.""" - corepath = __path__ - initpyLoc = corepath[-1] - import os - corepath.insert(0, os.path.join(initpyLoc, extra)) - -# add appropriate subdir for protocol-specific implementation -from .. import policies -_prependModulePath(policies.msgDataProtocol) - -from .publisher import Publisher - -from .callables import ( - AUTO_TOPIC, -) - -from .listener import ( - Listener, - getID as getListenerID, - ListenerMismatchError, - IListenerExcHandler, -) - -from .topicobj import ( - Topic, - SenderMissingReqdMsgDataError, - SenderUnknownMsgDataError, - MessageDataSpecError, - TopicDefnError, - ExcHandlerError, -) - -from .topicmgr import ( - TopicManager, - TopicDefnError, - TopicNameError, - ALL_TOPICS, -) - -from .topicdefnprovider import ( - ITopicDefnProvider, - TopicDefnProvider, - ITopicDefnDeserializer, - UnrecognizedSourceFormatError, - - exportTopicTreeSpec, - TOPIC_TREE_FROM_MODULE, - TOPIC_TREE_FROM_STRING, - TOPIC_TREE_FROM_CLASS, -) - -from .topictreetraverser import ( - TopicTreeTraverser, -) - -from .notificationmgr import ( - INotificationHandler, -) \ No newline at end of file diff --git a/wx/lib/pubsub/core/arg1/__init__.py b/wx/lib/pubsub/core/arg1/__init__.py deleted file mode 100644 index d84ec66d..00000000 --- a/wx/lib/pubsub/core/arg1/__init__.py +++ /dev/null @@ -1,16 +0,0 @@ -""" -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 -part of pubsub.core via pubsub.core's __path__. Hence, this should not -be imported directly, it is part of pubsub.core when the messaging -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' -raise RuntimeError(msg) \ No newline at end of file diff --git a/wx/lib/pubsub/core/arg1/listenerimpl.py b/wx/lib/pubsub/core/arg1/listenerimpl.py deleted file mode 100644 index f64c4a8d..00000000 --- a/wx/lib/pubsub/core/arg1/listenerimpl.py +++ /dev/null @@ -1,97 +0,0 @@ -""" -: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 -from .. import policies - - -class Message: - """ - A simple container object for the two components of a topic messages - in the pubsub legacy API: the - topic and the user data. An instance of Message is given to your - listener when called by sendMessage(topic). The data is accessed - via the 'data' attribute, and can be type of object. - """ - def __init__(self, topicNameTuple, data): - self.topic = topicNameTuple - self.data = data - - def __str__(self): - return '[Topic: '+repr(self.topic)+', Data: '+repr(self.data)+']' - - -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. - - A Listener instance has the same hash value as the callable that it wraps. - - A callable will be given data when a message is sent to it. In the arg1 - 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 - if listener is dead. Should always return True (False would require - the callable_ be dead but self hasn't yet been notified of it...).""" - kwargs = {} - if self._autoTopicArgName is not None: - kwargs[self._autoTopicArgName] = actualTopic - cb = self._callable() - if cb is None: - self._calledWhenDead() - msg = Message(actualTopic.getNameTuple(), data) - cb(msg, **kwargs) - return True - - -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 - # accept *args - # accept any keyword args - - if (paramsInfo.getAllArgs() == ()) and paramsInfo.acceptsAllUnnamedArgs: - return - - if paramsInfo.getAllArgs() == (): - msg = 'Must have at least one parameter (any name, with or without default value, or *arg)' - raise ListenerMismatchError(msg, listener, []) - - assert paramsInfo.getAllArgs() - #assert not paramsInfo.acceptsAllUnnamedArgs - - # verify at most one required arg - numReqdArgs = paramsInfo.numRequired - if numReqdArgs > 1: - allReqd = paramsInfo.getRequiredArgs() - msg = 'only one of %s can be a required agument' % (allReqd,) - raise ListenerMismatchError(msg, listener, allReqd) - - # if no required args but listener has *args, then we - # don't care about anything else: - if numReqdArgs == 0 and paramsInfo.acceptsAllUnnamedArgs: - return - - # if no policy set, any name ok; otherwise validate name: - needArgName = policies.msgDataArgName - firstArgName = paramsInfo.allParams[0] - if (needArgName is not None) and firstArgName != needArgName: - msg = 'listener arg name must be "%s" (is "%s")' % (needArgName, firstArgName) - effTopicArgs = [needArgName] - raise ListenerMismatchError(msg, listener, effTopicArgs) - - diff --git a/wx/lib/pubsub/core/arg1/publisher.py b/wx/lib/pubsub/core/arg1/publisher.py deleted file mode 100644 index d36c0a4a..00000000 --- a/wx/lib/pubsub/core/arg1/publisher.py +++ /dev/null @@ -1,40 +0,0 @@ -""" -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, - 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) - - # don't care if topic not final: topicObj.getListeners() - # will return nothing if not final but notification will still work - - topicObj.publish(data) - - def getMsgProtocol(self): - return 'arg1' - diff --git a/wx/lib/pubsub/core/arg1/publishermixin.py b/wx/lib/pubsub/core/arg1/publishermixin.py deleted file mode 100644 index 942dee50..00000000 --- a/wx/lib/pubsub/core/arg1/publishermixin.py +++ /dev/null @@ -1,34 +0,0 @@ -""" -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. - -Note that it is important that the PublisherMixin NOT modify any -state data during message sending, because in principle it could -happen that a listener causes another message of same topic to be -sent (presumably, the listener has a way of preventing infinite -loop). - -:copyright: Copyright since 2006 by Oliver Schoenborn, all rights reserved. -:license: BSD, see LICENSE_BSD_Simple.txt for details. - -""" - - -class PublisherMixin: - def __init__(self): - pass - - def publish(self, data=None): - self._publish(data) - - ############## IMPLEMENTATION ############### - - def _mix_prePublish(self, data, topicObj=None, iterState=None): - """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.""" - listener(self, data) diff --git a/wx/lib/pubsub/core/arg1/topicargspecimpl.py b/wx/lib/pubsub/core/arg1/topicargspecimpl.py deleted file mode 100644 index 63f941eb..00000000 --- a/wx/lib/pubsub/core/arg1/topicargspecimpl.py +++ /dev/null @@ -1,66 +0,0 @@ -""" - -: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 - the part of pubsub that uses ArgsInfos supports several API - versions using one common API. So the only difference between - an ArgsInfo and an ArgSpecGiven is that ArgsInfo refers to - parent topic's ArgsInfo; other data members are the same. - - 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 - - def __init__(self, topicNameTuple, specGiven, parentArgsInfo): - self.__argsDocs = specGiven.argsDocs or {'data':'message data'} - - self.argsSpecType = self.SPEC_COMPLETE - self.allOptional = () # list of topic message optional argument names - self.allRequired = ('data',) # list of topic message required argument names - - self.parentAI = WeakNone() - if parentArgsInfo is not None: - self.parentAI = weakref.ref(parentArgsInfo) - - def isComplete(self): - return True - - def getArgs(self): - return self.allOptional + self.allRequired - - def numArgs(self): - return len(self.allOptional) + len(self.allRequired) - - def getArgsDocs(self): - return self.__argsDocs.copy() - - diff --git a/wx/lib/pubsub/core/arg1/topicmgrimpl.py b/wx/lib/pubsub/core/arg1/topicmgrimpl.py deleted file mode 100644 index 31167cb8..00000000 --- a/wx/lib/pubsub/core/arg1/topicmgrimpl.py +++ /dev/null @@ -1,19 +0,0 @@ -""" -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.""" - argName = policies.msgDataArgName or 'data' - argsDocs = {argName : 'data for message sent'} - reqdArgs = (argName,) - return argsDocs, reqdArgs - diff --git a/wx/lib/pubsub/core/callables.py b/wx/lib/pubsub/core/callables.py deleted file mode 100644 index 3dbfca01..00000000 --- a/wx/lib/pubsub/core/callables.py +++ /dev/null @@ -1,191 +0,0 @@ -""" -Low level functions and classes related to callables. - -The AUTO_TOPIC -is the "marker" to use in callables to indicate that when a message -is sent to those callables, the topic object for that message should be -added to the data sent via the call arguments. See the docs in -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 - -from .. import py2and3 - -AUTO_TOPIC = '## your listener wants topic name ## (string unlikely to be used by caller)' - - -def getModule(obj): - """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). """ - if hasattr(obj, '__module__'): - module = obj.__module__ - else: - module = '__main__' - return module - - -def getID(callable_): - """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. """ - sc = callable_ - if ismethod(sc): - module = getModule(sc.__self__) - id = '%s.%s' % (sc.__self__.__class__.__name__, sc.__func__.__name__) - elif isfunction(sc): - module = getModule(sc) - id = sc.__name__ - else: # must be a functor (instance of a class that has __call__ method) - module = getModule(sc) - id = sc.__class__.__name__ - - return id, module - - -def getRawFunction(callable_): - """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).""" - firstArg = 0 - if isfunction(callable_): - #print('Function %s' % getID(callable_)) - func = callable_ - elif ismethod(callable_): - #print('Method %s' % getID(callable_)) - func = callable_ - if func.__self__ is not None: - # Method is bound, don't care about the self arg - firstArg = 1 - elif hasattr(callable_, '__call__'): - #print('Functor %s' % getID(callable_)) - func = callable_.__call__ - firstArg = 1 # don't care about the self arg - else: - msg = 'type "%s" not supported' % type(callable_).__name__ - raise ValueError(msg) - - return func, firstArg - - -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) - msg = 'Listener "%s" (from module "%s") inadequate: %s' % (idStr, module, msg) - ValueError.__init__(self, msg) - self.msg = msg - self.args = args - self.module = module - self.idStr = idStr - - def __str__(self): - return self.msg - - -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: - - 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 - args that is of use, so it is typically 0 if listener is a function, - and 1 if listener is a method. - - The acceptsAllKwargs should be true - if the listener has **kwargs in its protocol. - - After construction, - - self.allParams will contain the subset of 'args' without first - firstArgIdx items, - - self.numRequired will indicate number of required arguments - (ie self.allParams[:self.numRequired] are the required args names); - - self.acceptsAllKwargs = acceptsAllKwargs - - self.autoTopicArgName will be the name of argument - in which to put the topic object for which pubsub message is - sent, or None. This is identified by the argument that has a - default value of AUTO_TOPIC. - - For instance, listener(self, arg1, arg2=AUTO_TOPIC, arg3=None) will - have self.allParams = (arg1, arg2, arg3), self.numRequired=1, and - self.autoTopicArgName = 'arg2', whereas - listener(self, arg1, arg3=None) will have - self.allParams = (arg1, arg3), self.numRequired=1, and - self.autoTopicArgName = None.""" - - #args, firstArgIdx, defaultVals, acceptsAllKwargs - (allParams, varParamName, varOptParamName, defaultVals) = getargspec(func) - if defaultVals is None: - defaultVals = [] - else: - defaultVals = list(defaultVals) - - self.acceptsAllKwargs = (varOptParamName is not None) - self.acceptsAllUnnamedArgs = (varParamName is not None) - - self.allParams = allParams - del self.allParams[0:firstArgIdx] # does nothing if firstArgIdx == 0 - - self.numRequired = len(self.allParams) - len(defaultVals) - assert self.numRequired >= 0 - - # if listener wants topic, remove that arg from args/defaultVals - self.autoTopicArgName = None - if defaultVals: - self.__setupAutoTopic(defaultVals) - - def getAllArgs(self): - return tuple( self.allParams ) - - def getOptionalArgs(self): - 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 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.""" - for indx, defaultVal in enumerate(defaults): - if defaultVal == AUTO_TOPIC: - #del self.defaults[indx] - firstKwargIdx = self.numRequired - self.autoTopicArgName = self.allParams.pop(firstKwargIdx + indx) - break - - -def getArgs(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_) - except ValueError: - from .. import py2and3 - exc = py2and3.getexcobj() - raise ListenerMismatchError(str(exc), callable_) - - return CallArgsInfo(func, firstArgIdx) - - diff --git a/wx/lib/pubsub/core/imp2.py b/wx/lib/pubsub/core/imp2.py deleted file mode 100644 index d9bb7839..00000000 --- a/wx/lib/pubsub/core/imp2.py +++ /dev/null @@ -1,63 +0,0 @@ -""" -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.""" - if not hasattr(package, 'rindex'): - raise ValueError("'package' not set to a string") - dot = len(package) - for x in py2and3.xrange(level, 1, -1): - try: - dot = package.rindex('.', 0, dot) - except ValueError: - raise ValueError("attempted relative import beyond top-level " - "package") - return "%s.%s" % (package[:dot], name) - - -def _import_module(name, package=None): - """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") - level = 0 - for character in name: - if character != '.': - break - level += 1 - name = _resolve_name(name[level:], package, level) - __import__(name) - return sys.modules[name] - - -def load_module(moduleName, searchPath): - """Try to import moduleName. If this doesn't work, use the "imp" module - that is part of Python. """ - try: - module = _import_module(moduleName) - - except Exception: - import imp - fp, pathname, description = imp.find_module(moduleName, searchPath) - try: - module = imp.load_module(moduleName, fp, pathname, description) - - finally: - # Since we may exit via an exception, close fp explicitly. - if fp: - fp.close() - - return module diff --git a/wx/lib/pubsub/core/itopicdefnprovider.py b/wx/lib/pubsub/core/itopicdefnprovider.py deleted file mode 100644 index e69de29b..00000000 diff --git a/wx/lib/pubsub/core/kwargs/__init__.py b/wx/lib/pubsub/core/kwargs/__init__.py deleted file mode 100644 index 8fde7a75..00000000 --- a/wx/lib/pubsub/core/kwargs/__init__.py +++ /dev/null @@ -1,16 +0,0 @@ -""" -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 -part of pubsub.core via pubsub.core's __path__. Hence, this should not -be imported directly, it is part of pubsub.core when the messaging -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' -raise RuntimeError(msg) \ No newline at end of file diff --git a/wx/lib/pubsub/core/kwargs/datamsg.py b/wx/lib/pubsub/core/kwargs/datamsg.py deleted file mode 100644 index 15a7db15..00000000 --- a/wx/lib/pubsub/core/kwargs/datamsg.py +++ /dev/null @@ -1,27 +0,0 @@ -""" -: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) - gets an instance of Message. The given 'data' is accessed - via Message.data, while the topic name is available in Message.topic:: - - def listener(msg): - print("data is %s" % msg.data) - print("topic name is %s" % msg.topic) - 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 - - def __str__(self): - return '[Topic: '+repr(self.topic)+', Data: '+repr(self.data)+']' - diff --git a/wx/lib/pubsub/core/kwargs/listenerimpl.py b/wx/lib/pubsub/core/kwargs/listenerimpl.py deleted file mode 100644 index 1e75290c..00000000 --- a/wx/lib/pubsub/core/kwargs/listenerimpl.py +++ /dev/null @@ -1,93 +0,0 @@ -""" - -: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. - - A Listener instance - has the same hash value as the callable that it wraps. - - Callables that have 'argName=pub.AUTO_TOPIC' as a kwarg will - be given the Topic object for the message sent by sendMessage(). - Such a Listener will have wantsTopicObjOnCall() True. - - Callables that have a '\**kargs' argument will receive all message - data, not just that for the topic they are subscribed to. Such a listener - will have wantsAllMessageData() True. - """ - - def __call__(self, kwargs, actualTopic, allKwargs=None): - """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...).""" - if self.acceptsAllKwargs: - kwargs = allKwargs or kwargs # if allKwargs is None then use kwargs - - if self._autoTopicArgName is not None: - kwargs = kwargs.copy() - kwargs[self._autoTopicArgName] = actualTopic - - cb = self._callable() - if cb is None: - self._calledWhenDead() - cb(**kwargs) - - return True - - -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 - # accept *args - - # check if listener missing params (only possible if - # paramsInfo.acceptsAllKwargs is False) - allTopicMsgArgs = self._topicArgs | self._topicKwargs - allParams = set(paramsInfo.allParams) - if not paramsInfo.acceptsAllKwargs: - missingParams = allTopicMsgArgs - allParams - if missingParams: - msg = 'needs to accept %s more args (%s)' \ - % (len(missingParams), ''.join(missingParams)) - raise ListenerMismatchError(msg, listener, missingParams) - else: - # then can accept that some parameters missing from listener - # signature - pass - - # check if there are unknown parameters in listener signature: - extraArgs = allParams - allTopicMsgArgs - if extraArgs: - if allTopicMsgArgs: - msg = 'args (%s) not allowed, should be (%s)' \ - % (','.join(extraArgs), ','.join(allTopicMsgArgs)) - else: - msg = 'no args allowed, has (%s)' % ','.join(extraArgs) - raise ListenerMismatchError(msg, listener, extraArgs) - - # we accept listener that has fewer required paams than TMS - # since all args passed by name (previous showed that spec met - # for all parameters). - - # now make sure listener doesn't require params that are optional in TMS: - extraArgs = set( paramsInfo.getRequiredArgs() ) - self._topicArgs - if extraArgs: - msg = 'params (%s) missing default values' % (','.join(extraArgs),) - raise ListenerMismatchError(msg, listener, extraArgs) - diff --git a/wx/lib/pubsub/core/kwargs/publisher.py b/wx/lib/pubsub/core/kwargs/publisher.py deleted file mode 100644 index 9b62b7a8..00000000 --- a/wx/lib/pubsub/core/kwargs/publisher.py +++ /dev/null @@ -1,77 +0,0 @@ -""" -:copyright: Copyright since 2006 by Oliver Schoenborn, all rights reserved. -:license: BSD, see LICENSE_BSD_Simple.txt for details. -""" - - -from .publisherbase import PublisherBase -from .datamsg import Message -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. - - :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) - - def getMsgProtocol(self): - return 'kwargs' - - -class PublisherArg1Stage2(Publisher): - """ - This is used when transitioning from arg1 to kwargs - messaging protocol. - """ - - _base = Publisher - - class SenderTooManyKwargs(RuntimeError): - def __init__(self, kwargs, commonArgName): - extra = kwargs.copy() - del extra[commonArgName] - msg = 'Sender has too many kwargs (%s)' % ( py2and3.keys(extra),) - RuntimeError.__init__(self, msg) - - class SenderWrongKwargName(RuntimeError): - def __init__(self, actualKwargName, commonArgName): - msg = 'Sender uses wrong kwarg name ("%s" instead of "%s")' \ - % (actualKwargName, commonArgName) - RuntimeError.__init__(self, msg) - - def __init__(self, treeConfig = None): - self._base.__init__(self, treeConfig) - self.Msg = Message - - def sendMessage(self, _topicName, **kwarg): - commonArgName = policies.msgDataArgName - if len(kwarg) > 1: - raise self.SenderTooManyKwargs(kwarg, commonArgName) - elif len(kwarg) == 1 and commonArgName not in kwarg: - raise self.SenderWrongKwargName( py2and3.keys(kwarg)[0], commonArgName) - - data = kwarg.get(commonArgName, None) - kwargs = { commonArgName: self.Msg( _topicName, data) } - self._base.sendMessage( self, _topicName, **kwargs ) - - def getMsgProtocol(self): - return 'kwarg1' - - -if policies.msgProtocolTransStage is not None: - Publisher = PublisherArg1Stage2 - #print('Using protocol %s' % Publisher) - - diff --git a/wx/lib/pubsub/core/kwargs/publishermixin.py b/wx/lib/pubsub/core/kwargs/publishermixin.py deleted file mode 100644 index 778dc53a..00000000 --- a/wx/lib/pubsub/core/kwargs/publishermixin.py +++ /dev/null @@ -1,65 +0,0 @@ -""" -: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. - - Note that it is important that the PublisherMixin NOT modify any - state data during message sending, because in principle it could - 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 - - def publish(self, **msgKwargs): - self._publish(msgKwargs) - - ############## IMPLEMENTATION ############### - - class IterState: - def __init__(self, msgKwargs): - self.filteredArgs = msgKwargs - self.argsChecked = False - - def checkMsgArgs(self, spec): - spec.check(self.filteredArgs) - self.argsChecked = True - - def filterMsgArgs(self, topicObj): - if self.argsChecked: - self.filteredArgs = topicObj.filterMsgArgs(self.filteredArgs) - else: - self.filteredArgs = topicObj.filterMsgArgs(self.filteredArgs, True) - self.argsChecked = True - - def _mix_prePublish(self, msgKwargs, topicObj=None, iterState=None): - if iterState is None: - # do a first check that all args are there, costly so only do once - iterState = self.IterState(msgKwargs) - if self.hasMDS(): - iterState.checkMsgArgs( self._getListenerSpec() ) - else: - assert not self.hasListeners() - - else: - iterState.filterMsgArgs(topicObj) - - assert iterState is not None - return iterState - - def _mix_callListener(self, listener, msgKwargs, iterState): - """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. """ - listener(iterState.filteredArgs, self, msgKwargs) - diff --git a/wx/lib/pubsub/core/kwargs/topicargspecimpl.py b/wx/lib/pubsub/core/kwargs/topicargspecimpl.py deleted file mode 100644 index d167f950..00000000 --- a/wx/lib/pubsub/core/kwargs/topicargspecimpl.py +++ /dev/null @@ -1,217 +0,0 @@ -""" - -:copyright: Copyright since 2006 by Oliver Schoenborn, all rights reserved. -:license: BSD, see LICENSE_BSD_Simple.txt for details. - -""" - -import weakref - -from .topicutils import (stringize, WeakNone) -from .validatedefnargs import verifySubset -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) - missStr = ','.join(missing) - msg = "Some required args missing in call to sendMessage('%s', %s): %s" \ - % (stringize(topicName), argsStr, missStr) - RuntimeError.__init__(self, msg) - - -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) - extraStr = ','.join(extra) - msg = "Some optional args unknown in call to sendMessage('%s', %s): %s" \ - % (topicName, argsStr, extraStr) - RuntimeError.__init__(self, msg) - - -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, - created for the parent and children topics. - - The only difference - between an ArgsInfo and an ArgSpecGiven is that the latter is - what "user thinks is ok" whereas former has been validated: - the specification for this topic is a strict superset of the - specification of its parent, and a strict subset of the - specification of each of its children. Also, the instance - can be used to check validity and filter arguments. - - 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 - - - def __init__(self, topicNameTuple, specGiven, parentArgsInfo): - self.topicNameTuple = topicNameTuple - self.allOptional = () # topic message optional arg names - self.allDocs = {} # doc for each arg - self.allRequired = () # topic message required arg names - self.argsSpecType = self.SPEC_MISSING - self.parentAI = WeakNone() - if parentArgsInfo is not None: - self.parentAI = weakref.ref(parentArgsInfo) - parentArgsInfo.__addChildAI(self) - self.childrenAI = [] - - if specGiven.isComplete(): - self.__setAllArgs(specGiven) - - def isComplete(self): - return self.argsSpecType == self.SPEC_COMPLETE - - def getArgs(self): - return self.allOptional + self.allRequired - - def numArgs(self): - return len(self.allOptional) + len(self.allRequired) - - def getReqdArgs(self): - return self.allRequired - - def getOptArgs(self): - return self.allOptional - - def getArgsDocs(self): - return self.allDocs.copy() - - def setArgsDocs(self, docs): - """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 - data specification (MDS). Raises SenderMissingReqdMsgDataError if some required - args are missing or not known, and raises SenderUnknownMsgDataError if some - optional args are unknown. """ - all = set(msgKwargs) - # check that it has all required args - needReqd = set(self.allRequired) - hasReqd = (needReqd <= all) - if not hasReqd: - raise SenderMissingReqdMsgDataError( - self.topicNameTuple, py2and3.keys(msgKwargs), needReqd - all) - - # check that all other args are among the optional spec - optional = all - needReqd - ok = (optional <= set(self.allOptional)) - if not ok: - raise SenderUnknownMsgDataError( self.topicNameTuple, - py2and3.keys(msgKwargs), optional - set(self.allOptional) ) - - def filterArgs(self, 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).""" - assert self.isComplete() - if len(msgKwargs) == self.numArgs(): - return msgKwargs - - # only keep the keys from msgKwargs that are also in topic's kwargs - # method 1: SLOWEST - #newKwargs = dict( (k,msgKwargs[k]) for k in self.__msgArgs.allOptional if k in msgKwargs ) - #newKwargs.update( (k,msgKwargs[k]) for k in self.__msgArgs.allRequired ) - - # method 2: FAST: - #argNames = self.__msgArgs.getArgs() - #newKwargs = dict( (key, val) for (key, val) in msgKwargs.iteritems() if key in argNames ) - - # method 3: FASTEST: - argNames = set(self.getArgs()).intersection(msgKwargs) - newKwargs = dict( (k,msgKwargs[k]) for k in argNames ) - - return newKwargs - - def hasSameArgs(self, *argNames): - """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. """ - return set(argNames) == set( self.getArgs() ) - - def hasParent(self, argsInfo): - """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, - that is complete. So if self.isComplete() is True, then returns self, - otherwise returns parent (if parent.isComplete()), etc. """ - AI = self - while AI is not None: - if AI.isComplete(): - return AI - AI = AI.parentAI() # dereference weakref - return None - - def updateAllArgsFinal(self, topicDefn): - """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) - - def __addChildAI(self, childAI): - assert childAI not in self.childrenAI - self.childrenAI.append(childAI) - - def __notifyParentCompleted(self): - """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 - self.__validateArgsToParent() - return - - def __validateArgsToParent(self): - # validate relative to parent arg spec - closestParentAI = self.parentAI().getCompleteAI() - if closestParentAI is not None: - # verify that parent args is a subset of spec given: - topicName = stringize(self.topicNameTuple) - verifySubset(self.getArgs(), closestParentAI.getArgs(), topicName) - verifySubset(self.allRequired, closestParentAI.getReqdArgs(), - topicName, ' required args') - - def __setAllArgs(self, specGiven): - assert specGiven.isComplete() - self.allOptional = tuple( specGiven.getOptional() ) - self.allRequired = specGiven.reqdArgs - self.allDocs = specGiven.argsDocs.copy() # doc for each arg - self.argsSpecType= self.SPEC_COMPLETE - - if self.parentAI() is not None: - self.__validateArgsToParent() - - # notify our children - for childAI in self.childrenAI: - childAI.__notifyParentCompleted() - - diff --git a/wx/lib/pubsub/core/kwargs/topicmgrimpl.py b/wx/lib/pubsub/core/kwargs/topicmgrimpl.py deleted file mode 100644 index 716368b2..00000000 --- a/wx/lib/pubsub/core/kwargs/topicmgrimpl.py +++ /dev/null @@ -1,13 +0,0 @@ -""" - -: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.""" - argsDocs = {} - reqdArgs = () - return argsDocs, reqdArgs - diff --git a/wx/lib/pubsub/core/listener.py b/wx/lib/pubsub/core/listener.py deleted file mode 100644 index 97616456..00000000 --- a/wx/lib/pubsub/core/listener.py +++ /dev/null @@ -1,40 +0,0 @@ -""" -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, - getArgs, - getRawFunction, - ListenerMismatchError, - CallArgsInfo -) - -from .listenerimpl import ( - Listener, - ListenerValidator -) - -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:: - - from pubsub import pub - - class MyHandler(pub.IListenerExcHandler): - def __call__(self, listenerID, topicObj): - ... do something with listenerID ... - - pub.setListenerExcHandler(MyHandler()) - """ - def __call__(self, listenerID, topicObj): - raise NotImplementedError('%s must override __call__()' % self.__class__) - - diff --git a/wx/lib/pubsub/core/listenerbase.py b/wx/lib/pubsub/core/listenerbase.py deleted file mode 100644 index d69a1473..00000000 --- a/wx/lib/pubsub/core/listenerbase.py +++ /dev/null @@ -1,185 +0,0 @@ -""" -:copyright: Copyright since 2006 by Oliver Schoenborn, all rights reserved. -:license: BSD, see LICENSE_BSD_Simple.txt for details. -""" - -from . import weakmethod - -from .callables import ( - getID, - getArgs, - ListenerMismatchError, - CallArgsInfo, - AUTO_TOPIC as _AUTO_ARG -) - - -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 - 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). """ - # set call policies - self.acceptsAllKwargs = argsInfo.acceptsAllKwargs - - self._autoTopicArgName = argsInfo.autoTopicArgName - self._callable = weakmethod.getWeakRef(callable_, self.__notifyOnDead) - self.__onDead = onDead - - # save identity now in case callable dies: - name, mod = getID(callable_) # - self.__nameID = name - self.__module = mod - self.__id = str(id(callable_))[-4:] # only last four digits of id - self.__hash = hash(callable_) - - def __call__(self, args, kwargs, actualTopic, allArgs=None): - raise NotImplementedError - - def name(self): - """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).""" - 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. """ - return self.__nameID - - def module(self): - """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 - 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).""" - return self._callable() - - def isDead(self): - """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""" - return self._autoTopicArgName is not None - - def wantsAllMessageData(self): - """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.""" - 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).""" - 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). """ - if id(self) == id(rhs): - return True - - try: - c1 = self._callable() - c2 = rhs._callable() - - except Exception: - # then rhs is not a Listener, compare with c1 - return c1 == rhs - - # both side of == are Listener, but always compare unequal if both dead - if c2 is None and c1 is None: - return False - - return c1 == c2 - - def __ne__(self, rhs): - """Counterpart to __eq__ MUST be defined... equivalent to - 'not (self == rhs)'.""" - return not self.__eq__(rhs) - - def __hash__(self): - """Hash is an optimization for dict/set searches, it need not - return different numbers for every different object. """ - return self.__hash - - def __str__(self): - """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 - 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. """ - self._topicArgs = set(topicArgs) - self._topicKwargs = set(topicKwargs) - - - def validate(self, listener): - """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. - - Otherwise, returns an CallArgsInfo object containing information about - the listener's call arguments, such as whether listener wants topic - name (signified by a kwarg value = AUTO_TOPIC in listener protocol). - 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 - topic has kwargs keys topicKwargKeys. Just calls validate() in - a try-except clause.""" - try: - self.validate(listener) - return True - except ListenerMismatchError: - return False - - - def _validateArgs(self, listener, paramsInfo): - """Provide implementation in derived classes""" - raise NotImplementedError - - diff --git a/wx/lib/pubsub/core/notificationmgr.py b/wx/lib/pubsub/core/notificationmgr.py deleted file mode 100644 index 8dfac3c3..00000000 --- a/wx/lib/pubsub/core/notificationmgr.py +++ /dev/null @@ -1,185 +0,0 @@ -""" -: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 - notification handlers get corresponding method called with information - about the activity, such as which listener subscribed to which topic. - See INotificationHandler for which method gets called for each activity. - - If more than one notification handler has been registered, the order in - which they are notified is unspecified (do not rely on it). - - Note that this manager automatically unregisters all handlers when - the Python interpreter exits, to help avoid NoneType exceptions during - shutdown. This "shutdown" starts when the last line of app "main" has - executed; the Python interpreter then starts cleaning up, garbage - 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 - self.__notifyOnSubscribe = False - self.__notifyOnUnsubscribe = False - - self.__notifyOnNewTopic = False - self.__notifyOnDelTopic = False - self.__notifyOnDeadListener = False - - self.__handlers = [] - if notificationHandler is not None: - self.addHandler(notificationHandler) - - self.__atExitRegistered = False - - def addHandler(self, handler): - if not self.__atExitRegistered: - self.__registerForAppExit() - self.__handlers.append(handler) - - def getHandlers(self): - return self.__handlers[:] - - def clearHandlers(self): - self.__handlers = [] - - def notifySubscribe(self, *args, **kwargs): - if self.__notifyOnSubscribe and self.__handlers: - for handler in self.__handlers: - handler.notifySubscribe(*args, **kwargs) - - def notifyUnsubscribe(self, *args, **kwargs): - if self.__notifyOnUnsubscribe and self.__handlers: - for handler in self.__handlers: - handler.notifyUnsubscribe(*args, **kwargs) - - def notifySend(self, *args, **kwargs): - if self.__notifyOnSend and self.__handlers: - for handler in self.__handlers: - handler.notifySend(*args, **kwargs) - - def notifyNewTopic(self, *args, **kwargs): - if self.__notifyOnNewTopic and self.__handlers: - for handler in self.__handlers: - handler.notifyNewTopic(*args, **kwargs) - - def notifyDelTopic(self, *args, **kwargs): - if self.__notifyOnDelTopic and self.__handlers: - for handler in self.__handlers: - handler.notifyDelTopic(*args, **kwargs) - - def notifyDeadListener(self, *args, **kwargs): - if self.__notifyOnDeadListener and self.__handlers: - for handler in self.__handlers: - handler.notifyDeadListener(*args, **kwargs) - - def getFlagStates(self): - """Return state of each notification flag, as a dict.""" - return dict( - subscribe = self.__notifyOnSubscribe, - unsubscribe = self.__notifyOnUnsubscribe, - deadListener = self.__notifyOnDeadListener, - sendMessage = self.__notifyOnSend, - newTopic = self.__notifyOnNewTopic, - delTopic = self.__notifyOnDelTopic, - ) - - 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. - The kwargs that are None are left at their current value. The 'all', - if not None, is set first. E.g. - - mgr.setFlagStates(all=True, delTopic=False) - - 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 - self.setFlagStates( all=None, * ((numArgs-1)*[all]) ) - - if sendMessage is not None: - self.__notifyOnSend = sendMessage - if subscribe is not None: - self.__notifyOnSubscribe = subscribe - if unsubscribe is not None: - self.__notifyOnUnsubscribe = unsubscribe - - if newTopic is not None: - self.__notifyOnNewTopic = newTopic - if delTopic is not None: - self.__notifyOnDelTopic = delTopic - if deadListener is not None: - self.__notifyOnDeadListener = deadListener - - - def __registerForAppExit(self): - import atexit - atexit.register(self.clearHandlers) - self.__atExitRegistered = True - - - -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. - :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. """ - raise NotImplementedError - - def notifyUnsubscribe(self, pubListener, topicObj): - """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.""" - raise NotImplementedError - - def notifyDeadListener(self, pubListener, topicObj): - """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.""" - raise NotImplementedError - - def notifySend(self, stage, topicObj, pubListener=None): - """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.""" - raise NotImplementedError - - def notifyNewTopic(self, topicObj, description, required, argsDocs): - """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. """ - raise NotImplementedError - - def notifyDelTopic(self, topicName): - """Called whenever a topic is removed from topic tree. - :param topicName: name of topic removed.""" - raise NotImplementedError - - diff --git a/wx/lib/pubsub/core/publisherbase.py b/wx/lib/pubsub/core/publisherbase.py deleted file mode 100644 index acb1f89d..00000000 --- a/wx/lib/pubsub/core/publisherbase.py +++ /dev/null @@ -1,191 +0,0 @@ -""" -:copyright: Copyright since 2006 by Oliver Schoenborn, all rights reserved. -:license: BSD, see LICENSE_BSD_Simple.txt for details. -""" - -from .topicmgr import ( - TopicManager, - TreeConfig -) - -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.""" - self.__treeConfig = treeConfig or TreeConfig() - self.__topicMgr = TopicManager(self.__treeConfig) - - def getTopicMgr(self): - """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.""" - return self.__treeConfig.listenerExcHandler - - def setListenerExcHandler(self, handler): - """Set the function to call when a listener raises an exception - during a sendMessage(). The handler must adhere to the - 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. """ - self.__treeConfig.notificationMgr.addHandler(handler) - - def clearNotificationHandlers(self): - """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 - pubsub activity. The kwargs keys can be any of the following: - - - subscribe: if True, get notified whenever a listener subscribes to a topic; - - unsubscribe: if True, get notified whenever a listener unsubscribes from a topic; - - deadListener: if True, get notified whenever a subscribed listener has been garbage-collected; - - sendMessage: if True, get notified whenever sendMessage() is called; - - newTopic: if True, get notified whenever a new topic is created; - - delTopic: if True, get notified whenever a topic is "deleted" from topic tree; - - all: set all of the above to the given value (True or False). - - The kwargs that are None are left at their current value. Those that are - False will cause corresponding notification to be silenced. The 'all' - is set first, then the others. E.g. - - mgr.setFlagStates(all=True, delTopic=False) - - will toggle all notifications on, but will turn off the 'delTopic' - notification. - """ - self.__treeConfig.notificationMgr.setFlagStates(**kwargs) - - def getNotificationFlags(self): - """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. - - By default, pubsub will accept topic names for topics that - don't have a message data specification (MDS). This default behavior - makes pubsub easier to use initially, but allows topic - names with typos to go uncaught in common operations such as - sendMessage() and subscribe(). In a large application, this - can lead to nasty bugs. Pubsub's default behavior is equivalent - to setTopicUnspecifiedFatal(false). - - When called with newVal=True, any future pubsub operation that - requires a topic (such as subscribe and sendMessage) will require - an MDS; if none is available, pubsub will raise a TopicDefnError - exception. - - If checkExisting is not given or True, all existing - topics are validated. A TopicDefnError exception is - raised if one is found to be incomplete (has hasMDS() false). - - Returns previous value of newVal. - - Note that this method can be used in several ways: - - 1. Only use it in your application when something is not working - as expected: just add a call at the beginning of your app when - you have a problem with topic messages not being received - (for instance), and remove it when you have fixed the problem. - - 2. Use it from the beginning of your app and never use newVal=False: - add a call at the beginning of your app and you leave it in - (forever), and use Topic Definition Providers to provide the - listener specifications. These are easy to use via the - pub.addTopicDefnProvider(). - - 3. Use it as in #1 during app development, and once stable, use - #2. This is easiest to do in combination with - pub.exportTopicTreeSpec(). - """ - oldVal = self.__treeConfig.raiseOnTopicUnspecified - self.__treeConfig.raiseOnTopicUnspecified = newVal - - if newVal and checkExisting: - self.__topicMgr.checkAllTopicsHaveMDS() - - return oldVal - - def sendMessage(self, topicName, *args, **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.""" - raise NotImplementedError - - def subscribe(self, listener, topicName): - """Subscribe listener to named topic. Raises ListenerMismatchError - if listener isn't compatible with the topic's MDS. Returns - (pubsub.core.Listener, success), where success is False if listener - was already subscribed. The pub.core.Listener wraps the callable - subscribed and provides introspection-based info about - the callable. - - Note that if 'subscribe' notification is on, the handler's - '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 - 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. """ - topicObj = self.__topicMgr.getTopic(topicName) - unsubdLisnr = topicObj.unsubscribe(listener) - - return unsubdLisnr - - def unsubAll(self, topicName = None, - listenerFilter = None, topicFilter = None): - """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 - subscribed). A topicFilter can also be given so that only topics - that satisfy topicFilter(topic name) == True will be affected. - If only one topic should have listeners unsubscribed, then a topic - name 'topicName' can be given *instead* instead of a topic filter. - - Returns the list of all listeners (instances of pub.Listener) that - were unsubscribed from the topic tree). - - Note: this method will generate one 'unsubcribe' notification message - (see pub.setNotificationFlags()) for each listener unsubscribed.""" - unsubdListeners = [] - - if topicName is None: - # unsubscribe all listeners from all topics - topicsMap = self.__topicMgr._topicsMap - for topicName, topicObj in py2and3.iteritems(topicsMap): - if topicFilter is None or topicFilter(topicName): - tmp = topicObj.unsubscribeAllListeners(listenerFilter) - unsubdListeners.extend(tmp) - - else: - topicObj = self.__topicMgr.getTopic(topicName) - unsubdListeners = topicObj.unsubscribeAllListeners(listenerFilter) - - return unsubdListeners - - diff --git a/wx/lib/pubsub/core/topicargspec.py b/wx/lib/pubsub/core/topicargspec.py deleted file mode 100644 index a75eee3e..00000000 --- a/wx/lib/pubsub/core/topicargspec.py +++ /dev/null @@ -1,77 +0,0 @@ -""" -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 -from .validatedefnargs import MessageDataSpecError -from .topicargspecimpl import ( - SenderMissingReqdMsgDataError, - SenderUnknownMsgDataError, - ArgsInfo -) - - -def topicArgsFromCallable(_callable): - """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.""" - argsInfo = getListenerArgs(_callable) - required = argsInfo.getRequiredArgs() - defaultDoc = 'UNDOCUMENTED' - args = dict.fromkeys(argsInfo.allParams, defaultDoc) - return args, required - - -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 - documentation string for each argument. This instance will be transformed - 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 - - def __init__(self, argsDocs=None, reqdArgs=None): - self.reqdArgs = tuple(reqdArgs or ()) - - if argsDocs is None: - self.argsSpecType = ArgSpecGiven.SPEC_GIVEN_NONE - self.argsDocs = {} - else: - self.argsSpecType = ArgSpecGiven.SPEC_GIVEN_ALL - self.argsDocs = argsDocs - - # check that all args marked as required are in argsDocs - missingArgs = set(self.reqdArgs).difference(self.argsDocs.keys()) # py3: iter keys ok - if missingArgs: - msg = 'Params [%s] missing inherited required args [%%s]' % ','.join(argsDocs.keys()) # iter keys ok - raise MessageDataSpecError(msg, missingArgs) - - def setAll(self, allArgsDocs, reqdArgs = None): - self.argsDocs = allArgsDocs - self.reqdArgs = reqdArgs or () - self.argsSpecType = ArgSpecGiven.SPEC_GIVEN_ALL - - def isComplete(self): - """Returns True if the definition is usable, false otherwise.""" - return self.argsSpecType == ArgSpecGiven.SPEC_GIVEN_ALL - - def getOptional(self): - return tuple( set( self.argsDocs.keys() ).difference( self.reqdArgs ) ) - - def __str__(self): - return "%s, %s, %s" % \ - (self.argsDocs, self.reqdArgs, self.argsSpecType) - - diff --git a/wx/lib/pubsub/core/topicdefnprovider.py b/wx/lib/pubsub/core/topicdefnprovider.py deleted file mode 100644 index 9eaf0e15..00000000 --- a/wx/lib/pubsub/core/topicdefnprovider.py +++ /dev/null @@ -1,636 +0,0 @@ -""" -:copyright: Copyright since 2006 by Oliver Schoenborn, all rights reserved. -:license: BSD, see LICENSE_BSD_Simple.txt for details. -""" - - -import os, re, inspect -from textwrap import TextWrapper, dedent - -from .. import ( - policies, - py2and3 -) -from .topicargspec import ( - topicArgsFromCallable, - ArgSpecGiven -) -from .topictreetraverser import TopicTreeTraverser -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. - 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'. """ - msg = 'Must return (string, ArgSpecGiven), or (None, None)' - raise NotImplementedError(msg) - - def topicNames(self): - """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().""" - 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.""" - msg = 'Must return documentation string for root topic (tree)' - raise NotImplementedError(msg) - - def __iter__(self): - """Same as self.topicNames(), do NOT override.""" - return self.topicNames() - - -# name of method in class name assumed to represent topic's listener signature -# which will get checked against topic's Message Data Specification (MDS) -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().""" - - def __init__(self, nameTuple, description, argsDocs, required): - self.nameTuple = nameTuple - self.description = description - self.argsDocs = argsDocs - self.required = required - - def isComplete(self): - return (self.description is not None) and (self.argsDocs is not None) - - def getTreeDoc(self): - """Get the docstring for the topic tree.""" - raise NotImplementedError - - def getNextTopic(self): - """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.""" - raise NotImplementedError - - def doneIter(self): - """Called automatically by TopicDefnProvider once - it considers the iteration completed. Override this only if - deserializer needs to take action, such as closing a file.""" - pass - - def resetIter(self): - """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.""" - 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 - classes defining root topics; the root topics contain nested - classes defining subtopics; etc.""" - self.__rootTopics = [] - self.__iterStarted = False - self.__nextTopic = iter(self.__rootTopics) - self.__rootDoc = None - - if pyClassObj is not None: - self.__rootDoc = pyClassObj.__doc__ - topicClasses = self.__getTopicClasses(pyClassObj) - for topicName, pyClassObj in topicClasses: - self.__addDefnFromClassObj(pyClassObj) - - def getTreeDoc(self): - return self.__rootDoc - - def getNextTopic(self): - self.__iterStarted = True - try: - topicNameTuple, topicClassObj = py2and3.nextiter(self.__nextTopic) - except StopIteration: - return None - - # ok get the info from class - if hasattr(topicClassObj, SPEC_METHOD_NAME): - protoListener = getattr(topicClassObj, SPEC_METHOD_NAME) - argsDocs, required = topicArgsFromCallable(protoListener) - if protoListener.__doc__: - self.__setArgsDocsFromProtoDocs(argsDocs, protoListener.__doc__) - else: - # assume definition is implicitly that listener has no args - argsDocs = {} - required = () - desc = None - if topicClassObj.__doc__: - desc = dedent(topicClassObj.__doc__) - return self.TopicDefn(topicNameTuple, desc, argsDocs, required) - - def resetIter(self): - self.__iterStarted = False - self.__nextTopic = iter(self.__rootTopics) - - def getDefinedTopics(self): - return [nt for (nt, defn) in self.__rootTopics] - - def __addDefnFromClassObj(self, pyClassObj): - """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. """ - if self.__iterStarted: - raise RuntimeError('addDefnFromClassObj must be called before iteration started!') - - parentNameTuple = (pyClassObj.__name__, ) - if pyClassObj.__doc__ is not None: - self.__rootTopics.append( (parentNameTuple, pyClassObj) ) - if self.__rootDoc is None: - self.__rootDoc = pyClassObj.__doc__ - self.__findTopics(pyClassObj, parentNameTuple) - # iterator is now out of sync, so reset it; obviously this would - # screw up getNextTopic which is why we had to test for self.__iterStarted - self.__nextTopic = iter(self.__rootTopics) - - def __findTopics(self, pyClassObj, parentNameTuple): - assert not self.__iterStarted - assert parentNameTuple - assert pyClassObj.__name__ == parentNameTuple[-1] - - topicClasses = self.__getTopicClasses(pyClassObj, parentNameTuple) - pyClassObj._topicNameStr = '.'.join(parentNameTuple) - - # make sure to update rootTopics BEFORE we recurse, so that toplevel - # topics come first in the list - for parentNameTuple2, topicClassObj in topicClasses: - # we only keep track of topics that are documented, so that - # multiple providers can co-exist without having to duplicate - # information - if topicClassObj.__doc__ is not None: - self.__rootTopics.append( (parentNameTuple2, topicClassObj) ) - # now can find its subtopics - self.__findTopics(topicClassObj, parentNameTuple2) - - def __getTopicClasses(self, pyClassObj, parentNameTuple=()): - """Returns a list of pairs, (topicNameTuple, memberClassObj)""" - memberNames = dir(pyClassObj) - topicClasses = [] - for memberName in memberNames: - if memberName.startswith('_'): - continue # ignore special and non-public methods - member = getattr(pyClassObj, memberName) - if inspect.isclass( member ): - topicNameTuple = parentNameTuple + (memberName,) - topicClasses.append( (topicNameTuple, member) ) - return topicClasses - - def __setArgsDocsFromProtoDocs(self, argsDocs, protoDocs): - PAT_ITEM_STR = r'\A-\s*' # hyphen and any number of blanks - PAT_ARG_NAME = r'(?P\w*)' - PAT_DOC_STR = r'(?P.*)' - PAT_BLANK = r'\s*' - PAT_ITEM_SEP = r':' - argNamePat = re.compile( - PAT_ITEM_STR + PAT_ARG_NAME + PAT_BLANK + PAT_ITEM_SEP - + PAT_BLANK + PAT_DOC_STR) - protoDocs = dedent(protoDocs) - lines = protoDocs.splitlines() - argName = None - namesFound = [] - for line in lines: - match = argNamePat.match(line) - if match: - argName = match.group('argName') - namesFound.append(argName) - argsDocs[argName] = [match.group('doc1') ] - elif argName: - argsDocs[argName].append(line) - - for name in namesFound: - argsDocs[name] = '\n'.join( argsDocs[name] ) - - -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.""" - from . import imp2 - module = imp2.load_module(moduleName, searchPath) - self.__classDeserial = TopicDefnDeserialClass(module) - - def getTreeDoc(self): - return self.__classDeserial.getTreeDoc() - - def getNextTopic(self): - return self.__classDeserial.getNextTopic() - - def doneIter(self): - self.__classDeserial.doneIter() - - def resetIter(self): - self.__classDeserial.resetIter() - - def getDefinedTopics(self): - return self.__classDeserial.getDefinedTopics() - - -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 - 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.""" - - def createTmpModule(): - moduleNamePre = 'tmp_export_topics_' - import os, tempfile - creationDir = os.getcwd() - fileID, path = tempfile.mkstemp('.py', moduleNamePre, dir=creationDir) - stringFile = os.fdopen(fileID, 'w') - stringFile.write( dedent(source) ) - stringFile.close() - return path, [creationDir] - - self.__filename, searchPath = createTmpModule() - moduleName = os.path.splitext( os.path.basename(self.__filename) )[0] - self.__modDeserial = TopicDefnDeserialModule(moduleName, searchPath) - - def getTreeDoc(self): - return self.__modDeserial.getTreeDoc() - - def getNextTopic(self): - return self.__modDeserial.getNextTopic() - - def doneIter(self): - self.__modDeserial.doneIter() - # remove the temporary module and its compiled version (*.pyc) - os.remove(self.__filename) - try: # py3.2+ uses special folder/filename for .pyc files - from imp import cache_from_source - os.remove(cache_from_source(self.__filename)) - except ImportError: - os.remove(self.__filename + 'c') - - def resetIter(self): - self.__modDeserial.resetIter() - - def getDefinedTopics(self): - return self.__modDeserial.getDefinedTopics() - - -TOPIC_TREE_FROM_MODULE = 'module' -TOPIC_TREE_FROM_STRING = 'string' -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 - that converts source data into topic definitions. - - This provider is instantiated automatically by - ``pub.addTopicDefnProvider(source, format)`` - when source is *not* an ITopicDefnProvider. - - Additional de-serializers can be registered via registerTypeForImport(). - """ - - _typeRegistry = {} - - def __init__(self, source, format, **providerKwargs): - """Find the correct de-serializer class from registry for the given - format; instantiate it with given source and providerKwargs; get - all available topic definitions.""" - if format not in self._typeRegistry: - raise UnrecognizedSourceFormatError() - providerClassObj = self._typeRegistry[format] - provider = providerClassObj(source, **providerKwargs) - self.__topicDefns = {} - self.__treeDocs = provider.getTreeDoc() - try: - topicDefn = provider.getNextTopic() - while topicDefn is not None: - self.__topicDefns[topicDefn.nameTuple] = topicDefn - topicDefn = provider.getNextTopic() - finally: - provider.doneIter() - - def getDefn(self, topicNameTuple): - desc, spec = None, None - defn = self.__topicDefns.get(topicNameTuple, None) - if defn is not None: - assert defn.isComplete() - desc = defn.description - spec = ArgSpecGiven(defn.argsDocs, defn.required) - return desc, spec - - def topicNames(self): - return py2and3.iterkeys(self.__topicDefns) - - def getTreeDoc(self): - return self.__treeDocs - - @classmethod - def registerTypeForImport(cls, typeName, providerClassObj): - """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, :: - - from pubsub.core.topicdefnprovider import ITopicDefnDeserializer - class SomeNewImporter(ITopicDefnDeserializer): - ... - TopicDefnProvider.registerTypeForImport('some name', SomeNewImporter) - # will instantiate SomeNewImporter(source) - pub.addTopicDefnProvider(source, 'some name') - """ - assert issubclass(providerClassObj, ITopicDefnDeserializer) - cls._typeRegistry[typeName] = providerClassObj - - @classmethod - def initTypeRegistry(cls): - cls.registerTypeForImport(TOPIC_TREE_FROM_MODULE, TopicDefnDeserialModule) - cls.registerTypeForImport(TOPIC_TREE_FROM_STRING, TopicDefnDeserialString) - cls.registerTypeForImport(TOPIC_TREE_FROM_CLASS, TopicDefnDeserialClass) - - -TopicDefnProvider.initTypeRegistry() - - -def _backupIfExists(filename, bak): - import os, shutil - if os.path.exists(filename): - backupName = '%s.%s' % (filename, bak) - shutil.copy(filename, backupName) - - -defaultTopicTreeSpecHeader = \ -""" -Topic tree for application. -Used via pub.addTopicDefnProvider(thisModuleName). -""" - -defaultTopicTreeSpecFooter = \ -"""\ -# End of topic tree definition. Note that application may load -# more than one definitions provider. -""" - - -def exportTopicTreeSpec(moduleName = None, rootTopic=None, bak='bak', moduleDoc=None): - """Using TopicTreeSpecPrinter, exports the topic tree rooted at rootTopic to a - Python module (.py) file. This module will define module-level classes - representing root topics, nested classes for subtopics etc. Returns a string - representing the contents of the file. Parameters: - - - If moduleName is given, the topic tree is written to moduleName.py in - os.getcwd(). By default, it is first backed up, it it already exists, - using bak as the filename extension. If bak is None, existing module file - gets overwritten. - - If rootTopic is specified, the export only traverses tree from - corresponding topic. Otherwise, complete tree, using - pub.getDefaultTopicTreeRoot() as starting point. - - The moduleDoc is the doc string for the module ie topic tree. - """ - - if rootTopic is None: - from .. import pub - rootTopic = pub.getDefaultTopicMgr().getRootAllTopics() - elif py2and3.isstring(rootTopic): - from .. import pub - rootTopic = pub.getDefaultTopicMgr().getTopic(rootTopic) - - # create exporter - if moduleName is None: - capture = py2and3.StringIO() - TopicTreeSpecPrinter(rootTopic, fileObj=capture, treeDoc=moduleDoc) - return capture.getvalue() - - else: - filename = '%s.py' % moduleName - if bak: - _backupIfExists(filename, bak) - moduleFile = open(filename, 'w') - try: - TopicTreeSpecPrinter(rootTopic, fileObj=moduleFile, treeDoc=moduleDoc) - finally: - moduleFile.close() - -############################################################## - -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 - 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__.""" - self.__traverser = TopicTreeTraverser(self) - - import sys - fileObj = fileObj or sys.stdout - - self.__destination = fileObj - self.__output = [] - self.__header = self.__toDocString(treeDoc) - self.__footer = footer - self.__lastWasAll = False # True when last topic done was the ALL_TOPICS - - self.__width = width - self.__wrapper = TextWrapper(width) - self.__indentStep = indentStep - self.__indent = 0 - - args = dict(width=width, indentStep=indentStep, treeDoc=treeDoc, - footer=footer, fileObj=fileObj) - def fmItem(argName, argVal): - if py2and3.isstring(argVal): - MIN_OFFSET = 5 - lenAV = width - MIN_OFFSET - len(argName) - if lenAV > 0: - argVal = repr(argVal[:lenAV] + '...') - elif argName == 'fileObj': - argVal = fileObj.__class__.__name__ - return '# - %s: %s' % (argName, argVal) - fmtArgs = [fmItem(key, args[key]) for key in sorted(py2and3.iterkeys(args))] - self.__comment = [ - '# Automatically generated by %s(**kwargs).' % self.__class__.__name__, - '# The kwargs were:', - ] - self.__comment.extend(fmtArgs) - self.__comment.extend(['']) # two empty line after comment - - if rootTopic is not None: - 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)``.""" - 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. """ - self.__traverser.traverse(topicObj) - - def _accept(self, topicObj): - # accept every topic - return True - - def _startTraversal(self): - # output comment - self.__wrapper.initial_indent = '# ' - self.__wrapper.subsequent_indent = self.__wrapper.initial_indent - self.__output.extend( self.__comment ) - - # output header: - if self.__header: - self.__output.extend(['']) - self.__output.append(self.__header) - self.__output.extend(['']) - - def _doneTraversal(self): - if self.__footer: - self.__output.append('') - self.__output.append('') - self.__output.append(self.__footer) - - if self.__destination is not None: - self.__destination.write(self.getOutput()) - - def _onTopic(self, topicObj): - """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 - return - self.__lastWasAll = False - - self.__output.append( '' ) # empty line - # topic name - self.__wrapper.width = self.__width - head = 'class %s:' % topicObj.getNodeName() - self.__formatItem(head) - - # each extra content (assume constructor verified that chars are valid) - self.__printTopicDescription(topicObj) - if policies.msgDataProtocol != 'arg1': - self.__printTopicArgSpec(topicObj) - - def _startChildren(self): - """Increase the indent""" - if not self.__lastWasAll: - self.__indent += self.__indentStep - - def _endChildren(self): - """Decrease the indent""" - if not self.__lastWasAll: - self.__indent -= self.__indentStep - - def __toDocString(self, msg): - if not msg: - return msg - if msg.startswith("'''") or msg.startswith('"""'): - return msg - return '"""\n%s\n"""' % msg.strip() - - def __printTopicDescription(self, topicObj): - if topicObj.getDescription(): - extraIndent = self.__indentStep - self.__formatItem('"""', extraIndent) - self.__formatItem( topicObj.getDescription(), extraIndent ) - self.__formatItem('"""', extraIndent) - - def __printTopicArgSpec(self, topicObj): - extraIndent = self.__indentStep - - # generate the message data specification - reqdArgs, optArgs = topicObj.getArgs() - argsStr = [] - if reqdArgs: - argsStr.append( ", ".join(reqdArgs) ) - if optArgs: - optStr = ', '.join([('%s=None' % arg) for arg in optArgs]) - argsStr.append(optStr) - argsStr = ', '.join(argsStr) - - # print it only if there are args; ie if listener() don't print it - if argsStr: - # output a blank line and protocol - self.__formatItem('\n', extraIndent) - protoListener = 'def %s(%s):' % (SPEC_METHOD_NAME, argsStr) - self.__formatItem(protoListener, extraIndent) - - # and finally, the args docs - extraIndent += self.__indentStep - self.__formatItem('"""', extraIndent) - # but ignore the arg keys that are in parent args docs: - parentMsgKeys = () - if topicObj.getParent() is not None: - parentMsgKeys = topicObj.getParent().getArgDescriptions().keys() # keys iter ok - argsDocs = topicObj.getArgDescriptions() - for key in sorted(py2and3.iterkeys(argsDocs)): - if key not in parentMsgKeys: - argDesc = argsDocs[key] - msg = "- %s: %s" % (key, argDesc) - self.__formatItem(msg, extraIndent) - self.__formatItem('"""', extraIndent) - - def __formatItem(self, item, extraIndent=0): - indent = extraIndent + self.__indent - indentStr = self.INDENT_CH * indent - lines = item.splitlines() - for line in lines: - self.__output.append( '%s%s' % (indentStr, line) ) - - def __formatBlock(self, text, extraIndent=0): - self.__wrapper.initial_indent = self.INDENT_CH * (self.__indent + extraIndent) - self.__wrapper.subsequent_indent = self.__wrapper.initial_indent - self.__output.append( self.__wrapper.fill(text) ) - - diff --git a/wx/lib/pubsub/core/topicexc.py b/wx/lib/pubsub/core/topicexc.py deleted file mode 100644 index 7e41a60a..00000000 --- a/wx/lib/pubsub/core/topicexc.py +++ /dev/null @@ -1,72 +0,0 @@ -""" -: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. """ - 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) - RuntimeError.__init__(self, msg + - " See pub.addTopicDefnProvider() and/or pub.setTopicUnspecifiedFatal()") - - -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) - RuntimeError.__init__(self, 'Invalid message data spec: ' + argsMsg) - - -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 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. """ - self.badExcListenerID = badExcListenerID - import traceback - self.exc = traceback.format_exc() - msg = 'The exception handler registered with pubsub raised an ' \ - + 'exception, *while* handling an exception raised by listener ' \ - + ' "%s" of topic "%s"):\n%s' \ - % (self.badExcListenerID, topicObj.getName(), self.exc) - RuntimeError.__init__(self, msg) - - -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') - - diff --git a/wx/lib/pubsub/core/topicmgr.py b/wx/lib/pubsub/core/topicmgr.py deleted file mode 100644 index 7ee58149..00000000 --- a/wx/lib/pubsub/core/topicmgr.py +++ /dev/null @@ -1,456 +0,0 @@ -""" -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', - 'TopicNameError', - 'TopicDefnError', - ] - - -from .callables import getID - -from .topicutils import ( - ALL_TOPICS, - tupleize, - stringize, -) - -from .topicexc import ( - TopicNameError, - TopicDefnError, -) - -from .topicargspec import ( - ArgSpecGiven, - ArgsInfo, - topicArgsFromCallable, -) - -from .topicobj import ( - Topic, -) - -from .treeconfig import TreeConfig -from .topicdefnprovider import ITopicDefnProvider -from .topicmgrimpl import getRootTopicSpec - -from .. import py2and3 - - -# --------------------------------------------------------- - -ARGS_SPEC_ALL = ArgSpecGiven.SPEC_GIVEN_ALL -ARGS_SPEC_NONE = ArgSpecGiven.SPEC_GIVEN_NONE - - -# --------------------------------------------------------- - -class TopicManager: - """ - Manages the registry of all topics and creation/deletion - of topics. - - Note that any method that accepts a topic name can accept it in the - 'dotted' format such as ``'a.b.c.'`` or in tuple format such as - ``('a', 'b', 'c')``. Any such method will raise a ValueError - if name not valid (empty, invalid characters, etc). - """ - - # Allowed return values for isTopicSpecified() - TOPIC_SPEC_NOT_SPECIFIED = 0 # false - TOPIC_SPEC_ALREADY_CREATED = 1 # all other values equate to "true" but different reason - TOPIC_SPEC_ALREADY_DEFINED = 2 - - - def __init__(self, treeConfig=None): - """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()).""" - self.__allTopics = None # root of topic tree - self._topicsMap = {} # registry of all topics - self.__treeConfig = treeConfig or TreeConfig() - self.__defnProvider = _MasterTopicDefnProvider(self.__treeConfig) - - # define root of all topics - assert self.__allTopics is None - argsDocs, reqdArgs = getRootTopicSpec() - desc = 'Root of all topics' - specGiven = ArgSpecGiven(argsDocs, reqdArgs) - 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, - for default TopicManager instance created when this module is imported. - Some notes: - - - "root of all topics" topic satisfies isAll()==True, isRoot()==False, - getParent() is None; - - all root-level topics satisfy isAll()==False, isRoot()==True, and - getParent() is getDefaultTopicTreeRoot(); - - 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, - the first definition provider that has a definition - for the required topic is used to instantiate the topic. - - If providerOrSource is an instance of ITopicDefnProvider, register - it as a provider of topic definitions. Otherwise, register a new - instance of TopicDefnProvider(providerOrSource, format). In that case, - if format is not given, it defaults to TOPIC_TREE_FROM_MODULE. Either - way, returns the instance of ITopicDefnProvider registered. - """ - if isinstance(providerOrSource, ITopicDefnProvider): - provider = providerOrSource - else: - from .topicdefnprovider import (TopicDefnProvider, TOPIC_TREE_FROM_MODULE) - source = providerOrSource - provider = TopicDefnProvider(source, format or TOPIC_TREE_FROM_MODULE) - self.__defnProvider.addProvider(provider) - return provider - - def clearDefnProviders(self): - """Remove all registered topic definition providers""" - self.__defnProvider.clear() - - def getNumDefnProviders(self): - """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 - an TopicNameError exception if a topic with given name doesn't exist. If - okIfNone=True, returns None instead of raising an exception.""" - topicNameDotted = stringize(name) - #if not name: - # raise TopicNameError(name, 'Empty topic name not allowed') - obj = self._topicsMap.get(topicNameDotted, None) - if obj is not None: - return obj - - if okIfNone: - return None - - # NOT FOUND! Determine what problem is and raise accordingly: - # find the closest parent up chain that does exists: - parentObj, subtopicNames = self.__getClosestParent(topicNameDotted) - assert subtopicNames - - subtopicName = subtopicNames[0] - if parentObj is self.__allTopics: - raise TopicNameError(name, 'Root topic "%s" doesn\'t exist' % subtopicName) - - msg = 'Topic "%s" doesn\'t have "%s" as subtopic' % (parentObj.getName(), subtopicName) - raise TopicNameError(name, msg) - - def newTopic(self, _name, _desc, _required=(), **_argDocs): - """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().""" - topic = self.getTopic(_name, True) - if topic is None: - topic = self.getOrCreateTopic(_name) - topic.setDescription(_desc) - topic.setMsgArgSpec(_argDocs, _required) - return topic - - def getOrCreateTopic(self, name, protoListener=None): - """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. - - The name can be in dotted or string format (``'a.b.'`` or ``('a','b')``). - - This method always attempts to return a "complete" topic, i.e. one - with a Message Data Specification (MDS). So if the topic does not have - an MDS, it attempts to add it. It first tries to find an MDS - from a TopicDefnProvider (see addDefnProvider()). If none is available, - it attempts to set it from protoListener, if it has been given. If not, - the topic has no MDS. - - Once a topic's MDS has been set, it is never again changed or accessed - by this method. - - Examples:: - - # assume no topics exist - # but a topic definition provider has been added via - # pub.addTopicDefnProvider() and has definition for topics 'a' and 'a.b' - - # creates topic a and a.b; both will have MDS from the defn provider: - t1 = topicMgr.getOrCreateTopic('a.b') - t2 = topicMgr.getOrCreateTopic('a.b') - assert(t1 is t2) - assert(t1.getParent().getName() == 'a') - - def proto(req1, optarg1=None): pass - # creates topic c.d with MDS based on proto; creates c without an MDS - # since no proto for it, nor defn provider: - t1 = topicMgr.getOrCreateTopic('c.d', proto) - - 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, - # update its specification so that it is sendable - if (protoListener is not None) and not obj.hasMDS(): - allArgsDocs, required = topicArgsFromCallable(protoListener) - obj.setMsgArgSpec(allArgsDocs, required) - return obj - - # create missing parents - nameTuple = tupleize(name) - parentObj = self.__createParentTopics(nameTuple) - - # now the final topic object, args from listener if provided - desc, specGiven = self.__defnProvider.getDefn(nameTuple) - # POLICY: protoListener is used only if no definition available - if specGiven is None: - if protoListener is None: - desc = 'UNDOCUMENTED: created without spec' - else: - allArgsDocs, required = topicArgsFromCallable(protoListener) - specGiven = ArgSpecGiven(allArgsDocs, required) - desc = 'UNDOCUMENTED: created from protoListener "%s" in module %s' % getID(protoListener) - - 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 - 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.""" - return self.getTopic(name, okIfNone=True) is not None - - def hasTopicDefinition(self, name): - """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.""" - # in already existing Topic object: - alreadyCreated = self.getTopic(name, okIfNone=True) - if alreadyCreated is not None and alreadyCreated.hasMDS(): - return True - - # from provider? - nameTuple = tupleize(name) - if self.__defnProvider.isDefined(nameTuple): - return True - - 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.""" - 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 - if topic does not exist; True otherwise. Also unsubscribe any listeners - of topic and all subtopics. """ - # find from which parent the topic object should be removed - dottedName = stringize(name) - try: - #obj = weakref( self._topicsMap[dottedName] ) - obj = self._topicsMap[dottedName] - except KeyError: - return False - - #assert obj().getName() == dottedName - assert obj.getName() == dottedName - # notification must be before deletion in case - self.__treeConfig.notificationMgr.notifyDelTopic(dottedName) - - #obj()._undefineSelf_(self._topicsMap) - obj._undefineSelf_(self._topicsMap) - #assert obj() is None - - return True - - def getTopicsSubscribed(self, 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.""" - assocTopics = [] - for topicObj in py2and3.itervalues(self._topicsMap): - if topicObj.hasListener(listener): - assocTopics.append(topicObj) - return assocTopics - - def __getClosestParent(self, topicNameDotted): - """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. - - So if topicNameDotted = A.B.C.D, but only A.B exists (A.B.C and - A.B.C.D not created yet), then return is (A.B, ['C','D']). - 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. """ - subtopicNames = [] - headTail = topicNameDotted.rsplit('.', 1) - while len(headTail) > 1: - parentName = headTail[0] - subtopicNames.insert( 0, headTail[1] ) - obj = self._topicsMap.get( parentName, None ) - if obj is not None: - return obj, subtopicNames - - headTail = parentName.rsplit('.', 1) - - subtopicNames.insert( 0, headTail[0] ) - return self.__allTopics, subtopicNames - - def __createParentTopics(self, topicName): - """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.""" - assert self.getTopic(topicName, okIfNone=True) is None - parentObj, subtopicNames = self.__getClosestParent(stringize(topicName)) - - # will create subtopics of parentObj one by one from subtopicNames - if parentObj is self.__allTopics: - nextTopicNameList = [] - else: - nextTopicNameList = list(parentObj.getNameTuple()) - for name in subtopicNames[:-1]: - nextTopicNameList.append(name) - desc, specGiven = self.__defnProvider.getDefn( tuple(nextTopicNameList) ) - if desc is None: - desc = 'UNDOCUMENTED: created as parent without specification' - parentObj = self.__createTopic( tuple(nextTopicNameList), - desc, specGiven = specGiven, parent = parentObj) - - return parentObj - - def __createTopic(self, nameTuple, desc, specGiven, parent=None): - """Actual topic creation step. Adds new Topic instance to topic map, - and sends notification message (see ``Publisher.addNotificationMgr()``) - regarding topic creation.""" - if specGiven is None: - specGiven = ArgSpecGiven() - parentAI = None - if parent: - parentAI = parent._getListenerSpec() - argsInfo = ArgsInfo(nameTuple, specGiven, parentAI) - if (self.__treeConfig.raiseOnTopicUnspecified - and not argsInfo.isComplete()): - raise TopicDefnError(nameTuple) - - newTopicObj = Topic(self.__treeConfig, nameTuple, desc, - argsInfo, parent = parent) - # sanity checks: - assert newTopicObj.getName() not in self._topicsMap - if parent is self.__allTopics: - assert len( newTopicObj.getNameTuple() ) == 1 - else: - assert parent.getNameTuple() == newTopicObj.getNameTuple()[:-1] - assert nameTuple == newTopicObj.getNameTuple() - - # store new object and notify of creation - self._topicsMap[ newTopicObj.getName() ] = newTopicObj - self.__treeConfig.notificationMgr.notifyNewTopic( - newTopicObj, desc, specGiven.reqdArgs, specGiven.argsDocs) - - return newTopicObj - - -def validateNameHierarchy(topicTuple): - """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. """ - if not topicTuple: - topicName = stringize(topicTuple) - errMsg = 'empty topic name' - raise TopicNameError(topicName, errMsg) - - for indx, topic in enumerate(topicTuple): - errMsg = None - if topic is None: - topicName = list(topicTuple) - topicName[indx] = 'None' - errMsg = 'None at level #%s' - - elif not topic: - topicName = stringize(topicTuple) - errMsg = 'empty element at level #%s' - - elif topic.isspace(): - topicName = stringize(topicTuple) - errMsg = 'blank element at level #%s' - - if errMsg: - raise TopicNameError(topicName, errMsg % indx) - - -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. """ - assert(isinstance(provider, ITopicDefnProvider)) - if provider not in self.__providers: - self.__providers.append(provider) - - def clear(self): - """Remove all providers added.""" - self.__providers = [] - - def getNumProviders(self): - """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 - 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. """ - desc, defn = None, None - for provider in self.__providers: - tmpDesc, tmpDefn = provider.getDefn(topicNameTuple) - if (tmpDesc is not None) and (tmpDefn is not None): - assert tmpDefn.isComplete() - desc, defn = tmpDesc, tmpDefn - break - - 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).""" - desc, defn = self.getDefn(topicNameTuple) - if desc is None or defn is None: - return False - if defn.isComplete(): - return True - return False - - diff --git a/wx/lib/pubsub/core/topicobj.py b/wx/lib/pubsub/core/topicobj.py deleted file mode 100644 index 20d763ce..00000000 --- a/wx/lib/pubsub/core/topicobj.py +++ /dev/null @@ -1,472 +0,0 @@ -""" -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 - -from .listener import ( - Listener, - ListenerValidator, -) - -from .topicutils import ( - ALL_TOPICS, - stringize, - tupleize, - validateName, - smartDedent, -) - -from .topicexc import ( - TopicDefnError, - TopicNameError, - ExcHandlerError, -) - -from .publishermixin import PublisherMixin - -from .topicargspec import ( - ArgsInfo, - ArgSpecGiven, - topicArgsFromCallable, - SenderMissingReqdMsgDataError, - SenderUnknownMsgDataError, - MessageDataSpecError, -) - -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 - getOrCreateTopic() method (which gets called in several places - in pubsub, such as sendMessage, subscribe, and newTopic). - - :param treeConfig: topic tree configuration settings - :param nameTuple: topic name, in tuple format (no dots) - :param description: "docstring" for topic - :param ArgsInfo msgArgsInfo: object that defines MDS for topic - :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' - raise ValueError(msg % 'pub.ALL_TOPICS') - else: - validateName(nameTuple) - self.__tupleName = nameTuple - - self.__handlingUncaughtListenerExc = False - self._treeConfig = treeConfig - PublisherMixin.__init__(self) - - self.__validator = None - # Registered listeners were originally kept in a Python list; however - # a few methods require lookup of the Listener for the given callable, - # which is an O(n) operation. A set() could have been more suitable but - # there is no way of retrieving an element from a set without iterating - # over the set, again an O(n) operation. A dict() is ok too. Because - # Listener.__eq__(callable) returns true if the Listener instance wraps - # the given callable, and because Listener.__hash__ produces the hash - # value of the wrapped callable, calling dict[callable] on a - # dict(Listener -> Listener) mapping will be O(1) in most cases: - # the dict will take the callables hash, find the list of Listeners that - # have that hash, and then iterate over that inner list to find the - # Listener instance which satisfies Listener == callable, and will return - # the Listener. - self.__listeners = dict() - - # specification: - self.__description = None - self.setDescription(description) - self.__msgArgs = msgArgsInfo - if msgArgsInfo.isComplete(): - self.__finalize() - else: - assert not self._treeConfig.raiseOnTopicUnspecified - - # now that we know the args are fine, we can link to parent - self.__parentTopic = None - self.__subTopics = {} - if parent is None: - assert self.hasMDS() - else: - self.__parentTopic = weakref(parent) - assert self.__msgArgs.parentAI() is parent._getListenerSpec() - parent.__adoptSubtopic( self ) - - def setDescription(self, desc): - """Set the 'docstring' of topic""" - self.__description = desc - - def getDescription(self): - """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. - :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.""" - 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') - - if self.__msgArgs is None or not self.__msgArgs.isComplete(): - try: - specGiven = ArgSpecGiven(argsDocs, required) - self.__msgArgs = ArgsInfo(self.__tupleName, specGiven, - self.__parentTopic()._getListenerSpec()) - except MessageDataSpecError: - # discard the lower part of the stack trace - exc = py2and3.getexcobj() - raise exc - self.__finalize() - - else: - raise RuntimeError('Not allowed to call this: msg spec already set!') - - def getArgs(self): - """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).""" - sendable = self.__msgArgs.isComplete() - assert sendable == self.hasMDS() - if sendable: - return (self.__msgArgs.allRequired , - self.__msgArgs.allOptional) - return None, None - - def getArgDescriptions(self): - """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.""" - self.__msgArgs.setArgsDocs(docs) - - def hasMDS(self): - """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.""" - filteredArgs = self.__msgArgs.filterArgs(msgKwargs) - # if no check of args yet, do it now: - if check: - self.__msgArgs.check(filteredArgs) - 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. """ - return self.__tupleName == (ALL_TOPICS,) - - def isRoot(self): - """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.""" - parent = self.getParent() - if parent: - return parent.isAll() - assert self.isAll() - return False - - def getName(self): - """Return dotted form of full topic name""" - return stringize(self.__tupleName) - - def getNameTuple(self): - """Return tuple form of full topic name""" - return self.__tupleName - - def getNodeName(self): - """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.""" - 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.""" - 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. """ - if not relName: - raise ValueError("getSubtopic() arg can't be empty") - topicTuple = tupleize(relName) - assert topicTuple - - topicObj = self - for topicName in topicTuple: - child = topicObj.__subTopics.get(topicName) - if child is None: - msg = 'Topic "%s" doesn\'t have "%s" as subtopic' % (topicObj.getName(), topicName) - raise TopicNameError(relName, msg) - topicObj = child - - return topicObj - - def getSubtopics(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 - different from number of listeners that will get notified since more - 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 listener in self.__listeners - - def hasListeners(self): - """Return true if there are any listeners subscribed to - this topic, false otherwise.""" - return bool(self.__listeners) - - def getListeners(self): - """Get a copy of list of listeners subscribed to this topic. Safe to iterate over while listeners - get un/subscribed from this topics (such as while sending a message).""" - return py2and3.keys(self.__listeners) - - def getListenersIter(self): - """Get an iterator over listeners subscribed to this topic. Do not use if listeners can be - un/subscribed while iterating. """ - return py2and3.iterkeys(self.__listeners) - - def validate(self, listener): - """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().""" - 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, - otherwise returns False. Note that method raises TopicDefnError - 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 - (pub.Listener, success). The success is true only if listener - was not already subscribed and is now subscribed. """ - if listener in self.__listeners: - assert self.hasMDS() - subdLisnr, newSub = self.__listeners[listener], False - - else: - if self.__validator is None: - args, reqd = topicArgsFromCallable(listener) - self.setMsgArgSpec(args, reqd) - argsInfo = self.__validator.validate(listener) - weakListener = Listener( - listener, argsInfo, onDead=self.__onDeadListener) - self.__listeners[weakListener] = weakListener - subdLisnr, newSub = weakListener, True - - # notify of subscription - self._treeConfig.notificationMgr.notifySubscribe(subdLisnr, self, newSub) - - return subdLisnr, newSub - - def unsubscribe(self, listener): - """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).""" - unsubdLisnr = self.__listeners.pop(listener, None) - if unsubdLisnr is None: - return None - - unsubdLisnr._unlinkFromTopic_() - assert listener == unsubdLisnr.getCallable() - - # notify of unsubscription - self._treeConfig.notificationMgr.notifyUnsubscribe(unsubdLisnr, self) - - return unsubdLisnr - - def unsubscribeAllListeners(self, filter=None): - """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.""" - unsubd = [] - if filter is None: - for listener in self.__listeners: - listener._unlinkFromTopic_() - unsubd = py2and3.keys(self.__listeners) - self.__listeners = {} - else: - 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 - for unsubdLisnr in unsubd: - notificationMgr.notifyUnsubscribe(unsubdLisnr, self) - - return unsubd - - ############################################################# - # - # Impementation - # - ############################################################# - - def _getListenerSpec(self): - """Only to be called by pubsub package""" - return self.__msgArgs - - def _publish(self, data): - """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).""" - self._treeConfig.notificationMgr.notifySend('pre', self) - - # send to ourself - iterState = self._mix_prePublish(data) - self.__sendMessage(data, self, iterState) - - # send up the chain - topicObj = self.getParent() - while topicObj is not None: - if topicObj.hasListeners(): - iterState = self._mix_prePublish(data, topicObj, iterState) - self.__sendMessage(data, topicObj, iterState) - - # done for this topic, continue up branch to parent towards root - topicObj = topicObj.getParent() - - self._treeConfig.notificationMgr.notifySend('post', self) - - def __sendMessage(self, data, topicObj, iterState): - # now send message data to each listener for current topic; - # use list of listeners rather than iterator, so that if listeners added/removed during - # send loop, no runtime exception: - for listener in topicObj.getListeners(): - try: - self._treeConfig.notificationMgr.notifySend('in', topicObj, pubListener=listener) - self._mix_callListener(listener, data, iterState) - - except Exception: - # if exception handling is on, handle, otherwise re-raise - handler = self._treeConfig.listenerExcHandler - if handler is None or self.__handlingUncaughtListenerExc: - raise - - # try handling the exception so we can continue the send: - try: - self.__handlingUncaughtListenerExc = True - handler( listener.name(), topicObj ) - self.__handlingUncaughtListenerExc = False - except Exception: - exc = py2and3.getexcobj() - #print('Exception raised\n%s' % exc) - self.__handlingUncaughtListenerExc = False - raise ExcHandlerError(listener.name(), topicObj, exc) - - def __finalize(self): - """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).""" - assert self.__msgArgs.isComplete() - assert not self.hasMDS() - - # must make sure can adopt a validator - required = self.__msgArgs.allRequired - optional = self.__msgArgs.allOptional - self.__validator = ListenerValidator(required, list(optional) ) - assert not self.__listeners - - def _undefineSelf_(self, topicsMap): - """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, - then detach from parent. Parent is not notified, because method - assumes it has been called by parent""" - #print('Remove %s listeners (%s)' % (self.getName(), self.getNumListeners())) - self.unsubscribeAllListeners() - self.__parentTopic = None - - for subName, subObj in py2and3.iteritems(self.__subTopics): - assert isinstance(subObj, Topic) - #print('Unlinking %s from parent' % subObj.getName()) - subObj.__undefineBranch(topicsMap) - - self.__subTopics = {} - del topicsMap[self.getName()] - - def __adoptSubtopic(self, topicObj): - """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).""" - 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""" - pubListener = self.__listeners.pop(weakListener) - # notify: - self._treeConfig.notificationMgr.notifyDeadListener(pubListener, self) - - def __str__(self): - return "%s(%s)" % (self.getName(), self.getNumListeners()) - - diff --git a/wx/lib/pubsub/core/topictreetraverser.py b/wx/lib/pubsub/core/topictreetraverser.py deleted file mode 100644 index d79801be..00000000 --- a/wx/lib/pubsub/core/topictreetraverser.py +++ /dev/null @@ -1,145 +0,0 @@ -""" - -: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() - whenever it starts traversing the subtopics of a topic, and - visitor._endChildren() when it is done with the subtopics. Finally, it calls - visitor._doneTraversal() when traversal has been completed. The visitor must - therefore adhere to the ITopicTreeVisitor interface. - """ - 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 - ITopicTreeVisitor. The visitor can be changed or - set via setVisitor(visitor) before calling traverse().""" - self.__handler = visitor - - def setVisitor(self, visitor): - """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 - 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') - - self.__handler._startTraversal() - - if how == self.BREADTH: - self.__traverseBreadth(topicObj, onlyFiltered) - else: - assert how == self.DEPTH - self.__traverseDepth(topicObj, onlyFiltered) - - self.__handler._doneTraversal() - - def __traverseBreadth(self, topicObj, onlyFiltered): - visitor = self.__handler - - def extendQueue(subtopics): - topics.append(visitor._startChildren) - # put subtopics in list in alphabetical order - subtopics.sort(key=topicObj.__class__.getName) - topics.extend(subtopics) - topics.append(visitor._endChildren) - - topics = [topicObj] - while topics: - topicObj = topics.pop(0) - - if topicObj in (visitor._startChildren, visitor._endChildren): - topicObj() - continue - - if onlyFiltered: - if visitor._accept(topicObj): - extendQueue( topicObj.getSubtopics() ) - visitor._onTopic(topicObj) - else: - extendQueue( topicObj.getSubtopics() ) - visitor._onTopic(topicObj) - - def __traverseDepth(self, topicObj, onlyFiltered): - visitor = self.__handler - - def extendStack(topicTreeStack, subtopics): - topicTreeStack.insert(0, visitor._endChildren) # marker functor - # put subtopics in list in alphabetical order - subtopicsTmp = subtopics - subtopicsTmp.sort(reverse=True, key=topicObj.__class__.getName) - for sub in subtopicsTmp: - topicTreeStack.insert(0, sub) # this puts them in reverse order - topicTreeStack.insert(0, visitor._startChildren) # marker functor - - topics = [topicObj] - while topics: - topicObj = topics.pop(0) - - if topicObj in (visitor._startChildren, visitor._endChildren): - topicObj() - continue - - if onlyFiltered: - if visitor._accept(topicObj): - extendStack( topics, topicObj.getSubtopics() ) - visitor._onTopic(topicObj) - else: - extendStack( topics, topicObj.getSubtopics() ) - visitor._onTopic(topicObj) - - -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 - True (accept node) of False (reject node). Note that rejected - nodes cause traversal to move to next branch (no children - traversed).""" - return True - - def _startTraversal(self): - """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.""" - pass - - def _startChildren(self): - """Override this to take special action whenever a - new level of the topic hierarchy is started (e.g., indent - some output). """ - pass - - def _endChildren(self): - """Override this to take special action whenever a - level of the topic hierarchy is completed (e.g., dedent - some output). """ - pass - - def _doneTraversal(self): - """Override this to take special action when traversal done.""" - pass - diff --git a/wx/lib/pubsub/core/topicutils.py b/wx/lib/pubsub/core/topicutils.py deleted file mode 100644 index 995b3188..00000000 --- a/wx/lib/pubsub/core/topicutils.py +++ /dev/null @@ -1,118 +0,0 @@ -""" -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 - -from .topicexc import TopicNameError - -from .. import py2and3 - -__all__ = [] - - -UNDERSCORE = '_' # topic name can't start with this -# just want something unlikely to clash with user's topic names -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. """ - def __call__(self): - return None - - -def smartDedent(paragraph): - """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.''' - """ - if paragraph.startswith(' '): - para = dedent(paragraph) - else: - lines = paragraph.split('\n') - exceptFirst = dedent('\n'.join(lines[1:])) - para = lines[0]+exceptFirst - return para - - -import re -_validNameRE = re.compile(r'[-0-9a-zA-Z]\w*') - - -def validateName(topicName): - """Raise TopicNameError if nameTuple not valid as topic name.""" - topicNameTuple = tupleize(topicName) - if not topicNameTuple: - reason = 'name tuple must have at least one item!' - raise TopicNameError(None, reason) - - class topic: pass - for subname in topicNameTuple: - if not subname: - reason = 'can\'t contain empty string or None' - raise TopicNameError(topicNameTuple, reason) - - if subname.startswith(UNDERSCORE): - reason = 'must not start with "%s"' % UNDERSCORE - raise TopicNameError(topicNameTuple, reason) - - if subname == ALL_TOPICS: - reason = 'string "%s" is reserved for root topic' % ALL_TOPICS - raise TopicNameError(topicNameTuple, reason) - - if _validNameRE.match(subname) is None: - reason = 'element #%s ("%s") has invalid characters' % \ - (1+list(topicNameTuple).index(subname), subname) - raise TopicNameError(topicNameTuple, reason) - - -def stringize(topicName): - """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).""" - if py2and3.isstring(topicName): - return topicName - - if hasattr(topicName, "msgDataSpec"): - return topicName._topicNameStr - - try: - name = '.'.join(topicName) - except Exception: - exc = py2and3.getexcobj() - raise TopicNameError(topicName, str(exc)) - - return name - - -def tupleize(topicName): - """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).""" - # assume name is most often str; if more often tuple, - # then better use isinstance(name, tuple) - if hasattr(topicName, "msgDataSpec"): - topicName = topicName._topicNameStr - if py2and3.isstring(topicName): - topicTuple = tuple(topicName.split('.')) - else: - topicTuple = tuple(topicName) # assume already tuple of strings - - if not topicTuple: - raise TopicNameError(topicTuple, "Topic name can't be empty!") - - return topicTuple - - diff --git a/wx/lib/pubsub/core/treeconfig.py b/wx/lib/pubsub/core/treeconfig.py deleted file mode 100644 index 51b4087b..00000000 --- a/wx/lib/pubsub/core/treeconfig.py +++ /dev/null @@ -1,21 +0,0 @@ -""" - -: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) - self.listenerExcHandler = listenerExcHandler - self.raiseOnTopicUnspecified = False - - diff --git a/wx/lib/pubsub/core/validatedefnargs.py b/wx/lib/pubsub/core/validatedefnargs.py deleted file mode 100644 index 54ced128..00000000 --- a/wx/lib/pubsub/core/validatedefnargs.py +++ /dev/null @@ -1,29 +0,0 @@ -""" -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. """ - extra = set(allArgs).intersection(allParentArgs) - if extra: - msg = 'Args %%s already used in parent of "%s"' % topicName - raise MessageDataSpecError( msg, tuple(extra) ) - - -def verifySubset(all, sub, topicName, extraMsg=''): - """Verify that sub is a subset of all for topicName. Raise - MessageDataSpecError if fail. """ - notInAll = set(sub).difference(all) - if notInAll: - args = ','.join(all) - msg = 'Params [%s] missing inherited [%%s] for topic "%s"%s' % (args, topicName, extraMsg) - raise MessageDataSpecError(msg, tuple(notInAll) ) - - diff --git a/wx/lib/pubsub/core/weakmethod.py b/wx/lib/pubsub/core/weakmethod.py deleted file mode 100644 index 8c9750e4..00000000 --- a/wx/lib/pubsub/core/weakmethod.py +++ /dev/null @@ -1,102 +0,0 @@ -""" -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 -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 -# for weakly bound methods: -from types import MethodType -from weakref import ref as WeakRef - - -class WeakMethod: - """Represent a weak bound method, i.e. a method which doesn't keep alive the - object that it is bound to. """ - - def __init__(self, method, notifyDead = None): - """The method must be bound. notifyDead will be called when - object that method is bound to dies. """ - assert ismethod(method) - if method.__self__ is None: - raise ValueError('Unbound methods cannot be weak-referenced.') - - self.notifyDead = None - if notifyDead is None: - self.objRef = WeakRef(method.__self__) - else: - self.notifyDead = notifyDead - self.objRef = WeakRef(method.__self__, self.__onNotifyDeadObj) - - self.fun = method.__func__ - self.cls = method.__self__.__class__ - - def __onNotifyDeadObj(self, ref): - if self.notifyDead: - try: - self.notifyDead(self) - except Exception: - import traceback - traceback.print_exc() - - def __call__(self): - """Returns a MethodType if object for method still alive. - Otherwise return None. Note that MethodType causes a - strong reference to object to be created, so shouldn't save - the return value of this call. Note also that this __call__ - is required only for compatibility with WeakRef.ref(), otherwise - there would be more efficient ways of providing this functionality.""" - if self.objRef() is None: - return None - else: - return MethodType(self.fun, self.objRef()) - - def __eq__(self, method2): - """Two WeakMethod objects compare equal if they refer to the same method - of the same instance. Thanks to Josiah Carlson for patch and clarifications - on how dict uses eq/cmp and hashing. """ - if not isinstance(method2, WeakMethod): - return False - - return ( self.fun is method2.fun - and self.objRef() is method2.objRef() - and self.objRef() is not None ) - - def __hash__(self): - """Hash is an optimization for dict searches, it need not - return different numbers for every different object. Some objects - are not hashable (eg objects of classes derived from dict) so no - hash(objRef()) in there, and hash(self.cls) would only be useful - in the rare case where instance method was rebound. """ - return hash(self.fun) - - def __repr__(self): - dead = '' - if self.objRef() is None: - dead = '; DEAD' - obj = '<%s at %s%s>' % (self.__class__, id(self), dead) - return obj - - def refs(self, weakRef): - """Return true if we are storing same object referred to by weakRef.""" - return self.objRef == weakRef - - -def getWeakRef(obj, notifyDead=None): - """Get a weak reference to obj. If obj is a bound method, a WeakMethod - object, that behaves like a WeakRef, is returned; if it is - anything else a WeakRef is returned. If obj is an unbound method, - a ValueError will be raised.""" - if ismethod(obj): - createRef = WeakMethod - else: - createRef = WeakRef - - return createRef(obj, notifyDead) - diff --git a/wx/lib/pubsub/examples/advanced/README.txt b/wx/lib/pubsub/examples/advanced/README.txt deleted file mode 100644 index 77ae505a..00000000 --- a/wx/lib/pubsub/examples/advanced/README.txt +++ /dev/null @@ -1,15 +0,0 @@ -These two examples demonstrate a more advanced use of pubsub. One of the -examples uses the *kwargs* messaging protocol, the other uses the *arg1* -messaging protocol. There are two examples that can be run from this folder: - -**main_kwargs.py**: advanced example that shows other capabilities of - pubsub such as pubsub notification and listener exception - handling, in the 'kwargs' messaging protocol. All modules that - start with 'kwargs\_' are used, as well as some modules that are - independent of protocol and are shared with the arg1_main - example. - -**main_arg1.py**: same as kwargs_main but using the 'arg1' protocol. - All modules that start with 'kwargs\_' are used, as well as some - modules that are independent of protocol and are shared with the - kwargs_main example. \ No newline at end of file diff --git a/wx/lib/pubsub/examples/advanced/arg1_listeners.py b/wx/lib/pubsub/examples/advanced/arg1_listeners.py deleted file mode 100644 index 4ecf15f9..00000000 --- a/wx/lib/pubsub/examples/advanced/arg1_listeners.py +++ /dev/null @@ -1,39 +0,0 @@ -""" - -:copyright: Copyright since 2006 by Oliver Schoenborn, all rights reserved. -:license: BSD, see LICENSE.txt for details. -""" - -from pubsub import pub -from pubsub.py2and3 import print_ - -# ------------ create some listeners -------------- - -class Listener: - def onTopic11(self, msg): - data = msg.data - print_('Method Listener.onTopic11 received: ', repr(data)) - - def onTopic1(self, msg, topic=pub.AUTO_TOPIC): - info = 'Method Listener.onTopic1 received "%s" message: %s' - print_(info % (topic.getName(), repr(msg.data))) - - def __call__(self, msg): - print_('Listener instance received: ', msg.data) - -listenerObj = Listener() - - -def listenerFn(msg): - data = msg.data - print_('Function listenerFn received: ', repr(data)) - -# ------------ subscribe listeners ------------------ - -pub.subscribe(listenerObj, pub.ALL_TOPICS) # via its __call__ - -pub.subscribe(listenerFn, 'topic_1.subtopic_11') -pub.subscribe(listenerObj.onTopic11, 'topic_1.subtopic_11') - -pub.subscribe(listenerObj.onTopic1, 'topic_1') - diff --git a/wx/lib/pubsub/examples/advanced/arg1_senders.py b/wx/lib/pubsub/examples/advanced/arg1_senders.py deleted file mode 100644 index d59a2998..00000000 --- a/wx/lib/pubsub/examples/advanced/arg1_senders.py +++ /dev/null @@ -1,19 +0,0 @@ -""" - -:copyright: Copyright since 2006 by Oliver Schoenborn, all rights reserved. -:license: BSD, see LICENSE.txt for details. - -""" - -from pubsub import pub - - -def doSomething1(): - pub.sendMessage('topic_1.subtopic_11', ('message for subtopic 11', 'other message', 123)) - - -def doSomething2(): - pub.sendMessage('topic_1', 'message for topic 1') - pub.sendMessage('topic_2.subtopic_21', 'message for subtopic 2') - - diff --git a/wx/lib/pubsub/examples/advanced/arg1_topics.py b/wx/lib/pubsub/examples/advanced/arg1_topics.py deleted file mode 100644 index ecadb014..00000000 --- a/wx/lib/pubsub/examples/advanced/arg1_topics.py +++ /dev/null @@ -1,32 +0,0 @@ -# Automatically generated by TopicTreeSpecPrinter(**kwargs). -# The kwargs were: -# - fileObj: file -# - width: 70 -# - treeDoc: None -# - indentStep: 4 -# - footer: '# End of topic tree definition. Note that application may l...' - - -class topic_1: - """ - Explain when topic_1 should be used - """ - - class subtopic_11: - """ - Explain when subtopic_11 should be used - """ - -class topic_2: - """ - Some something useful about topic2 - """ - - class subtopic_21: - """ - description for subtopic 21 - """ - - -# End of topic tree definition. Note that application may load -# more than one definitions provider. diff --git a/wx/lib/pubsub/examples/advanced/arg1_topics_out.py b/wx/lib/pubsub/examples/advanced/arg1_topics_out.py deleted file mode 100644 index 5102fc7e..00000000 --- a/wx/lib/pubsub/examples/advanced/arg1_topics_out.py +++ /dev/null @@ -1,32 +0,0 @@ -# Automatically generated by TopicTreeSpecPrinter(**kwargs). -# The kwargs were: -# - fileObj: file -# - footer: '# End of topic tree definition. Note that application may l...' -# - indentStep: 4 -# - treeDoc: None -# - width: 70 - - -class topic_1: - """ - Explain when topic_1 should be used - """ - - class subtopic_11: - """ - Explain when subtopic_11 should be used - """ - -class topic_2: - """ - Some something useful about topic2 - """ - - class subtopic_21: - """ - description for subtopic 21 - """ - - -# End of topic tree definition. Note that application may load -# more than one definitions provider. diff --git a/wx/lib/pubsub/examples/advanced/exchandle.py b/wx/lib/pubsub/examples/advanced/exchandle.py deleted file mode 100644 index 9cc5887c..00000000 --- a/wx/lib/pubsub/examples/advanced/exchandle.py +++ /dev/null @@ -1,21 +0,0 @@ -""" - -:copyright: Copyright since 2006 by Oliver Schoenborn, all rights reserved. -:license: BSD, see LICENSE.txt for details. -""" - -from pubsub import pub -from pubsub.py2and3 import print_ - - -# create one special notification handler that ignores all except -# one type of notification -class MyPubsubExcHandler(pub.IListenerExcHandler): - - def __call__(self, listenerID): - print_('Exception raised in listener %s during sendMessage()' % listenerID) - print_(TracebackInfo()) - - -pub.setListenerExcHandler( MyPubsubExcHandler() ) - diff --git a/wx/lib/pubsub/examples/advanced/kwargs_listeners.py b/wx/lib/pubsub/examples/advanced/kwargs_listeners.py deleted file mode 100644 index 64cce59c..00000000 --- a/wx/lib/pubsub/examples/advanced/kwargs_listeners.py +++ /dev/null @@ -1,37 +0,0 @@ -""" - -:copyright: Copyright since 2006 by Oliver Schoenborn, all rights reserved. -:license: BSD, see LICENSE.txt for details. -""" - -from pubsub import pub -from pubsub.py2and3 import print_ - -# ------------ create some listeners -------------- - -class Listener: - def onTopic11(self, msg, msg2, extra=None): - print_('Method Listener.onTopic11 received: ', repr(msg), repr(msg2), repr(extra)) - - def onTopic1(self, msg, topic=pub.AUTO_TOPIC): - info = 'Method Listener.onTopic1 received "%s" message: %s' - print_(info % (topic.getName(), repr(msg))) - - def __call__(self, **kwargs): - print_('Listener instance received: ', kwargs) - -listenerObj = Listener() - - -def listenerFn(msg, msg2, extra=None): - print_('Function listenerFn received: ', repr(msg), repr(msg2), repr(extra)) - -# ------------ subscribe listeners ------------------ - -pub.subscribe(listenerObj, pub.ALL_TOPICS) # via its __call__ - -pub.subscribe(listenerFn, 'topic_1.subtopic_11') -pub.subscribe(listenerObj.onTopic11, 'topic_1.subtopic_11') - -pub.subscribe(listenerObj.onTopic1, 'topic_1') - diff --git a/wx/lib/pubsub/examples/advanced/kwargs_senders.py b/wx/lib/pubsub/examples/advanced/kwargs_senders.py deleted file mode 100644 index aed15126..00000000 --- a/wx/lib/pubsub/examples/advanced/kwargs_senders.py +++ /dev/null @@ -1,19 +0,0 @@ -""" - -:copyright: Copyright since 2006 by Oliver Schoenborn, all rights reserved. -:license: BSD, see LICENSE.txt for details. - -""" - -from pubsub import pub - -def doSomething1(): - pub.sendMessage('topic_1.subtopic_11', - msg='message for subtopic 11', msg2='other message', extra=123) - - -def doSomething2(): - pub.sendMessage('topic_1', msg='message for topic 1') - pub.sendMessage('topic_2.subtopic_21', msg='message for subtopic 2') - - diff --git a/wx/lib/pubsub/examples/advanced/kwargs_topics.py b/wx/lib/pubsub/examples/advanced/kwargs_topics.py deleted file mode 100644 index 56e1ea92..00000000 --- a/wx/lib/pubsub/examples/advanced/kwargs_topics.py +++ /dev/null @@ -1,53 +0,0 @@ -# Automatically generated by TopicTreeSpecPrinter(**kwargs). -# The kwargs were: -# - fileObj: file -# - width: 70 -# - treeDoc: None -# - indentStep: 4 -# - footer: '# End of topic tree definition. Note that application may l...' - - -class topic_1: - """ - Explain when topic_1 should be used - """ - - def msgDataSpec(msg): - """ - - msg: a text string message for recipient - """ - - class subtopic_11: - """ - Explain when subtopic_11 should be used - """ - - def msgDataSpec(msg, msg2, extra=None): - """ - - extra: something optional - - msg2: a text string message #2 for recipient - """ - -class topic_2: - """ - Some something useful about topic2 - """ - - def msgDataSpec(msg=None): - """ - - msg: a text string - """ - - class subtopic_21: - """ - description for subtopic 21 - """ - - def msgDataSpec(msg, arg1=None): - """ - - arg1: UNDOCUMENTED - """ - - -# End of topic tree definition. Note that application may load -# more than one definitions provider. diff --git a/wx/lib/pubsub/examples/advanced/kwargs_topics_out.py b/wx/lib/pubsub/examples/advanced/kwargs_topics_out.py deleted file mode 100644 index 356b756b..00000000 --- a/wx/lib/pubsub/examples/advanced/kwargs_topics_out.py +++ /dev/null @@ -1,53 +0,0 @@ -# Automatically generated by TopicTreeSpecPrinter(**kwargs). -# The kwargs were: -# - fileObj: file -# - footer: '# End of topic tree definition. Note that application may l...' -# - indentStep: 4 -# - treeDoc: None -# - width: 70 - - -class topic_1: - """ - Explain when topic_1 should be used - """ - - def msgDataSpec(msg): - """ - - msg: a text string message for recipient - """ - - class subtopic_11: - """ - Explain when subtopic_11 should be used - """ - - def msgDataSpec(msg, msg2, extra=None): - """ - - extra: something optional - - msg2: a text string message #2 for recipient - """ - -class topic_2: - """ - Some something useful about topic2 - """ - - def msgDataSpec(msg=None): - """ - - msg: a text string - """ - - class subtopic_21: - """ - description for subtopic 21 - """ - - def msgDataSpec(msg, arg1=None): - """ - - arg1: UNDOCUMENTED - """ - - -# End of topic tree definition. Note that application may load -# more than one definitions provider. diff --git a/wx/lib/pubsub/examples/advanced/main_arg1.py b/wx/lib/pubsub/examples/advanced/main_arg1.py deleted file mode 100644 index 0fb371a0..00000000 --- a/wx/lib/pubsub/examples/advanced/main_arg1.py +++ /dev/null @@ -1,51 +0,0 @@ -""" -Uses topic definition provider for arg1 messaging protocol. Compare with -main_kwargs.py which shows example using kwargs messaging protocol: -kwargs protocol provides for message data self-documentation and more -robustness (pubsub can determine if message data missing or unknown due -to type, etc). - -Experiment by changing arg1_topics.py and looking at the output tree -in arg1_topics_out.py. - -:copyright: Copyright since 2006 by Oliver Schoenborn, all rights reserved. -:license: BSD, see LICENSE.txt for details. -""" - -from pubsub import setuparg1 -from pubsub import pub -from pubsub.py2and3 import print_ - -import notifhandle -import exchandle - -import arg1_topics - -#***** actual application ********** - -print_('Using "arg1" messaging protocol of pubsub v3') - -try: - print_('------- init ----------') - - pub.addTopicDefnProvider( arg1_topics, pub.TOPIC_TREE_FROM_CLASS ) - pub.setTopicUnspecifiedFatal() - - import arg1_listeners - import arg1_senders as senders - - print_('-----------------------') - senders.doSomething1() - senders.doSomething2() - - print_('------- done ----------') - - print_('Exporting topic tree to', arg1_topics.__name__) - pub.exportTopicTreeSpec('arg1_topics_out') - -except Exception: - import traceback - traceback.print_exc() - print_(pub.exportTopicTreeSpec()) - -print_('------ exiting --------') \ No newline at end of file diff --git a/wx/lib/pubsub/examples/advanced/main_kwargs.py b/wx/lib/pubsub/examples/advanced/main_kwargs.py deleted file mode 100644 index 0f93d83f..00000000 --- a/wx/lib/pubsub/examples/advanced/main_kwargs.py +++ /dev/null @@ -1,50 +0,0 @@ -""" -Uses topic definition provider for kwargs messaging protocol. Compare with -main_arg1.py which shows example using arg1 messaging protocol: -kwargs protocol provides for message data self-documentation and more -robustness (pubsub can determine if message data missing or unknown due -to type, etc). - -Experiment by changing arg1_topics.py and looking at the output tree -in kwargs_topics_out.py. - -:copyright: Copyright since 2006 by Oliver Schoenborn, all rights reserved. -:license: BSD, see LICENSE.txt for details. -""" - -from pubsub import pub -from pubsub.py2and3 import print_ - -import notifhandle -import exchandle - -import kwargs_topics - -#***** actual application ********** - -print_('Using "kwargs" messaging protocol of pubsub v3') - -try: - print_('------- init ----------') - - pub.addTopicDefnProvider( kwargs_topics, pub.TOPIC_TREE_FROM_CLASS ) - pub.setTopicUnspecifiedFatal() - - import kwargs_listeners - import kwargs_senders as senders - - print_('-----------------------') - senders.doSomething1() - senders.doSomething2() - - print_('------- done ----------') - - print_('Exporting topic tree to', kwargs_topics.__name__) - pub.exportTopicTreeSpec('kwargs_topics_out') - -except Exception: - import traceback - traceback.print_exc() - print_(pub.exportTopicTreeSpec()) - -print_('------ exiting --------') \ No newline at end of file diff --git a/wx/lib/pubsub/examples/advanced/notifhandle.py b/wx/lib/pubsub/examples/advanced/notifhandle.py deleted file mode 100644 index 7fc21b28..00000000 --- a/wx/lib/pubsub/examples/advanced/notifhandle.py +++ /dev/null @@ -1,30 +0,0 @@ -""" - -:copyright: Copyright since 2006 by Oliver Schoenborn, all rights reserved. -:license: BSD, see LICENSE.txt for details. - -""" - -from pubsub import pub -from pubsub.py2and3 import print_ -from pubsub.utils.notification import useNotifyByWriteFile, IgnoreNotificationsMixin - - -# create one special notification handler that ignores all except -# one type of notification -class MyPubsubNotifHandler(IgnoreNotificationsMixin): - - def notifySubscribe(self, pubListener, topicObj, newSub): - newSubMsg = '' - if not newSub: - newSubMsg = ' was already' - msg = 'MyPubsubNotifHandler: listener %s%s subscribed to %s' - print_(msg % (pubListener.name(), newSubMsg, topicObj.getName())) - - -pub.addNotificationHandler( MyPubsubNotifHandler() ) - - -# print_(all notifications to stdout) -import sys -useNotifyByWriteFile(sys.stdout, prefix='NotifyByWriteFile:') diff --git a/wx/lib/pubsub/examples/basic_arg1/README.txt b/wx/lib/pubsub/examples/basic_arg1/README.txt deleted file mode 100644 index 8288d96e..00000000 --- a/wx/lib/pubsub/examples/basic_arg1/README.txt +++ /dev/null @@ -1,17 +0,0 @@ -These two examples demonstrate a simple use of pubsub with the *arg1* -messaging protocol. There are two examples that can be run from this folder: - -**console_main.py**: basic console based, uses the console_senders.py and - console_listeners.py modules, giving basic example of the *arg1* - messaging protocol. - -**wx.py**: basic wxPython GUI application that uses the *arg1* messaging - protocol. Note that it imports pubsub from - wxPython's wx.lib package. Therefore you must have (a copy of) - pubsub installed there for this example to work (pubsub can be - installed in multiple places independently and they will all work - without interfering with each other). - - Note that this example is copied almost - verbatim from the wxPython wiki site and is probably not a - good model of how an application should be structured. \ No newline at end of file diff --git a/wx/lib/pubsub/examples/basic_arg1/console_listeners.py b/wx/lib/pubsub/examples/basic_arg1/console_listeners.py deleted file mode 100644 index 1fba52d8..00000000 --- a/wx/lib/pubsub/examples/basic_arg1/console_listeners.py +++ /dev/null @@ -1,41 +0,0 @@ -""" - -:copyright: Copyright since 2006 by Oliver Schoenborn, all rights reserved. -:license: BSD, see LICENSE.txt for details. - -""" - -from pubsub import pub -from pubsub.py2and3 import print_ - -# ------------ create some listeners -------------- - -class Listener: - def onTopic11(self, msg): - msg, extra = msg.data - print_('Method Listener.onTopic11 received: ', repr(msg), repr(extra)) - - def onTopic1(self, msg, topic=pub.AUTO_TOPIC): - msg = msg.data[0] - info = 'Method Listener.onTopic1 received "%s" message: %s' - print_(info % (topic.getName(), repr(msg))) - - def __call__(self, msg): - print_('Listener instance received: ', repr(msg)) - -listenerObj = Listener() - - -def listenerFn(msg): - msg, extra = msg.data - print_('Function listenerFn received: ', repr(msg), repr(extra)) - -# ------------ subscribe listeners ------------------ - -pub.subscribe(listenerObj, pub.ALL_TOPICS) # via its __call__ - -pub.subscribe(listenerFn, 'topic1.subtopic11') -pub.subscribe(listenerObj.onTopic11, 'topic1.subtopic11') - -pub.subscribe(listenerObj.onTopic1, 'topic1') - diff --git a/wx/lib/pubsub/examples/basic_arg1/console_main.py b/wx/lib/pubsub/examples/basic_arg1/console_main.py deleted file mode 100644 index 9d559e69..00000000 --- a/wx/lib/pubsub/examples/basic_arg1/console_main.py +++ /dev/null @@ -1,23 +0,0 @@ -""" - -:copyright: Copyright since 2006 by Oliver Schoenborn, all rights reserved. -:license: BSD, see LICENSE.txt for details. - -""" - -from pubsub import setuparg1 -from pubsub.py2and3 import print_ - -import console_senders as senders -import console_listeners - - -def run(): - print_('Using "arg1" messaging protocol of pubsub v3') - - senders.doSomething1() - senders.doSomething2() - - -if __name__ == '__main__': - run() diff --git a/wx/lib/pubsub/examples/basic_arg1/console_senders.py b/wx/lib/pubsub/examples/basic_arg1/console_senders.py deleted file mode 100644 index 4f12c7c7..00000000 --- a/wx/lib/pubsub/examples/basic_arg1/console_senders.py +++ /dev/null @@ -1,22 +0,0 @@ -""" - -:copyright: Copyright since 2006 by Oliver Schoenborn, all rights reserved. -:license: BSD, see LICENSE.txt for details. - -""" - -from pubsub import pub -from pubsub.py2and3 import print_ - - -def doSomething1(): - print_('--- SENDING topic1.subtopic11 message ---') - pub.sendMessage('topic1.subtopic11', ('message for 11', 123)) - print_('---- SENT topic1.subtopic11 message ----') - -def doSomething2(): - print_('--- SENDING topic1 message ---') - pub.sendMessage('topic1', ('message for 1',) ) - print_('---- SENT topic1 message ----') - - diff --git a/wx/lib/pubsub/examples/basic_arg1/wx_main.py b/wx/lib/pubsub/examples/basic_arg1/wx_main.py deleted file mode 100644 index 0a551548..00000000 --- a/wx/lib/pubsub/examples/basic_arg1/wx_main.py +++ /dev/null @@ -1,106 +0,0 @@ -""" -Taken from wxPython wiki at http://wiki.wxpython.org/ModelViewController/. -Used to verify that the wx.lib.pubsub can be replaced by pubsub v3 if message -protocol set to "arg1" (as long as only main API functions are used such as -sendMessage and subscribe... a few peripheral ones have changed or been replaced -with more powerful features). - -Notes: -- the imports assume that pubsub package has been - copied to wx.lib, otherwise change them. -- this code is probably not a good example to follow, see instead - basic_kwargs_wx_*.py. - -:copyright: Copyright since 2006 by Oliver Schoenborn, all rights reserved. -:license: BSD, see LICENSE.txt for details. -""" - -import wx - -from wx.lib.pubsub import setuparg1 -from wx.lib.pubsub import pub - - -class Model: - def __init__(self): - self.myMoney = 0 - - def addMoney(self, value): - self.myMoney += value - #now tell anyone who cares that the value has been changed - pub.sendMessage("MONEY CHANGED", self.myMoney) - - def removeMoney(self, value): - self.myMoney -= value - #now tell anyone who cares that the value has been changed - pub.sendMessage("MONEY CHANGED", self.myMoney) - - -class View(wx.Frame): - def __init__(self, parent): - wx.Frame.__init__(self, parent, -1, "Main View") - - sizer = wx.BoxSizer(wx.VERTICAL) - text = wx.StaticText(self, -1, "My Money") - ctrl = wx.TextCtrl(self, -1, "") - sizer.Add(text, 0, wx.EXPAND|wx.ALL) - sizer.Add(ctrl, 0, wx.EXPAND|wx.ALL) - - self.moneyCtrl = ctrl - ctrl.SetEditable(False) - self.SetSizer(sizer) - - def SetMoney(self, money): - self.moneyCtrl.SetValue(str(money)) - -class ChangerWidget(wx.Frame): - def __init__(self, parent): - wx.Frame.__init__(self, parent, -1, "Main View") - - sizer = wx.BoxSizer(wx.VERTICAL) - self.add = wx.Button(self, -1, "Add Money") - self.remove = wx.Button(self, -1, "Remove Money") - sizer.Add(self.add, 0, wx.EXPAND|wx.ALL) - sizer.Add(self.remove, 0, wx.EXPAND|wx.ALL) - self.SetSizer(sizer) - -class Controller: - def __init__(self, app): - self.model = Model() - - #set up the first frame which displays the current Model value - self.view1 = View(None) - self.view1.SetMoney(self.model.myMoney) - - #set up the second frame which allows the user to modify the Model's value - self.view2 = ChangerWidget(self.view1) - self.view2.add.Bind(wx.EVT_BUTTON, self.AddMoney) - self.view2.remove.Bind(wx.EVT_BUTTON, self.RemoveMoney) - #subscribe to all "MONEY CHANGED" messages from the Model - #to subscribe to ALL messages (topics), omit the second argument below - pub.subscribe(self.MoneyChanged, "MONEY CHANGED") - - self.view1.Show() - self.view2.Show() - - def AddMoney(self, evt): - self.model.addMoney(10) - - def RemoveMoney(self, evt): - self.model.removeMoney(10) - - def MoneyChanged(self, message): - """ - This method is the handler for "MONEY CHANGED" messages, - which pubsub will call as messages are sent from the model. - - We already know the topic is "MONEY CHANGED", but if we - didn't, message.topic would tell us. - """ - self.view1.SetMoney(message.data) - - -if __name__ == "__main__": - app = wx.App() - Controller(app) - app.MainLoop() diff --git a/wx/lib/pubsub/examples/basic_kwargs/README.txt b/wx/lib/pubsub/examples/basic_kwargs/README.txt deleted file mode 100644 index 8a7ad4aa..00000000 --- a/wx/lib/pubsub/examples/basic_kwargs/README.txt +++ /dev/null @@ -1,13 +0,0 @@ -These two examples demonstrate a simple use of pubsub with the *kwargs* -messaging protocol (default). There are two examples that can be run from -this folder: - -**console_main.py**: basic console based, uses the console_senders.py and - console_listeners.py modules, giving example of the *kwargs* - messaging protocol. - -**wx_main.py**: wxPython GUI application with two windows (win1 and win2) - that exchange data without any reference to the other. This example - looks for pubsub on your system path so default install ok. - - diff --git a/wx/lib/pubsub/examples/basic_kwargs/console_listeners.py b/wx/lib/pubsub/examples/basic_kwargs/console_listeners.py deleted file mode 100644 index 901ddf18..00000000 --- a/wx/lib/pubsub/examples/basic_kwargs/console_listeners.py +++ /dev/null @@ -1,38 +0,0 @@ -""" - -:copyright: Copyright since 2006 by Oliver Schoenborn, all rights reserved. -:license: BSD, see LICENSE.txt for details. - -""" - -from pubsub import pub -from pubsub.py2and3 import print_ - -# ------------ create some listeners -------------- - -class Listener: - def onTopic11(self, msg, extra=None): - print_('Method Listener.onTopic11 received: ', repr(msg), repr(extra)) - - def onTopic1(self, msg, topic=pub.AUTO_TOPIC): - info = 'Method Listener.onTopic1 received "%s" message: %s' - print_(info % (topic.getName(), repr(msg))) - - def __call__(self, **kwargs): - print_('Listener instance received: ', kwargs) - -listenerObj = Listener() - - -def listenerFn(msg, extra=None): - print_('Function listenerFn received: ', repr(msg), repr(extra)) - -# ------------ subscribe listeners ------------------ - -pub.subscribe(listenerObj, pub.ALL_TOPICS) # via its __call__ - -pub.subscribe(listenerFn, 'topic1.subtopic11') -pub.subscribe(listenerObj.onTopic11, 'topic1.subtopic11') - -pub.subscribe(listenerObj.onTopic1, 'topic1') - diff --git a/wx/lib/pubsub/examples/basic_kwargs/console_main.py b/wx/lib/pubsub/examples/basic_kwargs/console_main.py deleted file mode 100644 index 5960be92..00000000 --- a/wx/lib/pubsub/examples/basic_kwargs/console_main.py +++ /dev/null @@ -1,21 +0,0 @@ -""" - -:copyright: Copyright since 2006 by Oliver Schoenborn, all rights reserved. -:license: BSD, see LICENSE.txt for details. - -""" - -import console_listeners -import console_senders as senders - -from pubsub.py2and3 import print_ - -def run(): - print_('Using "kwargs" messaging protocol of pubsub v3') - - senders.doSomething1() - senders.doSomething2() - - -if __name__ == '__main__': - run() diff --git a/wx/lib/pubsub/examples/basic_kwargs/console_senders.py b/wx/lib/pubsub/examples/basic_kwargs/console_senders.py deleted file mode 100644 index a23db203..00000000 --- a/wx/lib/pubsub/examples/basic_kwargs/console_senders.py +++ /dev/null @@ -1,22 +0,0 @@ -""" - -:copyright: Copyright since 2006 by Oliver Schoenborn, all rights reserved. -:license: BSD, see LICENSE.txt for details. - -""" - -from pubsub import pub -from pubsub.py2and3 import print_ - - -def doSomething1(): - print_('--- SENDING topic1.subtopic11 message ---') - pub.sendMessage('topic1.subtopic11', msg='message for 11', extra=123) - print_('---- SENT topic1.subtopic11 message ----') - -def doSomething2(): - print_('--- SENDING topic1 message ---') - pub.sendMessage('topic1', msg='message for 1') - print_('---- SENT topic1 message ----') - - diff --git a/wx/lib/pubsub/examples/basic_kwargs/wx_main.py b/wx/lib/pubsub/examples/basic_kwargs/wx_main.py deleted file mode 100644 index 73ea27fd..00000000 --- a/wx/lib/pubsub/examples/basic_kwargs/wx_main.py +++ /dev/null @@ -1,73 +0,0 @@ -""" -Adapted from wxPython website at http://wiki.wxpython.org/ModelViewController/. - -:copyright: Copyright since 2006 by Oliver Schoenborn, all rights reserved. -:license: BSD, see LICENSE.txt for details. -""" - -import wx - -from pubsub import pub -from pubsub.py2and3 import print_ - -print_('pubsub API version', pub.VERSION_API) - -# notification -from pubsub.utils.notification import useNotifyByWriteFile -import sys -useNotifyByWriteFile(sys.stdout) - -# the following two modules don't know about each other yet will -# exchange data via pubsub: -from wx_win1 import View -from wx_win2 import ChangerWidget - - -class Model: - - def __init__(self): - self.myMoney = 0 - - def addMoney(self, value): - self.myMoney += value - #now tell anyone who cares that the value has been changed - pub.sendMessage("money_changed", money=self.myMoney) - - def removeMoney(self, value): - self.myMoney -= value - #now tell anyone who cares that the value has been changed - pub.sendMessage("money_changed", money=self.myMoney) - - -class Controller: - - def __init__(self): - self.model = Model() - - #set up the first frame which displays the current Model value - self.view1 = View() - self.view1.setMoney(self.model.myMoney) - - #set up the second frame which allows the user to modify the Model's value - self.view2 = ChangerWidget() - - self.view1.Show() - self.view2.Show() - - pub.subscribe(self.changeMoney, 'money_changing') - - def changeMoney(self, amount): - if amount >= 0: - self.model.addMoney(amount) - else: - self.model.removeMoney(-amount) - - -if __name__ == "__main__": - app = wx.App() - c = Controller() - sys.stdout = sys.__stdout__ - - print_('---- Starting main event loop ----') - app.MainLoop() - print_('---- Exited main event loop ----') diff --git a/wx/lib/pubsub/examples/basic_kwargs/wx_win1.py b/wx/lib/pubsub/examples/basic_kwargs/wx_win1.py deleted file mode 100644 index d6ba32a8..00000000 --- a/wx/lib/pubsub/examples/basic_kwargs/wx_win1.py +++ /dev/null @@ -1,33 +0,0 @@ -""" - -:copyright: Copyright since 2006 by Oliver Schoenborn, all rights reserved. -:license: BSD, see LICENSE.txt for details. - -""" - -import wx -from pubsub import pub - - -class View(wx.Frame): - def __init__(self, parent=None): - wx.Frame.__init__(self, parent, -1, "Main View") - - sizer = wx.BoxSizer(wx.VERTICAL) - text = wx.StaticText(self, -1, "My Money") - ctrl = wx.TextCtrl(self, -1, "") - sizer.Add(text, 0, wx.EXPAND|wx.ALL) - sizer.Add(ctrl, 0, wx.EXPAND|wx.ALL) - - self.moneyCtrl = ctrl - ctrl.SetEditable(False) - self.SetSizer(sizer) - - #subscribe to all "MONEY CHANGED" messages from the Model - #to subscribe to ALL messages (topics), omit the second argument below - pub.subscribe(self.setMoney, "money_changed") - - def setMoney(self, money): - self.moneyCtrl.SetValue(str(money)) - - diff --git a/wx/lib/pubsub/examples/basic_kwargs/wx_win2.py b/wx/lib/pubsub/examples/basic_kwargs/wx_win2.py deleted file mode 100644 index 8464f1ac..00000000 --- a/wx/lib/pubsub/examples/basic_kwargs/wx_win2.py +++ /dev/null @@ -1,38 +0,0 @@ -""" -Widget from which money can be added or removed from account. - -:copyright: Copyright since 2006 by Oliver Schoenborn, all rights reserved. -:license: BSD, see LICENSE.txt for details. -""" - -import wx -from pubsub import pub -from pubsub.py2and3 import print_ - - -class ChangerWidget(wx.Frame): - - CHANGE = 10 # by how much money changes every time click - - def __init__(self, parent=None): - wx.Frame.__init__(self, parent, -1, "Changer View") - - sizer = wx.BoxSizer(wx.VERTICAL) - self.add = wx.Button(self, -1, "Add Money") - self.remove = wx.Button(self, -1, "Remove Money") - sizer.Add(self.add, 0, wx.EXPAND|wx.ALL) - sizer.Add(self.remove, 0, wx.EXPAND|wx.ALL) - self.SetSizer(sizer) - - self.add.Bind(wx.EVT_BUTTON, self.onAdd) - self.remove.Bind(wx.EVT_BUTTON, self.onRemove) - - def onAdd(self, evt): - print_('-----') - pub.sendMessage("money_changing", amount = self.CHANGE) - - def onRemove(self, evt): - print_('-----') - pub.sendMessage("money_changing", amount = - self.CHANGE) - - diff --git a/wx/lib/pubsub/examples/multithreadloop.py b/wx/lib/pubsub/examples/multithreadloop.py deleted file mode 100644 index 203accbd..00000000 --- a/wx/lib/pubsub/examples/multithreadloop.py +++ /dev/null @@ -1,130 +0,0 @@ -""" -This test gives an example of how some computation results from an -auxiliary thread could be 'published' via pubsub in a thread-safe -manner, in a 'gui'-like application, ie an application where the -main thread is in an infinite event loop and supports the callback -of user-defined functions when the gui is idle. - -The worker thread 'work' is to increment a counter -as fast as interpreter can handle. Every so often (every resultStep counts), -the thread stores the count in a synchronized queue, for later retrieval -by the main thread. In parallel to this, the main thread loops forever (or -until user interrupts via keyboard), doing some hypothetical work -(represented by the sleep(1) call) and calling all registered 'idle' -callbacks. The transfer is done by extracting items from the queue and -publishing them via pubsub. - -Oliver Schoenborn -May 2009 - -:copyright: Copyright 2008-2009 by Oliver Schoenborn, all rights reserved. -:license: BSD, see LICENSE.txt for details. - -""" - - -__author__="schoenb" -__date__ ="$31-May-2009 9:11:41 PM$" - -from Queue import Queue -import time -import threading - -from pubsub import pub -from pubsub.py2and3 import print_ - - -resultStep = 1000000 # how many counts for thread "result" to be available - - -def threadObserver(transfers, threadObj, count): - """Listener that listens for data from testTopic. This function - doesn't know where the data comes from (or in what thread it was - generated... but threadObj is the thread in which this - threadObserver is called and should indicate Main thread).""" - - print_(transfers, threadObj, count / resultStep) - -pub.subscribe(threadObserver, 'testTopic') - - -def onIdle(): - """This should be registered with 'gui' to be called when gui is idle - so we get a chance to transfer data from aux thread without blocking - the gui. Ie this function must spend as little time as possible so - 'gui' remains reponsive.""" - thread.transferData() - - -class ParaFunction(threading.Thread): - """ - Represent a function running in a parallel thread. The thread - just increments a counter and puts the counter value on a synchronized - queue every resultStep counts. The content of the queue can be published by - calling transferData(). - """ - - def __init__(self): - threading.Thread.__init__(self) - self.running = False # set to True when thread should stop - self.count = 0 # our workload: keep counting! - self.queue = Queue() # to transfer data to main thread - self.transfer = 0 # count how many transfers occurred - - def run(self): - print_('aux thread started') - self.running = True - while self.running: - self.count += 1 - if self.count % resultStep == 0: - self.queue.put(self.count) - - print_('aux thread done') - - def stop(self): - self.running = False - - def transferData(self): - """Send data from aux thread to main thread. The data was put in - self.queue by the aux thread, and this queue is a Queue.Queue which - is a synchronized queue for inter-thread communication. - Note: This method must be called from main thread.""" - self.transfer += 1 - while not self.queue.empty(): - pub.sendMessage('testTopic', - transfers = self.transfer, - threadObj = threading.currentThread(), - count = self.queue.get()) - - -thread = ParaFunction() - - -def main(): - idleFns = [] # list of functions to call when 'gui' idle - idleFns.append( onIdle ) - - try: - thread.start() - - print_('starting event loop') - eventLoop = True - while eventLoop: - time.sleep(1) # pretend that main thread does other stuff - for idleFn in idleFns: - idleFn() - - except KeyboardInterrupt: - print_('Main interrupted, stopping aux thread') - thread.stop() - - except Exception as exc: - from pubsub import py2and3 - exc = py2and3.getexcobj() - print_(exc) - print_('Exception, stopping aux thread') - thread.stop() - - -main() - diff --git a/wx/lib/pubsub/examples/runall.bat b/wx/lib/pubsub/examples/runall.bat deleted file mode 100644 index 0a34aa8d..00000000 --- a/wx/lib/pubsub/examples/runall.bat +++ /dev/null @@ -1,86 +0,0 @@ -echo off - -rem This script runs all examples. This should be mostly for using the -rem examples as regression tests (after all tests have passed in tests -rem folder). -rem One command line argument is required, the python version number to -rem use, no dots: 24 for 2.4, 30 for 3.0, etc. -rem -rem (C) Oliver Schoenborn 2009 - -set PY_VER=%1 -IF "%1" EQU "" ( - SET PY_VER=26 - echo Will use Python 2.6. To use other, put version ID as command line arg - echo Example: for Python 2.7 put 27, for 3.0 put 30, etc. -) - -set PYTHON_EXE=python -echo python exe is %PYTHON_EXE% - -echo. -echo. -echo ######################## basic - kwargs - console ######################### -echo. - -pushd basic_kwargs -%PYTHON_EXE% console_main.py -popd -pause - - -echo. -echo. -echo ######################## basic - kwargs - wx ######################### -echo. - -pushd basic_kwargs -%PYTHON_EXE% wx_main.py -popd -pause - - -echo. -echo. -echo ######################## basic - arg1 - console ######################### -echo. - -pushd basic_arg1 -%PYTHON_EXE% console_main.py -popd -pause - - -echo. -echo. -echo ######################## basic - arg1 - wx ######################### -echo. - -pushd basic_arg1 -%PYTHON_EXE% wx_main.py -popd -pause - - -echo. -echo. -echo ######################## advanced - kwargs - console ######################### -echo. - -pushd advanced -%PYTHON_EXE% main_kwargs.py -popd -pause - - -echo. -echo. -echo ######################## advanced - arg1 - console ######################### -echo. - -pushd advanced -%PYTHON_EXE% main_arg1.py -popd -pause - - diff --git a/wx/lib/pubsub/examples/runall_regression.txt b/wx/lib/pubsub/examples/runall_regression.txt deleted file mode 100644 index 083b3b58..00000000 --- a/wx/lib/pubsub/examples/runall_regression.txt +++ /dev/null @@ -1,152 +0,0 @@ -Will use Python 2.6. To use other, put version ID as command line arg -Example: for Python 2.7 put 27, for 3.0 put 30, etc. -python exe is python - - -######################## basic - kwargs - console ######################### - -Using "kwargs" messaging protocol of pubsub v3 ---- SENDING topic1.subtopic11 message --- -Method Listener.onTopic11 received: 'message for 11' 123 -Function listenerFn received: 'message for 11' 123 -Method Listener.onTopic1 received "topic1.subtopic11" message: 'message for 11' -Listener instance received: {'msg': 'message for 11', 'extra': 123} ----- SENT topic1.subtopic11 message ---- ---- SENDING topic1 message --- -Method Listener.onTopic1 received "topic1" message: 'message for 1' -Listener instance received: {'msg': 'message for 1'} ----- SENT topic1 message ---- -Press any key to continue . . . - - -######################## basic - kwargs - wx ######################### - -pubsub version 3.1.2.201112.r243 -PUBSUB: New topic "money_changed" created -PUBSUB: Subscribed listener "View.setMoney" to topic "money_changed" -PUBSUB: New topic "money_changing" created -PUBSUB: Subscribed listener "Controller.changeMoney" to topic "money_changing" ----- Starting main event loop ---- ------ -PUBSUB: Start sending message of topic "money_changing" -PUBSUB: Sending message of topic "money_changing" to listener Controller.changeMoney -PUBSUB: Start sending message of topic "money_changed" -PUBSUB: Sending message of topic "money_changed" to listener View.setMoney -PUBSUB: Done sending message of topic "money_changed" -PUBSUB: Done sending message of topic "money_changing" ------ -PUBSUB: Start sending message of topic "money_changing" -PUBSUB: Sending message of topic "money_changing" to listener Controller.changeMoney -PUBSUB: Start sending message of topic "money_changed" -PUBSUB: Sending message of topic "money_changed" to listener View.setMoney -PUBSUB: Done sending message of topic "money_changed" -PUBSUB: Done sending message of topic "money_changing" ----- Exited main event loop ---- -Press any key to continue . . . - - -######################## basic - arg1 - console ######################### - -Using "arg1" messaging protocol of pubsub v3 ---- SENDING topic1.subtopic11 message --- -Function listenerFn received: 'message for 11' 123 -Method Listener.onTopic11 received: 'message for 11' 123 -Method Listener.onTopic1 received "topic1.subtopic11" message: 'message for 11' -Listener instance received: ----- SENT topic1.subtopic11 message ---- ---- SENDING topic1 message --- -Method Listener.onTopic1 received "topic1" message: 'message for 1' -Listener instance received: ----- SENT topic1 message ---- -Press any key to continue . . . - - -######################## basic - arg1 - wx ######################### - -Press any key to continue . . . - - -######################## advanced - kwargs - console ######################### - -Using "kwargs" messaging protocol of pubsub v3 -------- init ---------- -NotifyByWriteFile: New topic "topic_2" created -NotifyByWriteFile: New topic "topic_2.subtopic_21" created -NotifyByWriteFile: New topic "topic_1" created -NotifyByWriteFile: New topic "topic_1.subtopic_11" created -MyPubsubNotifHandler: listener Listener_8304 subscribed to ALL_TOPICS -NotifyByWriteFile: Subscribed listener "Listener" to topic "ALL_TOPICS" -MyPubsubNotifHandler: listener listenerFn_2464 subscribed to topic_1.subtopic_11 -NotifyByWriteFile: Subscribed listener "listenerFn" to topic "topic_1.subtopic_11" -MyPubsubNotifHandler: listener Listener.onTopic11_8736 subscribed to topic_1.subtopic_11 -NotifyByWriteFile: Subscribed listener "Listener.onTopic11" to topic "topic_1.subtopic_11" -MyPubsubNotifHandler: listener Listener.onTopic1_8736 subscribed to topic_1 -NotifyByWriteFile: Subscribed listener "Listener.onTopic1" to topic "topic_1" ------------------------ -NotifyByWriteFile: Start sending message of topic "topic_1.subtopic_11" -NotifyByWriteFile: Sending message of topic "topic_1.subtopic_11" to listener Listener.onTopic11 -Method Listener.onTopic11 received: 'message for subtopic 11' 'other message' 123 -NotifyByWriteFile: Sending message of topic "topic_1.subtopic_11" to listener listenerFn -Function listenerFn received: 'message for subtopic 11' 'other message' 123 -NotifyByWriteFile: Sending message of topic "topic_1" to listener Listener.onTopic1 -Method Listener.onTopic1 received "topic_1.subtopic_11" message: 'message for subtopic 11' -NotifyByWriteFile: Sending message of topic "ALL_TOPICS" to listener Listener -Listener instance received: {'msg': 'message for subtopic 11', 'extra': 123, 'msg2': 'other message'} -NotifyByWriteFile: Done sending message of topic "topic_1.subtopic_11" -NotifyByWriteFile: Start sending message of topic "topic_1" -NotifyByWriteFile: Sending message of topic "topic_1" to listener Listener.onTopic1 -Method Listener.onTopic1 received "topic_1" message: 'message for topic 1' -NotifyByWriteFile: Sending message of topic "ALL_TOPICS" to listener Listener -Listener instance received: {'msg': 'message for topic 1'} -NotifyByWriteFile: Done sending message of topic "topic_1" -NotifyByWriteFile: Start sending message of topic "topic_2.subtopic_21" -NotifyByWriteFile: Sending message of topic "ALL_TOPICS" to listener Listener -Listener instance received: {'msg': 'message for subtopic 2'} -NotifyByWriteFile: Done sending message of topic "topic_2.subtopic_21" -------- done ---------- -Exporting topic tree to kwargs_topics ------- exiting -------- -Press any key to continue . . . - - -######################## advanced - arg1 - console ######################### - -Using "arg1" messaging protocol of pubsub v3 -------- init ---------- -NotifyByWriteFile: New topic "topic_2" created -NotifyByWriteFile: New topic "topic_2.subtopic_21" created -NotifyByWriteFile: New topic "topic_1" created -NotifyByWriteFile: New topic "topic_1.subtopic_11" created -MyPubsubNotifHandler: listener Listener_2608 subscribed to ALL_TOPICS -NotifyByWriteFile: Subscribed listener "Listener" to topic "ALL_TOPICS" -MyPubsubNotifHandler: listener listenerFn_1024 subscribed to topic_1.subtopic_11 -NotifyByWriteFile: Subscribed listener "listenerFn" to topic "topic_1.subtopic_11" -MyPubsubNotifHandler: listener Listener.onTopic11_8808 subscribed to topic_1.subtopic_11 -NotifyByWriteFile: Subscribed listener "Listener.onTopic11" to topic "topic_1.subtopic_11" -MyPubsubNotifHandler: listener Listener.onTopic1_8808 subscribed to topic_1 -NotifyByWriteFile: Subscribed listener "Listener.onTopic1" to topic "topic_1" ------------------------ -NotifyByWriteFile: Start sending message of topic "topic_1.subtopic_11" -NotifyByWriteFile: Sending message of topic "topic_1.subtopic_11" to listener Listener.onTopic11 -Method Listener.onTopic11 received: ('message for subtopic 11', 'other message', 123) -NotifyByWriteFile: Sending message of topic "topic_1.subtopic_11" to listener listenerFn -Function listenerFn received: ('message for subtopic 11', 'other message', 123) -NotifyByWriteFile: Sending message of topic "topic_1" to listener Listener.onTopic1 -Method Listener.onTopic1 received "topic_1.subtopic_11" message: ('message for subtopic 11', 'other message', 123) -NotifyByWriteFile: Sending message of topic "ALL_TOPICS" to listener Listener -Listener instance received: ('message for subtopic 11', 'other message', 123) -NotifyByWriteFile: Done sending message of topic "topic_1.subtopic_11" -NotifyByWriteFile: Start sending message of topic "topic_1" -NotifyByWriteFile: Sending message of topic "topic_1" to listener Listener.onTopic1 -Method Listener.onTopic1 received "topic_1" message: 'message for topic 1' -NotifyByWriteFile: Sending message of topic "ALL_TOPICS" to listener Listener -Listener instance received: message for topic 1 -NotifyByWriteFile: Done sending message of topic "topic_1" -NotifyByWriteFile: Start sending message of topic "topic_2.subtopic_21" -NotifyByWriteFile: Sending message of topic "ALL_TOPICS" to listener Listener -Listener instance received: message for subtopic 2 -NotifyByWriteFile: Done sending message of topic "topic_2.subtopic_21" -------- done ---------- -Exporting topic tree to arg1_topics ------- exiting -------- -Press any key to continue . . . diff --git a/wx/lib/pubsub/policies.py b/wx/lib/pubsub/policies.py deleted file mode 100644 index 16b90200..00000000 --- a/wx/lib/pubsub/policies.py +++ /dev/null @@ -1,26 +0,0 @@ -""" -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 - -msgDataProtocol = 'kwargs' -msgDataArgName = None -senderKwargNameAny = False - - -def setMsgDataArgName(stage, listenerArgName, senderArgNameAny=False): - global senderKwargNameAny - global msgDataArgName - global msgProtocolTransStage - senderKwargNameAny = senderArgNameAny - msgDataArgName = listenerArgName - msgProtocolTransStage = stage - #print('%s' % repr(policies.msgProtocolTransStage)) - #print('%s' % repr(policies.msgDataProtocol)) - #print('%s' % repr(policies.senderKwargNameAny)) - #print('%s' % repr(policies.msgDataArgName)) - #print('override "arg1" protocol arg name: %s' % argName) diff --git a/wx/lib/pubsub/pub.py b/wx/lib/pubsub/pub.py deleted file mode 100644 index 0a6493a3..00000000 --- a/wx/lib/pubsub/pub.py +++ /dev/null @@ -1,199 +0,0 @@ -""" -This is the main entry-point to pubsub's core functionality. The :mod:`~pubsub.pub` -module supports: - -* messaging: publishing and receiving messages of a given topic -* tracing: tracing pubsub activity in an application -* trapping exceptions: dealing with "badly behaved" listeners (ie that leak exceptions) -* specificatio of topic tree: defining (or just documenting) the topic tree of an - application; message data specification (MDS) - -The recommended usage is :: - - from pubsub import pub - - // use pub functions: - pub.sendMessage(...) - -Note that this module creates a "default" instance of -pubsub.core.Publisher and binds several local functions to some of its methods -and those of the pubsub.core.TopicManager instance that it contains. However, an -application may create as many independent instances of Publisher as -required (for instance, one in each thread; with a custom queue to mediate -message transfer between threads). -""" - -""" -: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_SVN = "$Rev: 243 $".split()[1] # DO NOT CHANGE: automatically updated by VCS - -from .core import ( - Publisher as _Publisher, - - AUTO_TOPIC, - - ListenerMismatchError, - TopicDefnError, - - IListenerExcHandler, - ExcHandlerError, - - SenderMissingReqdMsgDataError, - SenderUnknownMsgDataError, - - TopicDefnError, - TopicNameError, - UnrecognizedSourceFormatError, - - ALL_TOPICS, - - MessageDataSpecError, - exportTopicTreeSpec, - TOPIC_TREE_FROM_MODULE, - TOPIC_TREE_FROM_STRING, - TOPIC_TREE_FROM_CLASS, - - TopicTreeTraverser, - - INotificationHandler, -) - -__all__ = [ - # listener stuff: - 'subscribe', - 'unsubscribe', - 'unsubAll', - 'isSubscribed', - - 'isValid', - 'validate', - 'ListenerMismatchError', - 'AUTO_TOPIC', - - 'IListenerExcHandler', - 'getListenerExcHandler', - 'setListenerExcHandler', - 'ExcHandlerError', - - # topic stuff: - - 'ALL_TOPICS', - 'topicTreeRoot', - 'topicsMap', - - 'getDefaultTopicMgr', - - # topioc defn provider stuff - - 'addTopicDefnProvider', - 'clearTopicDefnProviders', - 'getNumTopicDefnProviders', - 'TOPIC_TREE_FROM_MODULE', - 'TOPIC_TREE_FROM_CLASS', - 'TOPIC_TREE_FROM_STRING', - 'exportTopicTreeSpec', - 'instantiateAllDefinedTopics' - - 'TopicDefnError', - 'TopicNameError', - - 'setTopicUnspecifiedFatal', - - # publisher stuff: - - 'sendMessage', - 'SenderMissingReqdMsgDataError', - 'SenderUnknownMsgDataError', - - # misc: - - 'addNotificationHandler', - 'setNotificationFlags', - 'getNotificationFlags', - 'clearNotificationHandlers', - - 'TopicTreeTraverser', - -] - - -# --------- Publisher singleton and bound methods ------------------------------------ - -_publisher = _Publisher() - -subscribe = _publisher.subscribe -unsubscribe = _publisher.unsubscribe -unsubAll = _publisher.unsubAll -sendMessage = _publisher.sendMessage - -getListenerExcHandler = _publisher.getListenerExcHandler -setListenerExcHandler = _publisher.setListenerExcHandler - -addNotificationHandler = _publisher.addNotificationHandler -clearNotificationHandlers = _publisher.clearNotificationHandlers -setNotificationFlags = _publisher.setNotificationFlags -getNotificationFlags = _publisher.getNotificationFlags - -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.""" - return _publisher - - -# ---------- default TopicManager instance and bound methods ------------------------ - -_topicMgr = _publisher.getTopicMgr() - -topicTreeRoot = _topicMgr.getRootAllTopics() -topicsMap = _topicMgr._topicsMap - - -def isValid(listener, topicName): - """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.""" - _topicMgr.getTopic(topicName).validate(listener) - - -def isSubscribed(listener, topicName): - """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. """ - return _topicMgr.getTopic(topicName).hasListener(listener) - - -def getDefaultTopicMgr(): - """Get the TopicManager instance created by default when this - module is imported. This function is a shortcut for - ``pub.getDefaultPublisher().getTopicMgr()``.""" - return _topicMgr - - -addTopicDefnProvider = _topicMgr.addDefnProvider -clearTopicDefnProviders = _topicMgr.clearDefnProviders -getNumTopicDefnProviders = _topicMgr.getNumDefnProviders - -def instantiateAllDefinedTopics(provider): - """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. """ - for topicName in provider: - _topicMgr.getOrCreateTopic(topicName) - -#--------------------------------------------------------------------------- diff --git a/wx/lib/pubsub/py2and3.py b/wx/lib/pubsub/py2and3.py deleted file mode 100644 index 0752e712..00000000 --- a/wx/lib/pubsub/py2and3.py +++ /dev/null @@ -1,608 +0,0 @@ -"""Utilities for writing code that runs on Python 2 and 3""" - -# Copyright (c) 2010-2013 Benjamin Peterson -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. - -import operator -import sys -import types - -__author__ = "Benjamin Peterson " -__version__ = "1.4.1" - - -# Useful for very coarse version differentiation. -PY2 = sys.version_info[0] == 2 -PY3 = sys.version_info[0] == 3 - -if PY3: - string_types = str, - integer_types = int, - class_types = type, - text_type = str - binary_type = bytes - - MAXSIZE = sys.maxsize -else: - string_types = basestring, - integer_types = (int, long) - class_types = (type, types.ClassType) - text_type = unicode - binary_type = str - - if sys.platform.startswith("java"): - # Jython always uses 32 bits. - MAXSIZE = int((1 << 31) - 1) - else: - # It's possible to have sizeof(long) != sizeof(Py_ssize_t). - class X(object): - def __len__(self): - return 1 << 31 - try: - len(X()) - except OverflowError: - # 32-bit - MAXSIZE = int((1 << 31) - 1) - else: - # 64-bit - MAXSIZE = int((1 << 63) - 1) - del X - - -def _add_doc(func, doc): - """Add documentation to a function.""" - func.__doc__ = doc - - -def _import_module(name): - """Import module, returning the module after the last dot.""" - __import__(name) - return sys.modules[name] - - -class _LazyDescr(object): - - def __init__(self, name): - self.name = name - - def __get__(self, obj, tp): - result = self._resolve() - setattr(obj, self.name, result) - # This is a bit ugly, but it avoids running this again. - delattr(tp, self.name) - return result - - -class MovedModule(_LazyDescr): - - def __init__(self, name, old, new=None): - super(MovedModule, self).__init__(name) - if PY3: - if new is None: - new = name - self.mod = new - else: - self.mod = old - - def _resolve(self): - return _import_module(self.mod) - - -class MovedAttribute(_LazyDescr): - - def __init__(self, name, old_mod, new_mod, old_attr=None, new_attr=None): - super(MovedAttribute, self).__init__(name) - if PY3: - if new_mod is None: - new_mod = name - self.mod = new_mod - if new_attr is None: - if old_attr is None: - new_attr = name - else: - new_attr = old_attr - self.attr = new_attr - else: - self.mod = old_mod - if old_attr is None: - old_attr = name - self.attr = old_attr - - def _resolve(self): - module = _import_module(self.mod) - return getattr(module, self.attr) - - - -class _MovedItems(types.ModuleType): - """Lazy loading of moved objects""" - - -_moved_attributes = [ - MovedAttribute("cStringIO", "cStringIO", "io", "StringIO"), - MovedAttribute("filter", "itertools", "builtins", "ifilter", "filter"), - MovedAttribute("filterfalse", "itertools", "itertools", "ifilterfalse", "filterfalse"), - MovedAttribute("input", "__builtin__", "builtins", "raw_input", "input"), - MovedAttribute("map", "itertools", "builtins", "imap", "map"), - MovedAttribute("range", "__builtin__", "builtins", "xrange", "range"), - MovedAttribute("reload_module", "__builtin__", "imp", "reload"), - MovedAttribute("reduce", "__builtin__", "functools"), - MovedAttribute("StringIO", "StringIO", "io"), - MovedAttribute("UserString", "UserString", "collections"), - MovedAttribute("xrange", "__builtin__", "builtins", "xrange", "range"), - MovedAttribute("zip", "itertools", "builtins", "izip", "zip"), - MovedAttribute("zip_longest", "itertools", "itertools", "izip_longest", "zip_longest"), - - MovedModule("builtins", "__builtin__"), - MovedModule("configparser", "ConfigParser"), - MovedModule("copyreg", "copy_reg"), - MovedModule("http_cookiejar", "cookielib", "http.cookiejar"), - MovedModule("http_cookies", "Cookie", "http.cookies"), - MovedModule("html_entities", "htmlentitydefs", "html.entities"), - MovedModule("html_parser", "HTMLParser", "html.parser"), - MovedModule("http_client", "httplib", "http.client"), - MovedModule("email_mime_multipart", "email.MIMEMultipart", "email.mime.multipart"), - MovedModule("email_mime_text", "email.MIMEText", "email.mime.text"), - MovedModule("email_mime_base", "email.MIMEBase", "email.mime.base"), - MovedModule("BaseHTTPServer", "BaseHTTPServer", "http.server"), - MovedModule("CGIHTTPServer", "CGIHTTPServer", "http.server"), - MovedModule("SimpleHTTPServer", "SimpleHTTPServer", "http.server"), - MovedModule("cPickle", "cPickle", "pickle"), - MovedModule("queue", "Queue"), - MovedModule("reprlib", "repr"), - MovedModule("socketserver", "SocketServer"), - MovedModule("tkinter", "Tkinter"), - MovedModule("tkinter_dialog", "Dialog", "tkinter.dialog"), - MovedModule("tkinter_filedialog", "FileDialog", "tkinter.filedialog"), - MovedModule("tkinter_scrolledtext", "ScrolledText", "tkinter.scrolledtext"), - MovedModule("tkinter_simpledialog", "SimpleDialog", "tkinter.simpledialog"), - MovedModule("tkinter_tix", "Tix", "tkinter.tix"), - MovedModule("tkinter_constants", "Tkconstants", "tkinter.constants"), - MovedModule("tkinter_dnd", "Tkdnd", "tkinter.dnd"), - MovedModule("tkinter_colorchooser", "tkColorChooser", - "tkinter.colorchooser"), - MovedModule("tkinter_commondialog", "tkCommonDialog", - "tkinter.commondialog"), - MovedModule("tkinter_tkfiledialog", "tkFileDialog", "tkinter.filedialog"), - MovedModule("tkinter_font", "tkFont", "tkinter.font"), - MovedModule("tkinter_messagebox", "tkMessageBox", "tkinter.messagebox"), - MovedModule("tkinter_tksimpledialog", "tkSimpleDialog", - "tkinter.simpledialog"), - MovedModule("urllib_parse", __name__ + ".moves.urllib_parse", "urllib.parse"), - MovedModule("urllib_error", __name__ + ".moves.urllib_error", "urllib.error"), - MovedModule("urllib", __name__ + ".moves.urllib", __name__ + ".moves.urllib"), - MovedModule("urllib_robotparser", "robotparser", "urllib.robotparser"), - MovedModule("winreg", "_winreg"), -] -for attr in _moved_attributes: - setattr(_MovedItems, attr.name, attr) -del attr - -moves = sys.modules[__name__ + ".moves"] = _MovedItems(__name__ + ".moves") - - - -class Module_six_moves_urllib_parse(types.ModuleType): - """Lazy loading of moved objects in six.moves.urllib_parse""" - - -_urllib_parse_moved_attributes = [ - MovedAttribute("ParseResult", "urlparse", "urllib.parse"), - MovedAttribute("parse_qs", "urlparse", "urllib.parse"), - MovedAttribute("parse_qsl", "urlparse", "urllib.parse"), - MovedAttribute("urldefrag", "urlparse", "urllib.parse"), - MovedAttribute("urljoin", "urlparse", "urllib.parse"), - MovedAttribute("urlparse", "urlparse", "urllib.parse"), - MovedAttribute("urlsplit", "urlparse", "urllib.parse"), - MovedAttribute("urlunparse", "urlparse", "urllib.parse"), - MovedAttribute("urlunsplit", "urlparse", "urllib.parse"), - MovedAttribute("quote", "urllib", "urllib.parse"), - MovedAttribute("quote_plus", "urllib", "urllib.parse"), - MovedAttribute("unquote", "urllib", "urllib.parse"), - MovedAttribute("unquote_plus", "urllib", "urllib.parse"), - MovedAttribute("urlencode", "urllib", "urllib.parse"), -] -for attr in _urllib_parse_moved_attributes: - setattr(Module_six_moves_urllib_parse, attr.name, attr) -del attr - -sys.modules[__name__ + ".moves.urllib_parse"] = Module_six_moves_urllib_parse(__name__ + ".moves.urllib_parse") -sys.modules[__name__ + ".moves.urllib.parse"] = Module_six_moves_urllib_parse(__name__ + ".moves.urllib.parse") - - -class Module_six_moves_urllib_error(types.ModuleType): - """Lazy loading of moved objects in six.moves.urllib_error""" - - -_urllib_error_moved_attributes = [ - MovedAttribute("URLError", "urllib2", "urllib.error"), - MovedAttribute("HTTPError", "urllib2", "urllib.error"), - MovedAttribute("ContentTooShortError", "urllib", "urllib.error"), -] -for attr in _urllib_error_moved_attributes: - setattr(Module_six_moves_urllib_error, attr.name, attr) -del attr - -sys.modules[__name__ + ".moves.urllib_error"] = Module_six_moves_urllib_error(__name__ + ".moves.urllib_error") -sys.modules[__name__ + ".moves.urllib.error"] = Module_six_moves_urllib_error(__name__ + ".moves.urllib.error") - - -class Module_six_moves_urllib_request(types.ModuleType): - """Lazy loading of moved objects in six.moves.urllib_request""" - - -_urllib_request_moved_attributes = [ - MovedAttribute("urlopen", "urllib2", "urllib.request"), - MovedAttribute("install_opener", "urllib2", "urllib.request"), - MovedAttribute("build_opener", "urllib2", "urllib.request"), - MovedAttribute("pathname2url", "urllib", "urllib.request"), - MovedAttribute("url2pathname", "urllib", "urllib.request"), - MovedAttribute("getproxies", "urllib", "urllib.request"), - MovedAttribute("Request", "urllib2", "urllib.request"), - MovedAttribute("OpenerDirector", "urllib2", "urllib.request"), - MovedAttribute("HTTPDefaultErrorHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPRedirectHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPCookieProcessor", "urllib2", "urllib.request"), - MovedAttribute("ProxyHandler", "urllib2", "urllib.request"), - MovedAttribute("BaseHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPPasswordMgr", "urllib2", "urllib.request"), - MovedAttribute("HTTPPasswordMgrWithDefaultRealm", "urllib2", "urllib.request"), - MovedAttribute("AbstractBasicAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPBasicAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("ProxyBasicAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("AbstractDigestAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPDigestAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("ProxyDigestAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPSHandler", "urllib2", "urllib.request"), - MovedAttribute("FileHandler", "urllib2", "urllib.request"), - MovedAttribute("FTPHandler", "urllib2", "urllib.request"), - MovedAttribute("CacheFTPHandler", "urllib2", "urllib.request"), - MovedAttribute("UnknownHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPErrorProcessor", "urllib2", "urllib.request"), - MovedAttribute("urlretrieve", "urllib", "urllib.request"), - MovedAttribute("urlcleanup", "urllib", "urllib.request"), - MovedAttribute("URLopener", "urllib", "urllib.request"), - MovedAttribute("FancyURLopener", "urllib", "urllib.request"), -] -for attr in _urllib_request_moved_attributes: - setattr(Module_six_moves_urllib_request, attr.name, attr) -del attr - -sys.modules[__name__ + ".moves.urllib_request"] = Module_six_moves_urllib_request(__name__ + ".moves.urllib_request") -sys.modules[__name__ + ".moves.urllib.request"] = Module_six_moves_urllib_request(__name__ + ".moves.urllib.request") - - -class Module_six_moves_urllib_response(types.ModuleType): - """Lazy loading of moved objects in six.moves.urllib_response""" - - -_urllib_response_moved_attributes = [ - MovedAttribute("addbase", "urllib", "urllib.response"), - MovedAttribute("addclosehook", "urllib", "urllib.response"), - MovedAttribute("addinfo", "urllib", "urllib.response"), - MovedAttribute("addinfourl", "urllib", "urllib.response"), -] -for attr in _urllib_response_moved_attributes: - setattr(Module_six_moves_urllib_response, attr.name, attr) -del attr - -sys.modules[__name__ + ".moves.urllib_response"] = Module_six_moves_urllib_response(__name__ + ".moves.urllib_response") -sys.modules[__name__ + ".moves.urllib.response"] = Module_six_moves_urllib_response(__name__ + ".moves.urllib.response") - - -class Module_six_moves_urllib_robotparser(types.ModuleType): - """Lazy loading of moved objects in six.moves.urllib_robotparser""" - - -_urllib_robotparser_moved_attributes = [ - MovedAttribute("RobotFileParser", "robotparser", "urllib.robotparser"), -] -for attr in _urllib_robotparser_moved_attributes: - setattr(Module_six_moves_urllib_robotparser, attr.name, attr) -del attr - -sys.modules[__name__ + ".moves.urllib_robotparser"] = Module_six_moves_urllib_robotparser(__name__ + ".moves.urllib_robotparser") -sys.modules[__name__ + ".moves.urllib.robotparser"] = Module_six_moves_urllib_robotparser(__name__ + ".moves.urllib.robotparser") - - -class Module_six_moves_urllib(types.ModuleType): - """Create a six.moves.urllib namespace that resembles the Python 3 namespace""" - parse = sys.modules[__name__ + ".moves.urllib_parse"] - error = sys.modules[__name__ + ".moves.urllib_error"] - request = sys.modules[__name__ + ".moves.urllib_request"] - response = sys.modules[__name__ + ".moves.urllib_response"] - robotparser = sys.modules[__name__ + ".moves.urllib_robotparser"] - - -sys.modules[__name__ + ".moves.urllib"] = Module_six_moves_urllib(__name__ + ".moves.urllib") - - -def add_move(move): - """Add an item to six.moves.""" - setattr(_MovedItems, move.name, move) - - -def remove_move(name): - """Remove item from six.moves.""" - try: - delattr(_MovedItems, name) - except AttributeError: - try: - del moves.__dict__[name] - except KeyError: - raise AttributeError("no such move, %r" % (name,)) - - -if PY3: - _meth_func = "__func__" - _meth_self = "__self__" - - _func_closure = "__closure__" - _func_code = "__code__" - _func_defaults = "__defaults__" - _func_globals = "__globals__" - - _iterkeys = "keys" - _itervalues = "values" - _iteritems = "items" - _iterlists = "lists" -else: - _meth_func = "im_func" - _meth_self = "im_self" - - _func_closure = "func_closure" - _func_code = "func_code" - _func_defaults = "func_defaults" - _func_globals = "func_globals" - - _iterkeys = "iterkeys" - _itervalues = "itervalues" - _iteritems = "iteritems" - _iterlists = "iterlists" - - -try: - advance_iterator = next -except NameError: - def advance_iterator(it): - return it.next() -next = advance_iterator - - -try: - callable = callable -except NameError: - def callable(obj): - return any("__call__" in klass.__dict__ for klass in type(obj).__mro__) - - -if PY3: - def get_unbound_function(unbound): - return unbound - - create_bound_method = types.MethodType - - Iterator = object -else: - def get_unbound_function(unbound): - return unbound.im_func - - def create_bound_method(func, obj): - return types.MethodType(func, obj, obj.__class__) - - class Iterator(object): - - def next(self): - return type(self).__next__(self) - - callable = callable -_add_doc(get_unbound_function, - """Get the function out of a possibly unbound function""") - - -get_method_function = operator.attrgetter(_meth_func) -get_method_self = operator.attrgetter(_meth_self) -get_function_closure = operator.attrgetter(_func_closure) -get_function_code = operator.attrgetter(_func_code) -get_function_defaults = operator.attrgetter(_func_defaults) -get_function_globals = operator.attrgetter(_func_globals) - - -def iterkeys(d, **kw): - """Return an iterator over the keys of a dictionary.""" - return iter(getattr(d, _iterkeys)(**kw)) - -def itervalues(d, **kw): - """Return an iterator over the values of a dictionary.""" - return iter(getattr(d, _itervalues)(**kw)) - -def iteritems(d, **kw): - """Return an iterator over the (key, value) pairs of a dictionary.""" - return iter(getattr(d, _iteritems)(**kw)) - -def iterlists(d, **kw): - """Return an iterator over the (key, [values]) pairs of a dictionary.""" - return iter(getattr(d, _iterlists)(**kw)) - - -if PY3: - def b(s): - return s.encode("latin-1") - def u(s): - return s - unichr = chr - if sys.version_info[1] <= 1: - def int2byte(i): - return bytes((i,)) - else: - # This is about 2x faster than the implementation above on 3.2+ - int2byte = operator.methodcaller("to_bytes", 1, "big") - byte2int = operator.itemgetter(0) - indexbytes = operator.getitem - iterbytes = iter - import io - StringIO = io.StringIO - BytesIO = io.BytesIO -else: - def b(s): - return s - def u(s): - return unicode(s, "unicode_escape") - unichr = unichr - int2byte = chr - def byte2int(bs): - return ord(bs[0]) - def indexbytes(buf, i): - return ord(buf[i]) - def iterbytes(buf): - return (ord(byte) for byte in buf) - import StringIO - StringIO = BytesIO = StringIO.StringIO -_add_doc(b, """Byte literal""") -_add_doc(u, """Text literal""") - - -if PY3: - import builtins - exec_ = getattr(builtins, "exec") - - - def reraise(tp, value, tb=None): - if value.__traceback__ is not tb: - raise value.with_traceback(tb) - raise value - - - print_ = getattr(builtins, "print") - del builtins - -else: - def exec_(_code_, _globs_=None, _locs_=None): - """Execute code in a namespace.""" - if _globs_ is None: - frame = sys._getframe(1) - _globs_ = frame.f_globals - if _locs_ is None: - _locs_ = frame.f_locals - del frame - elif _locs_ is None: - _locs_ = _globs_ - exec("""exec _code_ in _globs_, _locs_""") - - - exec_("""def reraise(tp, value, tb=None): - raise tp, value, tb -""") - - - def print_(*args, **kwargs): - """The new-style print function.""" - fp = kwargs.pop("file", sys.stdout) - if fp is None: - return - def write(data): - if not isinstance(data, basestring): - data = str(data) - fp.write(data) - want_unicode = False - sep = kwargs.pop("sep", None) - if sep is not None: - if isinstance(sep, unicode): - want_unicode = True - elif not isinstance(sep, str): - raise TypeError("sep must be None or a string") - end = kwargs.pop("end", None) - if end is not None: - if isinstance(end, unicode): - want_unicode = True - elif not isinstance(end, str): - raise TypeError("end must be None or a string") - if kwargs: - raise TypeError("invalid keyword arguments to print()") - if not want_unicode: - for arg in args: - if isinstance(arg, unicode): - want_unicode = True - break - if want_unicode: - newline = unicode("\n") - space = unicode(" ") - else: - newline = "\n" - space = " " - if sep is None: - sep = space - if end is None: - end = newline - for i, arg in enumerate(args): - if i: - write(sep) - write(arg) - write(end) - -_add_doc(reraise, """Reraise an exception.""") - - -def with_metaclass(meta, *bases): - """Create a base class with a metaclass.""" - return meta("NewBase", bases, {}) - -def add_metaclass(metaclass): - """Class decorator for creating a class with a metaclass.""" - def wrapper(cls): - orig_vars = cls.__dict__.copy() - orig_vars.pop('__dict__', None) - orig_vars.pop('__weakref__', None) - for slots_var in orig_vars.get('__slots__', ()): - orig_vars.pop(slots_var) - return metaclass(cls.__name__, cls.__bases__, orig_vars) - return wrapper - -def getexcobj(): - return sys.exc_info()[1] - -if PY3: - xrange = range -else: - xrange = xrange - -if PY3: - def keys(dictObj): - return list(dictObj.keys()) - def values(dictObj): - return list(dictObj.values()) - def nextiter(container): - return next(container) -else: - def keys(dictObj): - return dictObj.keys() - def values(dictObj): - return dictObj.values() - def nextiter(container): - return container.next() - -if PY3: - def isstring(obj): - return isinstance(obj, str) -else: - def isstring(obj): - return isinstance(obj, (str, unicode)) - diff --git a/wx/lib/pubsub/setuparg1.py b/wx/lib/pubsub/setuparg1.py deleted file mode 100644 index dd4d4a03..00000000 --- a/wx/lib/pubsub/setuparg1.py +++ /dev/null @@ -1,47 +0,0 @@ -""" -Setup pubsub for the *arg1* message protocol. In a default pubsub installation -the default protocol is *kargs*. - -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 must not be changed (i.e., importing it after the first -``from pubsub import pub`` statement has undefined behavior). -:: - - from .. import setuparg1 - from .. import pub - -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' - -import sys -sys.stdout.write(""" - -====================================================================== - *** ATTENTION *** -This messaging protocol is deprecated. This module, and hence arg1 -messaging protocol, will be removed in v3.4 of PyPubSub. Please make -the necessary changes to your code so that it no longer requires this -module. The pypubsub documentation provides steps that may be useful -to minimize the chance of introducing bugs in your application. -====================================================================== - -""") - - -def enforceArgName(commonName): - """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. """ - policies.setMsgDataArgName(1, commonName) diff --git a/wx/lib/pubsub/setupkwargs.py b/wx/lib/pubsub/setupkwargs.py deleted file mode 100644 index d01d591f..00000000 --- a/wx/lib/pubsub/setupkwargs.py +++ /dev/null @@ -1,29 +0,0 @@ -""" -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 -is not the default messaging protocol (such as in some versions of -wxPython). - -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 - 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) diff --git a/wx/lib/pubsub/utils/__init__.py b/wx/lib/pubsub/utils/__init__.py deleted file mode 100644 index e2ed17d3..00000000 --- a/wx/lib/pubsub/utils/__init__.py +++ /dev/null @@ -1,27 +0,0 @@ -""" -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 .topictreeprinter import printTreeDocs - -from .notification import ( - useNotifyByPubsubMessage, - useNotifyByWriteFile, - IgnoreNotificationsMixin, -) - -from .exchandling import ExcPublisher - -__all__ = [ - 'printTreeDocs', - 'useNotifyByPubsubMessage', - 'useNotifyByWriteFile', - 'IgnoreNotificationsMixin', - 'ExcPublisher' - ] \ No newline at end of file diff --git a/wx/lib/pubsub/utils/exchandling.py b/wx/lib/pubsub/utils/exchandling.py deleted file mode 100644 index 101fb7e8..00000000 --- a/wx/lib/pubsub/utils/exchandling.py +++ /dev/null @@ -1,100 +0,0 @@ -""" -Some utility classes for exception handling of exceptions raised -within listeners: - -- TracebackInfo: convenient way of getting stack trace of latest - exception raised. The handler can create the instance to retrieve - the stack trace and then log it, present it to user, etc. -- ExcPublisher: example handler that publishes a message containing - traceback info - -:copyright: Copyright since 2006 by Oliver Schoenborn, all rights reserved. -:license: BSD, see LICENSE_BSD_Simple.txt for details. - -""" - - -import sys, traceback - -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 - references (see docs for sys.exc_info()) which keeps - listeners alive even after the application is no longer - referring to them. - - Instances of this object are given to listeners of the - 'uncaughtExcInListener' topic as the excTraceback kwarg. - The instance calls sys.exc_info() to get the traceback - info but keeps only the following info: - - * self.ExcClass: the class of exception that was raised and not caught - * self.excArg: the argument given to exception when raised - * 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] - self.excArg = tmpInfo[1] - # for the traceback, skip the first 3 entries, since they relate to - # implementation details for pubsub. - self.traceback = traceback.extract_tb(tmpInfo[2])[3:] - # help avoid circular refs - del tmpInfo - - def getFormattedList(self): - """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.""" - return ''.join(self.getFormattedList()) - - def __str__(self): - return self.getFormattedString() - - -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(). - Otherwise, caller must call init() after pubsub imported. See - 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.""" - obj = topicMgr.getOrCreateTopic(self.topicUncaughtExc) - obj.setDescription('generated when a listener raises an exception') - obj.setMsgArgSpec( dict( - listenerStr = 'string representation of listener', - excTraceback = 'instance of TracebackInfo containing exception info')) - 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. """ - tbInfo = TracebackInfo() - self.__topicObj.publish(listenerStr=listenerID, excTraceback=tbInfo) - - diff --git a/wx/lib/pubsub/utils/misc.py b/wx/lib/pubsub/utils/misc.py deleted file mode 100644 index 6315a6d8..00000000 --- a/wx/lib/pubsub/utils/misc.py +++ /dev/null @@ -1,100 +0,0 @@ -""" -Provides useful functions and classes. Most useful are probably -printTreeDocs and printTreeSpec. - -:copyright: Copyright since 2006 by Oliver Schoenborn, all rights reserved. -:license: BSD, see LICENSE_BSD_Simple.txt for details. -""" - -import sys -from .. import py2and3 - -__all__ = ('printImported', 'StructMsg', 'Callback', 'Enum' ) - - -def printImported(): - """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 - self.data = Data() - self.data.__dict__.update(kwargs) - - -class Callback: - """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: - ....def __init__(self): self.a = func - then doing - >>> boo=A(); boo.a() - 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): - return self.__callable(*args, **kwargs) - - -class Enum: - """Used only internally. Represent one value out of an enumeration - set. It is meant to be used as:: - - class YourAllowedValues: - enum1 = Enum() - # or: - enum2 = Enum(value) - # or: - enum3 = Enum(value, 'descriptionLine1') - # or: - enum3 = Enum(None, 'descriptionLine1', 'descriptionLine2', ...) - - val = YourAllowedValues.enum1 - ... - if val is YourAllowedValues.enum1: - ... - """ - nextValue = 0 - values = set() - - def __init__(self, value=None, *desc): - """Use value if given, otherwise use next integer.""" - self.desc = '\n'.join(desc) - if value is None: - assert Enum.nextValue not in Enum.values - self.value = Enum.nextValue - Enum.values.add(self.value) - - Enum.nextValue += 1 - # check that we haven't run out of integers! - if Enum.nextValue == 0: - raise RuntimeError('Ran out of enumeration values?') - - else: - try: - value + Enum.nextValue - raise ValueError('Not allowed to assign integer to enumerations') - except TypeError: - pass - self.value = value - if self.value not in Enum.values: - Enum.values.add(self.value) - - diff --git a/wx/lib/pubsub/utils/notification.py b/wx/lib/pubsub/utils/notification.py deleted file mode 100644 index dd435d65..00000000 --- a/wx/lib/pubsub/utils/notification.py +++ /dev/null @@ -1,331 +0,0 @@ -""" -Provide an interface class for handling pubsub notification messages, -and an example class (though very useful in practice) showing how to -use it. - -Notification messages are generated by pubsub - -- if a handler has been configured via pub.addNotificationHandler() -- when pubsub does certain tasks, such as when a listener subscribes to - or unsubscribes from a topic - -Derive from this class to handle notification events from -various parts of pubsub. E.g. when a listener subscribes, -unsubscribes, or dies, a notification handler, if you -specified one via pub.addNotificationHandler(), is given the -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 - def notifyUnsubscribe(self, pubListener, topicObj): - pass - def notifyDeadListener(self, pubListener, topicObj): - pass - def notifySend(self, stage, topicObj, pubListener=None): - pass - - def notifyNewTopic(self, topicObj, description, required, argsDocs): - pass - def notifyDelTopic(self, topicName): - pass - - -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 - defaultPrefix as prefix for each line output, unless prefix - specified. """ - self.__pre = prefix or self.defaultPrefix - - if fileObj is None: - import sys - self.__fileObj = sys.stdout - else: - self.__fileObj = fileObj - - def changeFile(self, fileObj): - self.__fileObj = fileObj - - def notifySubscribe(self, pubListener, topicObj, newSub): - if newSub: - msg = '%s Subscribed listener "%s" to topic "%s"\n' - else: - msg = '%s Subscription of "%s" to topic "%s" redundant\n' - msg = msg % (self.__pre, pubListener, topicObj.getName()) - self.__fileObj.write(msg) - - def notifyUnsubscribe(self, pubListener, topicObj): - msg = '%s Unsubscribed listener "%s" from topic "%s"\n' - msg = msg % (self.__pre, pubListener, topicObj.getName()) - self.__fileObj.write(msg) - - def notifyDeadListener(self, pubListener, topicObj): - msg = '%s Listener "%s" of Topic "%s" has died\n' \ - % (self.__pre, pubListener, topicObj.getName()) - # a bug apparently: sometimes on exit, the stream gets closed before - # and leads to a TypeError involving NoneType - self.__fileObj.write(msg) - - def notifySend(self, stage, topicObj, pubListener=None): - if stage == 'in': - msg = '%s Sending message of topic "%s" to listener %s\n' % (self.__pre, topicObj.getName(), pubListener) - elif stage == 'pre': - msg = '%s Start sending message of topic "%s"\n' % (self.__pre, topicObj.getName()) - else: - msg = '%s Done sending message of topic "%s"\n' % (self.__pre, topicObj.getName()) - self.__fileObj.write(msg) - - def notifyNewTopic(self, topicObj, description, required, argsDocs): - msg = '%s New topic "%s" created\n' % (self.__pre, topicObj.getName()) - self.__fileObj.write(msg) - - def notifyDelTopic(self, topicName): - msg = '%s Topic "%s" destroyed\n' % (self.__pre, topicName) - self.__fileObj.write(msg) - - -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. - - Use it by calling:: - - import pubsub.utils - pubsub.utils.useNotifyByPubsubMessage() - ... - pub.setNotificationFlags(...) # optional - - E.g. whenever a listener is unsubscribed, a 'pubsub.unsubscribe' - 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' - - topics = dict( - send = '%s.sendMessage' % topicRoot, - subscribe = '%s.subscribe' % topicRoot, - unsubscribe = '%s.unsubscribe' % topicRoot, - newTopic = '%s.newTopic' % topicRoot, - delTopic = '%s.delTopic' % topicRoot, - deadListener = '%s.deadListener' % topicRoot) - - def __init__(self, topicMgr=None): - self._pubTopic = None - self.__sending = False # used to guard against infinite loop - if topicMgr is not None: - 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.""" - # see if the special topics have already been defined - try: - topicMgr.getTopic(self.topicRoot) - - except ValueError: - # no, so create them - self._pubTopic = topicMgr.getOrCreateTopic(self.topicRoot) - self._pubTopic.setDescription('root of all pubsub-specific topics') - - _createTopics(self.topics, topicMgr) - - def notifySubscribe(self, pubListener, topicObj, newSub): - if (self._pubTopic is None) or self.__sending: - return - - pubTopic = self._pubTopic.getSubtopic('subscribe') - if topicObj is not pubTopic: - kwargs = dict(listener=pubListener, topic=topicObj, newSub=newSub) - self.__doNotification(pubTopic, kwargs) - - def notifyUnsubscribe(self, pubListener, topicObj): - if (self._pubTopic is None) or self.__sending: - return - - pubTopic = self._pubTopic.getSubtopic('unsubscribe') - if topicObj is not pubTopic: - kwargs = dict( - topic = topicObj, - listenerRaw = pubListener.getCallable(), - listener = pubListener) - self.__doNotification(pubTopic, kwargs) - - def notifyDeadListener(self, pubListener, topicObj): - if (self._pubTopic is None) or self.__sending: - return - - pubTopic = self._pubTopic.getSubtopic('deadListener') - kwargs = dict(topic=topicObj, listener=pubListener) - self.__doNotification(pubTopic, kwargs) - - def notifySend(self, stage, topicObj, pubListener=None): - """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. """ - if (self._pubTopic is None) or self.__sending: - return - - sendMsgTopic = self._pubTopic.getSubtopic('sendMessage') - if stage == 'pre' and (topicObj is sendMsgTopic): - msg = 'Not allowed to send messages of topic %s' % topicObj.getName() - raise ValueError(msg) - - self.__doNotification(sendMsgTopic, dict(topic=topicObj, stage=stage)) - - def notifyNewTopic(self, topicObj, desc, required, argsDocs): - if (self._pubTopic is None) or self.__sending: - return - - pubTopic = self._pubTopic.getSubtopic('newTopic') - kwargs = dict(topic=topicObj, description=desc, required=required, args=argsDocs) - self.__doNotification(pubTopic, kwargs) - - def notifyDelTopic(self, topicName): - if (self._pubTopic is None) or self.__sending: - return - - pubTopic = self._pubTopic.getSubtopic('delTopic') - self.__doNotification(pubTopic, dict(name=topicName) ) - - def __doNotification(self, pubTopic, kwargs): - self.__sending = True - try: - pubTopic.publish( **kwargs ) - finally: - self.__sending = False - - -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) - topic.setMsgArgSpec(argsDocs, _required) - - newTopic( - _name = topicMap['subscribe'], - _desc = 'whenever a listener is subscribed to a topic', - topic = 'topic that listener has subscribed to', - listener = 'instance of pub.Listener containing listener', - newSub = 'false if listener was already subscribed, true otherwise') - - newTopic( - _name = topicMap['unsubscribe'], - _desc = 'whenever a listener is unsubscribed from a topic', - topic = 'instance of Topic that listener has been unsubscribed from', - listener = 'instance of pub.Listener unsubscribed; None if listener not found', - listenerRaw = 'listener unsubscribed') - - newTopic( - _name = topicMap['send'], - _desc = 'sent at beginning and end of sendMessage()', - topic = 'instance of topic for message being sent', - stage = 'stage of send operation: "pre" or "post" or "in"', - listener = 'which listener being sent to') - - newTopic( - _name = topicMap['newTopic'], - _desc = 'whenever a new topic is defined', - topic = 'instance of Topic created', - description = 'description of topic (use)', - args = 'the argument names/descriptions for arguments that listeners must accept', - required = 'which args are required (all others are optional)') - - newTopic( - _name = topicMap['delTopic'], - _desc = 'whenever a topic is deleted', - name = 'full name of the Topic instance that was destroyed') - - newTopic( - _name = topicMap['deadListener'], - _desc = 'whenever a listener dies without having unsubscribed', - topic = 'instance of Topic that listener was subscribed to', - listener = 'instance of pub.Listener containing dead listener') - - -def useNotifyByPubsubMessage(publisher=None, all=True, **kwargs): - """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. - - The 'all' and kwargs args are the same as pubsub's setNotificationFlags(), - except that 'all' defaults to True. - - The publisher is rarely needed: - - * The publisher must be specfied if pubsub is not installed - on the system search path (ie from pubsub import ... would fail or - import wrong pubsub -- such as if pubsub is within wxPython's - wx.lib package). Then pbuModule is the pub module to use:: - - from wx.lib.pubsub import pub - from wx.lib.pubsub.utils import notification - notification.useNotifyByPubsubMessage() - - """ - if publisher is None: - from .. import pub - publisher = pub.getDefaultPublisher() - topicMgr = publisher.getTopicMgr() - notifHandler = NotifyByPubsubMessage( topicMgr ) - - publisher.addNotificationHandler(notifHandler) - publisher.setNotificationFlags(all=all, **kwargs) - - -def useNotifyByWriteFile(fileObj=None, prefix=None, - publisher=None, all=True, **kwargs): - """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. - - The first two arguments are the same as those of NotifyByWriteFile - 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)""" - notifHandler = NotifyByWriteFile(fileObj, prefix) - - if publisher is None: - from .. import pub - publisher = pub.getDefaultPublisher() - publisher.addNotificationHandler(notifHandler) - publisher.setNotificationFlags(all=all, **kwargs) - - diff --git a/wx/lib/pubsub/utils/topictreeprinter.py b/wx/lib/pubsub/utils/topictreeprinter.py deleted file mode 100644 index 1ff673ab..00000000 --- a/wx/lib/pubsub/utils/topictreeprinter.py +++ /dev/null @@ -1,195 +0,0 @@ -""" -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 - -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 - printed is specified via the 'extra' kwarg. Its value must be a - list of characters, the order determines output order: - - D: print description of topic - - a: print kwarg names only - - A: print topic kwargs and their description - - L: print listeners currently subscribed to topic - - E.g. TopicTreePrinter(extra='LaDA') would print, for each topic, - the list of subscribed listeners, the topic's list of kwargs, the - topic description, and the description for each kwarg, - - >>> Topic "delTopic" - >> Listeners: - > listener1_2880 (from yourModule) - > listener2_3450 (from yourModule) - >> Names of Message arguments: - > arg1 - > arg2 - >> Description: whenever a topic is deleted - >> Descriptions of Message arguments: - > 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 - 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). """ - self.__contentMeth = dict( - D = self.__printTopicDescription, - A = self.__printTopicArgsAll, - a = self.__printTopicArgNames, - L = self.__printTopicListeners) - assert self.allowedExtras == set(self.__contentMeth.keys()) - import sys - self.__destination = fileObj or sys.stdout - self.__output = [] - - self.__content = extra or '' - unknownSel = set(self.__content) - self.allowedExtras - if unknownSel: - msg = 'These extra chars not known: %s' % ','.join(unknownSel) - raise ValueError(msg) - - self.__width = width - self.__wrapper = TextWrapper(width) - self.__indent = 0 - self.__indentStep = indentStep - self.__topicsBullet = bulletTopic - self.__topicItemsBullet = bulletTopicItem - self.__topicArgsBullet = bulletTopicArg - - def getOutput(self): - return '\n'.join( self.__output ) - - def _doneTraversal(self): - if self.__destination is not None: - self.__destination.write(self.getOutput()) - - def _onTopic(self, topicObj): - """This gets called for each topic. Print as per specified content.""" - - # topic name - self.__wrapper.width = self.__width - indent = self.__indent - if topicObj.isAll(): - topicName = self.ALL_TOPICS_NAME - else: - topicName = topicObj.getNodeName() - head = '%s Topic "%s"' % (self.__topicsBullet, topicName) - self.__output.append( self.__formatDefn(indent, head) ) - indent += self.__indentStep - - # each extra content (assume constructor verified that chars are valid) - for item in self.__content: - function = self.__contentMeth[item] - function(indent, topicObj) - - def _startChildren(self): - """Increase the indent""" - self.__indent += self.__indentStep - - def _endChildren(self): - """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, - has item name, and an optional definition separated from - item by sep. """ - if defn: - prefix = '%s%s%s' % (' '*indent, item, sep) - self.__wrapper.initial_indent = prefix - self.__wrapper.subsequent_indent = ' '*(indent+self.__indentStep) - return self.__wrapper.fill(defn) - else: - return '%s%s' % (' '*indent, item) - - def __printTopicDescription(self, indent, topicObj): - # topic description - defn = '%s Description' % self.__topicItemsBullet - self.__output.append( - self.__formatDefn(indent, defn, topicObj.getDescription()) ) - - def __printTopicArgsAll(self, indent, topicObj, desc=True): - # topic kwargs - args = topicObj.getArgDescriptions() - if args: - #required, optional, complete = topicObj.getArgs() - headName = 'Names of Message arguments:' - if desc: - headName = 'Descriptions of message arguments:' - head = '%s %s' % (self.__topicItemsBullet, headName) - self.__output.append( self.__formatDefn(indent, head) ) - tmpIndent = indent + self.__indentStep - required = topicObj.getArgs()[0] - for key, arg in args.items(): # iter in 3, list in 2 ok - if not desc: - arg = '' - elif key in required: - arg = '(required) %s' % arg - msg = '%s %s' % (self.__topicArgsBullet,key) - self.__output.append( self.__formatDefn(tmpIndent, msg, arg) ) - - def __printTopicArgNames(self, indent, topicObj): - self.__printTopicArgsAll(indent, topicObj, False) - - def __printTopicListeners(self, indent, topicObj): - if topicObj.hasListeners(): - item = '%s Listeners:' % self.__topicItemsBullet - self.__output.append( self.__formatDefn(indent, item) ) - tmpIndent = indent + self.__indentStep - for listener in topicObj.getListenersIter(): - item = '%s %s (from %s)' % (self.__topicArgsBullet, listener.name(), listener.module()) - self.__output.append( self.__formatDefn(tmpIndent, item) ) - - -def printTreeDocs(rootTopic=None, topicMgr=None, **kwargs): - """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. - - Printing the tree docs would normally involve this:: - - from pubsub import pub - from pubsub.utils.topictreeprinter import TopicTreePrinter - traverser = pub.TopicTreeTraverser( TopicTreePrinter(**kwargs) ) - traverser.traverse( pub.getDefaultTopicTreeRoot() ) - - With printTreeDocs, it looks like this:: - - from pubsub import pub - from pubsub.utils import printTreeDocs - printTreeDocs() - - 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.""" - if rootTopic is None: - if topicMgr is None: - from .. import pub - topicMgr = pub.getDefaultTopicMgr() - rootTopic = topicMgr.getRootAllTopics() - - printer = TopicTreePrinter(**kwargs) - traverser = TopicTreeTraverser(printer) - traverser.traverse(rootTopic) - - diff --git a/wx/lib/pubsub/utils/xmltopicdefnprovider.py b/wx/lib/pubsub/utils/xmltopicdefnprovider.py deleted file mode 100644 index e9f7b930..00000000 --- a/wx/lib/pubsub/utils/xmltopicdefnprovider.py +++ /dev/null @@ -1,286 +0,0 @@ -""" -Contributed by Joshua R English, adapted by Oliver Schoenborn to be -consistent with pubsub API. - -An extension for pubsub (http://pubsub.sourceforge.net) so topic tree -specification can be encoded in XML format rather than pubsub's default -Python nested class format. - -To use: - - xml = ''' - - Test showing topic hierarchy and inheritance - - Parent with a parameter and subtopics - - given name - surname - - - - This is the first child - - A nickname - - - - - ''' - -These topic definitions are loaded through an XmlTopicDefnProvider: - - pub.addTopicDefnProvider( XmlTopicDefnProvider(xml) ) - -The XmlTopicDefnProvider also accepts a filename instead of XML string: - - provider = XmlTopicDefnProvider("path/to/XMLfile.xml", TOPIC_TREE_FROM_FILE) - pub.addTopicDefnProvider( provider ) - -Topics can be exported to an XML file using the exportTopicTreeSpecXml function. -This will create a text file for the XML and return the string representation -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 -__date__ = '2013-07-27' - - -from ..core.topictreetraverser import ITopicTreeVisitor -from ..core.topicdefnprovider import ( - ITopicDefnProvider, - ArgSpecGiven, - TOPIC_TREE_FROM_STRING, - ) -from .. import py2and3 - -try: - from elementtree import ElementTree as ET -except ImportError: - try: # for Python 2.4, must use cElementTree: - from xml.etree import ElementTree as ET - except ImportError: - from cElementTree import ElementTree as ET - -__all__ = [ - 'XmlTopicDefnProvider', - 'exportTopicTreeSpecXml', - 'TOPIC_TREE_FROM_FILE' - ] - - -def _get_elem(elem): - """Assume an ETree.Element object or a string representation. - Return the ETree.Element object""" - if not ET.iselement(elem): - try: - elem = ET.fromstring(elem) - except Exception: - py2and3.print_("Value Error", elem) - raise ValueError("Cannot convert to element") - return elem - - -TOPIC_TREE_FROM_FILE = 'file' - - -class XmlTopicDefnProvider(ITopicDefnProvider): - - class XmlParserError(RuntimeError): pass - - class UnrecognizedSourceFormatError(ValueError): pass - - def __init__(self, xml, format=TOPIC_TREE_FROM_STRING): - self._topics = {} - self._treeDoc = '' - if format == TOPIC_TREE_FROM_FILE: - self._parse_tree(_get_elem(open(xml,mode="r").read())) - elif format == TOPIC_TREE_FROM_STRING: - self._parse_tree(_get_elem(xml)) - else: - raise UnrecognizedSourceFormatError() - - def _parse_tree(self, tree): - doc_node = tree.find('description') - - if doc_node is None: - self._treeDoc = "UNDOCUMENTED" - else: - self._treeDoc = ' '.join(doc_node.text.split()) - - for node in tree.findall('topic'): - self._parse_topic(node) - - def _parse_topic(self, node, parents=None, specs=None, reqlist=None): - parents = parents or [] - specs = specs or {} - reqlist = reqlist or [] - - descNode = node.find('description') - - if descNode is None: - desc = "UNDOCUMENTED" - else: - desc = ' '.join(descNode.text.split()) - - node_id = node.get('id') - if node_id is None: - raise XmlParserError("topic element must have an id attribute") - - for this in (node.findall('listenerspec/arg')): - this_id = this.get('id') - if this_id is None: - raise XmlParserError("arg element must have an id attribute") - - this_desc = this.text.strip() - this_desc = this_desc or "UNDOCUMENTED" - this_desc = ' '.join(this_desc.split()) - - specs[this_id] = this_desc - - if this.get('optional', '').lower() not in ['true', 't','yes','y']: - reqlist.append(this_id) - - defn = ArgSpecGiven(specs, tuple(reqlist)) - - parents.append(node.get('id')) - - self._topics[tuple(parents)] = desc, defn - - for subtopic in node.findall('topic'): - self._parse_topic(subtopic, parents[:], specs.copy(), reqlist[:]) - - - def getDefn(self, topicNameTuple): - return self._topics.get(topicNameTuple, (None, None)) - - def topicNames(self): - return py2and3.iterkeys(self._topics) # dict_keys iter in 3, list in 2 - - def getTreeDoc(self): - return self._treeDoc - - -class XmlVisitor(ITopicTreeVisitor): - def __init__(self, elem): - self.tree = elem - self.known_topics = [] - - def _startTraversal(self): - self.roots = [self.tree] - - def _onTopic(self, topicObj): - if topicObj.isAll(): - self.last_elem = self.tree - return - if self.roots: - this_elem = ET.SubElement(self.roots[-1], 'topic', - {'id':topicObj.getNodeName()}) - else: - this_elem = ET.Element('topic', {'id':topicObj.getNodeName()}) - req, opt = topicObj.getArgs() - req = req or () - opt = opt or () - desc_elem = ET.SubElement(this_elem, 'description') - topicDesc = topicObj.getDescription() - if topicDesc: - desc_elem.text = ' '.join(topicDesc.split()) - else: - desc_elem.text = "UNDOCUMENTED" - argDescriptions = topicObj.getArgDescriptions() - - # pubsub way of getting known_args - known_args = [] - parent = topicObj.getParent() - while parent: - if parent in self.known_topics: - p_req, p_opt = parent.getArgs() - if p_req: - known_args.extend(p_req) - if p_opt: - known_args.extend(p_opt) - parent = parent.getParent() - - # there is probably a cleaner way to do this - if req or opt: - spec = ET.SubElement(this_elem, 'listenerspec') - for arg in req: - if arg in known_args: - continue - arg_elem = ET.SubElement(spec, 'arg', {'id': arg}) - arg_elem.text = ' '.join(argDescriptions.get(arg, 'UNDOCUMENTED').split()) - for arg in opt: - if arg in known_args: - continue - arg_elem = ET.SubElement(spec, 'arg', {'id': arg, 'optional':'True'}) - arg_elem.text = ' '.join(argDescriptions.get(arg, 'UNDOCUMENTED').split()) - - self.last_elem = this_elem - self.known_topics.append(topicObj) - - def _startChildren(self): - self.roots.append(self.last_elem) - - def _endChildren(self): - self.roots.pop() - - -## http://infix.se/2007/02/06/gentlemen-indent-your-xml -def indent(elem, level=0): - i = "\n" + level*" " - if len(elem): - if not elem.text or not elem.text.strip(): - elem.text = i + " " - for e in elem: - indent(e, level+1) - if not e.tail or not e.tail.strip(): - e.tail = i + " " - if not e.tail or not e.tail.strip(): - e.tail = i - else: - if level and (not elem.tail or not elem.tail.strip()): - elem.tail = i - else: - elem.tail="\n" - - -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 - rootTopic = pub.getDefaultTopicTreeRoot() - elif py2and3.isstring(rootTopic): - from .. import pub - rootTopic = pub.getTopic(rootTopic) - - tree = ET.Element('topicdefntree') - if moduleDoc: - mod_desc = ET.SubElement(tree, 'description') - mod_desc.text = ' '.join(moduleDoc.split()) - - traverser = pub.TopicTreeTraverser(XmlVisitor(tree)) - traverser.traverse(rootTopic) - - indent(tree) - - if moduleName: - - filename = '%s.xml' % moduleName - if bak: - pub._backupIfExists(filename, bak) - - fulltree= ET.ElementTree(tree) - fulltree.write(filename, "utf-8", True) - - return ET.tostring(tree) - - - -