Skip to content

bpo-20104: Remove posix_spawn from 3.7 #6794

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
May 14, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 0 additions & 41 deletions Doc/library/os.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3365,47 +3365,6 @@ written in Python, such as a mail server's external command delivery program.
subprocesses.


.. function:: posix_spawn(path, argv, env, file_actions=None)

Wraps the :c:func:`posix_spawn` C library API for use from Python.

Most users should use :func:`subprocess.run` instead of :func:`posix_spawn`.

The *path*, *args*, and *env* arguments are similar to :func:`execve`.

The *file_actions* argument may be a sequence of tuples describing actions
to take on specific file descriptors in the child process between the C
library implementation's :c:func:`fork` and :c:func:`exec` steps.
The first item in each tuple must be one of the three type indicator
listed below describing the remaining tuple elements:

.. data:: POSIX_SPAWN_OPEN

(``os.POSIX_SPAWN_OPEN``, *fd*, *path*, *flags*, *mode*)

Performs ``os.dup2(os.open(path, flags, mode), fd)``.

.. data:: POSIX_SPAWN_CLOSE

(``os.POSIX_SPAWN_CLOSE``, *fd*)

Performs ``os.close(fd)``.

.. data:: POSIX_SPAWN_DUP2

(``os.POSIX_SPAWN_DUP2``, *fd*, *new_fd*)

Performs ``os.dup2(fd, new_fd)``.

These tuples correspond to the C library
:c:func:`posix_spawn_file_actions_addopen`,
:c:func:`posix_spawn_file_actions_addclose`, and
:c:func:`posix_spawn_file_actions_adddup2` API calls used to prepare
for the :c:func:`posix_spawn` call itself.

.. versionadded:: 3.7


.. function:: register_at_fork(*, before=None, after_in_parent=None, \
after_in_child=None)

Expand Down
4 changes: 0 additions & 4 deletions Doc/whatsnew/3.7.rst
Original file line number Diff line number Diff line change
Expand Up @@ -616,10 +616,6 @@ Exposed the system calls *preadv*, *preadv2*, *pwritev* and *pwritev2* through
the new functions :func:`~os.preadv` and :func:`~os.pwritev`. (Contributed by
Pablo Galindo in :issue:`31368`.)

Exposed the system call *posix_spawn* through the new function
:func:`~os.posix_spawn`. (Contributed by Pablo Galindo, Serhiy Storchaka and
Gregory P. Smith in :issue:`20104`.)

pdb
---

Expand Down
161 changes: 1 addition & 160 deletions Lib/test/test_posix.py
Original file line number Diff line number Diff line change
Expand Up @@ -1421,168 +1421,9 @@ def test_setgroups(self):
posix.setgroups(groups)
self.assertListEqual(groups, posix.getgroups())


@unittest.skipUnless(hasattr(os, 'posix_spawn'), "test needs os.posix_spawn")
class TestPosixSpawn(unittest.TestCase):
def test_returns_pid(self):
pidfile = support.TESTFN
self.addCleanup(support.unlink, pidfile)
script = f"""if 1:
import os
with open({pidfile!r}, "w") as pidfile:
pidfile.write(str(os.getpid()))
"""
pid = posix.posix_spawn(sys.executable,
[sys.executable, '-c', script],
os.environ)
self.assertEqual(os.waitpid(pid, 0), (pid, 0))
with open(pidfile) as f:
self.assertEqual(f.read(), str(pid))

def test_no_such_executable(self):
no_such_executable = 'no_such_executable'
try:
pid = posix.posix_spawn(no_such_executable,
[no_such_executable],
os.environ)
except FileNotFoundError as exc:
self.assertEqual(exc.filename, no_such_executable)
else:
pid2, status = os.waitpid(pid, 0)
self.assertEqual(pid2, pid)
self.assertNotEqual(status, 0)

