-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathobserver.py
101 lines (83 loc) · 3.33 KB
/
observer.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
"""
========
Observer
========
An observer pattern implementation in Python based on jQuery.
:author:
Fernando Felix do Nascimento Junior
:license:
MIT License
"""
class Event(object):
"""An event that handlers can express interest in."""
def __init__(self, handler=None):
if handler:
self.on(handler)
def __call__(self, *args, **kwargs):
"""Execute all event handlers using obj.trigger() or just obj()."""
return self.trigger(*args, **kwargs)
@property
def handlers(self):
"""Return all event handlers."""
if not hasattr(self, '_handlers'): # avoid call __init__ in subclasses
self._handlers = set()
return self._handlers
def on(self, handler):
"""Attach a handler (any Python callable) for the event."""
if not hasattr(handler, '__call__'):
raise TypeError('handler is not a callable object.')
self.handlers.add(handler)
def off(self, handler):
"""Deattach a handler for the event."""
self.handlers.remove(handler)
def trigger(self, *args, **kwargs):
"""Execute the handlers with a message, if any."""
for h in self.handlers:
h(*args, **kwargs)
class Observable(object):
"""A object to be observed. It can be subdivided into many events."""
@property
def events(self):
"""Return all events of the observable."""
if not hasattr(self, '_events'):
self._events = {}
return self._events
def on(self, event, handler=None):
"""Create, add or update an event with a handler or more attached."""
if isinstance(event, str) and ' ' in event: # event is list str-based
self.on(event.split(' '), handler)
elif isinstance(event, list): # many events contains same handler
for each in event:
self.on(each, handler)
elif isinstance(event, dict): # event is a dict of <event, handler>
for key, value in event.items():
self.on(key, value)
elif isinstance(handler, list): # handler is a list of handlers
for each in handler:
self.on(event, each)
elif isinstance(handler, Event): # handler is Event object
self.events[event] = handler # add or update an event
setattr(self, event, self.events[event]) # self.event.trigger()
elif event in self.events: # add a handler to an existing event
self.events[event].on(handler)
else: # create new event with a handler attached
self.on(event, Event(handler))
def off(self, event, handler=None):
"""Remove an event or a handler from it."""
if handler:
self.events[event].off(handler)
else:
del self.events[event]
delattr(self, event)
def trigger(self, *args, **kargs):
"""
Execute all event handlers with optional arguments for the observable.
"""
event = args[0]
if isinstance(event, str) and ' ' in event:
event = event.split(' ') # split event names ...
if isinstance(event, list): # event is a list of events
for each in event:
self.events[each].trigger(*args[1:], **kargs)
else:
self.events[event].trigger(*args[1:], **kargs)