"""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).
+ specification for L{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)
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):
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.
+ Implements the client side of the REPL protocol; see
+ L{pdm.srv.repl} for details on the protocol and its functionality.
"""
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
- the output of the command (as documented in pdm.srv.repl) as a
- string.
+ the output of the command (as documented in L{pdm.srv.repl})
+ as a string.
"""
while True:
ncode = code.replace("\n\n", "\n")
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)
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.
+ Implements the client side of the PERF protocol; see
+ L{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
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.
+ See L{pdm.srv.perf} for details on the various PERF interfaces
+ that the proxy objects might implement.
"""
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
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
+ See the documentation for L{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.
+ - 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
class attrinfo(object):
"""The return value of the `attrinfo' method on `attr' objects as
- described in pdm.srv.perf.
+ described in L{pdm.srv.perf}.
Currently contains a single data field, `desc', which should have
a human-readable description of the purpose of the attribute.
class perfobj(object):
def __init__(self, *args, **kwargs):
- super(perfobj, self).__init__()
+ super().__init__()
def pdm_protocols(self):
return []
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
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 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):
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
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
`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:
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):
"""A subclass of `procevent'. Intended to be emitted when 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()
"""REPL protocol handler
Provides a read-eval-print loop. The primary client-side interface
- is the pdm.cli.replclient class. Clients can send arbitrary code,
- which is compiled and run on its own thread in the server process,
- and output responses that are echoed back to the client.
+ is the L{pdm.cli.replclient} class. Clients can send arbitrary
+ code, which is compiled and run on its own thread in the server
+ process, and output responses that are echoed back to the client.
Each client is provided with its own module, in which the code
runs. The module is prepared with a function named `echo', which
self.mod = types.ModuleType("repl")
self.mod.echo = self.echo
self.printer = pprint.PrettyPrinter(indent = 4, depth = 6)
- cl.send("+REPL\n")
+ cl.send(b"+REPL\n")
def sendlines(self, text):
for line in text.split("\n"):
- self.cl.send(" " + line + "\n")
+ self.cl.send(b" " + line.encode("utf-8") + b"\n")
def echo(self, ob):
self.sendlines(self.printer.pformat(ob))
def command(self, cmd):
+ cmd = cmd.decode("utf-8")
try:
try:
ccode = compile(cmd, "PDM Input", "eval")
except SyntaxError:
ccode = compile(cmd, "PDM Input", "exec")
- exec ccode in self.mod.__dict__
- self.cl.send("+OK\n")
+ exec(ccode, self.mod.__dict__)
+ self.cl.send(b"+OK\n")
else:
self.echo(eval(ccode, self.mod.__dict__))
- self.cl.send("+OK\n")
+ self.cl.send(b"+OK\n")
except:
for line in traceback.format_exception(*sys.exc_info()):
- self.cl.send(" " + line)
- self.cl.send("+EXC\n")
+ self.cl.send(b" " + line.encode("utf-8"))
+ self.cl.send(b"+EXC\n")
def handle(self, buf):
- p = buf.find("\n\n")
+ p = buf.find(b"\n\n")
if p < 0:
return buf
cmd = buf[:p + 1]
already be imported. PDM will not import new modules for clients;
rather, the daemon process needs to import all modules that
clients should be able to interact with. PDM itself always imports
- the pdm.perf module, which contains a few basic PERF objects. See
- its documentation for details.
+ the L{pdm.perf} module, which contains a few basic PERF
+ objects. See its documentation for details.
The following interfaces are currently known to PERF.
- * attr:
+ - attr:
An object that implements the `attr' interface models an
attribute that can be read by clients. The attribute can be
anything, as long as its representation can be
description of the attribute. Both should be
idempotent. `readattr' can return any pickleable object, and
`attrinfo' should return either None to indicate that it has no
- description, or an instance of the pdm.perf.attrinfo class.
+ description, or an instance of the L{pdm.perf.attrinfo} class.
- * dir:
+ - dir:
The `dir' interface models a directory of other PERF
objects. An object implementing it must implement methods
called `lookup' and `listdir'. `lookup' is called with a single
used as argument to `lookup', but the list is not required to
be exhaustive and may also be empty.
- * invoke:
+ - invoke:
The `invoke' interface allows a more arbitrary form of method
calls to objects implementing it. Such objects must implement a
method called `invoke', which is called with one positional
the client. In case the method name is not recognized, `invoke'
should raise an AttributeError.
- * event:
+ - event:
The `event' interface allows PERF objects to notify clients of
events asynchronously. Objects implementing it must implement
methods called `subscribe' and `unsubscribe'. `subscribe' will
`event' object should then call all such registered callables
with a single argument describing the event. The argument could
be any object that can be pickled, but should be an instance of
- a subclass of the pdm.perf.event class. If `subscribe' is
+ a subclass of the L{pdm.perf.event} class. If `subscribe' is
called with a callback object that it has already registered,
it should raise a ValueError. `unsubscribe' is called with a
single argument, which is a previously registered callback
object is not, in fact, registered, a ValueError should be
raised.
- The pdm.perf module contains a few convenience classes which
+ The L{pdm.perf} module contains a few convenience classes which
implements the interfaces, but PERF objects are not required to be
instances of them. Any object can implement a PERF interface, as
long as it does so as described above.
- The pdm.cli.perfclient class is the client-side implementation.
+ The L{pdm.cli.perfclient} class is the client-side implementation.
"""
def __init__(self, cl):
self.cl = cl
self.odtab = {}
- cl.send("+PERF1\n")
+ cl.send(b"+PERF1\n")
self.buf = ""
self.lock = threading.Lock()
self.subscribed = {}
def closed(self):
- for id, recv in self.subscribed.iteritems():
+ for id, recv in self.subscribed.items():
ob = self.odtab[id]
if ob is None: continue
ob, protos = ob
raise ValueError("Object does not support PDM introspection")
try:
proto = ob.pdm_protocols()
- except Exception, exc:
+ except Exception as exc:
raise ValueError("PDM introspection failed", exc)
self.odtab[id] = ob, proto
return proto
return
try:
proto = self.bindob(id, ob)
- except Exception, exc:
+ except Exception as exc:
self.send("-", exc)
return
self.send("+", proto)
return
try:
ob = src.lookup(obnm)
- except KeyError, exc:
+ except KeyError as exc:
self.send("-", exc)
return
try:
proto = self.bindob(tgtid, ob)
- except Exception, exc:
+ except Exception as exc:
self.send("-", exc)
return
self.send("+", proto)
return
try:
ret = ob.readattr()
- except Exception, exc:
+ except Exception as exc:
self.send("-", Exception("Could not read attribute"))
return
self.send("+", ret)
return
try:
self.send("+", ob.invoke(method, *args, **kwargs))
- except Exception, exc:
+ except Exception as exc:
self.send("-", exc)
def event(self, id, ob, ev):
class client(threading.Thread):
def __init__(self, sk):
- super(client, self).__init__(name = "Management client")
+ super().__init__(name = "Management client")
self.setDaemon(True)
self.sk = sk
self.handler = self
return self.sk.send(data)
def choose(self, proto):
+ try:
+ proto = proto.decode("ascii")
+ except UnicodeError:
+ proto = None
if proto in protocols:
self.handler = protocols[proto](self)
else:
raise Exception()
def handle(self, buf):
- p = buf.find("\n")
+ p = buf.find(b"\n")
if p >= 0:
proto = buf[:p]
buf = buf[p + 1:]
def run(self):
try:
- buf = ""
- self.send("+PDM1\n")
+ buf = b""
+ self.send(b"+PDM1\n")
while True:
ret = self.sk.recv(1024)
- if ret == "":
+ if ret == b"":
return
buf += ret
while True:
try:
nbuf = self.handler.handle(buf)
except:
+ #for line in traceback.format_exception(*sys.exc_info()):
+ # print(line)
return
if nbuf == buf:
break
buf = nbuf
finally:
- #for line in traceback.format_exception(*sys.exc_info()):
- # print line
try:
self.sk.close()
finally:
tcplistener.
"""
def __init__(self):
- super(listener, self).__init__(name = "Management listener")
+ super().__init__(name = "Management listener")
self.setDaemon(True)
def listen(self, sk):
class unixlistener(listener):
"""Unix socket listener"""
- def __init__(self, name, mode = 0600, group = None):
+ 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(unixlistener, self).__init__()
+ super().__init__()
self.name = name
self.mode = mode
self.group = group
the given local interface. The socket will not actually be
bound until the listener is started.
"""
- super(tcplistener, self).__init__()
+ super().__init__()
self.port = port
self.bindaddr = bindaddr
last = spec
if "/" in first:
parts = spec.split(":")
- mode = 0600
+ mode = 0o600
group = None
if len(parts) > 1:
mode = int(parts[1], 8)