Skip to content

Commit d24dc3a

Browse files
committed
Merge branch 'main' into gh-115999-load-attr-instance-merged
2 parents 675eb82 + 1b39b50 commit d24dc3a

19 files changed

+277
-149
lines changed

Doc/library/functools.rst

Lines changed: 2 additions & 2 deletions
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

Doc/library/pdb.rst

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -179,13 +179,15 @@ slightly different way:
179179
.. versionadded:: 3.14
180180
The *commands* argument.
181181

182-
.. function:: post_mortem(traceback=None)
182+
.. function:: post_mortem(t=None)
183183

184-
Enter post-mortem debugging of the given *traceback* object. If no
185-
*traceback* is given, it uses the one of the exception that is currently
186-
being handled (an exception must be being handled if the default is to be
187-
used).
184+
Enter post-mortem debugging of the given exception or
185+
:ref:`traceback object <traceback-objects>`. If no value is given, it uses
186+
the exception that is currently being handled, or raises ``ValueError`` if
187+
there isn’t one.
188188

189+
.. versionchanged:: 3.13
190+
Support for exception objects was added.
189191

190192
.. function:: pm()
191193

Lib/_pyrepl/windows_console.py

Lines changed: 26 additions & 16 deletions
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

Lines changed: 12 additions & 1 deletion
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

Lines changed: 5 additions & 0 deletions
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

Lines changed: 2 additions & 0 deletions
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.
Lines changed: 2 additions & 0 deletions
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.
Lines changed: 4 additions & 0 deletions
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).
Lines changed: 1 addition & 0 deletions
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``.

Modules/_bz2module.c

Lines changed: 10 additions & 4 deletions
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;

Modules/_gdbmmodule.c

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,8 @@ typedef struct {
7676
GDBM_FILE di_dbm;
7777
} gdbmobject;
7878

79+
#define _gdbmobject_CAST(op) ((gdbmobject *)(op))
80+
7981
#include "clinic/_gdbmmodule.c.h"
8082

8183
#define check_gdbmobject_open(v, err) \
@@ -120,27 +122,29 @@ newgdbmobject(_gdbm_state *state, const char *file, int flags, int mode)
120122

121123
/* Methods */
122124
static int
123-
gdbm_traverse(gdbmobject *dp, visitproc visit, void *arg)
125+
gdbm_traverse(PyObject *op, visitproc visit, void *arg)
124126
{
125-
Py_VISIT(Py_TYPE(dp));
127+
Py_VISIT(Py_TYPE(op));
126128
return 0;
127129
}
128130

129131
static void
130-
gdbm_dealloc(gdbmobject *dp)
132+
gdbm_dealloc(PyObject *op)
131133
{
134+
gdbmobject *dp = _gdbmobject_CAST(op);
135+
PyTypeObject *tp = Py_TYPE(dp);
132136
PyObject_GC_UnTrack(dp);
133137
if (dp->di_dbm) {
134138
gdbm_close(dp->di_dbm);
135139
}
136-
PyTypeObject *tp = Py_TYPE(dp);
137140
tp->tp_free(dp);
138141
Py_DECREF(tp);
139142
}
140143

