Skip to content

Commit 1c03794

Browse files
authored
Merge branch 'main' into TracebackException-swallows-cause-context-flasey-exception
2 parents cc1ba32 + e77d678 commit 1c03794

25 files changed

+1120
-888
lines changed

Doc/howto/perf_profiling.rst

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -254,13 +254,28 @@ files in the current directory which are ELF images for all the JIT trampolines
254254
that were created by Python.
255255

256256
.. warning::
257-
Notice that when using ``--call-graph dwarf`` the ``perf`` tool will take
257+
When using ``--call-graph dwarf``, the ``perf`` tool will take
258258
snapshots of the stack of the process being profiled and save the
259-
information in the ``perf.data`` file. By default the size of the stack dump
260-
is 8192 bytes but the user can change the size by passing the size after
261-
comma like ``--call-graph dwarf,4096``. The size of the stack dump is
262-
important because if the size is too small ``perf`` will not be able to
263-
unwind the stack and the output will be incomplete. On the other hand, if
264-
the size is too big, then ``perf`` won't be able to sample the process as
265-
frequently as it would like as the overhead will be higher.
259+
information in the ``perf.data`` file. By default, the size of the stack dump
260+
is 8192 bytes, but you can change the size by passing it after
261+
a comma like ``--call-graph dwarf,16384``.
266262

263+
The size of the stack dump is important because if the size is too small
264+
``perf`` will not be able to unwind the stack and the output will be
265+
incomplete. On the other hand, if the size is too big, then ``perf`` won't
266+
be able to sample the process as frequently as it would like as the overhead
267+
will be higher.
268+
269+
The stack size is particularly important when profiling Python code compiled
270+
with low optimization levels (like ``-O0``), as these builds tend to have
271+
larger stack frames. If you are compiling Python with ``-O0`` and not seeing
272+
Python functions in your profiling output, try increasing the stack dump
273+
size to 65528 bytes (the maximum)::
274+
275+
$ perf record -F 9999 -g -k 1 --call-graph dwarf,65528 -o perf.data python -Xperf_jit my_script.py
276+
277+
Different compilation flags can significantly impact stack sizes:
278+
279+
- Builds with ``-O0`` typically have much larger stack frames than those with ``-O1`` or higher
280+
- Adding optimizations (``-O1``, ``-O2``, etc.) typically reduces stack size
281+
- Frame pointers (``-fno-omit-frame-pointer``) generally provide more reliable stack unwinding

Doc/library/dis.rst

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1354,9 +1354,6 @@ iterations of the loop.
13541354
If ``STACK[-1]`` is not ``None``, increments the bytecode counter by *delta*.
13551355
``STACK[-1]`` is popped.
13561356

1357-
This opcode is a pseudo-instruction, replaced in final bytecode by
1358-
the directed versions (forward/backward).
1359-
13601357
.. versionadded:: 3.11
13611358

13621359
.. versionchanged:: 3.12
@@ -1368,9 +1365,6 @@ iterations of the loop.
13681365
If ``STACK[-1]`` is ``None``, increments the bytecode counter by *delta*.
13691366
``STACK[-1]`` is popped.
13701367

1371-
This opcode is a pseudo-instruction, replaced in final bytecode by
1372-
the directed versions (forward/backward).
1373-
13741368
.. versionadded:: 3.11
13751369

13761370
.. versionchanged:: 3.12

Include/internal/pycore_uop_metadata.h

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Lib/ctypes/_layout.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ def get_layout(cls, input_fields, is_struct, base):
8484
raise ValueError('_align_ must be a non-negative integer')
8585
elif align == 0:
8686
# Setting `_align_ = 0` amounts to using the default alignment
87-
align == 1
87+
align = 1
8888

8989
if base:
9090
align = max(ctypes.alignment(base), align)

