-
-
Notifications
You must be signed in to change notification settings - Fork 31.5k
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
bpo-20104: Expose posix_spawn
in the os module
#5109
Conversation
360963b
to
c1fce61
Compare
Modules/posixmodule.c
Outdated
argv is a list or tuple of strings and env is a dictionary | ||
like posix.environ. */ | ||
|
||
if (!PyList_Check(argv) && !PyTuple_Check(argv)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PySequence_Check
Lib/os.py
Outdated
@@ -1076,3 +1076,46 @@ def __fspath__(self): | |||
@classmethod | |||
def __subclasshook__(cls, subclass): | |||
return hasattr(subclass, '__fspath__') | |||
|
|||
class FileActions(object): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This class does not map well to the POSIX posix_spawn() API. It take an arbitrary ordered sequence of file_actions populated by the underlying posix_spawn_file_actions_add{dup2,open.close}
C APIs.
What you really want is for the posix_spawn Python API to take a list of actions, each of which is an instance of a fileaction describing a dup2/open/close operation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @gpshead ! That makes a lot of sense. Should I create three different classes for dup2/open/close and pass a list of these of there is a better approach?
Lib/test/test_posix.py
Outdated
file_actions.add_open(3,os.path.realpath(__file__),0,0) | ||
file_actions.add_close(2) | ||
file_actions.add_dup2(1,4) | ||
pid = posix.posixspawn(sys.executable, [sys.executable, "-c", "pass"], os.environ, file_actions) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The Python API name should be .posix_spawn
to match the posix C API name.
A Python core developer has requested some changes be made to your pull request before we can consider merging it. If you could please address their requests along with any other requests in other reviews from core developers that would be appreciated. Once you have made the requested changes, please leave a comment on this pull request containing the phrase |
I suggest creating |
Is then ok to bring |
good point. given that, just define a trio of simple data classes of your own or some constants to indicate open/close/dup2 for use as the first element of a tuple? the "good" thing about os/posix module APIs is that they can be quite low level looking. This is the module providing a relatively raw wrapper of system calls. higher level abstrations to make using them easier often live in other modules. |
I have made the requested changes; please review again |
Thanks for making the requested changes! @gpshead: please review the changes made to this pull request. |
posix_spawn
in the os module posix_spawn
in the os module
posix_spawn
in the os module posix_spawn
in the os module
Lib/test/test_posix.py
Outdated
self.assertEqual(os.waitpid(pid,0),(pid,0)) | ||
|
||
|
||
@unittest.skipUnless(hasattr(os, 'posixspawn'), "test needs os.posix_spawn") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't this be hasattr(os, "posix_spawn")
. Same for the other test
@@ -0,0 +1,2 @@ | |||
Expose posix_spawn and a FileActions object to work with said function in |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
News needs update to no longer mention FileActions class
@pppery Thanks! Corrected in b9db2c8 |
7c9d964
to
b25b7d1
Compare
switch(mode) { | ||
|
||
case POSIX_SPAWN_OPEN: | ||
if(PySequence_Size(file_actions_obj) != 5){ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you need to check for errors in all of these cases: The PyXXX_AsYYY APIs can cause TypeError or OverflowError. PyLong_As
APIs return -1 on error, use PyErr_Occurred()
to disambiguate. Check for NULL from PyUnicode_As
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done in cb440d5
Modules/posixmodule.c
Outdated
|
||
|
||
} | ||
#endif |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
mention the name of the ifdef this pairs with.
Modules/posixmodule.c
Outdated
return PyLong_FromPid(pid); | ||
_Py_END_SUPPRESS_IPH | ||
|
||
path_error(path); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lack of indentation down here looks odd for everything but the "fail:" label.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done in cb440d5
I have made the requested changes; please review again |
Thanks for making the requested changes! @gpshead: please review the changes made to this pull request. |
argv is a list or tuple of strings and env is a dictionary | ||
like posix.environ. */ | ||
|
||
if (!PySequence_Check(argv)){ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not (!PyList_Check(argv) && !PyTuple_Check(argv))
as in other places?
Py_ssize_t argc, envc; | ||
|
||
/* posix_spawn has three arguments: (path, argv, env), where | ||
argv is a list or tuple of strings and env is a dictionary |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bad indentation.
posix_spawn_file_actions_t *file_actionsp = NULL; | ||
if (file_actions != NULL && file_actions != Py_None){ | ||
posix_spawn_file_actions_t _file_actions; | ||
if(posix_spawn_file_actions_init(&_file_actions) != 0){ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Doesn't conform PEP 7. Needed spaces after "if" and before "{".
"Error initializing file actions"); | ||
goto fail; | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Too much empty lines.
for (int i = 0; i < PySequence_Fast_GET_SIZE(seq); ++i) { | ||
file_actions_obj = PySequence_Fast_GET_ITEM(seq, i); | ||
|
||
if(!PySequence_Check(file_actions_obj) | !PySequence_Size(file_actions_obj)){ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PySequence_Size()
can be called before PySequence_Check()
.
Use PySequence_Fast_GET_SIZE()
instead of PySequence_Check()
.
goto fail; | ||
} | ||
|
||
long open_fd = PyLong_AsLong(PySequence_GetItem(file_actions_obj, 1)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PyLong_AsLong()
can call arbitrary Python code. This can cause changing the size of file_actions_obj
. Following PySequence_GetItem()
can fail.
I would require file_actions_obj
to be a tuple and use PyArg_ParseTuple()
for parsing it.
} | ||
|
||
long open_fd = PyLong_AsLong(PySequence_GetItem(file_actions_obj, 1)); | ||
if(PyErr_Occurred()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use idiomatic code:
if (open_fd == -1 && PyErr_Occurred()) {
|
||
_Py_BEGIN_SUPPRESS_IPH | ||
posix_spawn(&pid, path->narrow, file_actionsp, NULL, argvlist, envlist); | ||
return PyLong_FromPid(pid); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Return before _Py_END_SUPPRESS_IPH
? This looks like a bug.
} | ||
|
||
_Py_BEGIN_SUPPRESS_IPH | ||
posix_spawn(&pid, path->narrow, file_actionsp, NULL, argvlist, envlist); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The result of the call is not checked.
|
||
path_error(path); | ||
|
||
free_string_array(envlist, envc); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't it leaked in error case?
if(PyErr_Occurred()) { | ||
goto fail; | ||
} | ||
const char* open_path = PyUnicode_AsUTF8(PySequence_GetItem(file_actions_obj, 2)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use filesystem encoding. PyUnicode_FSDecoder()
or like.
goto fail; | ||
} | ||
|
||
long open_fd = PyLong_AsLong(PySequence_GetItem(file_actions_obj, 1)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
open_fd
should be of type int
. Use _PyLong_AsInt()
.
if(open_path == NULL){ | ||
goto fail; | ||
} | ||
long open_oflag = PyLong_AsLong(PySequence_GetItem(file_actions_obj, 3)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should be int
.
if(PyErr_Occurred()) { | ||
goto fail; | ||
} | ||
long open_mode = PyLong_AsLong(PySequence_GetItem(file_actions_obj, 4)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Check integer overflow when cast to mode_t
.
goto fail; | ||
} | ||
|
||
long close_fd = PyLong_AsLong(PySequence_GetItem(file_actions_obj, 1)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should be int
.
goto fail; | ||
} | ||
|
||
long fd1 = PyLong_AsLong(PySequence_GetItem(file_actions_obj, 1)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should be int
.
break; | ||
|
||
default: | ||
PyErr_SetString(PyExc_TypeError,"Unknown file_actions identifier"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wouldn't ValueError more appropriate exception type?
|
||
|
||
mode_obj = PySequence_Fast_GET_ITEM(file_actions_obj, 0); | ||
int mode = PyLong_AsLong(mode_obj); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should be checked for error.
Also there are trailing spaces added by this PR. |
if (file_actions != NULL && file_actions != Py_None){ | ||
posix_spawn_file_actions_t _file_actions; | ||
if(posix_spawn_file_actions_init(&_file_actions) != 0){ | ||
PyErr_SetString(PyExc_TypeError, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't errno
be included in the exception?
|
||
pid_t pid; | ||
posix_spawn_file_actions_t *file_actionsp = NULL; | ||
if (file_actions != NULL && file_actions != Py_None){ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
file_actions
is never NULL.
@serhiy-storchaka I am trying to correct all these new issues in Corrected in #6331. Please, notice that some of them (like the result of |
This PR exposes
posix_spawn
in the os module. This also adds a new class in said module to work withposix_spawn
file_actions argument.https://bugs.python.org/issue20104