aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/python/Twisted/py2/twisted/words/xish/utility.py
diff options
context:
space:
mode:
authorshmel1k <shmel1k@ydb.tech>2023-11-26 18:16:14 +0300
committershmel1k <shmel1k@ydb.tech>2023-11-26 18:43:30 +0300
commitb8cf9e88f4c5c64d9406af533d8948deb050d695 (patch)
tree218eb61fb3c3b96ec08b4d8cdfef383104a87d63 /contrib/python/Twisted/py2/twisted/words/xish/utility.py
parent523f645a83a0ec97a0332dbc3863bb354c92a328 (diff)
downloadydb-b8cf9e88f4c5c64d9406af533d8948deb050d695.tar.gz
add kikimr_configure
Diffstat (limited to 'contrib/python/Twisted/py2/twisted/words/xish/utility.py')
-rw-r--r--contrib/python/Twisted/py2/twisted/words/xish/utility.py375
1 files changed, 375 insertions, 0 deletions
diff --git a/contrib/python/Twisted/py2/twisted/words/xish/utility.py b/contrib/python/Twisted/py2/twisted/words/xish/utility.py
new file mode 100644
index 0000000000..6f8a11527d
--- /dev/null
+++ b/contrib/python/Twisted/py2/twisted/words/xish/utility.py
@@ -0,0 +1,375 @@
+# -*- test-case-name: twisted.words.test.test_xishutil -*-
+#
+# Copyright (c) Twisted Matrix Laboratories.
+# See LICENSE for details.
+
+"""
+Event Dispatching and Callback utilities.
+"""
+
+from __future__ import absolute_import, division
+
+from twisted.python import log
+from twisted.python.compat import iteritems
+from twisted.words.xish import xpath
+
+class _MethodWrapper(object):
+ """
+ Internal class for tracking method calls.
+ """
+ def __init__(self, method, *args, **kwargs):
+ self.method = method
+ self.args = args
+ self.kwargs = kwargs
+
+
+ def __call__(self, *args, **kwargs):
+ nargs = self.args + args
+ nkwargs = self.kwargs.copy()
+ nkwargs.update(kwargs)
+ self.method(*nargs, **nkwargs)
+
+
+
+class CallbackList:
+ """
+ Container for callbacks.
+
+ Event queries are linked to lists of callables. When a matching event
+ occurs, these callables are called in sequence. One-time callbacks
+ are removed from the list after the first time the event was triggered.
+
+ Arguments to callbacks are split spread across two sets. The first set,
+ callback specific, is passed to C{addCallback} and is used for all
+ subsequent event triggers. The second set is passed to C{callback} and is
+ event specific. Positional arguments in the second set come after the
+ positional arguments of the first set. Keyword arguments in the second set
+ override those in the first set.
+
+ @ivar callbacks: The registered callbacks as mapping from the callable to a
+ tuple of a wrapper for that callable that keeps the
+ callback specific arguments and a boolean that signifies
+ if it is to be called only once.
+ @type callbacks: C{dict}
+ """
+
+ def __init__(self):
+ self.callbacks = {}
+
+
+ def addCallback(self, onetime, method, *args, **kwargs):
+ """
+ Add callback.
+
+ The arguments passed are used as callback specific arguments.
+
+ @param onetime: If C{True}, this callback is called at most once.
+ @type onetime: C{bool}
+ @param method: The callback callable to be added.
+ @param args: Positional arguments to the callable.
+ @type args: C{list}
+ @param kwargs: Keyword arguments to the callable.
+ @type kwargs: C{dict}
+ """
+
+ if not method in self.callbacks:
+ self.callbacks[method] = (_MethodWrapper(method, *args, **kwargs),
+ onetime)
+
+
+ def removeCallback(self, method):
+ """
+ Remove callback.
+
+ @param method: The callable to be removed.
+ """
+
+ if method in self.callbacks:
+ del self.callbacks[method]
+
+
+ def callback(self, *args, **kwargs):
+ """
+ Call all registered callbacks.
+
+ The passed arguments are event specific and augment and override
+ the callback specific arguments as described above.
+
+ @note: Exceptions raised by callbacks are trapped and logged. They will
+ not propagate up to make sure other callbacks will still be
+ called, and the event dispatching always succeeds.
+
+ @param args: Positional arguments to the callable.
+ @type args: C{list}
+ @param kwargs: Keyword arguments to the callable.
+ @type kwargs: C{dict}
+ """
+
+ for key, (methodwrapper, onetime) in list(self.callbacks.items()):
+ try:
+ methodwrapper(*args, **kwargs)
+ except:
+ log.err()
+
+ if onetime:
+ del self.callbacks[key]
+
+
+ def isEmpty(self):
+ """
+ Return if list of registered callbacks is empty.
+
+ @rtype: C{bool}
+ """
+
+ return len(self.callbacks) == 0
+
+
+
+class EventDispatcher:
+ """
+ Event dispatching service.
+
+ The C{EventDispatcher} allows observers to be registered for certain events
+ that are dispatched. There are two types of events: XPath events and Named
+ events.
+
+ Every dispatch is triggered by calling L{dispatch} with a data object and,
+ for named events, the name of the event.
+
+ When an XPath type event is dispatched, the associated object is assumed to
+ be an L{Element<twisted.words.xish.domish.Element>} instance, which is
+ matched against all registered XPath queries. For every match, the
+ respective observer will be called with the data object.
+
+ A named event will simply call each registered observer for that particular
+ event name, with the data object. Unlike XPath type events, the data object
+ is not restricted to L{Element<twisted.words.xish.domish.Element>}, but can
+ be anything.
+
+ When registering observers, the event that is to be observed is specified
+ using an L{xpath.XPathQuery} instance or a string. In the latter case, the
+ string can also contain the string representation of an XPath expression.
+ To distinguish these from named events, each named event should start with
+ a special prefix that is stored in C{self.prefix}. It defaults to
+ C{//event/}.
+
+ Observers registered using L{addObserver} are persistent: after the
+ observer has been triggered by a dispatch, it remains registered for a
+ possible next dispatch. If instead L{addOnetimeObserver} was used to
+ observe an event, the observer is removed from the list of observers after
+ the first observed event.
+
+ Observers can also be prioritized, by providing an optional C{priority}
+ parameter to the L{addObserver} and L{addOnetimeObserver} methods. Higher
+ priority observers are then called before lower priority observers.
+
+ Finally, observers can be unregistered by using L{removeObserver}.
+ """
+
+ def __init__(self, eventprefix="//event/"):
+ self.prefix = eventprefix
+ self._eventObservers = {}
+ self._xpathObservers = {}
+ self._dispatchDepth = 0 # Flag indicating levels of dispatching
+ # in progress
+ self._updateQueue = [] # Queued updates for observer ops
+
+
+ def _getEventAndObservers(self, event):
+ if isinstance(event, xpath.XPathQuery):
+ # Treat as xpath
+ observers = self._xpathObservers
+ else:
+ if self.prefix == event[:len(self.prefix)]:
+ # Treat as event
+ observers = self._eventObservers
+ else:
+ # Treat as xpath
+ event = xpath.internQuery(event)
+ observers = self._xpathObservers
+
+ return event, observers
+
+
+ def addOnetimeObserver(self, event, observerfn, priority=0, *args, **kwargs):
+ """
+ Register a one-time observer for an event.
+
+ Like L{addObserver}, but is only triggered at most once. See there
+ for a description of the parameters.
+ """
+ self._addObserver(True, event, observerfn, priority, *args, **kwargs)
+
+
+ def addObserver(self, event, observerfn, priority=0, *args, **kwargs):
+ """
+ Register an observer for an event.
+
+ Each observer will be registered with a certain priority. Higher
+ priority observers get called before lower priority observers.
+
+ @param event: Name or XPath query for the event to be monitored.
+ @type event: C{str} or L{xpath.XPathQuery}.
+ @param observerfn: Function to be called when the specified event
+ has been triggered. This callable takes
+ one parameter: the data object that triggered
+ the event. When specified, the C{*args} and
+ C{**kwargs} parameters to addObserver are being used
+ as additional parameters to the registered observer
+ callable.
+ @param priority: (Optional) priority of this observer in relation to
+ other observer that match the same event. Defaults to
+ C{0}.
+ @type priority: C{int}
+ """
+ self._addObserver(False, event, observerfn, priority, *args, **kwargs)
+
+
+ def _addObserver(self, onetime, event, observerfn, priority, *args, **kwargs):
+ # If this is happening in the middle of the dispatch, queue
+ # it up for processing after the dispatch completes
+ if self._dispatchDepth > 0:
+ self._updateQueue.append(lambda:self._addObserver(onetime, event, observerfn, priority, *args, **kwargs))
+ return
+
+ event, observers = self._getEventAndObservers(event)
+
+ if priority not in observers:
+ cbl = CallbackList()
+ observers[priority] = {event: cbl}
+ else:
+ priorityObservers = observers[priority]
+ if event not in priorityObservers:
+ cbl = CallbackList()
+ observers[priority][event] = cbl
+ else:
+ cbl = priorityObservers[event]
+
+ cbl.addCallback(onetime, observerfn, *args, **kwargs)
+
+
+ def removeObserver(self, event, observerfn):
+ """
+ Remove callable as observer for an event.
+
+ The observer callable is removed for all priority levels for the
+ specified event.
+
+ @param event: Event for which the observer callable was registered.
+ @type event: C{str} or L{xpath.XPathQuery}
+ @param observerfn: Observer callable to be unregistered.
+ """
+
+ # If this is happening in the middle of the dispatch, queue
+ # it up for processing after the dispatch completes
+ if self._dispatchDepth > 0:
+ self._updateQueue.append(lambda:self.removeObserver(event, observerfn))
+ return
+
+ event, observers = self._getEventAndObservers(event)
+
+ emptyLists = []
+ for priority, priorityObservers in iteritems(observers):
+ for query, callbacklist in iteritems(priorityObservers):
+ if event == query:
+ callbacklist.removeCallback(observerfn)
+ if callbacklist.isEmpty():
+ emptyLists.append((priority, query))
+
+ for priority, query in emptyLists:
+ del observers[priority][query]
+
+
+ def dispatch(self, obj, event=None):
+ """
+ Dispatch an event.
+
+ When C{event} is L{None}, an XPath type event is triggered, and
+ C{obj} is assumed to be an instance of
+ L{Element<twisted.words.xish.domish.Element>}. Otherwise, C{event}
+ holds the name of the named event being triggered. In the latter case,
+ C{obj} can be anything.
+
+ @param obj: The object to be dispatched.
+ @param event: Optional event name.
+ @type event: C{str}
+ """
+
+ foundTarget = False
+
+ self._dispatchDepth += 1
+
+ if event != None:
+ # Named event
+ observers = self._eventObservers
+ match = lambda query, obj: query == event
+ else:
+ # XPath event
+ observers = self._xpathObservers
+ match = lambda query, obj: query.matches(obj)
+
+ priorities = list(observers.keys())
+ priorities.sort()
+ priorities.reverse()
+
+ emptyLists = []
+ for priority in priorities:
+ for query, callbacklist in iteritems(observers[priority]):
+ if match(query, obj):
+ callbacklist.callback(obj)
+ foundTarget = True
+ if callbacklist.isEmpty():
+ emptyLists.append((priority, query))
+
+ for priority, query in emptyLists:
+ del observers[priority][query]
+
+ self._dispatchDepth -= 1
+
+ # If this is a dispatch within a dispatch, don't
+ # do anything with the updateQueue -- it needs to
+ # wait until we've back all the way out of the stack
+ if self._dispatchDepth == 0:
+ # Deal with pending update operations
+ for f in self._updateQueue:
+ f()
+ self._updateQueue = []
+
+ return foundTarget
+
+
+
+class XmlPipe(object):
+ """
+ XML stream pipe.
+
+ Connects two objects that communicate stanzas through an XML stream like
+ interface. Each of the ends of the pipe (sink and source) can be used to
+ send XML stanzas to the other side, or add observers to process XML stanzas
+ that were sent from the other side.
+
+ XML pipes are usually used in place of regular XML streams that are
+ transported over TCP. This is the reason for the use of the names source
+ and sink for both ends of the pipe. The source side corresponds with the
+ entity that initiated the TCP connection, whereas the sink corresponds with
+ the entity that accepts that connection. In this object, though, the source
+ and sink are treated equally.
+
+ Unlike Jabber
+ L{XmlStream<twisted.words.protocols.jabber.xmlstream.XmlStream>}s, the sink
+ and source objects are assumed to represent an eternal connected and
+ initialized XML stream. As such, events corresponding to connection,
+ disconnection, initialization and stream errors are not dispatched or
+ processed.
+
+ @since: 8.2
+ @ivar source: Source XML stream.
+ @ivar sink: Sink XML stream.
+ """
+
+ def __init__(self):
+ self.source = EventDispatcher()
+ self.sink = EventDispatcher()
+ self.source.send = lambda obj: self.sink.dispatch(obj)
+ self.sink.send = lambda obj: self.source.dispatch(obj)