Lib/test/libregrtest/cmdline.py

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,19 @@
4444
doing memory analysis on the Python interpreter, which process tends to
4545
consume too many resources to run the full regression test non-stop.
4646
47-
-S is used to continue running tests after an aborted run. It will
48-
maintain the order a standard run (ie, this assumes -r is not used).
47+
-S is used to resume running tests after an interrupted run. It will
48+
maintain the order a standard run (i.e. it assumes -r is not used).
4949
This is useful after the tests have prematurely stopped for some external
50-
reason and you want to start running from where you left off rather
51-
than starting from the beginning.
50+
reason and you want to resume the run from where you left off rather
51+
than starting from the beginning. Note: this is different from --prioritize.
52+
53+
--prioritize is used to influence the order of selected tests, such that
54+
the tests listed as an argument are executed first. This is especially
55+
useful when combined with -j and -r to pin the longest-running tests
56+
to start at the beginning of a test run. Pass --prioritize=test_a,test_b
57+
to make test_a run first, followed by test_b, and then the other tests.
58+
If test_a wasn't selected for execution by regular means, --prioritize will
59+
not make it execute.
5260
5361
-f reads the names of tests from the file given as f's argument, one
5462
or more test names per line. Whitespace is ignored. Blank lines and
@@ -236,7 +244,7 @@ def _create_parser():
236244
help='wait for user input, e.g., allow a debugger '
237245
'to be attached')
238246
group.add_argument('-S', '--start', metavar='START',
239-
help='the name of the test at which to start.' +
247+
help='resume an interrupted run at the following test.' +
240248
more_details)
241249
group.add_argument('-p', '--python', metavar='PYTHON',
242250
help='Command to run Python test subprocesses with.')
@@ -263,6 +271,10 @@ def _create_parser():
263271
group = parser.add_argument_group('Selecting tests')
264272
group.add_argument('-r', '--randomize', action='store_true',
265273
help='randomize test execution order.' + more_details)
274+
group.add_argument('--prioritize', metavar='TEST1,TEST2,...',
275+
action='append', type=priority_list,
276+
help='select these tests first, even if the order is'
277+
' randomized.' + more_details)
266278
group.add_argument('-f', '--fromfile', metavar='FILE',
267279
help='read names of tests to run from a file.' +
268280
more_details)
@@ -406,6 +418,10 @@ def resources_list(string):
406418
return u
407419

408420

421+
def priority_list(string):
422+
return string.split(",")
423+
424+
409425
def _parse_args(args, **kwargs):
410426
# Defaults
411427
ns = Namespace()
@@ -551,4 +567,10 @@ def _parse_args(args, **kwargs):
551567
print(msg, file=sys.stderr, flush=True)
552568
sys.exit(2)
553569

570+
ns.prioritize = [
571+
test
572+
for test_list in (ns.prioritize or ())
573+
for test in test_list
574+
]
575+
554576
return ns

Lib/test/libregrtest/main.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ def __init__(self, ns: Namespace, _add_python_opts: bool = False):
142142
self.random_seed = random.getrandbits(32)
143143
else:
144144
self.random_seed = ns.random_seed
145+
self.prioritize_tests: tuple[str, ...] = tuple(ns.prioritize)
145146

146147
self.parallel_threads = ns.parallel_threads
147148

