Handle subscribed callbacks unregistering themselves.
[pdm.git] / pdm / perf.py
index 786ab98..7aef95a 100644 (file)
@@ -5,38 +5,47 @@ 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.
 
 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:
 
 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
+import os, sys, time, socket, threading
 
 __all__ = ["attrinfo", "simpleattr", "valueattr", "eventobj",
            "staticdir", "event", "procevent", "startevent",
 
 __all__ = ["attrinfo", "simpleattr", "valueattr", "eventobj",
            "staticdir", "event", "procevent", "startevent",
@@ -44,7 +53,7 @@ __all__ = ["attrinfo", "simpleattr", "valueattr", "eventobj",
 
 class attrinfo(object):
     """The return value of the `attrinfo' method on `attr' objects as
 
 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.
 
     Currently contains a single data field, `desc', which should have
     a human-readable description of the purpose of the attribute.
@@ -54,7 +63,7 @@ class attrinfo(object):
 
 class perfobj(object):
     def __init__(self, *args, **kwargs):
 
 class perfobj(object):
     def __init__(self, *args, **kwargs):
-        super(perfobj, self).__init__()
+        super().__init__()
     
     def pdm_protocols(self):
         return []
     
     def pdm_protocols(self):
         return []
@@ -65,7 +74,7 @@ class simpleattr(perfobj):
     read.
     """
     def __init__(self, func, info = None, *args, **kwargs):
     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()
         self.func = func
         if info is None:
             info = attrinfo()
@@ -78,7 +87,7 @@ class simpleattr(perfobj):
         return self.info
 
     def pdm_protocols(self):
         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
 
 class valueattr(perfobj):
     """An implementation of the `attr' interface, which is initialized
@@ -86,7 +95,7 @@ class valueattr(perfobj):
     updates to the value are reflected in subsequent reads.
     """
     def __init__(self, init, info = None, *args, **kwargs):
     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()
         self.value = init
         if info is None:
             info = attrinfo()
@@ -99,7 +108,7 @@ class valueattr(perfobj):
         return self.info
 
     def pdm_protocols(self):
         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
 
 class eventobj(perfobj):
     """An implementation of the `event' interface. It keeps track of
@@ -107,7 +116,7 @@ class eventobj(perfobj):
     subscribers when submitted with the `notify' method.
     """
     def __init__(self, *args, **kwargs):
     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 = set()
 
     def subscribe(self, cb):
@@ -120,13 +129,13 @@ class eventobj(perfobj):
 
     def notify(self, event):
         """Notify all subscribers with the given event object."""
 
     def notify(self, event):
         """Notify all subscribers with the given event object."""
-        for cb in self.subscribers:
+        for cb in list(self.subscribers):
             try:
                 cb(event)
             except: pass
 
     def pdm_protocols(self):
             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
 
 class staticdir(perfobj):
     """An implementation of the `dir' interface. Put other PERF
@@ -134,7 +143,7 @@ class staticdir(perfobj):
     return them to requesting clients.
     """
     def __init__(self, *args, **kwargs):
     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):
         self.map = {}
 
     def __setitem__(self, name, ob):
@@ -150,13 +159,37 @@ class staticdir(perfobj):
         return self.map.get(name, default)
 
     def listdir(self):
         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):
 
     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 simplefunc(perfobj):
+    """An implementation of the `invoke' interface. Put callables in
+    it using the normal dict assignment syntax, and it will call them
+    when invoked with the corresponding method name. Additionally, it
+    updates itself with any keyword-arguments it is initialized with."""
+    def __init__(self, *args, **kwargs):
+        super().__init__(*args)
+        self.map = {}
+        self.map.update(kwargs)
+
+    def __setitem__(self, name, func):
+        self.map[name] = func
+
+    def __delitem__(self, name):
+        del self.map[name]
+
+    def invoke(self, method, *args, **kwargs):
+        if method not in self.map:
+            raise AttributeError(method)
+        self.map[method](*args, **kwargs)
+
+    def pdm_protocols(self):
+        return super().pdm_protocols() + ["invoke"]
 
 class event(object):
     """This class should be subclassed by all event objects sent via
 
 class event(object):
     """This class should be subclassed by all event objects sent via
@@ -200,7 +233,7 @@ class procevent(event):
     `finishevent' emitted when the connection is closed.
     """
     def __init__(self, id):
     `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:
         if isinstance(id, procevent):
             self.id = id.id
         else:
@@ -209,7 +242,7 @@ class procevent(event):
 class startevent(procevent):
     """A subclass of `procevent'. See its documentation for details."""
     def __init__(self):
 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
 
 class finishevent(procevent):
     """A subclass of `procevent'. Intended to be emitted when a
@@ -218,24 +251,31 @@ class finishevent(procevent):
     distinction is meaningful. The `start' parameter should be the
     `startevent' instance used when the process was initiated."""
     def __init__(self, start, aborted = False):
     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()
 itime = time.time()
         self.aborted = aborted
 
 sysres = staticdir()
 itime = time.time()
-ires = resource.getrusage(resource.RUSAGE_SELF)
-def ct():
-    ru = resource.getrusage(resource.RUSAGE_SELF)
-    return (ru.ru_utime - ires.ru_utime) + (ru.ru_stime - ires.ru_stime)
 sysres["realtime"] = simpleattr(func = lambda: time.time() - itime)
 sysres["realtime"] = simpleattr(func = lambda: time.time() - itime)
-sysres["cputime"] = simpleattr(func = ct)
-sysres["utime"] = simpleattr(func = lambda: resource.getrusage(resource.RUSAGE_SELF).ru_utime - ires.ru_utime)
-sysres["stime"] = simpleattr(func = lambda: resource.getrusage(resource.RUSAGE_SELF).ru_stime - ires.ru_stime)
-sysres["maxrss"] = simpleattr(func = lambda: resource.getrusage(resource.RUSAGE_SELF).ru_maxrss)
-sysres["rusage"] = simpleattr(func = lambda: resource.getrusage(resource.RUSAGE_SELF))
+try:
+    import resource
+except ImportError:
+    pass
+else:
+    ires = resource.getrusage(resource.RUSAGE_SELF)
+    def ct():
+        ru = resource.getrusage(resource.RUSAGE_SELF)
+        return (ru.ru_utime - ires.ru_utime) + (ru.ru_stime - ires.ru_stime)
+    sysres["cputime"] = simpleattr(func = ct)
+    sysres["utime"] = simpleattr(func = lambda: resource.getrusage(resource.RUSAGE_SELF).ru_utime - ires.ru_utime)
+    sysres["stime"] = simpleattr(func = lambda: resource.getrusage(resource.RUSAGE_SELF).ru_stime - ires.ru_stime)
+    sysres["maxrss"] = simpleattr(func = lambda: resource.getrusage(resource.RUSAGE_SELF).ru_maxrss)
+    sysres["rusage"] = simpleattr(func = lambda: resource.getrusage(resource.RUSAGE_SELF))
 
 sysinfo = staticdir()
 sysinfo["pid"] = simpleattr(func = os.getpid)
 sysinfo["uname"] = simpleattr(func = os.uname)
 sysinfo["hostname"] = simpleattr(func = socket.gethostname)
 sysinfo["platform"] = valueattr(init = sys.platform)
 
 sysinfo = staticdir()
 sysinfo["pid"] = simpleattr(func = os.getpid)
 sysinfo["uname"] = simpleattr(func = os.uname)
 sysinfo["hostname"] = simpleattr(func = socket.gethostname)
 sysinfo["platform"] = valueattr(init = sys.platform)
+
+sysctl = simplefunc(exit=lambda status=0: os._exit(status))