[Pkg-privacy-commits] [pyptlib] 70/136: add a subprocess management module, which should make it easier to write - composed plugins such as obfs-flash - the 2-SIGINT termination behaviour as specified in the PT spec

Ximin Luo infinity0 at moszumanska.debian.org
Sat Aug 22 13:25:10 UTC 2015


This is an automated email from the git hooks/post-receive script.

infinity0 pushed a commit to branch master
in repository pyptlib.

commit 6700a975bb4409d22f156493484e86dde3cbb55c
Author: Ximin Luo <infinity0 at gmx.com>
Date:   Fri Aug 2 15:21:24 2013 +0100

    add a subprocess management module, which should make it easier to write
    - composed plugins such as obfs-flash
    - the 2-SIGINT termination behaviour as specified in the PT spec
---
 pyptlib/{util.py => util/__init__.py} |   0
 pyptlib/util/subproc.py               | 110 ++++++++++++++++++++++++++++++++++
 setup.py                              |   4 +-
 3 files changed, 112 insertions(+), 2 deletions(-)

diff --git a/pyptlib/util.py b/pyptlib/util/__init__.py
similarity index 100%
rename from pyptlib/util.py
rename to pyptlib/util/__init__.py
diff --git a/pyptlib/util/subproc.py b/pyptlib/util/subproc.py
new file mode 100644
index 0000000..4400f41
--- /dev/null
+++ b/pyptlib/util/subproc.py
@@ -0,0 +1,110 @@
+"""Common tasks for managing child processes.
+
+To have child processes actually be managed by this module, you should use
+the Popen() here rather than subprocess.Popen() directly.
+"""
+
+import atexit
+import inspect
+import signal
+import subprocess
+import time
+
+_CHILD_PROCS = []
+# TODO(infinity0): add functionality to detect when any child dies, and
+# offer different response strategies for them (e.g. restart the child? or die
+# and kill the other children too).
+
+a = inspect.getargspec(subprocess.Popen.__init__)
+_Popen_defaults = zip(a.args[-len(a.defaults):],a.defaults); del a
+class Popen(subprocess.Popen):
+    """Wrapper for subprocess.Popen that tracks every child process.
+
+    See the subprocess module for documentation.
+    """
+
+    def __init__(self, *args, **kwargs):
+        kwargs = dict(_Popen_defaults + kwargs.items())
+        # super() does some magic that makes **kwargs not work, so just call
+        # our super-constructor directly
+        subprocess.Popen.__init__(self, *args, **kwargs)
+        _CHILD_PROCS.append(self)
+
+    # TODO(infinity0): perhaps replace Popen.std* with wrapped file objects
+    # that don't buffer readlines() et. al. Currently one must avoid these and
+    # use while/readline(); see man page for "python -u" for more details.
+
+_SIGINT_RUN = {}
+def trap_sigint(handler, ignoreNum=0):
+    """Register a handler for an INT signal.
+
+    Args:
+        handler: a signal handler; see signal.signal() for details
+        ignoreNum: number of signals to ignore before activating the handler,
+            which will be run on all subsequent signals.
+    """
+    _SIGINT_RUN.setdefault(ignoreNum, []).append(handler)
+
+_intsReceived = 0
+def _run_sigint_handlers(signum=0, sframe=None):
+    global _intsReceived
+    _intsReceived += 1
+
+    # code snippet adapted from atexit._run_exitfuncs
+    exc_info = None
+    for i in xrange(_intsReceived).__reversed__():
+        for handler in _SIGINT_RUN.get(i, []).__reversed__():
+            try:
+                handler(signum, sframe)
+            except SystemExit:
+                exc_info = sys.exc_info()
+            except:
+                import traceback
+                print >> sys.stderr, "Error in atexit._run_exitfuncs:"
+                traceback.print_exc()
+                exc_info = sys.exc_info()
+
+    if exc_info is not None:
+        raise exc_info[0], exc_info[1], exc_info[2]
+
+signal.signal(signal.SIGINT, _run_sigint_handlers)
+
+_isTerminating = False
+def killall(wait_s=16):
+    """Attempt to gracefully terminate all child processes.
+
+    All children are told to terminate gracefully. A waiting period is then
+    applied, after which all children are killed forcefully. If all children
+    terminate before this waiting period is over, the function exits early.
+    """
+    # TODO(infinity0): log this somewhere, maybe
+    global _isTerminating, _CHILD_PROCS
+    if _isTerminating: return
+    _isTerminating = True
+    # terminate all
+    for proc in _CHILD_PROCS:
+        if proc.poll() is None:
+            proc.terminate()
+    # wait and make sure they're dead
+    for i in xrange(wait_s):
+        _CHILD_PROCS = [proc for proc in _CHILD_PROCS
+                        if proc.poll() is None]
+        if not _CHILD_PROCS: break
+        time.sleep(1)
+    # if still existing, kill them
+    for proc in _CHILD_PROCS:
+        if proc.poll() is None:
+            proc.kill()
+
+def auto_killall(ignoreNumSigInts=0):
+    """Automatically terminate all child processes on exit.
+
+    Args:
+        ignoreNumSigInts: this number of INT signals will be ignored before
+            attempting termination. This will be attempted unconditionally in
+            all other cases, such as on normal exit, or on a TERM signal.
+    """
+    killall_handler = lambda signum, sframe: killall()
+    trap_sigint(killall_handler, ignoreNumSigInts)
+    signal.signal(signal.SIGTERM, killall_handler)
+    atexit.register(killall)
diff --git a/setup.py b/setup.py
index 01ff775..6fabc51 100644
--- a/setup.py
+++ b/setup.py
@@ -2,7 +2,7 @@
 
 import sys
 
-from setuptools import setup
+from setuptools import setup, find_packages
 
 setup(name='pyptlib',
       version='0.0.3',
@@ -24,5 +24,5 @@ setup(name='pyptlib',
       keywords='cryptography privacy internet',
       license='BSD',
       package_dir={'pyptlib': 'pyptlib'},
-      packages=['pyptlib'],
+      packages=find_packages(),
      )

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-privacy/packages/pyptlib.git



More information about the Pkg-privacy-commits mailing list