Skip to content

Commit 39aebcb

Browse files
committed
Merge branch 'main' into zipapp_issue
2 parents b6ec2bf + 7ed3dc6 commit 39aebcb

File tree

12 files changed

+108
-22
lines changed

12 files changed

+108
-22
lines changed

Doc/c-api/module.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -415,7 +415,7 @@ The available slot types are:
415415
in one module definition.
416416
417417
If ``Py_mod_multiple_interpreters`` is not specified, the import
418-
machinery defaults to ``Py_MOD_MULTIPLE_INTERPRETERS_NOT_SUPPORTED``.
418+
machinery defaults to ``Py_MOD_MULTIPLE_INTERPRETERS_SUPPORTED``.
419419
420420
.. versionadded:: 3.12
421421

Doc/c-api/unicode.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -607,6 +607,15 @@ APIs:
607607
decref'ing the returned objects.
608608
609609
610+
.. c:function:: const char* PyUnicode_GetDefaultEncoding(void)
611+
612+
Return the name of the default string encoding, ``"utf-8"``.
613+
See :func:`sys.getdefaultencoding`.
614+
615+
The returned string does not need to be freed, and is valid
616+
until interpreter shutdown.
617+
618+
610619
.. c:function:: Py_ssize_t PyUnicode_GetLength(PyObject *unicode)
611620
612621
Return the length of the Unicode object, in code points.

Doc/data/refcounts.dat

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2770,6 +2770,9 @@ PyUnicode_FromFormatV:PyObject*::+1:
27702770
PyUnicode_FromFormatV:const char*:format::
27712771
PyUnicode_FromFormatV:va_list:args::
27722772

2773+
PyUnicode_GetDefaultEncoding:const char*:::
2774+
PyUnicode_GetDefaultEncoding::void::
2775+
27732776
PyUnicode_GetLength:Py_ssize_t:::
27742777
PyUnicode_GetLength:PyObject*:unicode:0:
27752778

Doc/library/sys.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -771,8 +771,8 @@ always available. Unless explicitly noted otherwise, all variables are read-only
771771

772772
.. function:: getdefaultencoding()
773773

774-
Return the name of the current default string encoding used by the Unicode
775-
implementation.
774+
Return ``'utf-8'``. This is the name of the default string encoding, used
775+
in methods like :meth:`str.encode`.
776776

777777

778778
.. function:: getdlopenflags()

Lib/_pyrepl/console.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,8 @@ def repaint(self) -> None: ...
152152

153153

154154
class InteractiveColoredConsole(code.InteractiveConsole):
155+
STATEMENT_FAILED = object()
156+
155157
def __init__(
156158
self,
157159
locals: dict[str, object] | None = None,
@@ -173,6 +175,16 @@ def _excepthook(self, typ, value, tb):
173175
limit=traceback.BUILTIN_EXCEPTION_LIMIT)
174176
self.write(''.join(lines))
175177

