55from unittest .mock import patch
66from textwrap import dedent
77
8- from test .support import force_not_colorized
8+ from test .support import captured_stdout , force_not_colorized
99
1010from _pyrepl .console import InteractiveColoredConsole
1111from _pyrepl .simple_interact import _more_lines
@@ -134,11 +134,7 @@ def test_runsource_shows_syntax_error_for_failed_compilation(self):
134134 def test_runsource_shows_syntax_error_for_failed_symtable_checks (self ):
135135 # Some checks cannot be performed by AST parsing only.
136136 # See https://github.com/python/cpython/issues/137376.
137- console = InteractiveColoredConsole ()
138- source = "x = 1; global x; x = 2"
139- with patch .object (console , "showsyntaxerror" ) as mock_showsyntaxerror :
140- console .runsource (source )
141- mock_showsyntaxerror .assert_called_once ()
137+ self ._test_runsource_error ("x = 1; global x; x = 2" )
142138
143139 def test_runsource_survives_null_bytes (self ):
144140 console = InteractiveColoredConsole ()
@@ -162,26 +158,46 @@ def test_no_active_future(self):
162158 self .assertEqual (f .getvalue (), "{'x': <class 'int'>}\n " )
163159
164160 def test_future_annotations (self ):
165- console = InteractiveColoredConsole ()
166- source = dedent ("""\
167- from __future__ import annotations
168- def g(x: int): ...
169- print(g.__annotations__)
170- """ )
171- f = io .StringIO ()
172- with contextlib .redirect_stdout (f ):
173- result = console .runsource (source )
174- self .assertFalse (result )
175- self .assertEqual (f .getvalue (), "{'x': 'int'}\n " )
161+ self ._test_runsource_future (
162+ "from __future__ import annotations" ,
163+ ["def g(x: int): ..." , "print(g.__annotations__)" ],
164+ "{'x': 'int'}\n " ,
165+ )
176166
177167 def test_future_barry_as_flufl (self ):
168+ self ._test_runsource_future (
169+ "from __future__ import barry_as_FLUFL" ,
170+ ["""print("black" <> 'blue')""" ],
171+ "True\n " ,
172+ )
173+
174+ def _test_runsource_error (self , buggy_source ):
178175 console = InteractiveColoredConsole ()
179- f = io .StringIO ()
180- with contextlib .redirect_stdout (f ):
181- result = console .runsource ("from __future__ import barry_as_FLUFL\n " )
182- result = console .runsource ("""print("black" <> 'blue')\n """ )
183- self .assertFalse (result )
184- self .assertEqual (f .getvalue (), "True\n " )
176+ with patch .object (console , "showsyntaxerror" ) as handler :
177+ result = console .runsource (buggy_source )
178+ handler .assert_called_once ()
179+
180+ def _test_runsource_future (self , future_statement , statements , expected ):
181+ """Run future_statement + statements.
182+
183+ This checks whether a standalone future statement remains active
184+ for the entire session lifetime.
185+ """
186+ with self .subTest ("standalone source" ):
187+ console = InteractiveColoredConsole ()
188+ source = "\n " .join ([future_statement , * statements ])
189+ with captured_stdout () as stdout :
190+ result = console .runsource (source )
191+ self .assertFalse (result )
192+ self .assertEqual (stdout .getvalue (), expected )
193+
194+ with self .subTest ("__future__ executed separtely" ):
195+ console = InteractiveColoredConsole ()
196+ with captured_stdout () as stdout :
197+ result = console .runsource (future_statement )
198+ result = console .runsource ("\n " .join (statements ))
199+ self .assertFalse (result )
200+ self .assertEqual (stdout .getvalue (), expected )
185201
186202
187203class TestMoreLines (unittest .TestCase ):
0 commit comments