Removed a few things not supported by Jython.
[pdm.git] / pdm / perf.py
CommitLineData
5463509c
FT
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
57808152 8See the documentation for L{pdm.srv.perf} for a description of the
5463509c
FT
9various PERF interfaces.
10
11It contains two named PERF objects:
12
57808152
FT
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
57808152
FT
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
57808152
FT
25 - hostname -- An attribute returning the hostname of the system.
26
27 - platform -- An attribute returning the Python build platform.
5463509c
FT
28"""
29
901a5a6f 30import os, sys, time, socket, threading
7f97a47e 31
5463509c
FT
32__all__ = ["attrinfo", "simpleattr", "valueattr", "eventobj",
33 "staticdir", "event", "procevent", "startevent",
34 "finishevent"]
35
7f97a47e 36class attrinfo(object):
5463509c 37 """The return value of the `attrinfo' method on `attr' objects as
57808152 38 described in L{pdm.srv.perf}.
5463509c
FT
39
40 Currently contains a single data field, `desc', which should have
41 a human-readable description of the purpose of the attribute.
42 """
7f97a47e
FT
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):
5463509c
FT
54 """An implementation of the `attr' interface, which is initialized
55 with a function, and returns whatever that function returns when
56 read.
57 """
7f97a47e
FT
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):
5463509c
FT
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 """
7f97a47e
FT
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
7f97a47e 95class eventobj(perfobj):
5463509c
FT
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 """
7f97a47e
FT
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):
5463509c 113 """Notify all subscribers with the given event object."""
7f97a47e
FT
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):
5463509c
FT
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 """
7f97a47e
FT
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
bcb622d6 152class event(object):
5463509c
FT
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 """
bcb622d6
FT
161 def __init__(self):
162 self.time = time.time()
163
87b07b36
FT
164idlock = threading.Lock()
165procevid = 0
709446f6
FT
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):
5463509c
FT
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 """
709446f6
FT
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):
5463509c 201 """A subclass of `procevent'. See its documentation for details."""
87b07b36 202 def __init__(self):
709446f6
FT
203 super(startevent, self).__init__(getprocid())
204
205class finishevent(procevent):
5463509c
FT
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."""
0f1f2a1b 211 def __init__(self, start, aborted = False):
709446f6 212 super(finishevent, self).__init__(start)
87b07b36
FT
213 self.aborted = aborted
214
7f97a47e
FT
215sysres = staticdir()
216itime = time.time()
7f97a47e 217sysres["realtime"] = simpleattr(func = lambda: time.time() - itime)
7f97a47e
FT
218
219sysinfo = staticdir()
220sysinfo["pid"] = simpleattr(func = os.getpid)
7f97a47e
FT
221sysinfo["hostname"] = simpleattr(func = socket.gethostname)
222sysinfo["platform"] = valueattr(init = sys.platform)