Skip to content

Commit ce948ca

Browse files
authored
Merge branch '3.13' into backport-3.13-ttyname_r
2 parents cf1909a + 65da5db commit ce948ca

29 files changed

+329
-102
lines changed

Doc/c-api/init.rst

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1445,6 +1445,17 @@ All of the following functions must be called after :c:func:`Py_Initialize`.
14451445
14461446
.. versionadded:: 3.8
14471447
1448+
1449+
.. c:function:: PyObject* PyUnstable_InterpreterState_GetMainModule(PyInterpreterState *interp)
1450+
1451+
Return a :term:`strong reference` to the ``__main__`` `module object <moduleobjects>`_
1452+
for the given interpreter.
1453+
1454+
The caller must hold the GIL.
1455+
1456+
.. versionadded:: 3.13
1457+
1458+
14481459
.. c:type:: PyObject* (*_PyFrameEvalFunction)(PyThreadState *tstate, _PyInterpreterFrame *frame, int throwflag)
14491460
14501461
Type of a frame evaluation function.

Doc/library/asyncio-eventloop.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,9 @@ Scheduling callbacks
236236
another thread, this function *must* be used, since :meth:`call_soon` is not
237237
thread-safe.
238238

239+
This function is safe to be called from a reentrant context or signal handler,
240+
however, it is not safe or fruitful to use the returned handle in such contexts.
241+
239242
Raises :exc:`RuntimeError` if called on a loop that's been closed.
240243
This can happen on a secondary thread when the main application is
241244
shutting down.
@@ -957,6 +960,9 @@ Watching file descriptors
957960
invoke *callback* with the specified arguments once *fd* is available for
958961
reading.
959962

963+
Any preexisting callback registered for *fd* is cancelled and replaced by
964+
*callback*.
965+
960966
.. method:: loop.remove_reader(fd)
961967

962968
Stop monitoring the *fd* file descriptor for read availability. Returns
@@ -968,6 +974,9 @@ Watching file descriptors
968974
invoke *callback* with the specified arguments once *fd* is available for
969975
writing.
970976

977+
Any preexisting callback registered for *fd* is cancelled and replaced by
978+
*callback*.
979+
971980
Use :func:`functools.partial` :ref:`to pass keyword arguments
972981
<asyncio-pass-keywords>` to *callback*.
973982

Doc/library/asyncio-queue.rst

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -115,11 +115,11 @@ Queue
115115

116116
.. method:: task_done()
117117

118-
Indicate that a formerly enqueued task is complete.
118+
Indicate that a formerly enqueued work item is complete.
119119

120120
Used by queue consumers. For each :meth:`~Queue.get` used to
121-
fetch a task, a subsequent call to :meth:`task_done` tells the
122-
queue that the processing on the task is complete.
121+
fetch a work item, a subsequent call to :meth:`task_done` tells the
122+
queue that the processing on the work item is complete.
123123

