return rv
class client(object):
+ """PDM client
+
+ This class provides general facilities to speak to PDM servers,
+ and is mainly intended to be subclassed to provide for the
+ specific protocols, such as replclient and perfclient do.
+
+ `client' instances can be passed as arguments to select.select(),
+ and can be used in `with' statements.
+ """
def __init__(self, sk, proto = None):
+ """Create a client object connected to the specified
+ server. `sk' can either be a socket object, which is used as
+ it is, or a string specification very similar to the
+ specification for pdm.srv.listen, so see its documentation for
+ details. The differences are only that this function does not
+ take arguments specific to socket creation, like the mode and
+ group arguments for Unix sockets. If `proto' is given, that
+ subprotocol will negotiated with the server (by calling the
+ 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)
return self.sk.fileno()
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:]
self.buf += ret
def select(self, proto):
- if "\n" in proto:
+ """Negotiate the given subprotocol with the server"""
+ 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):
return False
class replclient(client):
+ """REPL protocol client
+
+ Implements the client side of the REPL protocol; see pdm.srv.repl
+ for details on the protocol and its functionality.
+ """
def __init__(self, sk):
- super(replclient, self).__init__(sk, "repl")
+ """Create a connected client as documented in the `client' class."""
+ super().__init__(sk, "repl")
def run(self, code):
+ """Run a single block of Python code on the server. Returns
+ the output of the command (as documented in pdm.srv.repl) as a
+ string.
+ """
while True:
ncode = code.replace("\n\n", "\n")
if ncode == code: break
return False
class perfclient(client):
+ """PERF protocol client
+
+ Implements the client side of the PERF protocol; see pdm.srv.perf
+ for details on the protocol and its functionality.
+
+ This client class implements functions for finding PERF objects on
+ the server, and returns, for each server-side object looked up, a
+ proxy object that mimics exactly the PERF interfaces that the
+ object implements. As the proxy objects reference live objects on
+ the server, they should be released when they are no longer used;
+ they implement a close() method for that purpose, and can also be
+ used in `with' statements.
+
+ See pdm.srv.perf for details on the various PERF interfaces that
+ the proxy objects might implement.
+ """
def __init__(self, sk):
- super(perfclient, self).__init__(sk, "perf")
+ """Create a connected client as documented in the `client' class."""
+ super().__init__(sk, "perf")
self.nextid = 0
self.lock = threading.Lock()
self.proxies = {}
class listener(threading.Thread):
+ """PDM listener
+
+ This subclass of a thread listens to PDM connections and handles
+ client connections properly. It is intended to be subclassed by
+ providers of specific domains, such as unixlistener and
+ tcplistener.
+ """
def __init__(self):
- super(listener, self).__init__(name = "Management listener")
+ super().__init__(name = "Management listener")
self.setDaemon(True)
def listen(self, sk):
cl.start()
class unixlistener(listener):
- def __init__(self, name, mode = 0600, group = None):
+ """Unix socket listener"""
- super(unixlistener, self).__init__()
+ def __init__(self, name, mode = 0o600, group = None):
+ """Create a listener that will bind to the Unix socket named
+ by `name'. The socket will not actually be bound until the
+ listener is started. The socket will be chmodded to `mode',
+ and if `group' is given, the named group will be set as the
+ owner of the socket.
+ """
+ super().__init__()
self.name = name
self.mode = mode
self.group = group
os.unlink(self.name)
class tcplistener(listener):
+ """TCP socket listener"""
def __init__(self, port, bindaddr = "127.0.0.1"):
- super(tcplistener, self).__init__()
+ """Create a listener that will bind to the given TCP port, and
+ the given local interface. The socket will not actually be
+ bound until the listener is started.
+ """
+ super().__init__()
self.port = port
self.bindaddr = bindaddr