Set non-blocking mode on server sockets, for Jython's sake.
[pdm.git] / pdm / perf.py
... / ...
CommitLineData
1"""Python Daemon Management -- PERF utilities
2
3This module serves two purposes: It has a few utility classes
4for implementing PERF interfaces in common ways, and uses those
5classes to implement some standard PERF objects that can be used by
6PERF clients connecting to any PERF server.
7
8See the documentation for L{pdm.srv.perf} for a description of the
9various PERF interfaces.
10
11It contains two named PERF objects:
12
13 - sysres -- A directory containing the following objects pertaining
14 to the resource usage of the server process:
15
16 - realtime -- An attribute returning the amount of real time since
17 the PDM module was imported (which likely coincides with the
18 amount of time the server process has been running).
19
20 - sysinfo -- A directory containing the following objects pertaining
21 to the environment of the server process:
22
23 - pid -- An attribute returning the PID of the server process.
24
25 - hostname -- An attribute returning the hostname of the system.
26
27 - platform -- An attribute returning the Python build platform.
28"""
29
30import os, sys, time, socket, threading
31
32__all__ = ["attrinfo", "simpleattr", "valueattr", "eventobj",
33 "staticdir", "event", "procevent", "startevent",
34 "finishevent"]
35
36class attrinfo(object):
37 """The return value of the `attrinfo' method on `attr' objects as
38 described in L{pdm.srv.perf}.
39
40 Currently contains a single data field, `desc', which should have
41 a human-readable description of the purpose of the attribute.
42 """
43 def __init__(self, desc = None):
44 self.desc = desc
45
46class perfobj(object):
47 def __init__(self, *args, **kwargs):
48 super(perfobj, self).__init__()
49
50 def pdm_protocols(self):
51 return []
52
53class simpleattr(perfobj):
54 """An implementation of the `attr' interface, which is initialized
55 with a function, and returns whatever that function returns when
56 read.
57 """
58 def __init__(self, func, info = None, *args, **kwargs):
59 super(simpleattr, self).__init__(*args, **kwargs)
60 self.func = func
61 if info is None:
62 info = attrinfo()
63 self.info = info
64
65 def readattr(self):
66 return self.func()
67
68 def attrinfo(self):
69 return self.info
70
71 def pdm_protocols(self):
72 return super(simpleattr, self).pdm_protocols() + ["attr"]
73
74class valueattr(perfobj):
75 """An implementation of the `attr' interface, which is initialized
76 with a single value, and returns that value when read. Subsequent
77 updates to the value are reflected in subsequent reads.
78 """
79 def __init__(self, init, info = None, *args, **kwargs):
80 super(valueattr, self).__init__(*args, **kwargs)
81 self.value = init
82 if info is None:
83 info = attrinfo()
84 self.info = info
85
86 def readattr(self):
87 return self.value
88
89 def attrinfo(self):
90 return self.info
91
92 def pdm_protocols(self):
93 return super(valueattr, self).pdm_protocols() + ["attr"]
94
95class eventobj(perfobj):
96 """An implementation of the `event' interface. It keeps track of
97 subscribers, and will multiplex any event to all current
98 subscribers when submitted with the `notify' method.
99 """
100 def __init__(self, *args, **kwargs):
101 super(eventobj, self).__init__(*args, **kwargs)
102 self.subscribers = set()
103
104 def subscribe(self, cb):
105 if cb in self.subscribers:
106 raise ValueError("Already subscribed")
107 self.subscribers.add(cb)
108
109 def unsubscribe(self, cb):
110 self.subscribers.remove(cb)
111
112 def notify(self, event):
113 """Notify all subscribers with the given event object."""
114 for cb in self.subscribers:
115 try:
116 cb(event)
117 except: pass
118
119 def pdm_protocols(self):
120 return super(eventobj, self).pdm_protocols() + ["event"]
121
122class staticdir(perfobj):
123 """An implementation of the `dir' interface. Put other PERF
124 objects in it using the normal dict assignment syntax, and it will
125 return them to requesting clients.
126 """
127 def __init__(self, *args, **kwargs):
128 super(staticdir, self).__init__(*args, **kwargs)
129 self.map = {}
130
131 def __setitem__(self, name, ob):
132 self.map[name] = ob
133
134 def __delitem__(self, name):
135 del self.map[name]
136
137 def __getitem__(self, name):
138 return self.map[name]
139
140 def get(self, name, default = None):
141 return self.map.get(name, default)
142
143 def listdir(self):
144 return self.map.keys()
145
146 def lookup(self, name):
147 return self.map[name]
148
149 def pdm_protocols(self):
150 return super(staticdir, self).pdm_protocols() + ["dir"]
151
152class event(object):
153 """This class should be subclassed by all event objects sent via
154 the `event' interface. Its main utility is that it keeps track of
155 the time it was created, so that listening clients can precisely
156 measure the time between event notifications.
157
158 Subclasses should make sure to call the __init__ method if they
159 override it.
160 """
161 def __init__(self):
162 self.time = time.time()
163
164idlock = threading.Lock()
165procevid = 0
166
167def getprocid():
168 global procevid
169 idlock.acquire()
170 try:
171 ret = procevid
172 procevid += 1
173 return ret
174 finally:
175 idlock.release()
176
177class procevent(event):
178 """A subclass of the `event' class meant to group several events
179 related to the same process. Create a new process by creating (a
180 subclass of) the `startevent' class, and subsequent events in the
181 same process by passing that startevent as the `id' parameter.
182
183 It works by having `startevent' allocate a unique ID for each
184 process, and every other procevent initializing from that
185 startevent copying the ID. The data field `id' contains the ID so
186 that it can be compared by clients.
187
188 An example of such a process might be a client connection, where a
189 `startevent' is emitted when a client connects, another subclass
190 of `procevent' emitted when the client runs a command, and a
191 `finishevent' emitted when the connection is closed.
192 """
193 def __init__(self, id):
194 super(procevent, self).__init__()
195 if isinstance(id, procevent):
196 self.id = id.id
197 else:
198 self.id = id
199
200class startevent(procevent):
201 """A subclass of `procevent'. See its documentation for details."""
202 def __init__(self):
203 super(startevent, self).__init__(getprocid())
204
205class finishevent(procevent):
206 """A subclass of `procevent'. Intended to be emitted when a
207 process finishes and terminates. The `aborted' field can be used
208 to indicate whether the process completed successfully, if such a
209 distinction is meaningful. The `start' parameter should be the
210 `startevent' instance used when the process was initiated."""
211 def __init__(self, start, aborted = False):
212 super(finishevent, self).__init__(start)
213 self.aborted = aborted
214
215sysres = staticdir()
216itime = time.time()
217sysres["realtime"] = simpleattr(func = lambda: time.time() - itime)
218
219sysinfo = staticdir()
220sysinfo["pid"] = simpleattr(func = os.getpid)
221sysinfo["hostname"] = simpleattr(func = socket.gethostname)
222sysinfo["platform"] = valueattr(init = sys.platform)