Skip to content

Commit 6aca488

Browse files
authored
Merge branch 'main' into add_dedent
2 parents fe5c631 + 0fc5c56 commit 6aca488

File tree

5 files changed

+84
-2
lines changed

5 files changed

+84
-2
lines changed

Doc/library/pprint.rst

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,6 @@ The formatted representation keeps objects on a single line if it can, and
2222
breaks them onto multiple lines if they don't fit within the allowed width,
2323
adjustable by the *width* parameter defaulting to 80 characters.
2424

25-
Dictionaries are sorted by key before the display is computed.
26-
2725
.. versionchanged:: 3.9
2826
Added support for pretty-printing :class:`types.SimpleNamespace`.
2927

Lib/test/test_argparse.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5831,6 +5831,7 @@ def test_subparser_conflict(self):
58315831
# Help and Version option tests
58325832
# =============================
58335833

5834+
@force_not_colorized_test_class
58345835
class TestOptionalsHelpVersionActions(TestCase):
58355836
"""Test the help and version actions"""
58365837

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import unittest
2+
3+
from test.support import import_helper, os_helper, threading_helper
4+
from test.support.threading_helper import run_concurrently
5+
6+
import threading
7+
8+
gdbm = import_helper.import_module("dbm.gnu")
9+
10+
NTHREADS = 10
11+
KEY_PER_THREAD = 1000
12+
13+
gdbm_filename = "test_gdbm_file"
14+
15+
16+
@threading_helper.requires_working_threading()
17+
class TestGdbm(unittest.TestCase):
18+
def test_racing_dbm_gnu(self):
19+
def gdbm_multi_op_worker(db):
20+
# Each thread sets, gets, and iterates
21+
tid = threading.get_ident()
22+
23+
# Insert keys
24+
for i in range(KEY_PER_THREAD):
25+
db[f"key_{tid}_{i}"] = f"value_{tid}_{i}"
26+
27+
for i in range(KEY_PER_THREAD):
28+
# Keys and values are stored as bytes; encode values for
29+
# comparison
30+
key = f"key_{tid}_{i}"
31+
value = f"value_{tid}_{i}".encode()
32+
self.assertIn(key, db)
33+
self.assertEqual(db[key], value)
34+
self.assertEqual(db.get(key), value)
35+
self.assertIsNone(db.get("not_exist"))
36+
with self.assertRaises(KeyError):
37+
db["not_exist"]
38+
39+
# Iterate over the database keys and verify only those belonging
40+
# to this thread. Other threads may concurrently delete their keys.
41+
key_prefix = f"key_{tid}".encode()
42+
key = db.firstkey()
43+
key_count = 0
44+
while key:
45+
if key.startswith(key_prefix):
46+
self.assertIn(key, db)
47+
key_count += 1
48+
key = db.nextkey(key)
49+
50+
# Can't assert key_count == KEY_PER_THREAD because concurrent
51+
# threads may insert or delete keys during iteration. This can
52+
# cause keys to be skipped or counted multiple times, making the
53+
# count unreliable.
54+
# See: https://www.gnu.org.ua/software/gdbm/manual/Sequential.html
55+
# self.assertEqual(key_count, KEY_PER_THREAD)
56+
57+
# Delete this thread's keys
58+
for i in range(KEY_PER_THREAD):
59+
key = f"key_{tid}_{i}"
60+
del db[key]
61+
self.assertNotIn(key, db)
62+
with self.assertRaises(KeyError):
63+
del db["not_exist"]
64+
65+
# Re-insert keys
66+
for i in range(KEY_PER_THREAD):
67+
db[f"key_{tid}_{i}"] = f"value_{tid}_{i}"
68+
69+
with os_helper.temp_dir() as tmpdirname:
70+
db = gdbm.open(f"{tmpdirname}/{gdbm_filename}", "c")
71+
run_concurrently(
72+
worker_func=gdbm_multi_op_worker, nthreads=NTHREADS, args=(db,)
73+
)
74+
self.assertEqual(len(db), NTHREADS * KEY_PER_THREAD)
75+
db.close()
76+
77+
78+
if __name__ == "__main__":
79+
unittest.main()
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix a memory leak when failing to create a :class:`~typing.Union` type.
2+
Patch by Bénédikt Tran.

Objects/unionobject.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -474,11 +474,13 @@ _Py_union_from_tuple(PyObject *args)
474474
}
475475
if (PyTuple_CheckExact(args)) {
476476
if (!unionbuilder_add_tuple(&ub, args)) {
477+
unionbuilder_finalize(&ub);
477478
return NULL;
478479
}
479480
}
480481
else {
481482
if (!unionbuilder_add_single(&ub, args)) {
483+
unionbuilder_finalize(&ub);
482484
return NULL;
483485
}
484486
}

0 commit comments

Comments
 (0)