def test_specify_environment(self):
envfile = support.TESTFN
self.addCleanup(support.unlink, envfile)
script = f"""if 1:
import os
with open({envfile!r}, "w") as envfile:
envfile.write(os.environ['foo'])
"""
pid = posix.posix_spawn(sys.executable,
[sys.executable, '-c', script],
{**os.environ, 'foo': 'bar'})
self.assertEqual(os.waitpid(pid, 0), (pid, 0))
with open(envfile) as f:
self.assertEqual(f.read(), 'bar')

def test_empty_file_actions(self):
pid = posix.posix_spawn(
sys.executable,
[sys.executable, '-c', 'pass'],
os.environ,
[]
)
self.assertEqual(os.waitpid(pid, 0), (pid, 0))

def test_multiple_file_actions(self):
file_actions = [
(os.POSIX_SPAWN_OPEN, 3, os.path.realpath(__file__), os.O_RDONLY, 0),
(os.POSIX_SPAWN_CLOSE, 0),
(os.POSIX_SPAWN_DUP2, 1, 4),
]
pid = posix.posix_spawn(sys.executable,
[sys.executable, "-c", "pass"],
os.environ, file_actions)
self.assertEqual(os.waitpid(pid, 0), (pid, 0))

def test_bad_file_actions(self):
with self.assertRaises(TypeError):
posix.posix_spawn(sys.executable,
[sys.executable, "-c", "pass"],
os.environ, [None])
with self.assertRaises(TypeError):
posix.posix_spawn(sys.executable,
[sys.executable, "-c", "pass"],
os.environ, [()])
with self.assertRaises(TypeError):
posix.posix_spawn(sys.executable,
[sys.executable, "-c", "pass"],
os.environ, [(None,)])
with self.assertRaises(TypeError):
posix.posix_spawn(sys.executable,
[sys.executable, "-c", "pass"],
os.environ, [(12345,)])
with self.assertRaises(TypeError):
posix.posix_spawn(sys.executable,
[sys.executable, "-c", "pass"],
os.environ, [(os.POSIX_SPAWN_CLOSE,)])
with self.assertRaises(TypeError):
posix.posix_spawn(sys.executable,
[sys.executable, "-c", "pass"],
os.environ, [(os.POSIX_SPAWN_CLOSE, 1, 2)])
with self.assertRaises(TypeError):
posix.posix_spawn(sys.executable,
[sys.executable, "-c", "pass"],
os.environ, [(os.POSIX_SPAWN_CLOSE, None)])
with self.assertRaises(ValueError):
posix.posix_spawn(sys.executable,
[sys.executable, "-c", "pass"],
os.environ,
[(os.POSIX_SPAWN_OPEN, 3, __file__ + '\0',
os.O_RDONLY, 0)])

def test_open_file(self):
outfile = support.TESTFN
self.addCleanup(support.unlink, outfile)
script = """if 1:
import sys
sys.stdout.write("hello")
"""
file_actions = [
(os.POSIX_SPAWN_OPEN, 1, outfile,
os.O_WRONLY | os.O_CREAT | os.O_TRUNC,
stat.S_IRUSR | stat.S_IWUSR),
]
pid = posix.posix_spawn(sys.executable,
[sys.executable, '-c', script],
os.environ, file_actions)
self.assertEqual(os.waitpid(pid, 0), (pid, 0))
with open(outfile) as f:
self.assertEqual(f.read(), 'hello')

def test_close_file(self):
closefile = support.TESTFN
self.addCleanup(support.unlink, closefile)
script = f"""if 1:
import os
try:
os.fstat(0)
except OSError as e:
with open({closefile!r}, 'w') as closefile:
closefile.write('is closed %d' % e.errno)
"""
pid = posix.posix_spawn(sys.executable,
[sys.executable, '-c', script],
os.environ,
[(os.POSIX_SPAWN_CLOSE, 0),])
self.assertEqual(os.waitpid(pid, 0), (pid, 0))
with open(closefile) as f:
self.assertEqual(f.read(), 'is closed %d' % errno.EBADF)

