From: Fredrik Tolf Date: Fri, 23 Dec 2011 01:30:26 +0000 (+0100) Subject: Merge branch 'master' into python3 X-Git-Tag: 0.1~1 X-Git-Url: http://dolda2000.com/gitweb/?p=pdm.git;a=commitdiff_plain;h=28203f3fefeaa864b8f14390b7a72d5081a94ce8;hp=-c Merge branch 'master' into python3 Conflicts: pdm/perf.py --- 28203f3fefeaa864b8f14390b7a72d5081a94ce8 diff --combined pdm/cli.py index 9924d40,46276be..32580bb --- a/pdm/cli.py +++ b/pdm/cli.py @@@ -1,7 -1,9 +1,9 @@@ - """Management for daemon processes + """Python Daemon Management -- Client functions - This module provides some client support for the daemon management - provided in the pdm.srv module. + This module implements the client part of the PDM protocols. The + primary objects of interest are the replclient and perfclient classes, + which implement support for their respective protocols. See their + documentation for details. """ import socket, pickle, struct, select, threading @@@ -57,9 -59,9 +59,9 @@@ class client(object) select() method). """ self.sk = resolve(sk) - self.buf = "" + self.buf = b"" line = self.readline() - if line != "+PDM1": + if line != b"+PDM1": raise protoerr("Illegal protocol signature") if proto is not None: self.select(proto) @@@ -75,25 -77,23 +77,25 @@@ def readline(self): """Read a single NL-terminated line and return it.""" while True: - p = self.buf.find("\n") + p = self.buf.find(b"\n") if p >= 0: ret = self.buf[:p] self.buf = self.buf[p + 1:] return ret ret = self.sk.recv(1024) - if ret == "": + if ret == b"": return None self.buf += ret def select(self, proto): """Negotiate the given subprotocol with the server""" - if "\n" in proto: + if isinstance(proto, str): + proto = proto.encode("ascii") + if b"\n" in proto: raise Exception("Illegal protocol specified: %r" % proto) - self.sk.send(proto + "\n") + self.sk.send(proto + b"\n") rep = self.readline() - if len(rep) < 1 or rep[0] != "+": + if len(rep) < 1 or rep[0] != b"+"[0]: raise protoerr("Error reply when selecting protocol %s: %s" % (proto, rep[1:])) def __enter__(self): @@@ -111,7 -111,7 +113,7 @@@ class replclient(client) """ def __init__(self, sk): """Create a connected client as documented in the `client' class.""" - super(replclient, self).__init__(sk, "repl") + super().__init__(sk, "repl") def run(self, code): """Run a single block of Python code on the server. Returns @@@ -124,16 -124,16 +126,16 @@@ code = ncode while len(code) > 0 and code[-1] == "\n": code = code[:-1] - self.sk.send(code + "\n\n") - buf = "" + self.sk.send((code + "\n\n").encode("utf-8")) + buf = b"" while True: ln = self.readline() - if ln[0] == " ": - buf += ln[1:] + "\n" - elif ln[0] == "+": - return buf - elif ln[0] == "-": - raise protoerr("Error reply: %s" % ln[1:]) + if ln[0] == b" "[0]: + buf += ln[1:] + b"\n" + elif ln[0] == b"+"[0]: + return buf.decode("utf-8") + elif ln[0] == b"-"[0]: + raise protoerr("Error reply: %s" % ln[1:].decode("utf-8")) else: raise protoerr("Illegal reply: %s" % ln) @@@ -223,7 -223,7 +225,7 @@@ class perfclient(client) """ def __init__(self, sk): """Create a connected client as documented in the `client' class.""" - super(perfclient, self).__init__(sk, "perf") + super().__init__(sk, "perf") self.nextid = 0 self.lock = threading.Lock() self.proxies = {} @@@ -235,10 -235,10 +237,10 @@@ self.sk.send(buf) def recvb(self, num): - buf = "" + buf = b"" while len(buf) < num: data = self.sk.recv(num - len(buf)) - if data == "": + if data == b"": raise EOFError() buf += data return buf @@@ -315,6 -315,11 +317,11 @@@ of the slash. For instance, find("pdm.perf.sysres/cputime") will return the built-in attribute for reading the CPU time used by the server process. + + The proxy objects returned by this function are cached and the + same object are returned the next time the same name is + requested, which means that they are kept live until the + client connection is closed. """ ret = self.names.get(name) if ret is None: diff --combined pdm/perf.py index 7d8b48b,786ab98..feb8b4a --- a/pdm/perf.py +++ b/pdm/perf.py @@@ -1,19 -1,71 +1,71 @@@ + """Python Daemon Management -- PERF utilities + + This module serves two purposes: It has a few utility classes + for implementing PERF interfaces in common ways, and uses those + classes to implement some standard PERF objects that can be used by + PERF clients connecting to any PERF server. + + See the documentation for pdm.srv.perf for a description of the + various PERF interfaces. + + It contains two named PERF objects: + + * sysres -- A directory containing the following objects pertaining + to the resource usage of the server process: + * realtime -- An attribute returning the amount of real time + since the PDM module was imported (which likely + coincides with the amount of time the server process + has been running). + * cputime -- An attribute returning the amount of CPU time + consumed by the server process (in both user and + kernel mode). + * utime -- An attribute returning the amount of CPU time the + server process has spent in user mode. + * stime -- An attribute returning the amount of CPU time the + server process has spent in kernel mode. + * maxrss -- An attribute returning the largest resident set size + the server process has used during its lifetime. + * rusage -- An attribute returning the current rusage of the + server process. + * sysinfo -- A directory containing the following objects pertaining + to the environment of the server process: + * pid -- An attribute returning the PID of the server process. + * uname -- An attribute returning the uname information of the + system. + * hostname -- An attribute returning the hostname of the system. + * platform -- An attribute returning the Python build platform. + """ + import os, sys, resource, time, socket, threading + __all__ = ["attrinfo", "simpleattr", "valueattr", "eventobj", + "staticdir", "event", "procevent", "startevent", + "finishevent"] + class attrinfo(object): + """The return value of the `attrinfo' method on `attr' objects as + described in pdm.srv.perf. + + Currently contains a single data field, `desc', which should have + a human-readable description of the purpose of the attribute. + """ def __init__(self, desc = None): self.desc = desc class perfobj(object): def __init__(self, *args, **kwargs): - super(perfobj, self).__init__() + super().__init__() def pdm_protocols(self): return [] class simpleattr(perfobj): + """An implementation of the `attr' interface, which is initialized + with a function, and returns whatever that function returns when + read. + """ def __init__(self, func, info = None, *args, **kwargs): - super(simpleattr, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) self.func = func if info is None: info = attrinfo() @@@ -26,11 -78,15 +78,15 @@@ return self.info def pdm_protocols(self): - return super(simpleattr, self).pdm_protocols() + ["attr"] + return super().pdm_protocols() + ["attr"] class valueattr(perfobj): + """An implementation of the `attr' interface, which is initialized + with a single value, and returns that value when read. Subsequent + updates to the value are reflected in subsequent reads. + """ def __init__(self, init, info = None, *args, **kwargs): - super(valueattr, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) self.value = init if info is None: info = attrinfo() @@@ -43,12 -99,15 +99,15 @@@ return self.info def pdm_protocols(self): - return super(valueattr, self).pdm_protocols() + ["attr"] + return super().pdm_protocols() + ["attr"] - class eventobj(perfobj): + """An implementation of the `event' interface. It keeps track of + subscribers, and will multiplex any event to all current + subscribers when submitted with the `notify' method. + """ def __init__(self, *args, **kwargs): - super(eventobj, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) self.subscribers = set() def subscribe(self, cb): @@@ -60,17 -119,22 +119,22 @@@ self.subscribers.remove(cb) def notify(self, event): + """Notify all subscribers with the given event object.""" for cb in self.subscribers: try: cb(event) except: pass def pdm_protocols(self): - return super(eventobj, self).pdm_protocols() + ["event"] + return super().pdm_protocols() + ["event"] class staticdir(perfobj): + """An implementation of the `dir' interface. Put other PERF + objects in it using the normal dict assignment syntax, and it will + return them to requesting clients. + """ def __init__(self, *args, **kwargs): - super(staticdir, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) self.map = {} def __setitem__(self, name, ob): @@@ -86,15 -150,23 +150,23 @@@ return self.map.get(name, default) def listdir(self): - return self.map.keys() + return list(self.map.keys()) def lookup(self, name): return self.map[name] def pdm_protocols(self): - return super(staticdir, self).pdm_protocols() + ["dir"] + return super().pdm_protocols() + ["dir"] class event(object): + """This class should be subclassed by all event objects sent via + the `event' interface. Its main utility is that it keeps track of + the time it was created, so that listening clients can precisely + measure the time between event notifications. + + Subclasses should make sure to call the __init__ method if they + override it. + """ def __init__(self): self.time = time.time() @@@ -112,20 -184,41 +184,41 @@@ def getprocid() idlock.release() class procevent(event): + """A subclass of the `event' class meant to group several events + related to the same process. Create a new process by creating (a + subclass of) the `startevent' class, and subsequent events in the + same process by passing that startevent as the `id' parameter. + + It works by having `startevent' allocate a unique ID for each + process, and every other procevent initializing from that + startevent copying the ID. The data field `id' contains the ID so + that it can be compared by clients. + + An example of such a process might be a client connection, where a + `startevent' is emitted when a client connects, another subclass + of `procevent' emitted when the client runs a command, and a + `finishevent' emitted when the connection is closed. + """ def __init__(self, id): - super(procevent, self).__init__() + super().__init__() if isinstance(id, procevent): self.id = id.id else: self.id = id class startevent(procevent): + """A subclass of `procevent'. See its documentation for details.""" def __init__(self): - super(startevent, self).__init__(getprocid()) + super().__init__(getprocid()) class finishevent(procevent): - def __init__(self, start, aborted): + """A subclass of `procevent'. Intended to be emitted when a + process finishes and terminates. The `aborted' field can be used + to indicate whether the process completed successfully, if such a + distinction is meaningful. The `start' parameter should be the + `startevent' instance used when the process was initiated.""" + def __init__(self, start, aborted = False): - super(finishevent, self).__init__(start) + super().__init__(start) self.aborted = aborted sysres = staticdir()