Event fixes
This commit is contained in:
+65
-65
@@ -6,59 +6,61 @@
|
||||
# http://www.valuedlessons.com/2008/04/events-in-python.html
|
||||
#
|
||||
# This module is part of Axel and is released under
|
||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
||||
#
|
||||
# Source: http://pypi.python.org/pypi/axel
|
||||
# Docs: http://packages.python.org/axel
|
||||
|
||||
from couchpotato.core.helpers.variable import natcmp
|
||||
import Queue
|
||||
import hashlib
|
||||
import sys
|
||||
import threading
|
||||
import time
|
||||
|
||||
class Event(object):
|
||||
"""
|
||||
"""
|
||||
Event object inspired by C# events. Handlers can be registered and
|
||||
unregistered using += and -= operators. Execution and result are
|
||||
unregistered using += and -= operators. Execution and result are
|
||||
influenced by the arguments passed to the constructor and += method.
|
||||
|
||||
|
||||
from axel import Event
|
||||
|
||||
|
||||
event = Event()
|
||||
def on_event(*args, **kwargs):
|
||||
return (args, kwargs)
|
||||
|
||||
|
||||
event += on_event # handler registration
|
||||
print(event(10, 20, y=30))
|
||||
print(event(10, 20, y=30))
|
||||
>> ((True, ((10, 20), {'y': 30}), <function on_event at 0x00BAA270>),)
|
||||
|
||||
|
||||
event -= on_event # handler is unregistered
|
||||
print(event(10, 20, y=30))
|
||||
print(event(10, 20, y=30))
|
||||
>> None
|
||||
|
||||
|
||||
class Mouse(object):
|
||||
def __init__(self):
|
||||
self.click = Event(self)
|
||||
self.click += self.on_click # handler registration
|
||||
|
||||
|
||||
def on_click(self, sender, *args, **kwargs):
|
||||
assert isinstance(sender, Mouse), 'Wrong sender'
|
||||
return (args, kwargs)
|
||||
|
||||
|
||||
mouse = Mouse()
|
||||
print(mouse.click(10, 20))
|
||||
>> ((True, ((10, 20), {}),
|
||||
print(mouse.click(10, 20))
|
||||
>> ((True, ((10, 20), {}),
|
||||
>> <bound method Mouse.on_click of <__main__.Mouse object at 0x00B6F470>>),)
|
||||
|
||||
|
||||
mouse.click -= mouse.on_click # handler is unregistered
|
||||
print(mouse.click(10, 20))
|
||||
print(mouse.click(10, 20))
|
||||
>> None
|
||||
"""
|
||||
|
||||
def __init__(self, sender = None, asynch = False, exc_info = False,
|
||||
lock = None, threads = 3, traceback = False):
|
||||
""" Creates an event
|
||||
|
||||
""" Creates an event
|
||||
|
||||
asynch
|
||||
if True handler's are executes asynchronous
|
||||
exc_info
|
||||
@@ -66,31 +68,31 @@ class Event(object):
|
||||
lock
|
||||
threading.RLock used to synchronize execution
|
||||
sender
|
||||
event's sender. The sender is passed as the first argument to the
|
||||
event's sender. The sender is passed as the first argument to the
|
||||
handler, only if is not None. For this case the handler must have
|
||||
a placeholder in the arguments to receive the sender
|
||||
threads
|
||||
maximum number of threads that will be started
|
||||
traceback
|
||||
if True, the execution result will contain sys.exc_info()
|
||||
on error. exc_info must be also True to get the traceback
|
||||
|
||||
hash = hash(handler)
|
||||
|
||||
Handlers are stored in a dictionary that has as keys the handler's hash
|
||||
handlers = {
|
||||
if True, the execution result will contain sys.exc_info()
|
||||
on error. exc_info must be also True to get the traceback
|
||||
|
||||
hash = self.hash(handler)
|
||||
|
||||
Handlers are stored in a dictionary that has as keys the handler's hash
|
||||
handlers = {
|
||||
hash : (handler, memoize, timeout),
|
||||
hash : (handler, memoize, timeout), ...
|
||||
hash : (handler, memoize, timeout), ...
|
||||
}
|
||||
The execution result is cached using the following structure
|
||||
memoize = {
|
||||
hash : ((args, kwargs, result), (args, kwargs, result), ...),
|
||||
hash : ((args, kwargs, result), ...), ...
|
||||
}
|
||||
The execution result is cached using the following structure
|
||||
memoize = {
|
||||
hash : ((args, kwargs, result), (args, kwargs, result), ...),
|
||||
hash : ((args, kwargs, result), ...), ...
|
||||
}
|
||||
The execution result is returned as a tuple having this structure
|
||||
exec_result = (
|
||||
(True, result, handler), # on success
|
||||
(False, error_info, handler), # on error
|
||||
(False, error_info, handler), # on error
|
||||
(None, None, handler), ... # asynchronous execution
|
||||
)
|
||||
"""
|
||||
@@ -103,31 +105,34 @@ class Event(object):
|
||||
self.handlers = {}
|
||||
self.memoize = {}
|
||||
|
||||
def hash(self, handler):
|
||||
return hashlib.md5(str(handler)).hexdigest()
|
||||
|
||||
def handle(self, handler, priority = 0):
|
||||
""" Registers a handler. The handler can be transmitted together
|
||||
""" Registers a handler. The handler can be transmitted together
|
||||
with two arguments as a list or dictionary. The arguments are:
|
||||
|
||||
memoize
|
||||
|
||||
memoize
|
||||
if True, the execution result will be cached in self.memoize
|
||||
timeout
|
||||
timeout
|
||||
will allocate a predefined time interval for the execution
|
||||
|
||||
If arguments are provided as a list, they are considered to have
|
||||
this sequence: (handler, memoize, timeout)
|
||||
|
||||
|
||||
If arguments are provided as a list, they are considered to have
|
||||
this sequence: (handler, memoize, timeout)
|
||||
|
||||
Examples:
|
||||
event += handler
|
||||
event += handler
|
||||
event += (handler, True, 1.5)
|
||||
event += {'handler':handler, 'memoize':True, 'timeout':1.5}
|
||||
event += {'handler':handler, 'memoize':True, 'timeout':1.5}
|
||||
"""
|
||||
handler_, memoize, timeout = self._extract(handler)
|
||||
self.handlers['%s.%s' % (priority, hash(handler_))] = (handler_, memoize, timeout)
|
||||
self.handlers['%s.%s' % (priority, self.hash(handler_))] = (handler_, memoize, timeout)
|
||||
return self
|
||||
|
||||
def unhandle(self, handler):
|
||||
""" Unregisters a handler """
|
||||
handler_, memoize, timeout = self._extract(handler)
|
||||
key = hash(handler_)
|
||||
key = self.hash(handler_)
|
||||
if not key in self.handlers:
|
||||
raise ValueError('Handler "%s" was not found' % str(handler_))
|
||||
del self.handlers[key]
|
||||
@@ -139,6 +144,7 @@ class Event(object):
|
||||
self.result = []
|
||||
|
||||
if self.handlers:
|
||||
|
||||
max_threads = self._threads()
|
||||
|
||||
for i in range(max_threads):
|
||||
@@ -172,10 +178,8 @@ class Event(object):
|
||||
""" Executes all handlers stored in the queue """
|
||||
while True:
|
||||
try:
|
||||
handler, memoize, timeout = self.handlers[self.queue.get()]
|
||||
|
||||
if isinstance(self.lock, threading._RLock):
|
||||
self.lock.acquire() #synchronization
|
||||
h_ = self.queue.get()
|
||||
handler, memoize, timeout = self.handlers[h_]
|
||||
|
||||
try:
|
||||
r = self._memoize(memoize, timeout, handler, *args, **kwargs)
|
||||
@@ -189,14 +193,9 @@ class Event(object):
|
||||
else:
|
||||
self.error_handler(sys.exc_info())
|
||||
finally:
|
||||
if isinstance(self.lock, threading._RLock):
|
||||
self.lock.release()
|
||||
|
||||
if not self.asynchronous:
|
||||
try:
|
||||
self.queue.task_done()
|
||||
except ValueError:
|
||||
pass
|
||||
self.queue.task_done()
|
||||
|
||||
if self.queue.empty():
|
||||
raise Queue.Empty
|
||||
@@ -205,9 +204,9 @@ class Event(object):
|
||||
break
|
||||
|
||||
def _extract(self, queue_item):
|
||||
""" Extracts a handler and handler's arguments that can be provided
|
||||
as list or dictionary. If arguments are provided as list, they are
|
||||
considered to have this sequence: (handler, memoize, timeout)
|
||||
""" Extracts a handler and handler's arguments that can be provided
|
||||
as list or dictionary. If arguments are provided as list, they are
|
||||
considered to have this sequence: (handler, memoize, timeout)
|
||||
Examples:
|
||||
event += handler
|
||||
event += (handler, True, 1.5)
|
||||
@@ -234,11 +233,11 @@ class Event(object):
|
||||
return (handler, bool(memoize), float(timeout))
|
||||
|
||||
def _memoize(self, memoize, timeout, handler, *args, **kwargs):
|
||||
""" Caches the execution result of successful executions
|
||||
hash = hash(handler)
|
||||
memoize = {
|
||||
hash : ((args, kwargs, result), (args, kwargs, result), ...),
|
||||
hash : ((args, kwargs, result), ...), ...
|
||||
""" Caches the execution result of successful executions
|
||||
hash = self.hash(handler)
|
||||
memoize = {
|
||||
hash : ((args, kwargs, result), (args, kwargs, result), ...),
|
||||
hash : ((args, kwargs, result), ...), ...
|
||||
}
|
||||
"""
|
||||
if not isinstance(handler, Event) and self.sender is not None:
|
||||
@@ -247,7 +246,8 @@ class Event(object):
|
||||
|
||||
if not memoize:
|
||||
if timeout <= 0: #no time restriction
|
||||
return [True, handler(*args, **kwargs), handler]
|
||||
result = [True, handler(*args, **kwargs), handler]
|
||||
return result
|
||||
|
||||
result = self._timeout(timeout, handler, *args, **kwargs)
|
||||
if isinstance(result, tuple) and len(result) == 3:
|
||||
@@ -255,7 +255,7 @@ class Event(object):
|
||||
return [False, self._error(result), handler]
|
||||
return [True, result, handler]
|
||||
else:
|
||||
hash_ = hash(handler)
|
||||
hash_ = self.hash(handler)
|
||||
if hash_ in self.memoize:
|
||||
for args_, kwargs_, result in self.memoize[hash_]:
|
||||
if args_ == args and kwargs_ == kwargs:
|
||||
|
||||
Reference in New Issue
Block a user