summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy/event.py
blob: 5fcda0a6535558b4a87515312d25a91bd813719d (plain)
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
"""
The event system handles all events throughout the sqlalchemy
and sqlalchemy.orm packages.   

Event specifications:

:attr:`sqlalchemy.pool.Pool.events`

"""

from sqlalchemy import util

def listen(fn, identifier, target, *args):
    """Listen for events, passing to fn."""
    
    getattr(target.events, identifier).append(fn, target)

NO_RESULT = util.symbol('no_result')

class _DispatchMeta(type):
    def __init__(cls, classname, bases, dict_):
        for k in dict_:
            if k.startswith('on_'):
                setattr(cls, k, EventDescriptor(dict_[k]))
        return type.__init__(cls, classname, bases, dict_)

class Events(object):
    __metaclass__ = _DispatchMeta
    
    def __init__(self, parent_cls):
        self.parent_cls = parent_cls
    

class _ExecEvent(object):
    def exec_and_clear(self, *args, **kw):
        """Execute this event once, then clear all listeners."""
        
        self(*args, **kw)
        self[:] = []
    
    def exec_until_return(self, *args, **kw):
        """Execute listeners for this event until
        one returns a non-None value.
        
        Returns the value, or None.
        """
        
        if self:
            for fn in self:
                r = fn(*args, **kw)
                if r is not None:
                    return r
        return None
        
    def __call__(self, *args, **kw):
        """Execute this event."""
        
        if self:
            for fn in self:
                fn(*args, **kw)
    
class EventDescriptor(object):
    """Represent an event type associated with a :class:`Events` class
    as well as class-level listeners.
    
    """
    def __init__(self, fn):
        self.__name__ = fn.__name__
        self.__doc__ = fn.__doc__
        self._clslevel = []
    
    def append(self, obj, target):
        self._clslevel.append((obj, target))
    
    def __get__(self, obj, cls):
        if obj is None:
            return self
        obj.__dict__[self.__name__] = result = Listeners()
        result.extend([
            fn for fn, target in 
            self._clslevel
            if issubclass(obj.parent_cls, target)
        ])
        return result

class Listeners(_ExecEvent, list):
    """Represent a collection of listeners linked
    to an instance of :class:`Events`."""
    
    def append(self, obj, target):
        list.append(self, obj)

class dispatcher(object):
    def __init__(self, events):
        self.dispatch_cls = events
        
    def __get__(self, obj, cls):
        if obj is None:
            return self.dispatch_cls
        obj.__dict__['events'] = disp = self.dispatch_cls(cls)
        return disp