Skip to content

Commit 78bdd3d

Browse files
[3.14] gh-70331: Protect IDLE's imports from user files in the current directory (GH-157643) (#158035)
gh-70331: Protect IDLE's imports from user files in the current directory (GH-157643) Start the user process with -P, so that the current directory is not on sys.path while idlelib.run and its dependencies are imported. sys.path is set later by transfer_path(). Protect __main__, idle, and pyshell entry points. (cherry picked from commit 6893326) Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
1 parent c554143 commit 78bdd3d

5 files changed

Lines changed: 48 additions & 3 deletions

File tree

‎Lib/idlelib/__main__.py‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,12 @@
33
44
Run IDLE as python -m idlelib
55
"""
6+
import sys
7+
8+
if not sys.flags.safe_path:
9+
# Remove the current directory, prepended by "python -m", so that
10+
# user files do not shadow IDLE's imports (gh-70331).
11+
del sys.path[0]
12+
613
import idlelib.pyshell
714
idlelib.pyshell.main()

‎Lib/idlelib/idle.py‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
1-
import os.path
21
import sys
32

3+
if __spec__ is not None and not sys.flags.safe_path:
4+
# Remove the current directory, prepended by "python -m", so that
5+
# user files do not shadow IDLE's imports (gh-70331).
6+
del sys.path[0]
7+
8+
import os.path
9+
410

511
# Enable running IDLE with idlelib in a non-standard location.
612
# This was once used to run development versions of IDLE.

‎Lib/idlelib/idle_test/test_pyshell.py‎

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,11 @@
33

44
from idlelib import pyshell
55
import os
6+
import sys
67
import unittest
7-
from test.support import requires
8+
from unittest import mock
9+
from test.support import os_helper, requires
10+
from test.support.script_helper import assert_python_ok
811
from tkinter import Tk
912

1013

@@ -37,6 +40,24 @@ def test_fix_user_path(self):
3740
eq(pyshell.fix_user_path(['/a', '/b']), ['/a', '/b'])
3841
eq(pyshell.fix_user_path([idlelib_dir]), [])
3942

43+
def test_shadowed_stdlib(self):
44+
# gh-70331: user files in the current directory must not shadow
45+
# the stdlib modules imported by IDLE.
46+
with os_helper.temp_dir() as cwd:
47+
for name in ('os', 'random', 'tkinter'):
48+
os_helper.create_empty_file(os.path.join(cwd, f'{name}.py'))
49+
for module in 'idlelib', 'idlelib.idle', 'idlelib.pyshell':
50+
with self.subTest(module=module):
51+
assert_python_ok('-m', module, '-h',
52+
__isolated=False, __cwd=cwd)
53+
54+
def test_build_subprocess_arglist(self):
55+
interp = mock.Mock(port=1234)
56+
args = pyshell.ModifiedInterpreter.build_subprocess_arglist(interp)
57+
# gh-70331: -P keeps the current directory out of sys.path.
58+
self.assertEqual(args[:2], [sys.executable, '-P'])
59+
self.assertEqual(args[-1], '1234')
60+
4061

4162
class PyShellFileListTest(unittest.TestCase):
4263

‎Lib/idlelib/pyshell.py‎

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@
33
import sys
44
if __name__ == "__main__":
55
sys.modules['idlelib.pyshell'] = sys.modules['__main__']
6+
if __spec__ is not None and not sys.flags.safe_path:
7+
# Remove the current directory, prepended by "python -m", so that
8+
# user files do not shadow IDLE's imports (gh-70331).
9+
del sys.path[0]
610

711
try:
812
from tkinter import *
@@ -455,7 +459,10 @@ def build_subprocess_arglist(self):
455459
del_exitf = idleConf.GetOption('main', 'General', 'delete-exitfunc',
456460
default=False, type='bool')
457461
command = f"__import__('idlelib.run').run.main({del_exitf!r})"
458-
return [sys.executable] + w + ["-c", command, str(self.port)]
462+
# -P keeps the current directory off sys.path, so that user files
463+
# do not shadow run's imports (gh-70331). transfer_path() sets
464+
# sys.path later.
465+
return [sys.executable, '-P'] + w + ["-c", command, str(self.port)]
459466

460467
def start_subprocess(self):
461468
addr = (HOST, self.port)
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
IDLE no longer fails to start with ``python -m idlelib``, and its user process
2+
no longer fails to start, when the current directory contains user files with
3+
the same names as standard library modules that IDLE imports, such as
4+
``random.py`` or ``tkinter.py``.

0 commit comments

Comments
 (0)