Skip to content

Commit bcab963

Browse files
Merge branch 'main' into gh-127794
2 parents d04c9a2 + ce76b54 commit bcab963

File tree

13 files changed

+141
-48
lines changed

13 files changed

+141
-48
lines changed

Doc/c-api/init.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -567,6 +567,15 @@ Initializing and finalizing the interpreter
567567
customized Python that always runs in isolated mode using
568568
:c:func:`Py_RunMain`.
569569
570+
.. c:function:: int PyUnstable_AtExit(PyInterpreterState *interp, void (*func)(void *), void *data)
571+
572+
Register an :mod:`atexit` callback for the target interpreter *interp*.
573+
This is similar to :c:func:`Py_AtExit`, but takes an explicit interpreter and
574+
data pointer for the callback.
575+
576+
The :term:`GIL` must be held for *interp*.
577+
578+
.. versionadded:: 3.13
570579
571580
Process-wide parameters
572581
=======================

Doc/c-api/sys.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -426,3 +426,7 @@ Process Control
426426
function registered last is called first. Each cleanup function will be called
427427
at most once. Since Python's internal finalization will have completed before
428428
the cleanup function, no Python APIs should be called by *func*.
429+
430+
.. seealso::
431+
432+
:c:func:`PyUnstable_AtExit` for passing a ``void *data`` argument.

