Issue 8599: _execvpe behaves inconsistently when PATH includes a filename (original) (raw)

A. Description When running os._execvpe with a relative pathname that does not exist, I'd expect to get ENOENT error. But there is an edge case in which Python throws ENOTDIR error - when the LAST element in PATH is a regular file (e.g. /bin/ls). This case is caused by a sysadmin mistake, but it may happen, as in the system on which I've encountered this bug.

B. Explanation + How to reproduce: Consider the following case: PATH="/bin:/bin/ls" # Last part is a filename instead of a directory

import os; os.execvp("blabla", [""]) Throws: OSError: [Errno 20] Not a directory

Now, consider a similar but differently-ordered PATH: PATH="/bin/ls:/bin" # First part is a filename instead of a directory

import os; os.execvp("blabla", [""]) Throws: OSError: [Errno 2] No such file or directory

C. Why this behavior is not good: First, IMO there is a certain problem here - differently ordered PATH shouldn't throw different exception. In both cases the executable was not found in PATH, both cases are the same for this matter.

Second, the unix shell (e.g. bash) faces the same issue, and behaves differently. It'll return "command not found" to stdout for both ENOENT and ENOTDIR cases, regardless of the element order in PATH.

D. My recommendation I'd recommend throwing ENOENT even when ENODIR is thrown for some paths. I am not sure what's the least-evil way to do it, I've been thinking of the following patch, but it's not working because it depends on strerror. It also looks kinda ugly:

--- os.py.old 2010-05-02 17:41:21.481909804 +0300 +++ os.py 2010-05-02 18:03:11.261872651 +0300 @@ -386,7 +386,7 @@ saved_tb = tb if saved_exc: raise error, saved_exc, saved_tb

Change environ to automatically call putenv() if it exists

try:

The python functions are thin wrappers around the system calls, and are reporting the result of calling the corresponding system call. The fact that the shell chooses to catch both errors and report a single one would be equivalent to, say, the cmd module doing something similar. It would not be appropriate for the os module to combine the distinct errors into one.