[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