@@ -237,6 +238,16 @@ def find_tests(self, tests: TestList | None = None) -> tuple[TestTuple, TestList
237238
if self.randomize:
238239
random.shuffle(selected)
239240

241+
for priority_test in reversed(self.prioritize_tests):
242+
try:
243+
selected.remove(priority_test)
244+
except ValueError:
245+
print(f"warning: --prioritize={priority_test} used"
246+
f" but test not actually selected")
247+
continue
248+
else:
249+
selected.insert(0, priority_test)
250+
240251
return (tuple(selected), tests)
241252

242253
@staticmethod

Lib/test/test_ctypes/test_aligned_structures.py

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from ctypes import (
22
c_char, c_uint32, c_uint16, c_ubyte, c_byte, alignment, sizeof,
33
BigEndianStructure, LittleEndianStructure,
4-
BigEndianUnion, LittleEndianUnion,
4+
BigEndianUnion, LittleEndianUnion, Structure,
55
)
66
import struct
77
import unittest
@@ -69,6 +69,41 @@ class Main(base):
6969
self.assertEqual(Main.z.offset, 8)
7070
self.assertEqual(main.z, 7)
7171

72+
def test_negative_align(self):
73+
for base in (Structure, LittleEndianStructure, BigEndianStructure):
74+
with (
75+
self.subTest(base=base),
76+
self.assertRaisesRegex(
77+
ValueError,
78+
'_align_ must be a non-negative integer',
79+
)
80+
):
81+
class MyStructure(base):
82+
_align_ = -1
83+
_fields_ = []
84+
85+
def test_zero_align_no_fields(self):
86+
for base in (Structure, LittleEndianStructure, BigEndianStructure):
87+
with self.subTest(base=base):
88+
class MyStructure(base):
89+
_align_ = 0
90+
_fields_ = []
91+
92+
self.assertEqual(alignment(MyStructure), 1)
93+
self.assertEqual(alignment(MyStructure()), 1)
94+
95+
def test_zero_align_with_fields(self):
96+
for base in (Structure, LittleEndianStructure, BigEndianStructure):
97+
with self.subTest(base=base):
98+
class MyStructure(base):
99+
_align_ = 0
100+
_fields_ = [
101+
("x", c_ubyte),
102+
]
103+
104+
self.assertEqual(alignment(MyStructure), 1)
105+
self.assertEqual(alignment(MyStructure()), 1)
106+
72107
def test_oversized_structure(self):
73108
data = bytearray(b"\0" * 8)
74109
for base in (LittleEndianStructure, BigEndianStructure):

Lib/test/test_ctypes/test_pickling.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from ctypes import (CDLL, Structure, CFUNCTYPE, pointer,
44
c_void_p, c_char_p, c_wchar_p,
55
c_char, c_wchar, c_int, c_double)
6-
from test.support import import_helper
6+
from test.support import import_helper, thread_unsafe
77
_ctypes_test = import_helper.import_module("_ctypes_test")
88

99

@@ -21,7 +21,6 @@ def __init__(self, *args, **kw):
2121
class Y(X):
2222
_fields_ = [("str", c_char_p)]
2323

24-
2524
class PickleTest:
2625
def dumps(self, item):
2726
return pickle.dumps(item, self.proto)
@@ -39,6 +38,7 @@ def test_simple(self):
3938
self.assertEqual(memoryview(src).tobytes(),
4039
memoryview(dst).tobytes())
4140

41+
@thread_unsafe('not thread safe')
4242
def test_struct(self):
4343
X.init_called = 0
4444

Lib/test/test_ctypes/test_refcounts.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import sys
44
import unittest
55
from test import support
6-
from test.support import import_helper
6+
from test.support import import_helper, thread_unsafe
77
from test.support import script_helper
88
_ctypes_test = import_helper.import_module("_ctypes_test")
99

@@ -13,7 +13,7 @@
1313

1414
dll = ctypes.CDLL(_ctypes_test.__file__)
1515

16-
16+
@thread_unsafe('not thread safe')
1717
class RefcountTestCase(unittest.TestCase):
1818
@support.refcount_test
1919
def test_1(self):
@@ -82,7 +82,7 @@ class X(ctypes.Structure):
8282
gc.collect()
8383
self.assertEqual(sys.getrefcount(func), orig_refcount)
8484

85-
85+
@thread_unsafe('not thread safe')
8686
class AnotherLeak(unittest.TestCase):
8787
def test_callback(self):
8888
proto = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_int, ctypes.c_int)

Lib/test/test_fstring.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1795,6 +1795,31 @@ def test_gh129093(self):
17951795
self.assertEqual(f'{f'{1!=2=}'=}', "f'{1!=2=}'='1!=2=True'")
17961796
self.assertEqual(f'{f'{1 != 2=}'=}', "f'{1 != 2=}'='1 != 2=True'")
17971797

1798+
def test_newlines_in_format_specifiers(self):
1799+
cases = [
1800+
"""f'{1:d\n}'""",
1801+
"""f'__{
1802+
1:d
1803+
}__'""",
1804+
'''f"{value:.
1805+
{'2f'}}"''',
1806+
'''f"{value:
1807+
{'.2f'}f}"''',
1808+
'''f"{value:
1809+
#{'x'}}"''',
1810+
]
1811+
self.assertAllRaise(SyntaxError, "f-string: newlines are not allowed in format specifiers", cases)
1812+
1813+
valid_cases = [
1814+
"""f'''__{
1815+
1:d
1816+
}__'''""",
1817+
"""f'''{1:d\n}'''""",
1818+
]
1819+
1820+
for case in valid_cases:
1821+
compile(case, "<string>", "exec")
1822+
17981823

17991824
if __name__ == '__main__':
18001825
unittest.main()

0 commit comments

Comments
 (0)