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.
 
 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.
@@ -120,7 +129,7 @@ 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
             try:
                 cb(event)
             except: pass
@@ -158,6 +167,30 @@ class staticdir(perfobj):
     def pdm_protocols(self):
         return super().pdm_protocols() + ["dir"]
 
     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
 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()
 
 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))