Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitb1b8962

Browse files
whitphxambv
andauthored
gh-127960 Fix the REPL to set the correct namespace by setting the correct__main__ module (gh-134275)
The `__main__` module imported in the `_pyrepl` module points to the `_pyrepl` module itself when the interpreter was launched without `-m` option and didn't execute a module,while it's an unexpected behavior that `__main__` can be `_pyrepl` and relative imports such as `from . import *` works based on the `_pyrepl` module.Co-authored-by: Łukasz Langa <lukasz@langa.pl>
1 parenta66bae8 commitb1b8962

File tree

7 files changed

+85
-34
lines changed

7 files changed

+85
-34
lines changed

‎Lib/_pyrepl/_module_completer.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@
1717

1818

1919
defmake_default_module_completer()->ModuleCompleter:
20-
# Inside pyrepl, __package__ is set to'_pyrepl'
21-
returnModuleCompleter(namespace={'__package__':'_pyrepl'})
20+
# Inside pyrepl, __package__ is set toNone by default
21+
returnModuleCompleter(namespace={'__package__':None})
2222

2323

2424
classModuleCompleter:

‎Lib/_pyrepl/main.py

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
importerrno
22
importos
33
importsys
4+
importtypes
45

56

67
CAN_USE_PYREPL:bool
@@ -29,12 +30,10 @@ def interactive_console(mainmodule=None, quiet=False, pythonstartup=False):
2930
print(FAIL_REASON,file=sys.stderr)
3031
returnsys._baserepl()
3132

32-
ifmainmodule:
33-
namespace=mainmodule.__dict__
34-
else:
35-
import__main__
36-
namespace=__main__.__dict__
37-
namespace.pop("__pyrepl_interactive_console",None)
33+
ifnotmainmodule:
34+
mainmodule=types.ModuleType("__main__")
35+
36+
namespace=mainmodule.__dict__
3837

3938
# sys._baserepl() above does this internally, we do it here
4039
startup_path=os.getenv("PYTHONSTARTUP")

‎Lib/_pyrepl/readline.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -606,6 +606,7 @@ def _setup(namespace: Mapping[str, Any]) -> None:
606606
# set up namespace in rlcompleter, which requires it to be a bona fide dict
607607
ifnotisinstance(namespace,dict):
608608
namespace=dict(namespace)
609+
_wrapper.config.module_completer=ModuleCompleter(namespace)
609610
_wrapper.config.readline_completer=RLCompleter(namespace).complete
610611

611612
# this is not really what readline.c does. Better than nothing I guess

‎Lib/test/support/__init__.py

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2929,12 +2929,6 @@ def make_clean_env() -> dict[str, str]:
29292929
returnclean_env
29302930

29312931