178+
def runcode(self, code):
179+
try:
180+
exec(code, self.locals)
181+
except SystemExit:
182+
raise
183+
except BaseException:
184+
self.showtraceback()
185+
return self.STATEMENT_FAILED
186+
return None
187+
176188
def runsource(self, source, filename="<input>", symbol="single"):
177189
try:
178190
tree = self.compile.compiler(
@@ -209,5 +221,7 @@ def runsource(self, source, filename="<input>", symbol="single"):
209221
if code is None:
210222
return True
211223

212-
self.runcode(code)
224+
result = self.runcode(code)
225+
if result is self.STATEMENT_FAILED:
226+
break
213227
return False

Lib/asyncio/__main__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ def callback():
7575
self.write("\nKeyboardInterrupt\n")
7676
else:
7777
self.showtraceback()
78-
78+
return self.STATEMENT_FAILED
7979

8080
class REPLThread(threading.Thread):
8181

Lib/test/test_import/__init__.py

Lines changed: 28 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2386,8 +2386,10 @@ def test_single_init_extension_compat(self):
23862386

23872387
@unittest.skipIf(_testmultiphase is None, "test requires _testmultiphase module")
23882388
def test_multi_init_extension_compat(self):
2389+
# Module with Py_MOD_PER_INTERPRETER_GIL_SUPPORTED
23892390
module = '_testmultiphase'
23902391
require_extension(module)
2392+
23912393
if not Py_GIL_DISABLED:
23922394
with self.subTest(f'{module}: not strict'):
23932395
self.check_compatible_here(module, strict=False)
@@ -2398,6 +2400,8 @@ def test_multi_init_extension_compat(self):
23982400

23992401
@unittest.skipIf(_testmultiphase is None, "test requires _testmultiphase module")
24002402
def test_multi_init_extension_non_isolated_compat(self):
2403+
# Module with Py_MOD_MULTIPLE_INTERPRETERS_NOT_SUPPORTED
2404+
# and Py_MOD_GIL_NOT_USED
24012405
modname = '_test_non_isolated'
24022406
filename = _testmultiphase.__file__
24032407
module = import_extension_from_file(modname, filename)
@@ -2413,20 +2417,31 @@ def test_multi_init_extension_non_isolated_compat(self):
24132417

24142418
@unittest.skipIf(_testmultiphase is None, "test requires _testmultiphase module")
24152419
def test_multi_init_extension_per_interpreter_gil_compat(self):
2416-
modname = '_test_shared_gil_only'
2417-
filename = _testmultiphase.__file__
2418-
module = import_extension_from_file(modname, filename)
24192420

2420-
require_extension(module)
2421-
with self.subTest(f'{modname}: isolated, strict'):
2422-
self.check_incompatible_here(modname, filename, isolated=True)
2423-
with self.subTest(f'{modname}: not isolated, strict'):
2424-
self.check_compatible_here(modname, filename,
2425-
strict=True, isolated=False)
2426-
if not Py_GIL_DISABLED:
2427-
with self.subTest(f'{modname}: not isolated, not strict'):
2428-
self.check_compatible_here(modname, filename,
2429-
strict=False, isolated=False)
2421+
# _test_shared_gil_only:
2422+
# Explicit Py_MOD_MULTIPLE_INTERPRETERS_SUPPORTED (default)
2423+
# and Py_MOD_GIL_NOT_USED
2424+
# _test_no_multiple_interpreter_slot:
2425+
# No Py_mod_multiple_interpreters slot
2426+
# and Py_MOD_GIL_NOT_USED
2427+
for modname in ('_test_shared_gil_only',
2428+
'_test_no_multiple_interpreter_slot'):
2429+
with self.subTest(modname=modname):
2430+
2431+
filename = _testmultiphase.__file__
2432+
module = import_extension_from_file(modname, filename)
2433+
2434+
require_extension(module)
2435+
with self.subTest(f'{modname}: isolated, strict'):
2436+
self.check_incompatible_here(modname, filename,
2437+
isolated=True)
2438+
with self.subTest(f'{modname}: not isolated, strict'):
2439+
self.check_compatible_here(modname, filename,
2440+
strict=True, isolated=False)
2441+
if not Py_GIL_DISABLED:
2442+
with self.subTest(f'{modname}: not isolated, not strict'):
2443+
self.check_compatible_here(
2444+
modname, filename, strict=False, isolated=False)
24302445

24312446
@unittest.skipIf(_testinternalcapi is None, "requires _testinternalcapi")
24322447
def test_python_compat(self):

Lib/test/test_pyrepl/test_interact.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,19 @@ def test_multiple_statements_output(self):
5353
self.assertFalse(more)
5454
self.assertEqual(f.getvalue(), "1\n")
5555

56+
@force_not_colorized
57+
def test_multiple_statements_fail_early(self):
58+
console = InteractiveColoredConsole()
59+
code = dedent("""\
60+
raise Exception('foobar')
61+
print('spam&eggs')
62+
""")
63+
f = io.StringIO()
64+
with contextlib.redirect_stderr(f):
65+
console.runsource(code)
66+
self.assertIn('Exception: foobar', f.getvalue())
67+
self.assertNotIn('spam&eggs', f.getvalue())
68+
5669
def test_empty(self):
5770
namespace = {}
5871
code = ""

Lib/test/test_repl.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -294,7 +294,15 @@ def f():
294294
self.assertEqual(traceback_lines, expected_lines)
295295

296296

297-
class TestAsyncioREPLContextVars(unittest.TestCase):
297+
class TestAsyncioREPL(unittest.TestCase):
298+
def test_multiple_statements_fail_early(self):
299+
user_input = "1 / 0; print('afterwards')"
300+
p = spawn_repl("-m", "asyncio")
301+
p.stdin.write(user_input)
302+
output = kill_python(p)
303+
self.assertIn("ZeroDivisionError", output)
304+
self.assertNotIn("afterwards", output)
305+
298306
def test_toplevel_contextvars_sync(self):
299307
user_input = dedent("""\
300308
from contextvars import ContextVar
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Execution of multiple statements in the new REPL now stops immediately upon
2+
the first exception encountered. Patch by Bartosz Sławecki.

0 commit comments

Comments
 (0)