Skip to content

Commit a2c1656

Browse files
committed
Merge branch 'main' into gnu-stack-overflow-check
2 parents b3ccffc + 8a76eb8 commit a2c1656

18 files changed

+452
-266
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()

Doc/library/wsgiref.rst

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,8 @@ in type annotations.
119119
applications to set up dummy environments. It should NOT be used by actual WSGI
120120
servers or applications, since the data is fake!
121121

122-
Example usage::
122+
Example usage (see also :func:`~wsgiref.simple_server.demo_app`
123+
for another example)::
123124

124125
from wsgiref.util import setup_testing_defaults
125126
from wsgiref.simple_server import make_server
@@ -312,6 +313,8 @@ request. (E.g., using the :func:`shift_path_info` function from
312313
as :mod:`wsgiref.simple_server`) is able to run a simple WSGI application
313314
correctly.
314315

316+
The *start_response* callable should follow the :class:`.StartResponse` protocol.
317+
315318

316319
.. class:: WSGIServer(server_address, RequestHandlerClass)
317320

@@ -679,7 +682,9 @@ input, output, and error streams.
679682

680683
This method can access the current error using ``sys.exception()``,
681684
and should pass that information to *start_response* when calling it (as
682-
described in the "Error Handling" section of :pep:`3333`).
685+
described in the "Error Handling" section of :pep:`3333`). In particular,
686+
the *start_response* callable should follow the :class:`.StartResponse`
687+
protocol.
683688

684689
The default implementation just uses the :attr:`error_status`,
685690
:attr:`error_headers`, and :attr:`error_body` attributes to generate an output
@@ -781,7 +786,7 @@ in :pep:`3333`.
781786
.. versionadded:: 3.11
782787

783788

784-
.. class:: StartResponse()
789+
.. class:: StartResponse
785790

786791
A :class:`typing.Protocol` describing :pep:`start_response()
787792
<3333#the-start-response-callable>`
@@ -816,7 +821,8 @@ in :pep:`3333`.
816821
Examples
817822
--------
818823

819-
This is a working "Hello World" WSGI application::
824+
This is a working "Hello World" WSGI application, where the *start_response*
825+
callable should follow the :class:`.StartResponse` protocol::
820826

821827
"""
822828
Every WSGI application must have an application object - a callable

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

Lib/test/test_sys.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1103,8 +1103,14 @@ def test_getallocatedblocks(self):
11031103
# code objects is a large fraction of the total number of
11041104
# references, this can cause the total number of allocated
11051105
# blocks to exceed the total number of references.
1106-
if not support.Py_GIL_DISABLED:
1107-
self.assertLess(b, sys.gettotalrefcount())
1106+
#
1107+
# For some reason, iOS seems to trigger the "unlikely to happen"
1108+
# case reliably under CI conditions. It's not clear why; but as
1109+
# this test is checking the behavior of getallocatedblock()
1110+
# under garbage collection, we can skip this pre-condition check
1111+
# for now. See GH-130384.
1112+
if not support.Py_GIL_DISABLED and not support.is_apple_mobile:
1113+
self.assertLess(a, sys.gettotalrefcount())
11081114
except AttributeError:
11091115
# gettotalrefcount() not available
11101116
pass
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.

Modules/_testmultiphase.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -969,3 +969,21 @@ PyInit__test_shared_gil_only(void)
969969
{
970970
return PyModuleDef_Init(&shared_gil_only_def);
971971
}
972+
973+
974+
static PyModuleDef_Slot no_multiple_interpreter_slot_slots[] = {
975+
{Py_mod_exec, execfunc},
976+
{Py_mod_gil, Py_MOD_GIL_NOT_USED},
977+
{0, NULL},
978+
};
979+
980+
static PyModuleDef no_multiple_interpreter_slot_def = TEST_MODULE_DEF(
981+
"_test_no_multiple_interpreter_slot",
982+
no_multiple_interpreter_slot_slots,
983+
testexport_methods);
984+
985+
PyMODINIT_FUNC
986+
PyInit__test_no_multiple_interpreter_slot(void)
987+
{
988+
return PyModuleDef_Init(&no_multiple_interpreter_slot_def);
989+
}

0 commit comments

Comments
 (0)