-
Notifications
You must be signed in to change notification settings - Fork 3
/
about.txt
182 lines (146 loc) · 8.02 KB
/
about.txt
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
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
More and more applications are coming to rely on pyevent. The problem is that pyevent itself requires compiling external modules and therefore presents a deployment hurdle. The rel module is a drop-in replacement for pyevent that completely emulates pyevent's interface, behavior and functionality on any system with Python. It can use pyevent if preferred/available, and otherwise rel will use the fastest method supported by the system. Epoll, poll, kqueue, and select are currently implemented.
### Get rel
Install rel with pip:
pip3 install rel
Current version: [0.4.9.19](https://pypi.org/project/rel/)
### Basic Structure
The listener module contains classes for handling individual events. Instances
of these classes are managed by the Registrar subclasses (defined in the registrar
module).
The core rel module contains functions for selecting and initializing a Registrar
subclass, as well as user-facing functions (such as `read()`, `write()`, `signal()`, and
`timeout()`), which utilize said instance.
### Examples
One of the most common usage patterns consists of calling a function on an interval,
stopping only once the function fails to return True. This is done with the `rel.timeout()`
function - one simple example can be found in the [admin module](https://github.com/bubbleboy14/cantools/blob/master/cantools/util/admin.py) of the [util subsystem](https://github.com/bubbleboy14/cantools/tree/master/cantools/util)
of the [cantools](https://github.com/bubbleboy14/cantools) web framework:
class Creeper(object):
def __init__(self):
self.last = None
self.diffs = []
self.start()
def signed(self, num):
return num < 0 and num or "+%s"%(num,)
def pad(self, s, target=12):
ls = len(s)
if ls < target:
return "%s%s"%(s, " " * (target - ls))
return s
def calc(self, diff):
dz = self.diffs
dz.append(diff)
if len(dz) > 10:
dz.pop(0)
dl = len(dz)
line = self.pad("diff: %s"%(self.signed(diff),))
if dl > 1:
return "%s ; %s-sec average: %s"%(line, dl, self.signed(sum(dz) / dl))
return line
def creep(self):
current = int(output("free | grep Mem | awk '{print $3}'", True))
if self.last:
log(self.calc(current - self.last))
self.last = current
return True
def start(self):
import rel
rel.signal(2, rel.abort)
rel.timeout(1, self.creep)
rel.dispatch()
def memcreep():
Creeper()
To see this in action, install cantools and type in the command line:
ctutil admin memcreep
This calls the `memcreep()` function, which instantiates a Creeper. The Creeper
constructor calls `start()`, which: 1) tells rel (via `signal()`) to `abort()` on
a KeyBoardInterrupt (operator presses CTR-C); 2) tells rel (via `timeout()`) to
call the `creep()` function every second; and 3) starts (via `dispatch()`) the
microevent engine.
The `creep()` function analyzes (via the `calc()` function) memory usage, and
returns True, which tells rel to keep calling it. The cantools [cron](https://github.com/bubbleboy14/cantools/blob/master/cantools/web/dez_server/cron.py) module
essentially functions the same way.
The cantools [util module](https://github.com/bubbleboy14/cantools/blob/master/cantools/util/__init__.py) also has a good example of adjusting engine rates:
def init_rel():
import rel
from cantools import config
if config.rel.sleep:
rel.set_sleep(config.rel.sleep)
if config.rel.turbo:
rel.set_turbo(config.rel.turbo)
Here, `set_sleep()` and `set_turbo()` are used to adjust the SLEEP_SEC and SLEEP_TURBO
values, the defaults of which may be found in the rel [registrar](https://github.com/bubbleboy14/registeredeventlistener/blob/master/rel/registrar.py) module:
SLEEP_SEC = .03
SLEEP_TURBO = 0.0001
SLEEP_TURBO is used whenever the engine is managing active write events.
More usage examples can be found throughout a rel-based asynchronous network library
called [dez](https://github.com/bubbleboy14/dez). Note that since dez originally
used pyevent, the rel functions are referenced throughout dez under the event
module, e.g. `event.dispatch()` instead of `rel.dispatch()`. This all works out due
to the dez [init file](https://github.com/bubbleboy14/dez/blob/master/dez/__init__.py) calling `rel.override()`:
import rel
rel.override()
This enables a pyevent-based application (such as dez) to instead run on rel
without the need to change anything else. However, if this isn't your use case
(converting a pyevent application), you can pretty much ignore this and use
rel directly.
Anyway, dez is full of rel example code. One concise example is the SocketDaemon
in the [server submodule](https://github.com/bubbleboy14/dez/blob/master/dez/network/server.py) of [dez.network](https://github.com/bubbleboy14/dez/tree/master/dez/network):
class SocketDaemon(object):
def __init__(self, hostname, port, cb=None, b64=False, cbargs=[], certfile=None, keyfile=None, cacerts=None):
self.log = default_get_logger("SocketDaemon")
self.hostname = hostname
self.port = port
self.sock = io.server_socket(self.port, certfile, keyfile, cacerts)
self.cb = cb
self.cbargs = cbargs
self.b64 = b64
self.secure = bool(certfile)
self.listen = event.read(self.sock, self.accept_connection)
def handshake_cb(self, sock, addr):
def cb():
conn = Connection(addr, sock, b64=self.b64)
if self.cb:
self.cb(conn, *self.cbargs)
return cb
def accept_connection(self):
try:
sock, addr = self.sock.accept()
cb = self.handshake_cb(sock, addr)
if self.secure:
io.ssl_handshake(sock, cb)
return True
except io.socket.error as e:
self.log.info("abandoning connection on socket error: %s"%(e,))
return True
cb()
return True
def start(self):
event.signal(2, event.abort)
event.dispatch()
Another short example, also in [dez.network](https://github.com/bubbleboy14/dez/tree/master/dez/network), is to be found in the [controller submodule](https://github.com/bubbleboy14/dez/blob/master/dez/network/controller.py):
class SocketController(object):
def __init__(self):
self.daemons = {}
def register_address(self, hostname, port, callback=None, cbargs=[], b64=False, daemon="socket", dclass=None):
d = self.daemons.get((hostname, port))
if d:
d.cb = callback
d.cbargs = cbargs
else:
dclass = dclass and daemon_wrapper(dclass) or heads[daemon]
d = dclass(hostname, port, callback, b64, cbargs=cbargs)
self.daemons[(hostname, port)] = d
return d
def _abort(self):
if self.onstop:
self.onstop()
event.abort()
def start(self, onstop=False):
if not self.daemons:
print("SocketController doesn't know where to listen. Use register_address(hostname, port, callback) to register server addresses.")
return
self.onstop = onstop
event.signal(2, self._abort)
event.dispatch()
Other brief examples are sprinkled throughout [dez.http](https://github.com/bubbleboy14/dez/tree/master/dez/http), including in [HTTPApplication](https://github.com/bubbleboy14/dez/blob/master/dez/http/application.py), [Shield](https://github.com/bubbleboy14/dez/blob/master/dez/http/server/shield.py), [fetch](https://github.com/bubbleboy14/dez/blob/master/dez/http/fetch.py), [inotify](https://github.com/bubbleboy14/dez/blob/master/dez/http/inotify.py), and elsewhere.