Include/cpython/tracemalloc.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
#ifndef Py_LIMITED_API
22
#ifndef Py_TRACEMALLOC_H
33
#define Py_TRACEMALLOC_H
4+
#ifdef __cplusplus
5+
extern "C" {
6+
#endif
47

58
/* Track an allocated memory block in the tracemalloc module.
69
Return 0 on success, return -1 on error (failed to allocate memory to store
@@ -22,5 +25,8 @@ PyAPI_FUNC(int) PyTraceMalloc_Untrack(
2225
unsigned int domain,
2326
uintptr_t ptr);
2427

28+
#ifdef __cplusplus
29+
}
30+
#endif
2531
#endif // !Py_TRACEMALLOC_H
2632
#endif // !Py_LIMITED_API

Include/internal/pycore_atexit.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@ typedef struct {
4444

4545
struct atexit_state {
4646
atexit_callback *ll_callbacks;
47-
atexit_callback *last_ll_callback;
4847

4948
// XXX The rest of the state could be moved to the atexit module state
5049
// and a low-level callback added for it during module exec.

Lib/pickle.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1387,7 +1387,7 @@ def load_int(self):
13871387
elif data == TRUE[1:]:
13881388
val = True
13891389
else:
1390-
val = int(data, 0)
1390+
val = int(data)
13911391
self.append(val)
13921392
dispatch[INT[0]] = load_int
13931393

@@ -1407,7 +1407,7 @@ def load_long(self):
14071407
val = self.readline()[:-1]
14081408
if val and val[-1] == b'L'[0]:
14091409
val = val[:-1]
1410-
self.append(int(val, 0))
1410+
self.append(int(val))
14111411
dispatch[LONG[0]] = load_long
14121412

14131413
def load_long1(self):

Lib/test/pickletester.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1012,6 +1012,26 @@ def test_constants(self):
10121012
self.assertIs(self.loads(b'I01\n.'), True)
10131013
self.assertIs(self.loads(b'I00\n.'), False)
10141014

1015+
def test_zero_padded_integers(self):
1016+
self.assertEqual(self.loads(b'I010\n.'), 10)
1017+
self.assertEqual(self.loads(b'I-010\n.'), -10)
1018+
self.assertEqual(self.loads(b'I0010\n.'), 10)
1019+
self.assertEqual(self.loads(b'I-0010\n.'), -10)
1020+
self.assertEqual(self.loads(b'L010\n.'), 10)
1021+
self.assertEqual(self.loads(b'L-010\n.'), -10)
1022+
self.assertEqual(self.loads(b'L0010\n.'), 10)
1023+
self.assertEqual(self.loads(b'L-0010\n.'), -10)
1024+
self.assertEqual(self.loads(b'L010L\n.'), 10)
1025+
self.assertEqual(self.loads(b'L-010L\n.'), -10)
1026+
1027+
def test_nondecimal_integers(self):
1028+
self.assertRaises(ValueError, self.loads, b'I0b10\n.')
1029+
self.assertRaises(ValueError, self.loads, b'I0o10\n.')
1030+
self.assertRaises(ValueError, self.loads, b'I0x10\n.')
1031+
self.assertRaises(ValueError, self.loads, b'L0b10L\n.')
1032+
self.assertRaises(ValueError, self.loads, b'L0o10L\n.')
1033+
self.assertRaises(ValueError, self.loads, b'L0x10L\n.')
1034+
10151035
def test_empty_bytestring(self):
10161036
# issue 11286
10171037
empty = self.loads(b'\x80\x03U\x00q\x00.', encoding='koi8-r')

Lib/test/test_pickletools.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -443,6 +443,43 @@ def test_persid(self):
443443
highest protocol among opcodes = 0
444444
''')
445445

446+
def test_constants(self):
447+
self.check_dis(b"(NI00\nI01\n\x89\x88t.", '''\
448+
0: ( MARK
449+
1: N NONE
450+
2: I INT False
451+
6: I INT True
452+
10: \\x89 NEWFALSE
453+
11: \\x88 NEWTRUE
454+
12: t TUPLE (MARK at 0)
455+
13: . STOP
456+
highest protocol among opcodes = 2
457+
''')
458+
459+
def test_integers(self):
460+
self.check_dis(b"(I0\nI1\nI10\nI011\nL12\nL13L\nL014\nL015L\nt.", '''\
461+
0: ( MARK
462+
1: I INT 0
463+
4: I INT 1
464+
7: I INT 10
465+
11: I INT 11
466+
16: L LONG 12
467+
20: L LONG 13
468+
25: L LONG 14
469+
30: L LONG 15
470+
36: t TUPLE (MARK at 0)
471+
37: . STOP
472+
highest protocol among opcodes = 0
473+
''')
474+
475+
def test_nondecimal_integers(self):
476+
self.check_dis_error(b'I0b10\n.', '', 'invalid literal for int')
477+
self.check_dis_error(b'I0o10\n.', '', 'invalid literal for int')
478+
self.check_dis_error(b'I0x10\n.', '', 'invalid literal for int')
479+
self.check_dis_error(b'L0b10L\n.', '', 'invalid literal for int')
480+
self.check_dis_error(b'L0o10L\n.', '', 'invalid literal for int')
481+
self.check_dis_error(b'L0x10L\n.', '', 'invalid literal for int')
482+
446483

447484
class MiscTestCase(unittest.TestCase):
448485
def test__all__(self):
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix loss of callbacks after more than one call to
2+
:c:func:`PyUnstable_AtExit`.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix LONG and INT opcodes to only use base 10 for string to integer conversion in :mod:`pickle`.

Modules/_pickle.c

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5211,16 +5211,14 @@ load_int(PickleState *state, UnpicklerObject *self)
52115211
return bad_readline(state);
52125212

52135213
errno = 0;
5214-
/* XXX: Should the base argument of strtol() be explicitly set to 10?
5215-
XXX(avassalotti): Should this uses PyOS_strtol()? */
5216-
x = strtol(s, &endptr, 0);
5214+
/* XXX(avassalotti): Should this uses PyOS_strtol()? */
5215+
x = strtol(s, &endptr, 10);
52175216

52185217
if (errno || (*endptr != '\n' && *endptr != '\0')) {
52195218
/* Hm, maybe we've got something long. Let's try reading
52205219
* it as a Python int object. */
52215220
errno = 0;
5222-
/* XXX: Same thing about the base here. */
5223-
value = PyLong_FromString(s, NULL, 0);
5221+
value = PyLong_FromString(s, NULL, 10);
52245222
if (value == NULL) {
52255223
PyErr_SetString(PyExc_ValueError,
52265224
"could not convert string to int");
@@ -5370,8 +5368,7 @@ load_long(PickleState *state, UnpicklerObject *self)
53705368
the 'L' to be present. */
53715369
if (s[len-2] == 'L')
53725370
s[len-2] = '\0';
5373-
/* XXX: Should the base argument explicitly set to 10? */
5374-
value = PyLong_FromString(s, NULL, 0);
5371+
value = PyLong_FromString(s, NULL, 10);
53755372
if (value == NULL)
53765373
return -1;
53775374

0 commit comments

Comments
 (0)