Handle subscribed callbacks unregistering themselves.
[pdm.git] / pdm / perf.py
index feb8b4a..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.
 
-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
+import os, sys, time, socket, threading
 
 __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
-    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.
@@ -120,7 +129,7 @@ class eventobj(perfobj):
 
     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
@@ -158,6 +167,30 @@ class staticdir(perfobj):
     def pdm_protocols(self):
         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
     the `event' interface. Its main utility is that it keeps track of
@@ -223,19 +256,26 @@ class finishevent(procevent):
 
 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["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)
+
+sysctl = simplefunc(exit=lambda status=0: os._exit(status))