Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 34k
gh-140287: Handle PYTHONSTARTUP script exceptions in the asyncio REPL#140288
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
base:main
Are you sure you want to change the base?
Changes from all commits
faa22f45b3ad2c00edac4e39662240790740440d0eaf6f657848638dd158dbf9355da73dc4cacc786584b76db67cad1748d114ed5e6e10adac6cd83149740abffac1ca4c307ea774da5b3ed3d4df6dfd8b004839ce03cce9e925100a50a50f8b8d535701fef393aaad875fd2a4377d82c4e488eb9ffea7ab799e62f8f99b6020996125efa6cc624d1812d22f6749b83e76426d376c7a4File filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -5,6 +5,7 @@ | ||
| import subprocess | ||
| import sys | ||
| import unittest | ||
| from contextlib import contextmanager | ||
| from functools import partial | ||
| from textwrap import dedent | ||
| from test import support | ||
| @@ -67,6 +68,19 @@ def spawn_repl(*args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, custom=F | ||
| spawn_asyncio_repl = partial(spawn_repl, "-m", "asyncio", custom=True) | ||
| @contextmanager | ||
| def new_pythonstartup_env(*, code: str, histfile: str = ".pythonhist"): | ||
| """Create environment variables for a PYTHONSTARTUP script in a temporary directory.""" | ||
| with os_helper.temp_dir() as tmpdir: | ||
| filename = os.path.join(tmpdir, "pythonstartup.py") | ||
| with open(filename, "w") as f: | ||
| f.write(code) | ||
| yield{ | ||
| "PYTHONSTARTUP": filename, | ||
| "PYTHON_HISTORY": os.path.join(tmpdir, histfile) | ||
| } | ||
| def run_on_interactive_mode(source): | ||
| """Spawn a new Python interpreter, pass the given | ||
| input source code from the stdin and return the | ||
| @@ -260,8 +274,6 @@ def make_repl(env): | ||
| """) % script | ||
| self.assertIn(expected, output) | ||
| def test_runsource_show_syntax_error_location(self): | ||
| user_input = dedent("""def f(x, x): ... | ||
| """) | ||
| @@ -356,6 +368,45 @@ def test_asyncio_repl_is_ok(self): | ||
| self.assertEqual(exit_code, 0, "".join(output)) | ||
| def test_pythonstartup_success(self): | ||
| # errors based on https://github.com/python/cpython/issues/137576 | ||
| # case 1: error in user input, but PYTHONSTARTUP is fine | ||
| startup_code = "print('notice from pythonstartup')" | ||
| startup_env = self.enterContext(new_pythonstartup_env(code=startup_code)) | ||
| p = spawn_repl("-q", env=os.environ | startup_env, isolated=False) | ||
| p.stdin.write("1/0") | ||
| output = kill_python(p) | ||
| self.assertStartsWith(output, 'notice from pythonstartup') | ||
| expected = dedent("""\ | ||
| Traceback (most recent call last): | ||
| File "<stdin>", line 1, in <module> | ||
| 1/0 | ||
| ~^~ | ||
| ZeroDivisionError: division by zero | ||
| """) | ||
| self.assertIn(expected, output) | ||
| def test_pythonstartup_failure(self): | ||
| # case 2: error in PYTHONSTARTUP triggered by user input | ||
| startup_code = "def foo():\n 1/0\n" | ||
| startup_env = self.enterContext(new_pythonstartup_env(code=startup_code)) | ||
| p = spawn_repl("-q", env=os.environ | startup_env, isolated=False) | ||
| p.stdin.write("foo()") | ||
| output = kill_python(p) | ||
| expected = dedent(f"""\ | ||
| Traceback (most recent call last): | ||
| File "<stdin>", line 1, in <module> | ||
| foo() | ||
| ~~~^^ | ||
| File "{startup_env['PYTHONSTARTUP']}", line 2, in foo | ||
| 1/0 | ||
| ~^~ | ||
| ZeroDivisionError: division by zero | ||
| """) | ||
| self.assertIn(expected, output) | ||
| @support.force_not_colorized_test_class | ||
| class TestInteractiveModeSyntaxErrors(unittest.TestCase): | ||
| @@ -376,6 +427,7 @@ def f(): | ||
| self.assertEqual(traceback_lines, expected_lines) | ||
| @support.force_not_colorized_test_class | ||
MemberAuthor
| ||
| class TestAsyncioREPL(unittest.TestCase): | ||
| def test_multiple_statements_fail_early(self): | ||
| user_input = "1 / 0; print(f'afterwards:{1+1}')" | ||
| @@ -426,6 +478,50 @@ def test_quiet_mode(self): | ||
| self.assertEqual(p.returncode, 0) | ||
| self.assertEqual(output[:3], ">>>") | ||
| def test_pythonstartup_success(self): | ||
| startup_code = dedent("print('notice from pythonstartup in asyncio repl')") | ||
| startup_env = self.enterContext( | ||
| new_pythonstartup_env(code=startup_code, histfile=".asyncio_history")) | ||
| p = spawn_repl( | ||
| "-qm", "asyncio", | ||
| env=os.environ | startup_env, | ||
| isolated=False, | ||
| custom=True) | ||
| p.stdin.write("1/0") | ||
| output = kill_python(p) | ||
| self.assertStartsWith(output, 'notice from pythonstartup in asyncio repl') | ||
| expected = dedent("""\ | ||
| File "<stdin>", line 1, in <module> | ||
| 1/0 | ||
| ~^~ | ||
| ZeroDivisionError: division by zero | ||
| """) | ||
| self.assertIn(expected, output) | ||
| def test_pythonstartup_failure(self): | ||
| startup_code = "def foo():\n 1/0\n" | ||
| startup_env = self.enterContext( | ||
| new_pythonstartup_env(code=startup_code, histfile=".asyncio_history")) | ||
| p = spawn_repl( | ||
| "-qm", "asyncio", | ||
| env=os.environ | startup_env, | ||
| isolated=False, | ||
| custom=True) | ||
| p.stdin.write("foo()") | ||
| output = kill_python(p) | ||
| expected = dedent(f"""\ | ||
| File "<stdin>", line 1, in <module> | ||
| foo() | ||
| ~~~^^ | ||
| File "{startup_env['PYTHONSTARTUP']}", line 2, in foo | ||
| 1/0 | ||
| ~^~ | ||
| ZeroDivisionError: division by zero | ||
| """) | ||
| self.assertIn(expected, output) | ||
| if __name__ == "__main__": | ||
| unittest.main() | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| The :mod:`asyncio` REPL now properly handles exceptions in :envvar:`PYTHONSTARTUP` scripts. | ||
| Previously, any startup exception could prevent the REPL from starting or even cause | ||
| a fatal error. | ||
| Patch by Bartosz Sławecki in :gh:`140287`. |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I really don't like this, but it resembles the original behavior of the REPLs -- see GH-143023.
I don't think this is correct, but it's not super clear to me, so please chime in to GH-143023 to put your two cents in.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
GH-143023 is also why I didn't add a test for this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We've got some good news, this is not bad! I'll add a test shortly.