def test_dup2(self):
dupfile = support.TESTFN
self.addCleanup(support.unlink, dupfile)
script = """if 1:
import sys
sys.stdout.write("hello")
"""
with open(dupfile, "wb") as childfile:
file_actions = [
(os.POSIX_SPAWN_DUP2, childfile.fileno(), 1),
]
pid = posix.posix_spawn(sys.executable,
[sys.executable, '-c', script],
os.environ, file_actions)
self.assertEqual(os.waitpid(pid, 0), (pid, 0))
with open(dupfile) as f:
self.assertEqual(f.read(), 'hello')


def test_main():
try:
support.run_unittest(PosixTester, PosixGroupsTester, TestPosixSpawn)
support.run_unittest(PosixTester, PosixGroupsTester)
finally:
support.reap_children()

Expand Down
1 change: 1 addition & 0 deletions Misc/NEWS.d/3.7.0b1.rst
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ Remove the STORE_ANNOTATION bytecode.
.. section: Core and Builtins

Expose posix_spawn as a low level API in the os module.
(removed before 3.7.0rc1)

..

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
The new `os.posix_spawn` added in 3.7.0b1 was removed as we are still
working on what the API should look like. Expect this in 3.8 instead.
54 changes: 1 addition & 53 deletions Modules/clinic/posixmodule.c.h
Original file line number Diff line number Diff line change
Expand Up @@ -1727,54 +1727,6 @@ os_execve(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *k

#endif /* defined(HAVE_EXECV) */

#if defined(HAVE_POSIX_SPAWN)

PyDoc_STRVAR(os_posix_spawn__doc__,
"posix_spawn($module, path, argv, env, file_actions=None, /)\n"
"--\n"
"\n"
"Execute the program specified by path in a new process.\n"
"\n"
" path\n"
" Path of executable file.\n"
" argv\n"
" Tuple or list of strings.\n"
" env\n"
" Dictionary of strings mapping to strings.\n"
" file_actions\n"
" A sequence of file action tuples.");

#define OS_POSIX_SPAWN_METHODDEF \
{"posix_spawn", (PyCFunction)os_posix_spawn, METH_FASTCALL, os_posix_spawn__doc__},

static PyObject *
os_posix_spawn_impl(PyObject *module, path_t *path, PyObject *argv,
PyObject *env, PyObject *file_actions);

static PyObject *
os_posix_spawn(PyObject *module, PyObject *const *args, Py_ssize_t nargs)
{
PyObject *return_value = NULL;
path_t path = PATH_T_INITIALIZE("posix_spawn", "path", 0, 0);
PyObject *argv;
PyObject *env;
PyObject *file_actions = Py_None;

if (!_PyArg_ParseStack(args, nargs, "O&OO|O:posix_spawn",
path_converter, &path, &argv, &env, &file_actions)) {
goto exit;
}
return_value = os_posix_spawn_impl(module, &path, argv, env, file_actions);

exit:
/* Cleanup for path */
path_cleanup(&path);

return return_value;
}

#endif /* defined(HAVE_POSIX_SPAWN) */

#if (defined(HAVE_SPAWNV) || defined(HAVE_WSPAWNV))

PyDoc_STRVAR(os_spawnv__doc__,
Expand Down Expand Up @@ -6194,10 +6146,6 @@ os_getrandom(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject
#define OS_EXECVE_METHODDEF
#endif /* !defined(OS_EXECVE_METHODDEF) */

#ifndef OS_POSIX_SPAWN_METHODDEF
#define OS_POSIX_SPAWN_METHODDEF
#endif /* !defined(OS_POSIX_SPAWN_METHODDEF) */

#ifndef OS_SPAWNV_METHODDEF
#define OS_SPAWNV_METHODDEF
#endif /* !defined(OS_SPAWNV_METHODDEF) */
Expand Down Expand Up @@ -6589,4 +6537,4 @@ os_getrandom(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject
#ifndef OS_GETRANDOM_METHODDEF
#define OS_GETRANDOM_METHODDEF
#endif /* !defined(OS_GETRANDOM_METHODDEF) */
/*[clinic end generated code: output=8d3d9dddf254c3c2 input=a9049054013a1b77]*/
/*[clinic end generated code: output=c966c821d557b7c0 input=a9049054013a1b77]*/
Loading