1 """Python Daemon Management -- PERF utilities
3 This module serves two purposes: It has a few utility classes
4 for implementing PERF interfaces in common ways, and uses those
5 classes to implement some standard PERF objects that can be used by
6 PERF clients connecting to any PERF server.
8 See the documentation for L{pdm.srv.perf} for a description of the
9 various PERF interfaces.
11 It contains two named PERF objects:
13 - sysres -- A directory containing the following objects pertaining
14 to the resource usage of the server process:
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).
20 - sysinfo -- A directory containing the following objects pertaining
21 to the environment of the server process:
23 - pid -- An attribute returning the PID of the server process.
25 - hostname -- An attribute returning the hostname of the system.
27 - platform -- An attribute returning the Python build platform.
30 import os, sys, time, socket, threading
32 __all__ = ["attrinfo", "simpleattr", "valueattr", "eventobj",
33 "staticdir", "event", "procevent", "startevent",
36 class attrinfo(object):
37 """The return value of the `attrinfo' method on `attr' objects as
38 described in L{pdm.srv.perf}.
40 Currently contains a single data field, `desc', which should have
41 a human-readable description of the purpose of the attribute.
43 def __init__(self, desc = None):
46 class perfobj(object):
47 def __init__(self, *args, **kwargs):
48 super(perfobj, self).__init__()
50 def pdm_protocols(self):
53 class simpleattr(perfobj):
54 """An implementation of the `attr' interface, which is initialized
55 with a function, and returns whatever that function returns when
58 def __init__(self, func, info = None, *args, **kwargs):
59 super(simpleattr, self).__init__(*args, **kwargs)
71 def pdm_protocols(self):
72 return super(simpleattr, self).pdm_protocols() + ["attr"]
74 class 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.
79 def __init__(self, init, info = None, *args, **kwargs):
80 super(valueattr, self).__init__(*args, **kwargs)
92 def pdm_protocols(self):
93 return super(valueattr, self).pdm_protocols() + ["attr"]
95 class 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.
100 def __init__(self, *args, **kwargs):
101 super(eventobj, self).__init__(*args, **kwargs)
102 self.subscribers = set()
104 def subscribe(self, cb):
105 if cb in self.subscribers:
106 raise ValueError("Already subscribed")
107 self.subscribers.add(cb)
109 def unsubscribe(self, cb):
110 self.subscribers.remove(cb)
112 def notify(self, event):
113 """Notify all subscribers with the given event object."""
114 for cb in self.subscribers:
119 def pdm_protocols(self):
120 return super(eventobj, self).pdm_protocols() + ["event"]
122 class 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.
127 def __init__(self, *args, **kwargs):
128 super(staticdir, self).__init__(*args, **kwargs)
131 def __setitem__(self, name, ob):
134 def __delitem__(self, name):
137 def __getitem__(self, name):
138 return self.map[name]
140 def get(self, name, default = None):
141 return self.map.get(name, default)
144 return self.map.keys()
146 def lookup(self, name):
147 return self.map[name]
149 def pdm_protocols(self):
150 return super(staticdir, self).pdm_protocols() + ["dir"]
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.
158 Subclasses should make sure to call the __init__ method if they
162 self.time = time.time()
164 idlock = threading.Lock()
177 class 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.
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.
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.
193 def __init__(self, id):
194 super(procevent, self).__init__()
195 if isinstance(id, procevent):
200 class startevent(procevent):
201 """A subclass of `procevent'. See its documentation for details."""
203 super(startevent, self).__init__(getprocid())
205 class 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
217 sysres["realtime"] = simpleattr(func = lambda: time.time() - itime)
219 sysinfo = staticdir()
220 sysinfo["pid"] = simpleattr(func = os.getpid)
221 sysinfo["hostname"] = simpleattr(func = socket.gethostname)
222 sysinfo["platform"] = valueattr(init = sys.platform)