bpo-20104: Expose posix_spawn in the os module#5109
Conversation
360963b to
c1fce61
Compare
There was a problem hiding this comment.
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.
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?
There was a problem hiding this comment.
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
There was a problem hiding this comment.
Shouldn't this be hasattr(os, "posix_spawn"). Same for the other test
There was a problem hiding this comment.
News needs update to no longer mention FileActions class
|
@pppery Thanks! Corrected in b9db2c8 |
7c9d964 to
b25b7d1
Compare
There was a problem hiding this comment.
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.
mention the name of the ifdef this pairs with.
There was a problem hiding this comment.
lack of indentation down here looks odd for everything but the "fail:" label.
|
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.
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 |
| 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.
Doesn't conform PEP 7. Needed spaces after "if" and before "{".
| "Error initializing file actions"); | ||
| goto fail; | ||
| } | ||
|
|
| 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.
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.
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.
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.
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.
The result of the call is not checked.
|
|
||
| path_error(path); | ||
|
|
||
| free_string_array(envlist, envc); |
There was a problem hiding this comment.
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.
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.
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)); |
| if(PyErr_Occurred()) { | ||
| goto fail; | ||
| } | ||
| long open_mode = PyLong_AsLong(PySequence_GetItem(file_actions_obj, 4)); |
There was a problem hiding this comment.
Check integer overflow when cast to mode_t.
| goto fail; | ||
| } | ||
|
|
||
| long close_fd = PyLong_AsLong(PySequence_GetItem(file_actions_obj, 1)); |
| goto fail; | ||
| } | ||
|
|
||
| long fd1 = PyLong_AsLong(PySequence_GetItem(file_actions_obj, 1)); |
| break; | ||
|
|
||
| default: | ||
| PyErr_SetString(PyExc_TypeError,"Unknown file_actions identifier"); |
There was a problem hiding this comment.
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.
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.
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.
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_spawnin the os module. This also adds a new class in said module to work withposix_spawnfile_actions argument.https://bugs.python.org/issue20104