Repository navigation
Interpreter exits on Windows due to ValueError raised in linecache.py #122170
Copy link
Copy link
Closed
Labels
Description
Bug report
Bug description:
It's possible to make the interpreter exit on Windows in 3.13.0b4 and main due to ValueError: stat: path too long for Windows being raised by os.stat() in updatecache in linecache.py when trying to print a traceback:
>>> exec(compile("print(2**100000)", "s"*99999, "exec"))
Exception ignored in the internal traceback machinery:
Traceback (most recent call last):
File "~\PycharmProjects\cpython\Lib\traceback.py", line 139, in _print_exception_bltin
return print_exception(exc, limit=BUILTIN_EXCEPTION_LIMIT, file=file, colorize=colorize)
File "~\PycharmProjects\cpython\Lib\traceback.py", line 129, in print_exception
te = TracebackException(type(value), value, tb, limit=limit, compact=True)
File "~\PycharmProjects\cpython\Lib\traceback.py", line 1133, in __init__
context = TracebackException(
File "~\PycharmProjects\cpython\Lib\traceback.py", line 1043, in __init__
self.stack = StackSummary._extract_from_extended_frame_gen(
File "~\PycharmProjects\cpython\Lib\traceback.py", line 492, in _extract_from_extended_frame_gen
f.line
File "~\PycharmProjects\cpython\Lib\traceback.py", line 369, in line
self._set_lines()
File "~\PycharmProjects\cpython\Lib\traceback.py", line 350, in _set_lines
lines.append(linecache.getline(self.filename, lineno).rstrip())
File "~\PycharmProjects\cpython\Lib\linecache.py", line 25, in getline
lines = getlines(filename, module_globals)
File "~\PycharmProjects\cpython\Lib\linecache.py", line 41, in getlines
return updatecache(filename, module_globals)
File "~\PycharmProjects\cpython\Lib\linecache.py", line 100, in updatecache
stat = os.stat(fullname)
ValueError: stat: path too long for Windows
Traceback (most recent call last):
File "~\PycharmProjects\cpython\Lib\code.py", line 91, in runcode
exec(code, self.locals)
File "<python-input-2>", line 1, in <module>
File "sss[...]sssssss", line 1, in <module>
ValueError: Exceeds the limit (4300 digits) for integer string conversion; use sys.set_int_max_str_digits() to increase the limit
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "~\PycharmProjects\cpython\Lib\runpy.py", line 198, in _run_module_as_main
return _run_code(code, main_globals, None,
File "~\PycharmProjects\cpython\Lib\runpy.py", line 88, in _run_code
exec(code, run_globals)
File "~\PycharmProjects\cpython\Lib\_pyrepl\__main__.py", line 6, in <module>
__pyrepl_interactive_console()
File "~\PycharmProjects\cpython\Lib\_pyrepl\main.py", line 59, in interactive_console
run_multiline_interactive_console(console)
File "~\PycharmProjects\cpython\Lib\_pyrepl\simple_interact.py", line 156, in run_multiline_interactive_console
more = console.push(_strip_final_indent(statement), filename=input_name, _symbol="single") # type: ignore[call-arg]
File "~\PycharmProjects\cpython\Lib\code.py", line 303, in push
more = self.runsource(source, filename, symbol=_symbol)
File "~\PycharmProjects\cpython\Lib\_pyrepl\console.py", line 200, in runsource
self.runcode(code)
File "~\PycharmProjects\cpython\Lib\code.py", line 95, in runcode
self.showtraceback()
File "~\PycharmProjects\cpython\Lib\_pyrepl\console.py", line 168, in showtraceback
super().showtraceback(colorize=self.can_colorize)
File "~\PycharmProjects\cpython\Lib\code.py", line 147, in showtraceback
lines = traceback.format_exception(ei[0], ei[1], last_tb.tb_next, colorize=colorize)
File "~\PycharmProjects\cpython\Lib\traceback.py", line 154, in format_exception
te = TracebackException(type(value), value, tb, limit=limit, compact=True)
File "~\PycharmProjects\cpython\Lib\traceback.py", line 1043, in __init__
self.stack = StackSummary._extract_from_extended_frame_gen(
File "~\PycharmProjects\cpython\Lib\traceback.py", line 492, in _extract_from_extended_frame_gen
f.line
File "~\PycharmProjects\cpython\Lib\traceback.py", line 369, in line
self._set_lines()
File "~\PycharmProjects\cpython\Lib\traceback.py", line 350, in _set_lines
lines.append(linecache.getline(self.filename, lineno).rstrip())
File "~\PycharmProjects\cpython\Lib\linecache.py", line 25, in getline
lines = getlines(filename, module_globals)
File "~\PycharmProjects\cpython\Lib\linecache.py", line 41, in getlines
return updatecache(filename, module_globals)
File "~\PycharmProjects\cpython\Lib\linecache.py", line 100, in updatecache
stat = os.stat(fullname)
ValueError: stat: path too long for Windows
[Thread 30100.0x6108 exited with code 1]
[Thread 30100.0x5da4 exited with code 1]
[Thread 30100.0x90a0 exited with code 1]
[Inferior 1 (process 30100) exited with code 01]CPython versions tested on:
3.13, CPython main branch
Operating systems tested on:
Windows
Linked PRs
- gh-122170: Fix interpreter exiting due to ValueError from
os.statfor too long filename on Windows #122175 - gh-122170: Handle
ValueErrorraised byos.statinlinecache#122176 - [3.13] gh-122170: Handle ValueError raised by os.stat() in linecache (GH-122176) #122348
- [3.12] gh-122170: Handle ValueError raised by os.stat() in linecache (GH-122176) #122349
Reactions are currently unavailable
Activity
picnixz commentedon Jul 23, 2024
I'm surprised that
os.statraises a ValueError in this case actually and not an OSError. I'll make a PR for handling this case in linecache.devdanzin commentedon Jul 23, 2024
I have one ready.
picnixz commentedon Jul 23, 2024
Oh I was just going to push it now xD Go for yours then, I'll review it.
devdanzin commentedon Jul 23, 2024
Yours is much more complete and polished, is it OK if I close mine?
picnixz commentedon Jul 23, 2024
Err... it's up to you.
devdanzin commentedon Jul 23, 2024
I'll close it, I'd rather have code of higher quality in the standard library than code I wrote :)
eryksun commentedon Jul 23, 2024
For some reason, code was added to the
path_tconverter to raiseValueErrorif the path length exceeds 32767 characters. The length limit is actually 32766 characters, because the terminating null must be included in the buffer size. Properly, the expressionUNICODE_STRING_MAX_CHARS - 1should be used instead of 32766. But really, apath_tusing function should just let the system API fail and raise anOSError, either for the system error codeERROR_FILENAME_EXCED_RANGEorERROR_PATH_NOT_FOUND, depending on the system API function that's called.cpython/Modules/posixmodule.c
Lines 1331 to 1336 in 2c1b1e7
cpython/Modules/posixmodule.c
Lines 1398 to 1403 in 2c1b1e7
ZeroIntensity commentedon Jul 23, 2024
I get a different error when using the reproducer on Linux:
CC @picnixz, this might be a separate issue. This still occurs on both PRs.
devdanzin commentedon Jul 23, 2024
That's #122145.
picnixz commentedon Jul 23, 2024
Which is fixed by #122161
picnixz commentedon Jul 23, 2024
@eryksun I see the ValueError was implemented in #118355, which is recent enough. However, I think that
os.statraising an OSError would be more correct because on Linux, this is what happens:OSError: [Errno 36] File name too long: 'but on Windows, IIRC, it should be a "FileNotFound" (but I have no way to check this now). Do you think I should open another issue foros.stat? (and maybe more functions?)eryksun commentedon Jul 23, 2024
The issue is with the
path_tconverter, not specifically withos.stat(). I'd be in favor of letting the system API call fail and raising whicheverOSErrorexception corresponds to the error code. CENAMETOOLONGisn't used on Windows. It's going to be eitherENOENTorEINVAL, and only the former gets specialized asFileNotFoundError.If long paths are enabled at the system level (via the "LongPathsEnabled" registry value in Windows 10+), then the legacy DOS
MAX_PATH(260) limit is removed, and only native NT limits are enforced. In this case, WinAPICreateFileW()fails withERROR_FILENAME_EXCED_RANGE(206) if the pathname length exceeds the system's pathname length limitUNICODE_STRING_MAX_CHARS(32767, including the terminating null), or fails withERROR_INVALID_NAME(123) if the filename length exceeds the filesystem's filename length limit, which is typically the system's filename length limitMAXIMUM_FILENAME_LENGTH(256, including the terminating null). The C runtime library mapsERROR_FILENAME_EXCED_RANGEto CENOENT(2), and it mapsERROR_INVALID_NAMEto CEINVAL(22). For the sake of consistency, Python follows suit in "PC/errmap.h".If long paths are disabled or not supported (i.e. prior to Windows 10), then pathnames that aren't prefixed by "\\?\" are limited to
MAX_PATH(260), and filenames are typically limited toMAXIMUM_FILENAME_LENGTH. In this case, WinAPICreateFileW()fails withERROR_PATH_NOT_FOUND(3) if the pathname length exceedsMAX_PATHor withERROR_FILENAME_EXCED_RANGEif the pathname length exceedsUNICODE_STRING_MAX_CHARS. Usually in this case the filename length limit doesn't affect the error, assuming the filesystem's limit isMAXIMUM_FILENAME_LENGTH. In the rare case where the pathname of the working directory is only 4 characters (e.g. "C:\" plus the terminating null), then a filename length of exactly 256 characters causes the error to beERROR_INVALID_NAMEinstead ofERROR_PATH_NOT_FOUND. The C runtime library mapsERROR_PATH_NOT_FOUNDto CENOENT, and Python follows suit.picnixz commentedon Jul 24, 2024
I'll file an issue to summarize what you told me (and to make the discussion distinct from this issue). If we were to patch the
path_tconverter, then we would not need the patch for linecache and it could be better IMO (I don't really like to intercept this ValueError that is raised byos.statbecause of a conversion function and not because of the underlying C call).devdanzin commentedon Jul 24, 2024
I believe that would also fix two minor related issues I've found (posting here so I don't forget about them):
picnixz commentedon Jul 24, 2024
Actually, I probably found another bug in linecache where requesting a filename with null bytes makes it fail:
but linecache is not supposed to fail =/.
picnixz commentedon Jul 24, 2024
(So the PR is still correct, would not be fixed even if we change the
path_tconverter since raising ValueError for NUL bytes seems to be something that was done for ages)gh-122170: Handle ValueError raised by os.stat() in linecache (GH-122176
pythongh-122170: Handle ValueError raised by os.stat() in linecache (p…
pythongh-122170: Handle ValueError raised by os.stat() in linecache (p…
[3.12] gh-122170: Handle ValueError raised by os.stat() in linecache (G…
[3.13] gh-122170: Handle ValueError raised by os.stat() in linecache (G…
picnixz commentedon Jul 27, 2024
Now that the PRs have been merged, I think this specific issue can be closed. It remains to fix the other issue at the level of imports and
sys.path.insert, but I'm not sure it's something related to linecache. For now, I'm closing this issue (though, it remains to merge #122161 so if anyone can do it, I'd be happy) and we should open a new one for the imports related issue (could you do it @devdanzin please?)zooba commentedon Aug 6, 2024
For the record, while I think it's okay for
linecacheto silently ignore this issue, I don't think that applies generally. Any code that's attempting to use the bad value ought to raiseValueError, and most likely it ought to propagate out to whoever provided the bad value.So I'm not going to insist on reverting anything here, but I do intend to hold up further changes until they've been properly considered.