Merge branch 'master' into python3
authorFredrik Tolf <fredrik@dolda2000.com>
Fri, 23 Dec 2011 01:30:26 +0000 (02:30 +0100)
committerFredrik Tolf <fredrik@dolda2000.com>
Fri, 23 Dec 2011 01:30:26 +0000 (02:30 +0100)
Conflicts:
pdm/perf.py

1  2 
pdm/cli.py
pdm/perf.py

diff --combined 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)
      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
              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 = {}
          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
          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
@@@ -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()
          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()
          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):
          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):
          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()