141144
static Py_ssize_t
142-
gdbm_length(gdbmobject *dp)
145+
gdbm_length(PyObject *op)
143146
{
147+
gdbmobject *dp = _gdbmobject_CAST(op);
144148
_gdbm_state *state = PyType_GetModuleState(Py_TYPE(dp));
145149
if (dp->di_dbm == NULL) {
146150
PyErr_SetString(state->gdbm_error, "GDBM object has already been closed");
@@ -185,8 +189,9 @@ gdbm_length(gdbmobject *dp)
185189
}
186190

187191
static int
188-
gdbm_bool(gdbmobject *dp)
192+
gdbm_bool(PyObject *op)
189193
{
194+
gdbmobject *dp = _gdbmobject_CAST(op);
190195
_gdbm_state *state = PyType_GetModuleState(Py_TYPE(dp));
191196
if (dp->di_dbm == NULL) {
192197
PyErr_SetString(state->gdbm_error, "GDBM object has already been closed");
@@ -235,10 +240,11 @@ parse_datum(PyObject *o, datum *d, const char *failmsg)
235240
}
236241

237242
static PyObject *
238-
gdbm_subscript(gdbmobject *dp, PyObject *key)
243+
gdbm_subscript(PyObject *op, PyObject *key)
239244
{
240245
PyObject *v;
241246
datum drec, krec;
247+
gdbmobject *dp = _gdbmobject_CAST(op);
242248
_gdbm_state *state = PyType_GetModuleState(Py_TYPE(dp));
243249

244250
if (!parse_datum(key, &krec, NULL)) {
@@ -275,7 +281,7 @@ _gdbm_gdbm_get_impl(gdbmobject *self, PyObject *key, PyObject *default_value)
275281
{
276282
PyObject *res;
277283

278-
res = gdbm_subscript(self, key);
284+
res = gdbm_subscript((PyObject *)self, key);
279285
if (res == NULL && PyErr_ExceptionMatches(PyExc_KeyError)) {
280286
PyErr_Clear();
281287
return Py_NewRef(default_value);
@@ -284,10 +290,11 @@ _gdbm_gdbm_get_impl(gdbmobject *self, PyObject *key, PyObject *default_value)
284290
}
285291

286292
static int
287-
gdbm_ass_sub(gdbmobject *dp, PyObject *v, PyObject *w)
293+
gdbm_ass_sub(PyObject *op, PyObject *v, PyObject *w)
288294
{
289295
datum krec, drec;
290296
const char *failmsg = "gdbm mappings have bytes or string indices only";
297+
gdbmobject *dp = _gdbmobject_CAST(op);
291298
_gdbm_state *state = PyType_GetModuleState(Py_TYPE(dp));
292299

293300
if (!parse_datum(v, &krec, failmsg)) {
@@ -345,12 +352,12 @@ _gdbm_gdbm_setdefault_impl(gdbmobject *self, PyObject *key,
345352
{
346353
PyObject *res;
347354

348-
res = gdbm_subscript(self, key);
355+
res = gdbm_subscript((PyObject *)self, key);
349356
if (res == NULL && PyErr_ExceptionMatches(PyExc_KeyError)) {
350357
PyErr_Clear();
351-
if (gdbm_ass_sub(self, key, default_value) < 0)
358+
if (gdbm_ass_sub((PyObject *)self, key, default_value) < 0)
352359
return NULL;
353-
return gdbm_subscript(self, key);
360+
return gdbm_subscript((PyObject *)self, key);
354361
}
355362
return res;
356363
}
@@ -841,7 +848,7 @@ _gdbm_module_clear(PyObject *module)
841848
static void
842849
_gdbm_module_free(void *module)
843850
{
844-
_gdbm_module_clear((PyObject *)module);
851+
(void)_gdbm_module_clear((PyObject *)module);
845852
}
846853

847854
static PyModuleDef_Slot _gdbm_module_slots[] = {

Modules/_interpretersmodule.c

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -459,7 +459,12 @@ _run_in_interpreter(PyInterpreterState *interp,
459459

460460
// Prep and switch interpreters.
461461
if (_PyXI_Enter(&session, interp, shareables) < 0) {
462-
assert(!PyErr_Occurred());
462+
if (PyErr_Occurred()) {
463+
// If an error occured at this step, it means that interp
464+
// was not prepared and switched.
465+
return -1;
466+
}
467+
// Now, apply the error from another interpreter:
463468
PyObject *excinfo = _PyXI_ApplyError(session.error);
464469
if (excinfo != NULL) {
465470
*p_excinfo = excinfo;

0 commit comments

Comments
 (0)