Skip to content

Commit d158dbf

Browse files
committed
Improve test structure
1 parent 848638d commit d158dbf

File tree

1 file changed

+76
-77
lines changed

1 file changed

+76
-77
lines changed

Lib/test/test_repl.py

Lines changed: 76 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import subprocess
66
import sys
77
import unittest
8+
from contextlib import contextmanager
89
from functools import partial
910
from textwrap import dedent
1011
from test import support
@@ -28,7 +29,7 @@
2829
raise unittest.SkipTest("test module requires subprocess")
2930

3031

31-
def spawn_repl(*args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, custom=False, **kw):
32+
def spawn_repl(*args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, custom=False, isolated=True, **kw):
3233
"""Run the Python REPL with the given arguments.
3334
3435
kw is extra keyword args to pass to subprocess.Popen. Returns a Popen
@@ -42,7 +43,11 @@ def spawn_repl(*args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, custom=F
4243
# path may be used by PyConfig_Get("module_search_paths") to build the
4344
# default module search path.
4445
stdin_fname = os.path.join(os.path.dirname(sys.executable), "<stdin>")
45-
cmd_line = [stdin_fname, '-I']
46+
cmd_line = [stdin_fname]
47+
# Isolated mode implies -E, -P and -s, purifies sys.path and ignores PYTHON*
48+
# variables.
49+
if isolated:
50+
cmd_line.append('-I')
4651
# Don't re-run the built-in REPL from interactive mode
4752
# if we're testing a custom REPL (such as the asyncio REPL).
4853
if not custom:
@@ -197,63 +202,6 @@ def foo(x):
197202
]
198203
self.assertEqual(traceback_lines, expected_lines)
199204

200-
def test_pythonstartup_success(self):
201-
# errors based on https://github.com/python/cpython/issues/137576
202-
# case 1: error in user input, but PYTHONSTARTUP is fine
203-
for repl_name, repl in ("REPL", spawn_repl), ("asyncio REPL", spawn_asyncio_repl):
204-
with os_helper.temp_dir() as tmpdir:
205-
script = os.path.join(tmpdir, "pythonstartup.py")
206-
with open(script, "w") as f:
207-
f.write("print('from pythonstartup')" + os.linesep)
208-
209-
env = os.environ.copy()
210-
env['PYTHONSTARTUP'] = script
211-
env["PYTHON_HISTORY"] = os.path.join(tmpdir, ".pythonhist")
212-
p = repl(env=env)
213-
p.stdin.write("1/0")
214-
output = kill_python(p)
215-
216-
with self.subTest(repl_name):
217-
self.assertIn("Traceback (most recent call last):", output)
218-
expected = dedent("""
219-
File "<stdin>", line 1, in <module>
220-
1/0
221-
~^~
222-
ZeroDivisionError: division by zero
223-
""")
224-
self.assertIn("from pythonstartup", output)
225-
self.assertIn(expected, output)
226-
227-
def test_pythonstartup_failure(self):
228-
# case 2: error in PYTHONSTARTUP triggered by user input
229-
for repl_name, repl in ("REPL", spawn_repl), ("asyncio REPL", spawn_asyncio_repl):
230-
with os_helper.temp_dir() as tmpdir:
231-
script = os.path.join(tmpdir, "pythonstartup.py")
232-
with open(script, "w") as f:
233-
f.write("def foo():\n 1/0\n")
234-
235-
env = os.environ.copy()
236-
env['PYTHONSTARTUP'] = script
237-
env["PYTHON_HISTORY"] = os.path.join(tmpdir, ".pythonhist")
238-
p = repl(env=env)
239-
p.stdin.write('foo()')
240-
output = kill_python(p)
241-
242-
with self.subTest(repl_name):
243-
self.assertIn("Traceback (most recent call last):", output)
244-
expected = dedent("""
245-
File "<stdin>", line 1, in <module>
246-
foo()
247-
~~~^^
248-
File "%s", line 2, in foo
249-
1/0
250-
~^~
251-
ZeroDivisionError: division by zero
252-
""") % script
253-
self.assertIn(expected, output)
254-
255-
256-
257205
def test_runsource_show_syntax_error_location(self):
258206
user_input = dedent("""def f(x, x): ...
259207
""")
@@ -287,24 +235,6 @@ def bar(x):
287235
expected = "(30, None, [\'def foo(x):\\n\', \' return x + 1\\n\', \'\\n\'], \'<stdin>\')"
288236
self.assertIn(expected, output, expected)
289237

290-
def test_asyncio_repl_reaches_python_startup_script(self):
291-
with os_helper.temp_dir() as tmpdir:
292-
script = os.path.join(tmpdir, "pythonstartup.py")
293-
with open(script, "w") as f:
294-
f.write("print('pythonstartup done!')" + os.linesep)
295-
f.write("exit(0)" + os.linesep)
296-
297-
env = os.environ.copy()
298-
env["PYTHON_HISTORY"] = os.path.join(tmpdir, ".asyncio_history")
299-
env["PYTHONSTARTUP"] = script
300-
subprocess.check_call(
301-
[sys.executable, "-m", "asyncio"],
302-
stdout=subprocess.PIPE,
303-
stderr=subprocess.PIPE,
304-
env=env,
305-
timeout=SHORT_TIMEOUT,
306-
)
307-
308238
@unittest.skipUnless(pty, "requires pty")
309239
def test_asyncio_repl_is_ok(self):
310240
m, s = pty.openpty()
@@ -341,6 +271,75 @@ def test_asyncio_repl_is_ok(self):
341271
self.assertEqual(exit_code, 0, "".join(output))
342272

343273

274+
@contextmanager
275+
def pythonstartup_env(*, script: str, histfile: str = ".pythonhist", env=None):
276+
with os_helper.temp_dir() as tmpdir:
277+
filename = os.path.join(tmpdir, "pythonstartup.py")
278+
with open(filename, "w") as f:
279+
f.write(os.linesep.join(script.splitlines()))
280+
if env is None:
281+
env = os.environ.copy()
282+
yield env | {"PYTHONSTARTUP": filename, "PYTHON_HISTORY": os.path.join(tmpdir, histfile)}
283+
284+
285+
class TestPythonStartup(unittest.TestCase):
286+
REPLS = [
287+
("REPL", spawn_repl, ".pythonhist"),
288+
("asyncio REPL", spawn_asyncio_repl, ".asyncio_history"),
289+
]
290+
291+
def test_pythonstartup_success(self):
292+
# errors based on https://github.com/python/cpython/issues/137576
293+
# case 1: error in user input, but PYTHONSTARTUP is fine
294+
for repl_name, repl_factory, histfile in self.REPLS:
295+
with (
296+
self.subTest(repl_name),
297+
pythonstartup_env(script="print('from pythonstartup')", histfile=histfile) as env
298+
):
299+
p = repl_factory(env=env, isolated=False)
300+
p.stdin.write("1/0")
301+
output = kill_python(p)
302+
303+
for chunk in (
304+
"from pythonstartup",
305+
"Traceback (most recent call last):",
306+
"""\
307+
File "<stdin>", line 1, in <module>
308+
1/0
309+
~^~
310+
ZeroDivisionError: division by zero
311+
"""
312+
):
313+
self.assertIn(dedent(chunk), output)
314+
315+
def test_pythonstartup_failure(self):
316+
# case 2: error in PYTHONSTARTUP triggered by user input
317+
for repl_name, repl_factory, histfile in self.REPLS:
318+
with (
319+
self.subTest(repl_name),
320+
pythonstartup_env(script="def foo():\n 1/0\n", histfile=histfile) as env
321+
):
322+
p = repl_factory(env=env, isolated=False)
323+
p.stdin.write('foo()')
324+
output = kill_python(p)
325+
326+
for chunk in (
327+
"Traceback (most recent call last):",
328+
"""\
329+
File "<stdin>", line 1, in <module>
330+
foo()
331+
~~~^^
332+
""",
333+
f"""\
334+
File "{env['PYTHONSTARTUP']}", line 2, in foo
335+
1/0
336+
~^~
337+
ZeroDivisionError: division by zero
338+
"""
339+
):
340+
self.assertIn(dedent(chunk), output)
341+
342+
344343
@support.force_not_colorized_test_class
345344
class TestInteractiveModeSyntaxErrors(unittest.TestCase):
346345

0 commit comments

Comments
 (0)