Skip to content

Commit e8b0992

Browse files
committed
Merge in the main branch
2 parents bea7568 + 802556a commit e8b0992

23 files changed

+353
-151
lines changed

Doc/library/functools.rst

+2-2
Original file line numberDiff line numberDiff line change
@@ -368,8 +368,8 @@ The :mod:`functools` module defines the following functions:
368368

369369
If :data:`Placeholder` sentinels are present in *args*, they will be filled first
370370
when :func:`!partial` is called. This makes it possible to pre-fill any positional
371-
argument with a call to :func:`!partial`; without :data:`!Placeholder`, only the
372-
first positional argument can be pre-filled.
371+
argument with a call to :func:`!partial`; without :data:`!Placeholder`,
372+
only the chosen number of leading positional arguments can be pre-filled.
373373

374374
If any :data:`!Placeholder` sentinels are present, all must be filled at call time:
375375

Include/internal/pycore_object.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ PyAPI_FUNC(void) _Py_NO_RETURN _Py_FatalRefcountErrorFunc(
120120
PyAPI_DATA(Py_ssize_t) _Py_RefTotal;
121121

122122
extern void _Py_AddRefTotal(PyThreadState *, Py_ssize_t);
123-
extern void _Py_IncRefTotal(PyThreadState *);
123+
extern PyAPI_FUNC(void) _Py_IncRefTotal(PyThreadState *);
124124
extern void _Py_DecRefTotal(PyThreadState *);
125125

126126
# define _Py_DEC_REFTOTAL(interp) \

Lib/_pyrepl/windows_console.py

+26-16
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,10 @@ def __init__(self, err: int | None, descr: str | None = None) -> None:
102102
MOVE_DOWN = "\x1b[{}B"
103103
CLEAR = "\x1b[H\x1b[J"
104104

105+
# State of control keys: https://learn.microsoft.com/en-us/windows/console/key-event-record-str
106+
ALT_ACTIVE = 0x01 | 0x02
107+
CTRL_ACTIVE = 0x04 | 0x08
108+
105109

106110
class _error(Exception):
107111
pass
@@ -407,31 +411,37 @@ def get_event(self, block: bool = True) -> Event | None:
407411
continue
408412
return None
409413

410-
key = rec.Event.KeyEvent.uChar.UnicodeChar
414+
key_event = rec.Event.KeyEvent
415+
raw_key = key = key_event.uChar.UnicodeChar
411416

412-
if rec.Event.KeyEvent.uChar.UnicodeChar == "\r":
413-
# Make enter make unix-like
417+
if key == "\r":
418+
# Make enter unix-like
414419
return Event(evt="key", data="\n", raw=b"\n")
415-
elif rec.Event.KeyEvent.wVirtualKeyCode == 8:
420+
elif key_event.wVirtualKeyCode == 8:
416421
# Turn backspace directly into the command
417-
return Event(
418-
evt="key",
419-
data="backspace",
420-
raw=rec.Event.KeyEvent.uChar.UnicodeChar,
421-
)
422-
elif rec.Event.KeyEvent.uChar.UnicodeChar == "\x00":
422+
key = "backspace"
423+
elif key == "\x00":
423424
# Handle special keys like arrow keys and translate them into the appropriate command
424-
code = VK_MAP.get(rec.Event.KeyEvent.wVirtualKeyCode)
425-
if code:
426-
return Event(
427-
evt="key", data=code, raw=rec.Event.KeyEvent.uChar.UnicodeChar
428-
)
425+
key = VK_MAP.get(key_event.wVirtualKeyCode)
426+
if key:
427+
if key_event.dwControlKeyState & CTRL_ACTIVE:
428+
key = f"ctrl {key}"
429+
elif key_event.dwControlKeyState & ALT_ACTIVE:
430+
# queue the key, return the meta command
431+
self.event_queue.insert(0, Event(evt="key", data=key, raw=key))
432+
return Event(evt="key", data="\033") # keymap.py uses this for meta
433+
return Event(evt="key", data=key, raw=key)
429434
if block:
430435
continue
431436

432437
return None
433438

434-
return Event(evt="key", data=key, raw=rec.Event.KeyEvent.uChar.UnicodeChar)
439+
if key_event.dwControlKeyState & ALT_ACTIVE:
440+
# queue the key, return the meta command
441+
self.event_queue.insert(0, Event(evt="key", data=key, raw=raw_key))
442+
return Event(evt="key", data="\033") # keymap.py uses this for meta
443+
444+
return Event(evt="key", data=key, raw=raw_key)
435445

436446
def push_char(self, char: int | bytes) -> None:
437447
"""

Lib/test/test__interpreters.py

+12-1
Original file line numberDiff line numberDiff line change
@@ -557,7 +557,7 @@ def setUp(self):
557557
self.id = _interpreters.create()
558558

559559
def test_signatures(self):
560-
# for method in ['exec', 'run_string', 'run_func']:
560+
# See https://github.com/python/cpython/issues/126654
561561
msg = "expected 'shared' to be a dict"
562562
with self.assertRaisesRegex(TypeError, msg):
563563
_interpreters.exec(self.id, 'a', 1)
@@ -568,6 +568,17 @@ def test_signatures(self):
568568
with self.assertRaisesRegex(TypeError, msg):
569569
_interpreters.run_func(self.id, lambda: None, shared=1)
570570

571+
def test_invalid_shared_encoding(self):
572+
# See https://github.com/python/cpython/issues/127196
573+
bad_shared = {"\uD82A": 0}
574+
msg = 'surrogates not allowed'
575+
with self.assertRaisesRegex(UnicodeEncodeError, msg):
576+
_interpreters.exec(self.id, 'a', shared=bad_shared)
577+
with self.assertRaisesRegex(UnicodeEncodeError, msg):
578+
_interpreters.run_string(self.id, 'a', shared=bad_shared)
579+
with self.assertRaisesRegex(UnicodeEncodeError, msg):
580+
_interpreters.run_func(self.id, lambda: None, shared=bad_shared)
581+
571582

572583
class RunStringTests(TestBase):
573584

Lib/test/test_inspect/test_inspect.py

+5
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from annotationlib import Format, ForwardRef
2+
import asyncio
23
import builtins
34
import collections
45
import copy
@@ -2791,6 +2792,10 @@ async def number_asyncgen():
27912792
async def asyncTearDown(self):
27922793
await self.asyncgen.aclose()
27932794

2795+
@classmethod
2796+
def tearDownClass(cls):
2797+
asyncio._set_event_loop_policy(None)
2798+
27942799
def _asyncgenstate(self):
27952800
return inspect.getasyncgenstate(self.asyncgen)
27962801

Lib/test/test_typing.py

+65
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
import textwrap
4646
import typing
4747
import weakref
48+
import warnings
4849
import types
4950

5051
from test.support import captured_stderr, cpython_only, infinite_recursion, requires_docstrings, import_helper, run_code
@@ -7152,6 +7153,25 @@ class C:
71527153
self.assertEqual(get_type_hints(C, format=annotationlib.Format.STRING),
71537154
{'x': 'undefined'})
71547155

7156+
def test_get_type_hints_format_function(self):
7157+
def func(x: undefined) -> undefined: ...
7158+
7159+
# VALUE
7160+
with self.assertRaises(NameError):
7161+
get_type_hints(func)
7162+
with self.assertRaises(NameError):
7163+
get_type_hints(func, format=annotationlib.Format.VALUE)
7164+
7165+
# FORWARDREF
7166+
self.assertEqual(
7167+
get_type_hints(func, format=annotationlib.Format.FORWARDREF),
7168+
{'x': ForwardRef('undefined'), 'return': ForwardRef('undefined')},
7169+
)
7170+
7171+
# STRING
7172+
self.assertEqual(get_type_hints(func, format=annotationlib.Format.STRING),
7173+
{'x': 'undefined', 'return': 'undefined'})
7174+
71557175

71567176
class GetUtilitiesTestCase(TestCase):
71577177
def test_get_origin(self):
@@ -7254,6 +7274,51 @@ class C(Generic[T]): pass
72547274
self.assertEqual(get_args(Unpack[tuple[Unpack[Ts]]]), (tuple[Unpack[Ts]],))
72557275

72567276

7277+
class EvaluateForwardRefTests(BaseTestCase):
7278+
def test_evaluate_forward_ref(self):
7279+
int_ref = ForwardRef('int')
7280+
missing = ForwardRef('missing')
7281+
self.assertIs(
7282+
typing.evaluate_forward_ref(int_ref, type_params=()),
7283+
int,
7284+
)
7285+
self.assertIs(
7286+
typing.evaluate_forward_ref(
7287+
int_ref, type_params=(), format=annotationlib.Format.FORWARDREF,
7288+
),
7289+
int,
7290+
)
7291+
self.assertIs(
7292+
typing.evaluate_forward_ref(
7293+
missing, type_params=(), format=annotationlib.Format.FORWARDREF,
7294+
),
7295+
missing,
7296+
)
7297+
self.assertEqual(
7298+
typing.evaluate_forward_ref(
7299+
int_ref, type_params=(), format=annotationlib.Format.STRING,
7300+
),
7301+
'int',
7302+
)
7303+
7304+
def test_evaluate_forward_ref_no_type_params(self):
7305+
ref = ForwardRef('int')
7306+
with self.assertWarnsRegex(
7307+
DeprecationWarning,
7308+
(
7309+
"Failing to pass a value to the 'type_params' parameter "
7310+
"of 'typing.evaluate_forward_ref' is deprecated, "
7311+
"as it leads to incorrect behaviour"
7312+
),
7313+
):
7314+
typing.evaluate_forward_ref(ref)
7315+
7316+
# No warnings when `type_params` is passed:
7317+
with warnings.catch_warnings(record=True) as w:
7318+
typing.evaluate_forward_ref(ref, type_params=())
7319+
self.assertEqual(w, [])
7320+
7321+
72577322
class CollectionsAbcTests(BaseTestCase):
72587323

72597324
def test_hashable(self):

Lib/typing.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1024,7 +1024,7 @@ def evaluate_forward_ref(
10241024
owner=None,
10251025
globals=None,
10261026
locals=None,
1027-
type_params=None,
1027+
type_params=_sentinel,
10281028
format=annotationlib.Format.VALUE,
10291029
_recursive_guard=frozenset(),
10301030
):
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix a possible overflow when a class inherits from an absurd number of
2+
super-classes. Reported by Valery Fedorenko. Patch by Bénédikt Tran.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix crash when dict with keys in invalid encoding were passed to several
2+
functions in ``_interpreters`` module.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
When a descriptive error message cannot be provided for an
2+
:exc:`ssl.SSLError`, the "unknown error" message now shows the internal
3+
error code (as retrieved by ``ERR_get_error`` and similar OpenSSL
4+
functions).
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix ``PyREPL`` on Windows to support more keybindings, like the :kbd:`Control-` and :kbd:`Control-` word-skipping keybindings and those with meta (i.e. :kbd:`Alt`), e.g. :kbd:`Alt-d` to ``kill-word`` or :kbd:`Alt-Backspace` ``backward-kill-word``.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fixes :func:`typing.evaluate_forward_ref` not showing deprecation when
2+
``type_params`` arg is not passed.

Modules/_asynciomodule.c

+14-5
Original file line numberDiff line numberDiff line change
@@ -3772,11 +3772,20 @@ _asyncio_all_tasks_impl(PyObject *module, PyObject *loop)
37723772

37733773
llist_for_each_safe(node, &state->asyncio_tasks_head) {
37743774
TaskObj *task = llist_data(node, TaskObj, task_node);
3775-
if (PyList_Append(tasks, (PyObject *)task) < 0) {
3776-
Py_DECREF(tasks);
3777-
Py_DECREF(loop);
3778-
err = 1;
3779-
break;
3775+
// The linked list holds borrowed references to task
3776+
// as such it is possible that the task is concurrently
3777+
// deallocated while added to this list.
3778+
// To protect against concurrent deallocations,
3779+
// we first try to incref the task which would fail
3780+
// if it is concurrently getting deallocated in another thread,
3781+
// otherwise it gets added to the list.
3782+
if (_Py_TryIncref((PyObject *)task)) {
3783+
if (_PyList_AppendTakeRef((PyListObject *)tasks, (PyObject *)task) < 0) {
3784+
Py_DECREF(tasks);
3785+
Py_DECREF(loop);
3786+
err = 1;
3787+
break;
3788+
}
37803789
}
37813790
}
37823791
ASYNCIO_STATE_UNLOCK(state);

Modules/_bz2module.c

+10-4
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,9 @@ typedef struct {
129129
PyThread_type_lock lock;
130130
} BZ2Decompressor;
131131

132+
#define _BZ2Compressor_CAST(op) ((BZ2Compressor *)(op))
133+
#define _BZ2Decompressor_CAST(op) ((BZ2Decompressor *)(op))
134+
132135
/* Helper functions. */
133136

134137
static int
@@ -376,8 +379,9 @@ _bz2_BZ2Compressor_impl(PyTypeObject *type, int compresslevel)
376379
}
377380

378381
static void
379-
BZ2Compressor_dealloc(BZ2Compressor *self)
382+
BZ2Compressor_dealloc(PyObject *op)
380383
{
384+
BZ2Compressor *self = _BZ2Compressor_CAST(op);
381385
BZ2_bzCompressEnd(&self->bzs);
382386
if (self->lock != NULL) {
383387
PyThread_free_lock(self->lock);
@@ -388,7 +392,7 @@ BZ2Compressor_dealloc(BZ2Compressor *self)
388392
}
389393

390394
static int
391-
BZ2Compressor_traverse(BZ2Compressor *self, visitproc visit, void *arg)
395+
BZ2Compressor_traverse(PyObject *self, visitproc visit, void *arg)
392396
{
393397
Py_VISIT(Py_TYPE(self));
394398
return 0;
@@ -680,8 +684,10 @@ _bz2_BZ2Decompressor_impl(PyTypeObject *type)
680684
}
681685

682686
static void
683-
BZ2Decompressor_dealloc(BZ2Decompressor *self)
687+
BZ2Decompressor_dealloc(PyObject *op)
684688
{
689+
BZ2Decompressor *self = _BZ2Decompressor_CAST(op);
690+
685691
if(self->input_buffer != NULL) {
686692
PyMem_Free(self->input_buffer);
687693
}
@@ -697,7 +703,7 @@ BZ2Decompressor_dealloc(BZ2Decompressor *self)
697703
}
698704

699705
static int
700-
BZ2Decompressor_traverse(BZ2Decompressor *self, visitproc visit, void *arg)
706+
BZ2Decompressor_traverse(PyObject *self, visitproc visit, void *arg)
701707
{
702708
Py_VISIT(Py_TYPE(self));
703709
return 0;

0 commit comments

Comments
 (0)