diff --git a/Lib/_pyrepl/console.py b/Lib/_pyrepl/console.py
index 3e72a56807f..03266c4dfc2 100644
--- a/Lib/_pyrepl/console.py
+++ b/Lib/_pyrepl/console.py
@@ -174,7 +174,13 @@ class InteractiveColoredConsole(code.InteractiveConsole):
def runsource(self, source, filename="", symbol="single"):
try:
- tree = ast.parse(source)
+ tree = self.compile.compiler(
+ source,
+ filename,
+ "exec",
+ ast.PyCF_ONLY_AST,
+ incomplete_input=False,
+ )
except (SyntaxError, OverflowError, ValueError):
self.showsyntaxerror(filename, source=source)
return False
@@ -185,7 +191,7 @@ class InteractiveColoredConsole(code.InteractiveConsole):
the_symbol = symbol if stmt is last_stmt else "exec"
item = wrapper([stmt])
try:
- code = self.compile.compiler(item, filename, the_symbol, dont_inherit=True)
+ code = self.compile.compiler(item, filename, the_symbol)
except SyntaxError as e:
if e.args[0] == "'await' outside function":
python = os.path.basename(sys.executable)
diff --git a/Lib/codeop.py b/Lib/codeop.py
index a0276b52d48..adf000ba29f 100644
--- a/Lib/codeop.py
+++ b/Lib/codeop.py
@@ -44,6 +44,7 @@ __all__ = ["compile_command", "Compile", "CommandCompiler"]
# Caveat emptor: These flags are undocumented on purpose and depending
# on their effect outside the standard library is **unsupported**.
PyCF_DONT_IMPLY_DEDENT = 0x200
+PyCF_ONLY_AST = 0x400
PyCF_ALLOW_INCOMPLETE_INPUT = 0x4000
def _maybe_compile(compiler, source, filename, symbol):
@@ -109,12 +110,14 @@ class Compile:
def __init__(self):
self.flags = PyCF_DONT_IMPLY_DEDENT | PyCF_ALLOW_INCOMPLETE_INPUT
- def __call__(self, source, filename, symbol, **kwargs):
- flags = self.flags
+ def __call__(self, source, filename, symbol, flags=0, **kwargs):
+ flags |= self.flags
if kwargs.get('incomplete_input', True) is False:
flags &= ~PyCF_DONT_IMPLY_DEDENT
flags &= ~PyCF_ALLOW_INCOMPLETE_INPUT
codeob = compile(source, filename, symbol, flags, True)
+ if flags & PyCF_ONLY_AST:
+ return codeob # this is an ast.Module in this case
for feature in _features:
if codeob.co_flags & feature.compiler_flag:
self.flags |= feature.compiler_flag
diff --git a/Lib/test/test_pyrepl/test_interact.py b/Lib/test/test_pyrepl/test_interact.py
index b7adaffbac0..0c6df4e5dae 100644
--- a/Lib/test/test_pyrepl/test_interact.py
+++ b/Lib/test/test_pyrepl/test_interact.py
@@ -119,13 +119,38 @@ SyntaxError: duplicate argument 'x' in function definition"""
def test_no_active_future(self):
console = InteractiveColoredConsole()
- source = "x: int = 1; print(__annotate__(1))"
+ source = dedent("""\
+ x: int = 1
+ print(__annotate__(1))
+ """)
f = io.StringIO()
with contextlib.redirect_stdout(f):
result = console.runsource(source)
self.assertFalse(result)
self.assertEqual(f.getvalue(), "{'x': }\n")
+ def test_future_annotations(self):
+ console = InteractiveColoredConsole()
+ source = dedent("""\
+ from __future__ import annotations
+ def g(x: int): ...
+ print(g.__annotations__)
+ """)
+ f = io.StringIO()
+ with contextlib.redirect_stdout(f):
+ result = console.runsource(source)
+ self.assertFalse(result)
+ self.assertEqual(f.getvalue(), "{'x': 'int'}\n")
+
+ def test_future_barry_as_flufl(self):
+ console = InteractiveColoredConsole()
+ f = io.StringIO()
+ with contextlib.redirect_stdout(f):
+ result = console.runsource("from __future__ import barry_as_FLUFL\n")
+ result = console.runsource("""print("black" <> 'blue')\n""")
+ self.assertFalse(result)
+ self.assertEqual(f.getvalue(), "True\n")
+
class TestMoreLines(unittest.TestCase):
def test_invalid_syntax_single_line(self):
diff --git a/Misc/NEWS.d/next/Library/2024-10-05-15-49-53.gh-issue-124960.Bol9hT.rst b/Misc/NEWS.d/next/Library/2024-10-05-15-49-53.gh-issue-124960.Bol9hT.rst
new file mode 100644
index 00000000000..332d6bb54d8
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2024-10-05-15-49-53.gh-issue-124960.Bol9hT.rst
@@ -0,0 +1 @@
+Fix support for the ``barry_as_FLUFL`` future flag in the new REPL.