This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: posix_spawnp returns error when used with file_actions
Type: behavior Stage: resolved
Components: Library (Lib) Versions: Python 3.8
process
Status: closed Resolution: not a bug
Dependencies: Superseder:
Assigned To: Nosy List: Matthew Tanous, a.badger, vstinner
Priority: normal Keywords:

Created on 2019-05-06 15:03 by Matthew Tanous, last changed 2022-04-11 14:59 by admin. This issue is now closed.

Messages (7)
msg341518 - (view) Author: Matthew Tanous (Matthew Tanous) Date: 2019-05-06 15:03
Ran into this on macOS while trying to play around with the new posix_spawn bindings. It appears to me that the file_actions path is not what is being used by file_actions here.  It may be that I am misunderstanding something, but I thought I would bring it up.

Python 3.8.0a3 (v3.8.0a3:9a448855b5, Mar 25 2019, 17:05:20)
[Clang 6.0 (clang-600.0.57)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import os
>>> file_actions = [(os.POSIX_SPAWN_OPEN, 1, '.tmp/temp_file', os.O_CREAT | os.O_RDWR, 777)]
>>> os.posix_spawnp('whoami', ['whoami'], file_actions=file_actions)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: posix_spawnp() takes exactly 3 positional arguments (2 given)
>>> os.posix_spawnp('whoami', ['whoami'], os.environ, file_actions=file_actions)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
PermissionError: [Errno 13] Permission denied: 'whoami'
msg341567 - (view) Author: Matthew Tanous (Matthew Tanous) Date: 2019-05-06 17:14
I have some updated information.  This works as expected when I set the permissions properly using an octal number 0o777.

The issue appears to be that when the permissions don't exist as specified, the PermissionError reports the process name, not the file path.
msg341712 - (view) Author: Toshio Kuratomi (a.badger) * Date: 2019-05-07 12:27
The error message is reporting the path.  However, it is only the path component that is specified in the call to the function.  

This behaviour is not limited to the posix_spawnp() function but happens with any interface that can look up a command in the path.  For instance, here's what subprocess.Popen() gives me when I look use it against a 0644 file that is present in my PATH:

>>> subprocess.Popen(['fever.py'])
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib64/python3.7/subprocess.py", line 775, in __init__
    restore_signals, start_new_session)
  File "/usr/lib64/python3.7/subprocess.py", line 1522, in _execute_child
    raise child_exception_type(errno_num, err_msg, err_filename)
PermissionError: [Errno 13] Permission denied: 'fever.py'
msg341757 - (view) Author: Matthew Tanous (Matthew Tanous) Date: 2019-05-07 15:47
Your example is an attempt to use Popen to run a file you don't have execute permissions for.

In my example, it is not `whoami` that it is failing to create/open, but '.tmp/temp_file'.  I would expect a `PermissionError: [Errno 13] Permission denied: '.tmp/temp_file'` returned.
msg341840 - (view) Author: Toshio Kuratomi (a.badger) * Date: 2019-05-07 23:59
Ah okay, I'll see what information posix_spawnp() (the C function) returns on error for that case.
msg341842 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2019-05-08 00:06
Depending on your libc implementation and your libc version, you may get more or less info about what gone wrong.

The initial issue was not a bug but a mistake in the file mode. I close the issue.
msg341870 - (view) Author: Toshio Kuratomi (a.badger) * Date: 2019-05-08 12:52
Yeah, I've verified what Victor said about the OS not giving us enough information to tell what file is causing the issue.  However, I wonder if we should change the error message to be less confusing?  I'm a godawful C programmer but maybe something like this:

-        PyErr_SetFromErrnoWithFilenameObject(PyExc_OSError, path->object);
+        if (file_actionsp != NULL) {
+            /* OSErrors can be triggered by the program being invoked or by a
+             * problem with the files in file_actions.  Change the default
+             * error message so as not to confuse the programmer
+             */
+            if (path->narrow != NULL) {
+                char *err_msg_fmt = "While spawning %s\0";
+                unsigned int err_msg_size = strlen(path->narrow) + strlen(err_msg_fmt) + 1;
+                char* err_msg = malloc(err_msg_size);
+
+                PyOS_snprintf(err_msg, err_msg_size, err_msg_fmt, path->narrow);
+                /* Slight abuse, we're sending an error message rather than
+                 * a filename
+                 */
+                PyErr_SetFromErrnoWithFilename(PyExc_OSError, err_msg);
+            }
+        }
+        else
+        {
+            PyErr_SetFromErrnoWithFilenameObject(PyExc_OSError, path->object);
+        }


Which leads to output like this:

>>> import os
>>> file_actions = [(os.POSIX_SPAWN_OPEN, 1, '.tmp/temp_file', os.O_CREAT | os.O_RDWR, 777)]
>>> os.posix_spawnp('whoami', ['whoami'], os.environ, file_actions=file_actions)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
FileNotFoundError: [Errno 2] No such file or directory: 'While spawning whoami'


I can submit a PR for that and people can teach me how to fix my C if it's considered useful.
History
Date User Action Args
2022-04-11 14:59:14adminsetgithub: 80993
2019-05-08 12:52:52a.badgersetmessages: + msg341870
2019-05-08 00:06:25vstinnersetstatus: open -> closed

nosy: + vstinner
messages: + msg341842

resolution: not a bug
stage: resolved
2019-05-07 23:59:13a.badgersetmessages: + msg341840
2019-05-07 15:47:31Matthew Tanoussetmessages: + msg341757
2019-05-07 12:27:46a.badgersetnosy: + a.badger
messages: + msg341712
2019-05-06 17:14:21Matthew Tanoussetmessages: + msg341567
2019-05-06 15:03:31Matthew Tanouscreate