2932-
definitialized_with_pyrepl():
2933-
"""Detect whether PyREPL was used during Python initialization."""
2934-
# If the main module has a __file__ attribute it's a Python module, which means PyREPL.
2935-
returnhasattr(sys.modules["__main__"],"__file__")
2936-
2937-
29382932
WINDOWS_STATUS= {
29392933
0xC0000005:"STATUS_ACCESS_VIOLATION",
29402934
0xC00000FD:"STATUS_STACK_OVERFLOW",

‎Lib/test/test_pyrepl/test_pyrepl.py

Lines changed: 62 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -926,6 +926,7 @@ def tearDown(self):
926926
defprepare_reader(self,events,namespace):
927927
console=FakeConsole(events)
928928
config=ReadlineConfig()
929+
config.module_completer=ModuleCompleter(namespace)
929930
config.readline_completer=rlcompleter.Completer(namespace).complete
930931
reader=ReadlineAlikeReader(console=console,config=config)
931932
returnreader
@@ -1022,13 +1023,15 @@ def test_builtin_completion_top_level(self):
10221023

10231024
deftest_relative_import_completions(self):
10241025
cases= (
1025-
("from .readl\t\n","from .readline"),
1026-
("from . import readl\t\n","from . import readline"),
1026+
(None,"from .readl\t\n","from .readl"),
1027+
(None,"from . import readl\t\n","from . import readl"),
1028+
("_pyrepl","from .readl\t\n","from .readline"),
1029+
("_pyrepl","from . import readl\t\n","from . import readline"),
10271030
)
1028-
forcode,expectedincases:
1031+
forpackage,code,expectedincases:
10291032
withself.subTest(code=code):
10301033
events=code_to_events(code)
1031-
reader=self.prepare_reader(events,namespace={})
1034+
reader=self.prepare_reader(events,namespace={"__package__":package})
10321035
output=reader.readline()
10331036
self.assertEqual(output,expected)
10341037

@@ -1397,7 +1400,7 @@ def _assertMatchOK(
13971400
)
13981401

13991402
@force_not_colorized
1400-
def_run_repl_globals_test(self,expectations,*,as_file=False,as_module=False):
1403+
def_run_repl_globals_test(self,expectations,*,as_file=False,as_module=False,pythonstartup=False):
14011404
clean_env=make_clean_env()
14021405
clean_env["NO_COLOR"]="1"# force_not_colorized doesn't touch subprocesses
14031406

@@ -1406,9 +1409,13 @@ def _run_repl_globals_test(self, expectations, *, as_file=False, as_module=False
14061409
blue.mkdir()
14071410
mod=blue/"calx.py"
14081411
mod.write_text("FOO = 42",encoding="utf-8")
1412+
startup=blue/"startup.py"
1413+
startup.write_text("BAR = 64",encoding="utf-8")
14091414
commands= [
14101415
"print(f'^{"+var+"=}')"forvarinexpectations
14111416
]+ ["exit()"]
1417+
ifpythonstartup:
1418+
clean_env["PYTHONSTARTUP"]=str(startup)
14121419
ifas_fileandas_module:
14131420
self.fail("as_file and as_module are mutually exclusive")
14141421
elifas_file:
@@ -1427,7 +1434,13 @@ def _run_repl_globals_test(self, expectations, *, as_file=False, as_module=False
14271434
skip=True,
14281435
)
14291436
else:
1430-
self.fail("Choose one of as_file or as_module")
1437+
output,exit_code=self.run_repl(
1438+
commands,
1439+
cmdline_args=[],
1440+
env=clean_env,
1441+
cwd=td,
1442+
skip=True,
1443+
)
14311444

14321445
self.assertEqual(exit_code,0)
14331446
forvar,expectedinexpectations.items():
@@ -1440,6 +1453,23 @@ def _run_repl_globals_test(self, expectations, *, as_file=False, as_module=False
14401453
self.assertNotIn("Exception",output)
14411454
self.assertNotIn("Traceback",output)
14421455

1456+
deftest_globals_initialized_as_default(self):
1457+
expectations= {
1458+
"__name__":"'__main__'",
1459+
"__package__":"None",
1460+
# "__file__" is missing in -i, like in the basic REPL
1461+
}
1462+
self._run_repl_globals_test(expectations)
1463+
1464+
deftest_globals_initialized_from_pythonstartup(self):
1465+
expectations= {
1466+
"BAR":"64",
1467+
"__name__":"'__main__'",
1468+
"__package__":"None",
1469+
# "__file__" is missing in -i, like in the basic REPL
1470+
}
1471+
self._run_repl_globals_test(expectations,pythonstartup=True)
1472+
14431473
deftest_inspect_keeps_globals_from_inspected_file(self):
14441474
expectations= {
14451475
"FOO":"42",
@@ -1449,6 +1479,16 @@ def test_inspect_keeps_globals_from_inspected_file(self):
14491479
}
14501480
self._run_repl_globals_test(expectations,as_file=True)
14511481

1482+
deftest_inspect_keeps_globals_from_inspected_file_with_pythonstartup(self):
1483+
expectations= {
1484+
"FOO":"42",
1485+
"BAR":"64",
1486+
"__name__":"'__main__'",
1487+
"__package__":"None",
1488+
# "__file__" is missing in -i, like in the basic REPL
1489+
}
1490+
self._run_repl_globals_test(expectations,as_file=True,pythonstartup=True)
1491+
14521492
deftest_inspect_keeps_globals_from_inspected_module(self):
14531493
expectations= {
14541494
"FOO":"42",
@@ -1458,26 +1498,32 @@ def test_inspect_keeps_globals_from_inspected_module(self):
14581498
}
14591499
self._run_repl_globals_test(expectations,as_module=True)
14601500

1501+
deftest_inspect_keeps_globals_from_inspected_module_with_pythonstartup(self):
1502+
expectations= {
1503+
"FOO":"42",
1504+
"BAR":"64",
1505+
"__name__":"'__main__'",
1506+
"__package__":"'blue'",
1507+
"__file__":re.compile(r"^'.*calx.py'$"),
1508+
}
1509+
self._run_repl_globals_test(expectations,as_module=True,pythonstartup=True)
1510+
14611511
@force_not_colorized
14621512
deftest_python_basic_repl(self):
14631513
env=os.environ.copy()
1464-
commands= ("from test.support import initialized_with_pyrepl\n"
1465-
"initialized_with_pyrepl()\n"
1466-
"exit()\n")
1467-
1514+
pyrepl_commands="clear\nexit()\n"
14681515
env.pop("PYTHON_BASIC_REPL",None)
1469-
output,exit_code=self.run_repl(commands,env=env,skip=True)
1516+
output,exit_code=self.run_repl(pyrepl_commands,env=env,skip=True)
14701517
self.assertEqual(exit_code,0)
1471-
self.assertIn("True",output)
1472-
self.assertNotIn("False",output)
14731518
self.assertNotIn("Exception",output)
1519+
self.assertNotIn("NameError",output)
14741520
self.assertNotIn("Traceback",output)
14751521

1522+
basic_commands="help\nexit()\n"
14761523
env["PYTHON_BASIC_REPL"]="1"
1477-
output,exit_code=self.run_repl(commands,env=env)
1524+
output,exit_code=self.run_repl(basic_commands,env=env)
14781525
self.assertEqual(exit_code,0)
1479-
self.assertIn("False",output)
1480-
self.assertNotIn("True",output)
1526+
self.assertIn("Type help() for interactive help",output)
14811527
self.assertNotIn("Exception",output)
14821528
self.assertNotIn("Traceback",output)
14831529

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
PyREPL interactive shell no longer starts with ``__package__`` and
2+
``__file__`` global names set to ``_pyrepl`` package internals. Contributed
3+
by Yuichiro Tachibana.

‎Modules/main.c

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -269,13 +269,14 @@ pymain_run_command(wchar_t *command)
269269

270270

271271
staticint
272-
pymain_start_pyrepl_no_main(void)
272+
pymain_start_pyrepl(intpythonstartup)
273273
{
274274
intres=0;
275275
PyObject*console=NULL;
276276
PyObject*empty_tuple=NULL;
277277
PyObject*kwargs=NULL;
278278
PyObject*console_result=NULL;
279+
PyObject*main_module=NULL;
279280

280281
PyObject*pyrepl=PyImport_ImportModule("_pyrepl.main");
281282
if (pyrepl==NULL) {
@@ -299,7 +300,13 @@ pymain_start_pyrepl_no_main(void)
299300
res=pymain_exit_err_print();
300301
gotodone;
301302
}
302-
if (!PyDict_SetItemString(kwargs,"pythonstartup",_PyLong_GetOne())) {
303+
main_module=PyImport_AddModuleRef("__main__");
304+
if (main_module==NULL) {
305+
res=pymain_exit_err_print();
306+
gotodone;
307+
}
308+
if (!PyDict_SetItemString(kwargs,"mainmodule",main_module)
309+
&& !PyDict_SetItemString(kwargs,"pythonstartup",pythonstartup ?Py_True :Py_False)) {
303310
console_result=PyObject_Call(console,empty_tuple,kwargs);
304311
if (console_result==NULL) {
305312
res=pymain_exit_err_print();
@@ -311,6 +318,7 @@ pymain_start_pyrepl_no_main(void)
311318
Py_XDECREF(empty_tuple);
312319
Py_XDECREF(console);
313320
Py_XDECREF(pyrepl);
321+
Py_XDECREF(main_module);
314322
returnres;
315323
}
316324

@@ -562,7 +570,7 @@ pymain_run_stdin(PyConfig *config)
562570
intrun=PyRun_AnyFileExFlags(stdin,"<stdin>",0,&cf);
563571
return (run!=0);
564572
}
565-
returnpymain_run_module(L"_pyrepl",0);
573+
returnpymain_start_pyrepl(0);
566574
}
567575

568576

@@ -595,7 +603,7 @@ pymain_repl(PyConfig *config, int *exitcode)
595603
*exitcode= (run!=0);
596604
return;
597605
}
598-
intrun=pymain_start_pyrepl_no_main();
606+
intrun=pymain_start_pyrepl(1);
599607
*exitcode= (run!=0);
600608
return;
601609
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp