Skip to content

Commit 7a6d4cc

Browse files
authored
pythongh-122170: Handle ValueError raised by os.stat() in linecache (pythonGH-122176)
1 parent 8ac5565 commit 7a6d4cc

File tree

3 files changed

+37
-2
lines changed

3 files changed

+37
-2
lines changed

Lib/linecache.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ def checkcache(filename=None):
7070
return
7171
try:
7272
stat = os.stat(fullname)
73-
except OSError:
73+
except (OSError, ValueError):
7474
cache.pop(filename, None)
7575
continue
7676
if size != stat.st_size or mtime != stat.st_mtime:
@@ -135,10 +135,12 @@ def updatecache(filename, module_globals=None):
135135
try:
136136
stat = os.stat(fullname)
137137
break
138-
except OSError:
138+
except (OSError, ValueError):
139139
pass
140140
else:
141141
return []
142+
except ValueError: # may be raised by os.stat()
143+
return []
142144
try:
143145
with tokenize.open(fullname) as fp:
144146
lines = fp.readlines()

Lib/test/test_linecache.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,37 @@ def test_loader(self):
280280
self.assertEqual(linecache.getlines(filename, module_globals),
281281
['source for x.y.z\n'])
282282

283+
def test_invalid_names(self):
284+
for name, desc in [
285+
('\x00', 'NUL bytes filename'),
286+
(__file__ + '\x00', 'filename with embedded NUL bytes'),
287+
# A filename with surrogate codes. A UnicodeEncodeError is raised
288+
# by os.stat() upon querying, which is a subclass of ValueError.
289+
("\uD834\uDD1E.py", 'surrogate codes (MUSICAL SYMBOL G CLEF)'),
290+
# For POSIX platforms, an OSError will be raised but for Windows
291+
# platforms, a ValueError is raised due to the path_t converter.
292+
# See: https://github.com/python/cpython/issues/122170
293+
('a' * 1_000_000, 'very long filename'),
294+
]:
295+
with self.subTest(f'updatecache: {desc}'):
296+
linecache.clearcache()
297+
lines = linecache.updatecache(name)
298+
self.assertListEqual(lines, [])
299+
self.assertNotIn(name, linecache.cache)
300+
301+
# hack into the cache (it shouldn't be allowed
302+
# but we never know what people do...)
303+
for key, fullname in [(name, 'ok'), ('key', name), (name, name)]:
304+
with self.subTest(f'checkcache: {desc}',
305+
key=key, fullname=fullname):
306+
linecache.clearcache()
307+
linecache.cache[key] = (0, 1234, [], fullname)
308+
linecache.checkcache(key)
309+
self.assertNotIn(key, linecache.cache)
310+
311+
# just to be sure that we did not mess with cache
312+
linecache.clearcache()
313+
283314

284315
class LineCacheInvalidationTests(unittest.TestCase):
285316
def setUp(self):
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Handle :exc:`ValueError`\s raised by :func:`os.stat` in :mod:`linecache`.
2+
Patch by Bénédikt Tran.

0 commit comments

Comments
 (0)