-
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathhooks.txt
More file actions
164 lines (133 loc) · 5.74 KB
/
Copy pathhooks.txt
File metadata and controls
164 lines (133 loc) · 5.74 KB
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
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
.. module:: firebird.base.hooks
:synopsis: Hook manager
####################
hooks - Hook manager
####################
Overview
========
This module provides a general framework for callbacks and "hookable" events,
implementing a variation of the publish-subscribe pattern. It allows different
parts of an application to register interest in events triggered by specific
objects or classes and execute custom code (callbacks) when those events occur.
Architecture
------------
The callback extension mechanism is based on the following:
* The `Event source` provides one or more "hookable events" that work like connection points.
The event source represents "origin of event" and is always identified by class, class
instance or name. Event sources that are identified by classes (or their instances) must
be registered along with events they provide.
* `Event` is typically linked to particular event source, but it's not mandatory and it's
possible to define global events. Event is represented as value of any type, that must
be unique in used context (particular event source or global).
Each event should be properly documented along with required signature for callback
function.
* `Event provider` is a class or function that implements the event for event source, and
asks the `.hook_manager` for list of event consumers (callbacks) registered for particular
event and source.
* `Event consumer` is a function or class method that implements the callback for particular
event. The callback must be registered in `.hook_manager` before it could be called by
event providers.
The architecture supports multiple usage strategies:
* If event provider uses class instance to identify the event source, it's possible to
register callbacks to all instances (by registering to class), or particular instance(s).
* It's possible to register callback to particular instance by name, if instance is associated
with name by `register_name()` function.
* It's possible to register callback to `.ANY` event from particular source, or particular
event from `.ANY` source, or even to `.ANY` event from `.ANY` source.
Example
-------
.. code-block:: python
from __future__ import annotations
from enum import Enum, auto
from firebird.base.types import *
from firebird.base.hooks import hook_manager
class MyEvents(Enum):
"Sample definition of events"
CREATE = auto()
ACTION = auto()
class MyHookable:
"Example of hookable class, i.e. a class that calls hooks registered for events."
def __init__(self, name: str):
self.name: str = name
for hook in hook_manager.get_callbacks(MyEvents.CREATE, self):
try:
hook(self, MyEvents.CREATE)
except Exception as e:
print(f"{self.name}.CREATE hook call outcome: ERROR ({e.args[0]})")
else:
print(f"{self.name}.CREATE hook call outcome: OK")
def action(self):
print(f"{self.name}.ACTION!")
for hook in hook_manager.get_callbacks(MyEvents.ACTION, self):
try:
hook(self, MyEvents.ACTION)
except Exception as e:
print(f"{self.name}.ACTION hook call outcome: ERROR ({e.args[0]})")
else:
print(f"{self.name}.ACTION hook call outcome: OK")
class MyHook:
"Example of hook implementation"
def __init__(self, name: str):
self.name: str = name
def callback(self, subject: MyHookable, event: MyEvents):
print(f"Hook {self.name} event {event.name} called by {subject.name}")
def err_callback(self, subject: MyHookable, event: MyEvents):
self.callback(subject, event)
raise Exception("Error in hook")
# Example code that installs and uses hooks
hook_manager.register_class(MyHookable, MyEvents)
hook_A: MyHook = MyHook('Hook-A')
hook_B: MyHook = MyHook('Hook-B')
hook_C: MyHook = MyHook('Hook-C')
print("Install hooks")
hook_manager.add_hook(MyEvents.CREATE, MyHookable, hook_A.callback)
hook_manager.add_hook(MyEvents.CREATE, MyHookable, hook_B.err_callback)
hook_manager.add_hook(MyEvents.ACTION, MyHookable, hook_C.callback)
print("Create event sources, emits CREATE")
src_A: MyHookable = MyHookable('Source-A')
src_B: MyHookable = MyHookable('Source-B')
print("Install instance hooks")
hook_manager.add_hook(MyEvents.ACTION, src_A, hook_A.callback)
hook_manager.add_hook(MyEvents.ACTION, src_B, hook_B.callback)
print("And action!")
src_A.action()
src_B.action()
Output from sample code::
Install hooks
Create event sources, emits CREATE
Hook Hook-A event CREATE called by Source-A
Source-A.CREATE hook call outcome: OK
Hook Hook-B event CREATE called by Source-A
Source-A.CREATE hook call outcome: ERROR (Error in hook)
Hook Hook-A event CREATE called by Source-B
Source-B.CREATE hook call outcome: OK
Hook Hook-B event CREATE called by Source-B
Source-B.CREATE hook call outcome: ERROR (Error in hook)
Install instance hooks
And action!
Source-A.ACTION!
Hook Hook-A event ACTION called by Source-A
Source-A.ACTION hook call outcome: OK
Hook Hook-C event ACTION called by Source-A
Source-A.ACTION hook call outcome: OK
Source-B.ACTION!
Hook Hook-B event ACTION called by Source-B
Source-B.ACTION hook call outcome: OK
Hook Hook-C event ACTION called by Source-B
Source-B.ACTION hook call outcome: OK
Functions
=========
.. autofunction:: register_class
.. autofunction:: register_name
.. autofunction:: add_hook
.. autofunction:: get_callbacks
Classes
=======
.. autoclass:: HookManager
Globals
=======
.. autodata:: hook_manager
:no-value:
Dataclasses
===========
.. autoclass:: Hook