summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavanum Srinivas <davanum@gmail.com>2015-06-24 14:24:50 -0400
committerDavanum Srinivas <davanum@gmail.com>2015-07-07 10:15:48 -0700
commit80bfd1a781bbf1515c411e81619b3cd037eb5489 (patch)
tree3712a204c3a44826347acafa5f45cbf94e3020ee
parent3c6bcf4cd62302f949e47f1102fa7f986e82c596 (diff)
downloadoslo-concurrency-80bfd1a781bbf1515c411e81619b3cd037eb5489.tar.gz
Allow preexec_fn method for processutils.execute1.8.1
If the user specifies preexec_fn, we should call that in our existing _subprocess_setup. On windows, we silently drop this preexec_fn as subprocess.Popen raises a ValueError if we do pass it in. Change-Id: I0176c66fa2de001aa14f0d928d06fd894de55511
-rw-r--r--oslo_concurrency/processutils.py20
-rw-r--r--oslo_concurrency/tests/unit/test_processutils.py18
2 files changed, 34 insertions, 4 deletions
diff --git a/oslo_concurrency/processutils.py b/oslo_concurrency/processutils.py
index 3328ae8..ebade0a 100644
--- a/oslo_concurrency/processutils.py
+++ b/oslo_concurrency/processutils.py
@@ -17,6 +17,7 @@
System-level utilities and helper functions.
"""
+import functools
import logging
import multiprocessing
import os
@@ -86,10 +87,12 @@ class NoRootWrapSpecified(Exception):
super(NoRootWrapSpecified, self).__init__(message)
-def _subprocess_setup():
+def _subprocess_setup(on_preexec_fn):
# Python installs a SIGPIPE handler by default. This is usually not what
# non-Python subprocesses expect.
signal.signal(signal.SIGPIPE, signal.SIG_DFL)
+ if on_preexec_fn:
+ on_preexec_fn()
LOG_ALL_ERRORS = 1
@@ -155,6 +158,13 @@ def execute(*cmd, **kwargs):
`processutils.execute` to track process completion
asynchronously.
:type on_completion: function(:class:`subprocess.Popen`)
+ :param preexec_fn: This function will be called
+ in the child process just before the child
+ is executed. WARNING: On windows, we silently
+ drop this preexec_fn as it is not supported by
+ subprocess.Popen on windows (throws a
+ ValueError)
+ :type preexec_fn: function()
:returns: (stdout, stderr) from process execution
:raises: :class:`UnknownArgumentError` on
receiving unknown arguments
@@ -176,6 +186,7 @@ def execute(*cmd, **kwargs):
log_errors = kwargs.pop('log_errors', None)
on_execute = kwargs.pop('on_execute', None)
on_completion = kwargs.pop('on_completion', None)
+ preexec_fn = kwargs.pop('preexec_fn', None)
if isinstance(check_exit_code, bool):
ignore_exit_code = not check_exit_code
@@ -213,10 +224,11 @@ def execute(*cmd, **kwargs):
_PIPE = subprocess.PIPE # pylint: disable=E1101
if os.name == 'nt':
- preexec_fn = None
+ on_preexec_fn = None
close_fds = False
else:
- preexec_fn = _subprocess_setup
+ on_preexec_fn = functools.partial(_subprocess_setup,
+ preexec_fn)
close_fds = True
obj = subprocess.Popen(cmd,
@@ -224,7 +236,7 @@ def execute(*cmd, **kwargs):
stdout=_PIPE,
stderr=_PIPE,
close_fds=close_fds,
- preexec_fn=preexec_fn,
+ preexec_fn=on_preexec_fn,
shell=shell,
cwd=cwd,
env=env_variables)
diff --git a/oslo_concurrency/tests/unit/test_processutils.py b/oslo_concurrency/tests/unit/test_processutils.py
index 7e77e6d..aa75999 100644
--- a/oslo_concurrency/tests/unit/test_processutils.py
+++ b/oslo_concurrency/tests/unit/test_processutils.py
@@ -20,6 +20,7 @@ import logging
import multiprocessing
import os
import stat
+import subprocess
import tempfile
import fixtures
@@ -72,6 +73,23 @@ class UtilsTest(test_base.BaseTestCase):
self.assertEqual(1, on_execute_callback.call_count)
self.assertEqual(1, on_completion_callback.call_count)
+ def test_execute_with_preexec_fn(self):
+ # NOTE(dims): preexec_fn is set to a callable object, this object
+ # will be called in the child process just before the child is
+ # executed. So we cannot pass share variables etc, simplest is to
+ # check if a specific exception is thrown which can be caught here.
+ def preexec_fn():
+ raise processutils.InvalidArgumentError()
+
+ processutils.execute("/bin/true")
+
+ expected_exception = (processutils.InvalidArgumentError if six.PY2
+ else subprocess.SubprocessError)
+ self.assertRaises(expected_exception,
+ processutils.execute,
+ "/bin/true",
+ preexec_fn=preexec_fn)
+
class ProcessExecutionErrorTest(test_base.BaseTestCase):