-
-
Notifications
You must be signed in to change notification settings - Fork 32.1k
gh-118908: Use __main__ for the default PyREPL namespace #121054
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,51 +1,3 @@ | ||
import os | ||
import sys | ||
|
||
CAN_USE_PYREPL: bool | ||
if sys.platform != "win32": | ||
CAN_USE_PYREPL = True | ||
else: | ||
CAN_USE_PYREPL = sys.getwindowsversion().build >= 10586 # Windows 10 TH2 | ||
|
||
|
||
def interactive_console(mainmodule=None, quiet=False, pythonstartup=False): | ||
global CAN_USE_PYREPL | ||
if not CAN_USE_PYREPL: | ||
return sys._baserepl() | ||
|
||
startup_path = os.getenv("PYTHONSTARTUP") | ||
if pythonstartup and startup_path: | ||
import tokenize | ||
with tokenize.open(startup_path) as f: | ||
startup_code = compile(f.read(), startup_path, "exec") | ||
exec(startup_code) | ||
|
||
# set sys.{ps1,ps2} just before invoking the interactive interpreter. This | ||
# mimics what CPython does in pythonrun.c | ||
if not hasattr(sys, "ps1"): | ||
sys.ps1 = ">>> " | ||
if not hasattr(sys, "ps2"): | ||
sys.ps2 = "... " | ||
|
||
run_interactive = None | ||
try: | ||
import errno | ||
if not os.isatty(sys.stdin.fileno()): | ||
raise OSError(errno.ENOTTY, "tty required", "stdin") | ||
from .simple_interact import check | ||
if err := check(): | ||
raise RuntimeError(err) | ||
from .simple_interact import run_multiline_interactive_console | ||
run_interactive = run_multiline_interactive_console | ||
except Exception as e: | ||
from .trace import trace | ||
msg = f"warning: can't use pyrepl: {e}" | ||
trace(msg) | ||
print(msg, file=sys.stderr) | ||
CAN_USE_PYREPL = False | ||
if run_interactive is None: | ||
return sys._baserepl() | ||
return run_interactive(mainmodule) | ||
|
||
if __name__ == "__main__": | ||
interactive_console() | ||
from .main import interactive_console as __pyrepl_interactive_console | ||
__pyrepl_interactive_console() |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
import os | ||
import sys | ||
|
||
CAN_USE_PYREPL: bool | ||
if sys.platform != "win32": | ||
CAN_USE_PYREPL = True | ||
else: | ||
CAN_USE_PYREPL = sys.getwindowsversion().build >= 10586 # Windows 10 TH2 | ||
|
||
|
||
def interactive_console(mainmodule=None, quiet=False, pythonstartup=False): | ||
global CAN_USE_PYREPL | ||
if not CAN_USE_PYREPL: | ||
return sys._baserepl() | ||
|
||
if mainmodule: | ||
namespace = mainmodule.__dict__ | ||
else: | ||
import __main__ | ||
namespace = __main__.__dict__ | ||
namespace.pop("__pyrepl_interactive_console", None) | ||
|
||
startup_path = os.getenv("PYTHONSTARTUP") | ||
if pythonstartup and startup_path: | ||
import tokenize | ||
with tokenize.open(startup_path) as f: | ||
startup_code = compile(f.read(), startup_path, "exec") | ||
exec(startup_code, namespace) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note: manual PYTHONSTARTUP also executes in our custom namespace, otherwise it doesn't work. That's why namespace extraction from the optional mainmodule passed happens here already. |
||
|
||
# set sys.{ps1,ps2} just before invoking the interactive interpreter. This | ||
# mimics what CPython does in pythonrun.c | ||
if not hasattr(sys, "ps1"): | ||
sys.ps1 = ">>> " | ||
if not hasattr(sys, "ps2"): | ||
sys.ps2 = "... " | ||
|
||
run_interactive = None | ||
try: | ||
import errno | ||
if not os.isatty(sys.stdin.fileno()): | ||
raise OSError(errno.ENOTTY, "tty required", "stdin") | ||
from .simple_interact import check | ||
if err := check(): | ||
raise RuntimeError(err) | ||
from .simple_interact import run_multiline_interactive_console | ||
run_interactive = run_multiline_interactive_console | ||
except Exception as e: | ||
from .trace import trace | ||
msg = f"warning: can't use pyrepl: {e}" | ||
trace(msg) | ||
print(msg, file=sys.stderr) | ||
CAN_USE_PYREPL = False | ||
if run_interactive is None: | ||
return sys._baserepl() | ||
run_interactive(namespace) |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -843,15 +843,26 @@ def test_bracketed_paste_single_line(self): | |
class TestMain(TestCase): | ||
@force_not_colorized | ||
def test_exposed_globals_in_repl(self): | ||
expected_output = ( | ||
"[\'__annotations__\', \'__builtins__\', \'__doc__\', \'__loader__\', " | ||
"\'__name__\', \'__package__\', \'__spec__\']" | ||
) | ||
pre = "['__annotations__', '__builtins__'" | ||
post = "'__loader__', '__name__', '__package__', '__spec__']" | ||
output, exit_code = self.run_repl(["sorted(dir())", "exit"]) | ||
if "can\'t use pyrepl" in output: | ||
if "can't use pyrepl" in output: | ||
self.skipTest("pyrepl not available") | ||
self.assertEqual(exit_code, 0) | ||
self.assertIn(expected_output, output) | ||
|
||
# if `__main__` is not a file (impossible with pyrepl) | ||
case1 = f"{pre}, '__doc__', {post}" in output | ||
|
||
# if `__main__` is an uncached .py file (no .pyc) | ||
case2 = f"{pre}, '__doc__', '__file__', {post}" in output | ||
|
||
# if `__main__` is a cached .pyc file and the .py source exists | ||
case3 = f"{pre}, '__cached__', '__doc__', '__file__', {post}" in output | ||
|
||
# if `__main__` is a cached .pyc file but there's no .py source file | ||
case4 = f"{pre}, '__cached__', '__doc__', {post}" in output | ||
|
||
self.assertTrue(case1 or case2 or case3 or case4, output) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. LMK if you know a more elegant solution for "one of these needs to pass". There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess this is what I would have done, but I like yours better. cases = [
f"{pre}, '__doc__', {post}", # if `__main__` is not a file (impossible with pyrepl)
...
]
self.assertTrue(any(case in output for case in cases), output) |
||
|
||
def test_dumb_terminal_exits_cleanly(self): | ||
env = os.environ.copy() | ||
|
Uh oh!
There was an error while loading. Please reload this page.