124124
If a :meth:`join` is currently blocking, it will resume when all
125125
items have been processed (meaning that a :meth:`task_done`

Doc/library/json.rst

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,10 @@ Basic Usage
324324
:raises JSONDecodeError:
325325
When the data being deserialized is not a valid JSON document.
326326

327+
:raises UnicodeDecodeError:
328+
When the data being deserialized does not contain
329+
UTF-8, UTF-16 or UTF-32 encoded data.
330+
327331
.. versionchanged:: 3.1
328332

329333
* Added the optional *object_pairs_hook* parameter.
@@ -343,15 +347,11 @@ Basic Usage
343347

344348
.. function:: loads(s, *, cls=None, object_hook=None, parse_float=None, parse_int=None, parse_constant=None, object_pairs_hook=None, **kw)
345349

346-
Deserialize *s* (a :class:`str`, :class:`bytes` or :class:`bytearray`
350+
Identical to :func:`load`, but instead of a file-like object,
351+
deserialize *s* (a :class:`str`, :class:`bytes` or :class:`bytearray`
347352
instance containing a JSON document) to a Python object using this
348353
:ref:`conversion table <json-to-py-table>`.
349354

350-
The other arguments have the same meaning as in :func:`load`.
351-
352-
If the data being deserialized is not a valid JSON document, a
353-
:exc:`JSONDecodeError` will be raised.
354-
355355
.. versionchanged:: 3.6
356356
*s* can now be of type :class:`bytes` or :class:`bytearray`. The
357357
input encoding should be UTF-8, UTF-16 or UTF-32.

Lib/asyncio/base_events.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -477,7 +477,12 @@ def create_task(self, coro, *, name=None, context=None):
477477

478478
task.set_name(name)
479479

480-
return task
480+
try:
481+
return task
482+
finally:
483+
# gh-128552: prevent a refcycle of
484+
# task.exception().__traceback__->BaseEventLoop.create_task->task
485+
del task
481486

482487
def set_task_factory(self, factory):
483488
"""Set a task factory that will be used by loop.create_task().

Lib/asyncio/taskgroups.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,12 @@ def create_task(self, coro, *, name=None, context=None):
205205
else:
206206
self._tasks.add(task)
207207
task.add_done_callback(self._on_task_done)
208-
return task
208+
try:
209+
return task
210+
finally:
211+
# gh-128552: prevent a refcycle of
212+
# task.exception().__traceback__->TaskGroup.create_task->task
213+
del task
209214

210215
# Since Python 3.8 Tasks propagate all exceptions correctly,
211216
# except for KeyboardInterrupt and SystemExit which are

Lib/pydoc.py

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ class or function within a module or module in a package. If the
5454
# the current directory is changed with os.chdir(), an incorrect
5555
# path will be displayed.
5656

57+
import ast
5758
import __future__
5859
import builtins
5960
import importlib._bootstrap
@@ -381,21 +382,29 @@ def ispackage(path):
381382
return False
382383

383384
def source_synopsis(file):
384-
line = file.readline()
385-
while line[:1] == '#' or not line.strip():
386-
line = file.readline()
387-
if not line: break
388-
line = line.strip()
389-
if line[:4] == 'r"""': line = line[1:]
390-
if line[:3] == '"""':
391-
line = line[3:]
392-
if line[-1:] == '\\': line = line[:-1]
393-
while not line.strip():
394-
line = file.readline()
395-
if not line: break
396-
result = line.split('"""')[0].strip()
397-
else: result = None
398-
return result
385+
"""Return the one-line summary of a file object, if present"""
386+
387+
string = ''
388+
try:
389+
tokens = tokenize.generate_tokens(file.readline)
390+
for tok_type, tok_string, _, _, _ in tokens:
391+
if tok_type == tokenize.STRING:
392+
string += tok_string
393+
elif tok_type == tokenize.NEWLINE:
394+
with warnings.catch_warnings():
395+
# Ignore the "invalid escape sequence" warning.
396+
warnings.simplefilter("ignore", SyntaxWarning)
397+
docstring = ast.literal_eval(string)
398+
if not isinstance(docstring, str):
399+
return None
400+
return docstring.strip().split('\n')[0].strip()
401+
elif tok_type == tokenize.OP and tok_string in ('(', ')'):
402+
string += tok_string
403+
elif tok_type not in (tokenize.COMMENT, tokenize.NL, tokenize.ENCODING):
404+
return None
405+
except (tokenize.TokenError, UnicodeDecodeError, SyntaxError):
406+
return None
407+
return None
399408

400409
def synopsis(filename, cache={}):
401410
"""Get the one-line summary out of a module file."""

Lib/test/libregrtest/findtests.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import os
22
import sys
33
import unittest
4+
from collections.abc import Container
45

56
from test import support
67

@@ -34,7 +35,7 @@ def findtestdir(path: StrPath | None = None) -> StrPath:
3435
return path or os.path.dirname(os.path.dirname(__file__)) or os.curdir
3536

3637

37-
def findtests(*, testdir: StrPath | None = None, exclude=(),
38+
def findtests(*, testdir: StrPath | None = None, exclude: Container[str] = (),
3839
split_test_dirs: set[TestName] = SPLITTESTDIRS,
3940
base_mod: str = "") -> TestList:
4041
"""Return a list of all applicable test modules."""
@@ -60,8 +61,9 @@ def findtests(*, testdir: StrPath | None = None, exclude=(),
6061
return sorted(tests)
6162

6263

63-
def split_test_packages(tests, *, testdir: StrPath | None = None, exclude=(),
64-
split_test_dirs=SPLITTESTDIRS):
64+
def split_test_packages(tests, *, testdir: StrPath | None = None,
65+
exclude: Container[str] = (),
66+
split_test_dirs=SPLITTESTDIRS) -> list[TestName]:
6567
testdir = findtestdir(testdir)
6668
splitted = []
6769
for name in tests:
@@ -75,9 +77,9 @@ def split_test_packages(tests, *, testdir: StrPath | None = None, exclude=(),
7577
return splitted
7678

7779

78-
def _list_cases(suite):
80+
def _list_cases(suite: unittest.TestSuite) -> None:
7981
for test in suite:
80-
if isinstance(test, unittest.loader._FailedTest):
82+
if isinstance(test, unittest.loader._FailedTest): # type: ignore[attr-defined]
8183
continue
8284
if isinstance(test, unittest.TestSuite):
8385
_list_cases(test)
@@ -87,7 +89,7 @@ def _list_cases(suite):
8789

8890
def list_cases(tests: TestTuple, *,
8991
match_tests: TestFilter | None = None,
90-
test_dir: StrPath | None = None):
92+
test_dir: StrPath | None = None) -> None:
9193
support.verbose = False
9294
set_match_tests(match_tests)
9395

Lib/test/libregrtest/main.py

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import sysconfig
77
import time
88
import trace
9+
from typing import NoReturn
910

1011
from test.support import (os_helper, MS_WINDOWS, flush_std_streams,
1112
suppress_immortalization)
@@ -155,7 +156,7 @@ def __init__(self, ns: Namespace, _add_python_opts: bool = False):
155156
self.next_single_test: TestName | None = None
156157
self.next_single_filename: StrPath | None = None
157158

158-
def log(self, line=''):
159+
def log(self, line: str = '') -> None:
159160
self.logger.log(line)
160161

161162
def find_tests(self, tests: TestList | None = None) -> tuple[TestTuple, TestList | None]:
@@ -233,11 +234,11 @@ def find_tests(self, tests: TestList | None = None) -> tuple[TestTuple, TestList
233234
return (tuple(selected), tests)
234235

235236
@staticmethod
236-
def list_tests(tests: TestTuple):
237+
def list_tests(tests: TestTuple) -> None:
237238
for name in tests:
238239
print(name)
239240

240-
def _rerun_failed_tests(self, runtests: RunTests):
241+
def _rerun_failed_tests(self, runtests: RunTests) -> RunTests:
241242
# Configure the runner to re-run tests
242243
if self.num_workers == 0 and not self.single_process:
243244
# Always run tests in fresh processes to have more deterministic
@@ -269,7 +270,7 @@ def _rerun_failed_tests(self, runtests: RunTests):
269270
self.run_tests_sequentially(runtests)
270271
return runtests
271272

272-
def rerun_failed_tests(self, runtests: RunTests):
273+
def rerun_failed_tests(self, runtests: RunTests) -> None:
273274
if self.python_cmd:
274275
# Temp patch for https://github.com/python/cpython/issues/94052
275276
self.log(
@@ -338,7 +339,7 @@ def run_bisect(self, runtests: RunTests) -> None:
338339
if not self._run_bisect(runtests, name, progress):
339340
return
340341

341-
def display_result(self, runtests):
342+
def display_result(self, runtests: RunTests) -> None:
342343
# If running the test suite for PGO then no one cares about results.
343344
if runtests.pgo:
344345
return
@@ -368,7 +369,7 @@ def run_test(
368369

369370
return result
370371

371-
def run_tests_sequentially(self, runtests) -> None:
372+
def run_tests_sequentially(self, runtests: RunTests) -> None:
372373
if self.coverage:
373374
tracer = trace.Trace(trace=False, count=True)
374375
else:
@@ -425,7 +426,7 @@ def run_tests_sequentially(self, runtests) -> None:
425426
if previous_test:
426427
print(previous_test)
427428

428-
def get_state(self):
429+
def get_state(self) -> str:
429430
state = self.results.get_state(self.fail_env_changed)
430431
if self.first_state:
431432
state = f'{self.first_state} then {state}'
@@ -474,7 +475,7 @@ def display_summary(self) -> None:
474475
state = self.get_state()
475476
print(f"Result: {state}")
476477

477-
def create_run_tests(self, tests: TestTuple):
478+
def create_run_tests(self, tests: TestTuple) -> RunTests:
478479
return RunTests(
479480
tests,
480481
fail_fast=self.fail_fast,
@@ -685,9 +686,9 @@ def _execute_python(self, cmd, environ):
685686
f"Command: {cmd_text}")
686687
# continue executing main()
687688

688-
def _add_python_opts(self):
689-
python_opts = []
690-
regrtest_opts = []
689+
def _add_python_opts(self) -> None:
690+
python_opts: list[str] = []
691+
regrtest_opts: list[str] = []
691692

692693
environ, keep_environ = self._add_cross_compile_opts(regrtest_opts)
693694
if self.ci_mode:
@@ -728,7 +729,7 @@ def tmp_dir(self) -> StrPath:
728729
)
729730
return self._tmp_dir
730731

731-
def main(self, tests: TestList | None = None):
732+
def main(self, tests: TestList | None = None) -> NoReturn:
732733
if self.want_add_python_opts:
733734
self._add_python_opts()
734735

@@ -757,7 +758,7 @@ def main(self, tests: TestList | None = None):
757758
sys.exit(exitcode)
758759

759760

760-
def main(tests=None, _add_python_opts=False, **kwargs):
761+
def main(tests=None, _add_python_opts=False, **kwargs) -> NoReturn:
761762
"""Run the Python suite."""
762763
ns = _parse_args(sys.argv[1:], **kwargs)
763764
Regrtest(ns, _add_python_opts=_add_python_opts).main(tests=tests)

Lib/test/libregrtest/pgo.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@
5050
'test_xml_etree_c',
5151
]
5252

53-
def setup_pgo_tests(cmdline_args, pgo_extended: bool):
53+
def setup_pgo_tests(cmdline_args, pgo_extended: bool) -> None:
5454
if not cmdline_args and not pgo_extended:
5555
# run default set of tests for PGO training
5656
cmdline_args[:] = PGO_TESTS[:]

0 commit comments

Comments
 (0)