-
-
Notifications
You must be signed in to change notification settings - Fork 30.3k
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
Python fails to read a script whose path is /dev/fd/X
#87235
Comments
Sometimes (especially from wrapper scripts) one would like to call Python with a script that should be read from a file-descriptor, like However on OSX (I've tried it on For example:
The file I've tried both Python 3.9.1, 3.8.2 and even 2.7.18 and 2.7.16, all with the same results. (This makes me think it's actually an OSX issue?) On Linux this works flawlesly. Furthermore if one uses something else like Also as seen from the examples, this is not a "shell issue" or something similar; it just seems to hit a corner case when the script path is |
I can reproduce the issue on macOS 11.1. As you write:
If I read the code in Modules/main.c correctly the main difference between the two scenario's is that the first scenario using the C stdio library to read the file (in pymain_run_file_obj), and the latter uses the normal Python io stack. Reading /dev/fd/9 works fine when using either stdio or open/read in C code. I have not yet tried to untangle the layers of C code from pymain_run_file_obj to actually reading the script, there might be something there that sheds light on what's going on here. I'm not yet willing to claim this is an OS bug as I've failed to reproduce this outside of Python. |
I would suggest to start digging from the following piece of code in int ispyc = 0;
if (ftell(fp) == 0) {
if (fread(buf, 1, 2, fp) == 2 &&
((unsigned int)buf[1]<<8 | buf[0]) == halfmagic)
ispyc = 1;
rewind(fp);
} |
The problem is not in pythonrun.c as the program doesn't get there. The issue is very weird, and could be a bug in the OS. The script is read by Modules/main.c: pymain_run_file_obj. That calls git diff ../Modules/main.c (gh-83765-doc-large-shmmem)cpython
diff --git a/Modules/main.c b/Modules/main.c
index 7edfeb3365..d5a7577098 100644
--- a/Modules/main.c
+++ b/Modules/main.c
@@ -329,6 +329,8 @@ pymain_run_file_obj(PyObject *program_name, PyObject *filename,
program_name, filename, errno, strerror(errno));
return 2;
}
+ PySys_FormatStderr("opened %R at %ld\n", filename, ftell(fp));
+ rewind(fp);
if (skip_source_first_line) {
int ch; With this patch a small file prints: Tracing further: _Py_fopen_obj is defined in Python/fileutils.c, and calls fopen (on unix-y systems like macOS). Similar instrumentation in that file shows that the file offset is wrong directly after calling fopen(3). But that's in the context of Python, a small program that contains similar code works fine. However... If I change that program to first read some binds and then reopen /dev/fd/... the second open inherits the file offset from the first file pointer! E.g. (in pseudo C without error checking):
I do not yet know how relevant this is, from what I've seen so far |
I've done some more sleuthing, and the finding in the previous message was relevant... Turns out the file is opened a second time, but not using This also explains why small files work fine: those are to small to seek to the offset of the zip directory, and hence zipimporter fails when it tries to change the file offset. In a quick hack I changed zipimporter._read_directory to reset the file offset and that fixes this issue: diff --git a/Lib/zipimport.py b/Lib/zipimport.py
index 016f1b8a79..9d0e6c808d 100644
--- a/Lib/zipimport.py
+++ b/Lib/zipimport.py
@@ -347,6 +347,8 @@ def _read_directory(archive):
raise ZipImportError(f"can't open Zip file: {archive!r}", path=archive)
with fp:
+ ro_off = fp.tell()
+ try:
try:
fp.seek(-END_CENTRAL_DIR_SIZE, 2)
header_position = fp.tell()
@@ -455,6 +457,8 @@ def _read_directory(archive):
t = (path, compress, data_size, file_size, file_offset, time, date, crc)
files[name] = t
count += 1
+ finally:
+ fp.seek(ro_off, 0)
_bootstrap._verbose_message('zipimport: found {} names in {!r}', count, archive)
return files
Inline patch because it is not ready for a PR at this point. |
Summary:
Creating a PR for this shouldn't be too hard, but not today... This definitely needs a test. |
…ks on macOS On macOS all file descriptors for a particular file in /dev/fd share the same file offset, that is ``open("/dev/fd/9", "r")`` behaves more like ``dup(9)`` than a regular open. This causes problems when a user tries to run "/dev/fd/9" as a script because zipimport changes the file offset to try to read a zipfile directory. Therefore change zipimport to reset the file offset after trying to read the zipfile directory.
I've added a PR that fixes the issue, including a test that failed before I changed the zipimport module. |
…macOS (#99768) On macOS all file descriptors for a particular file in /dev/fd share the same file offset, that is ``open("/dev/fd/9", "r")`` behaves more like ``dup(9)`` than a regular open. This causes problems when a user tries to run "/dev/fd/9" as a script because zipimport changes the file offset to try to read a zipfile directory. Therefore change zipimport to reset the file offset after trying to read the zipfile directory.
…ks on macOS (pythonGH-99768) On macOS all file descriptors for a particular file in /dev/fd share the same file offset, that is ``open("/dev/fd/9", "r")`` behaves more like ``dup(9)`` than a regular open. This causes problems when a user tries to run "/dev/fd/9" as a script because zipimport changes the file offset to try to read a zipfile directory. Therefore change zipimport to reset the file offset after trying to read the zipfile directory. (cherry picked from commit d08fb25) Co-authored-by: Ronald Oussoren <[email protected]>
…ks on macOS (pythonGH-99768) On macOS all file descriptors for a particular file in /dev/fd share the same file offset, that is ``open("/dev/fd/9", "r")`` behaves more like ``dup(9)`` than a regular open. This causes problems when a user tries to run "/dev/fd/9" as a script because zipimport changes the file offset to try to read a zipfile directory. Therefore change zipimport to reset the file offset after trying to read the zipfile directory. (cherry picked from commit d08fb25) Co-authored-by: Ronald Oussoren <[email protected]>
…macOS (GH-99768) On macOS all file descriptors for a particular file in /dev/fd share the same file offset, that is ``open("/dev/fd/9", "r")`` behaves more like ``dup(9)`` than a regular open. This causes problems when a user tries to run "/dev/fd/9" as a script because zipimport changes the file offset to try to read a zipfile directory. Therefore change zipimport to reset the file offset after trying to read the zipfile directory. (cherry picked from commit d08fb25) Co-authored-by: Ronald Oussoren <[email protected]>
Nice catch! Looks like the 3.10 backport is still pending |
…rks on macOS (GH-99768) (#99817) On macOS all file descriptors for a particular file in /dev/fd share the same file offset, that is ``open("/dev/fd/9", "r")`` behaves more like ``dup(9)`` than a regular open. This causes problems when a user tries to run "/dev/fd/9" as a script because zipimport changes the file offset to try to read a zipfile directory. Therefore change zipimport to reset the file offset after trying to read the zipfile directory. (cherry picked from commit d08fb25) Co-authored-by: Ronald Oussoren <[email protected]> * Regen zipimport --------- Co-authored-by: Ronald Oussoren <[email protected]> Co-authored-by: Shantanu <[email protected]> Co-authored-by: Łukasz Langa <[email protected]>
Fixed in 3.10 as well. |
Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields:
bugs.python.org fields:
Linked PRs
The text was updated successfully, but these errors were encountered: