Skip to content

Commit 998bcf4

Browse files
authored
Merge branch '3.14' into backport-a74280e-3.14
2 parents cf33d89 + f98f943 commit 998bcf4

8 files changed

Lines changed: 84 additions & 4 deletions

File tree

Lib/imaplib.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,9 @@
131131
# We compile these in _mode_xxx.
132132
_Literal = br'.*{(?P<size>\d+)}$'
133133
_Untagged_status = br'\* (?P<data>\d+) (?P<type>[A-Z-]+)( (?P<data2>.*))?'
134+
# Only NUL, CR and LF are unsafe (they cannot be represented even in
135+
# a quoted string); other control characters are sent quoted.
136+
_control_chars = re.compile(b'[\x00\r\n]')
134137
_non_astring_char = re.compile(br'[(){ \x00-\x1f\x7f-\xff%*\\"]')
135138
_non_list_char = re.compile(br'[(){ \x00-\x1f\x7f-\xff\\"]')
136139
_quoted = re.compile(br'"(?:[^"\\]|\\.)*+"')
@@ -1155,6 +1158,8 @@ def _command(self, name, *args):
11551158
if arg is None: continue
11561159
if isinstance(arg, str):
11571160
arg = bytes(arg, self._encoding)
1161+
if _control_chars.search(arg):
1162+
raise ValueError("NUL, CR and LF not allowed in commands")
11581163
data = data + b' ' + arg
11591164

11601165
literal = self.literal

Lib/test/test_array.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,11 @@ def __init__(self, typecode, newarg=None):
3535

3636
class MiscTest(unittest.TestCase):
3737

38+
def test_array_type_importable(self):
39+
from array import ArrayType
40+
41+
self.assertIs(array.array, ArrayType)
42+
3843
def test_array_is_sequence(self):
3944
self.assertIsInstance(array.array("B"), collections.abc.MutableSequence)
4045
self.assertIsInstance(array.array("B"), collections.abc.Reversible)

Lib/test/test_imaplib.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1741,6 +1741,22 @@ def test_uppercase_command_names(self):
17411741
with self.assertRaises(AttributeError):
17421742
client.NONEXISTENT
17431743

1744+
def test_control_characters(self):
1745+
client, server = self._setup(SimpleIMAPHandler)
1746+
client.login('user', 'pass')
1747+
for c in '\0\r\n':
1748+
with self.assertRaises(ValueError):
1749+
client.select(f'a{c}b')
1750+
# Other control characters are valid in a quoted string and can
1751+
# occur in mailbox names returned by the server, so the client
1752+
# must be able to send them back.
1753+
for c in support.control_characters_c0():
1754+
if c in '\0\r\n':
1755+
continue
1756+
typ, _ = client.select(f'a{c}b')
1757+
self.assertEqual(typ, 'OK')
1758+
self.assertEqual(server.is_selected, [f'"a{c}b"'])
1759+
17441760
# property tests
17451761

17461762
def test_file_property_should_not_be_accessed(self):

Lib/test/test_tkinter/test_misc.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
import functools
22
import platform
33
import sys
4+
import textwrap
45
import unittest
56
import weakref
67
import tkinter
78
from tkinter import TclError
89
import enum
910
from test import support
1011
from test.support import os_helper
12+
from test.support.script_helper import assert_python_ok
1113
from test.test_tkinter.support import setUpModule # noqa: F401
1214
from test.test_tkinter.support import (AbstractTkTest, AbstractDefaultRootTest,
1315
requires_tk, get_tk_patchlevel,
@@ -52,6 +54,33 @@ class Button2(tkinter.Button):
5254
b4 = Button2(f2)
5355
self.assertEqual(len({str(b), str(b2), str(b3), str(b4)}), 4)
5456

57+
def test_dealloc_in_wrong_thread(self):
58+
# gh-83274: deallocating the interpreter in the wrong thread must not
59+
# crash.
60+
script = textwrap.dedent("""
61+
import threading
62+
import tkinter
63+
root = tkinter.Tk()
64+
root.destroy()
65+
# Let another thread drop the last reference.
66+
ready = threading.Event()
67+
t = threading.Thread(target=lambda obj: ready.wait(), args=(root,))
68+
t.start()
69+
del root
70+
ready.set()
71+
t.join()
72+
print('ok')
73+
""")
74+
rc, out, err = assert_python_ok('-c', script)
75+
self.assertEqual(out.strip(), b'ok')
76+
if not support.Py_GIL_DISABLED:
77+
# On the free-threaded build the interpreter may instead be
78+
# deallocated in its own thread (deferred reference counting), so
79+
# the warning is not necessarily emitted. The crucial guarantee --
80+
# no crash -- is already checked by assert_python_ok() above.
81+
self.assertIn(b'RuntimeWarning', err)
82+
self.assertIn(b'gh-83274', err)
83+
5584
@requires_tk(8, 6, 6)
5685
def test_tk_busy(self):
5786
root = self.root
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Deallocating a :mod:`tkinter` application from a thread other than the one it
2+
was created in no longer crashes the interpreter. The underlying Tcl
3+
interpreter is leaked instead, and a :exc:`RuntimeWarning` is reported.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Reject NUL, CR and LF characters in IMAP commands. Other control
2+
characters are allowed and sent quoted.

Modules/_tkinter.c

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3099,10 +3099,24 @@ Tkapp_Dealloc(PyObject *op)
30993099
{
31003100
TkappObject *self = TkappObject_CAST(op);
31013101
PyTypeObject *tp = Py_TYPE(self);
3102-
/*CHECK_TCL_APPARTMENT;*/
3103-
ENTER_TCL
3104-
Tcl_DeleteInterp(Tkapp_Interp(self));
3105-
LEAVE_TCL
3102+
if (self->threaded && self->thread_id != Tcl_GetCurrentThread()) {
3103+
/* Deleting the interpreter from another thread aborts the process
3104+
("Tcl_AsyncDelete: async handler deleted by the wrong thread").
3105+
Leak it instead (gh-83274). */
3106+
if (PyErr_WarnEx(PyExc_RuntimeWarning,
3107+
"the Tcl interpreter is leaked because it was "
3108+
"deallocated in a thread other than the one it was "
3109+
"created in (see gh-83274)", 1) < 0)
3110+
{
3111+
PyErr_FormatUnraisable("Exception ignored while finalizing "
3112+
"a Tcl interpreter");
3113+
}
3114+
}
3115+
else {
3116+
ENTER_TCL
3117+
Tcl_DeleteInterp(Tkapp_Interp(self));
3118+
LEAVE_TCL
3119+
}
31063120
Py_XDECREF(self->trace);
31073121
PyObject_Free(self);
31083122
Py_DECREF(tp);

Modules/arraymodule.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3295,6 +3295,12 @@ array_modexec(PyObject *m)
32953295
CREATE_TYPE(m, state->ArrayIterType, &arrayiter_spec);
32963296
Py_SET_TYPE(state->ArrayIterType, &PyType_Type);
32973297

3298+
// Older undocumented alias:
3299+
if (PyModule_AddObjectRef(m, "ArrayType",
3300+
(PyObject *)state->ArrayType) < 0) {
3301+
return -1;
3302+
}
3303+
32983304
PyObject *mutablesequence = PyImport_ImportModuleAttrString(
32993305
"collections.abc", "MutableSequence");
33003306
if (!mutablesequence) {

0 commit comments

Comments
 (0)