Skip to content

Commit 9679c08

Browse files
Merge branch 'main' into pythongh-130052-test-external-inspection-setexception
2 parents 23fdc5f + 140e69c commit 9679c08

29 files changed

+1210
-778
lines changed

.github/workflows/build.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -621,6 +621,7 @@ jobs:
621621
- build_wasi
622622
- build_windows
623623
- build_windows_msi
624+
- cross-build-linux
624625
- test_hypothesis
625626
- build_asan
626627
- build_tsan

Doc/c-api/typeobj.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1830,7 +1830,7 @@ and :c:data:`PyType_Type` effectively act as defaults.)
18301830
dictionary, so it is may be more efficient to call :c:func:`PyObject_GetAttr`
18311831
when accessing an attribute on the object.
18321832

1833-
It is an error to set both the :c:macro:`Py_TPFLAGS_MANAGED_WEAKREF` bit and
1833+
It is an error to set both the :c:macro:`Py_TPFLAGS_MANAGED_DICT` bit and
18341834
:c:member:`~PyTypeObject.tp_dictoffset`.
18351835

18361836
**Inheritance:**

Doc/library/traceback.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,11 @@ Module-Level Functions
257257

258258
.. versionadded:: 3.5
259259

260+
.. versionchanged:: 3.14
261+
This function previously returned a generator that would walk the stack
262+
when first iterated over. The generator returned now is the state of the
263+
stack when ``walk_stack`` is called.
264+
260265
.. function:: walk_tb(tb)
261266

262267
Walk a traceback following :attr:`~traceback.tb_next` yielding the frame and

Doc/whatsnew/3.14.rst

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ Improved error messages
165165
error message prints the received number of values in more cases than before.
166166
(Contributed by Tushar Sadhwani in :gh:`122239`.)
167167

168-
.. code-block:: pycon
168+
.. code-block:: python
169169
170170
>>> x, y, z = 1, 2, 3, 4
171171
Traceback (most recent call last):
@@ -175,6 +175,17 @@ Improved error messages
175175
ValueError: too many values to unpack (expected 3, got 4)
176176
177177
178+
* When incorrectly closed strings are detected, the error message suggests
179+
that the string may be intended to be part of the string. (Contributed by
180+
Pablo Galindo in :gh:`88535`.)
181+
182+
.. code-block:: python
183+
184+
>>> "The interesting object "The important object" is very important"
185+
Traceback (most recent call last):
186+
SyntaxError: invalid syntax. Is this intended to be part of the string?
187+
188+
178189
.. _whatsnew314-pep741:
179190

180191
PEP 741: Python Configuration C API

Grammar/python.gram

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1178,6 +1178,9 @@ invalid_type_param:
11781178
}
11791179

11801180
invalid_expression:
1181+
| STRING a=(!STRING expression_without_invalid)+ STRING {
1182+
RAISE_SYNTAX_ERROR_KNOWN_RANGE( PyPegen_first_item(a, expr_ty), PyPegen_last_item(a, expr_ty),
1183+
"invalid syntax. Is this intended to be part of the string?") }
11811184
# !(NAME STRING) is not matched so we don't show this error with some invalid string prefixes like: kf"dsfsdf"
11821185
# Soft keywords need to also be ignored because they can be parsed as NAME NAME
11831186
| !(NAME STRING | SOFT_KEYWORD) a=disjunction b=expression_without_invalid {

Lib/test/test_ast/test_ast.py

Lines changed: 0 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -3239,46 +3239,6 @@ def test_folding_tuple(self):
32393239

32403240
self.assert_ast(code, non_optimized_target, optimized_target)
32413241

3242-
def test_folding_comparator(self):
3243-
code = "1 %s %s1%s"
3244-
operators = [("in", ast.In()), ("not in", ast.NotIn())]
3245-
braces = [
3246-
("[", "]", ast.List, (1,)),
3247-
("{", "}", ast.Set, frozenset({1})),
3248-
]
3249-
for left, right, non_optimized_comparator, optimized_comparator in braces:
3250-
for op, node in operators:
3251-
non_optimized_target = self.wrap_expr(ast.Compare(
3252-
left=ast.Constant(1), ops=[node],
3253-
comparators=[non_optimized_comparator(elts=[ast.Constant(1)])]
3254-
))
3255-
optimized_target = self.wrap_expr(ast.Compare(
3256-
left=ast.Constant(1), ops=[node],
3257-
comparators=[ast.Constant(value=optimized_comparator)]
3258-
))
3259-
self.assert_ast(code % (op, left, right), non_optimized_target, optimized_target)
3260-
3261-
def test_folding_iter(self):
3262-
code = "for _ in %s1%s: pass"
3263-
braces = [
3264-
("[", "]", ast.List, (1,)),
3265-
("{", "}", ast.Set, frozenset({1})),
3266-
]
3267-
3268-
for left, right, ast_cls, optimized_iter in braces:
3269-
non_optimized_target = self.wrap_statement(ast.For(
3270-
target=ast.Name(id="_", ctx=ast.Store()),
3271-
iter=ast_cls(elts=[ast.Constant(1)]),
3272-
body=[ast.Pass()]
3273-
))
3274-
optimized_target = self.wrap_statement(ast.For(
3275-
target=ast.Name(id="_", ctx=ast.Store()),
3276-
iter=ast.Constant(value=optimized_iter),
3277-
body=[ast.Pass()]
3278-
))
3279-
3280-
self.assert_ast(code % (left, right), non_optimized_target, optimized_target)
3281-
32823242
def test_folding_type_param_in_function_def(self):
32833243
code = "def foo[%s = 1 + 1](): pass"
32843244

Lib/test/test_cmd_line_script.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -660,7 +660,7 @@ def test_syntaxerror_invalid_escape_sequence_multi_line(self):
660660
self.assertEqual(
661661
stderr.splitlines()[-3:],
662662
[ b' foo = """\\q"""',
663-
b' ^^^^^^^^',
663+
b' ^^',
664664
b'SyntaxError: "\\q" is an invalid escape sequence. '
665665
b'Did you mean "\\\\q"? A raw string is also an option.'
666666
],

Lib/test/test_compile.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -798,7 +798,7 @@ def check_same_constant(const):
798798
f3 = lambda x: x in {("not a name",)}
799799
self.assertIs(f1.__code__.co_consts[0],
800800
f2.__code__.co_consts[0][0])
801-
self.assertIs(next(iter(f3.__code__.co_consts[0])),
801+
self.assertIs(next(iter(f3.__code__.co_consts[1])),
802802
f2.__code__.co_consts[0])
803803

804804
# {0} is converted to a constant frozenset({0}) by the peephole

Lib/test/test_exceptions.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2303,7 +2303,7 @@ def test_encodings(self):
23032303
)
23042304
err = run_script(source.encode('cp437'))
23052305
self.assertEqual(err[-3], ' "¢¢¢¢¢¢" + f(4, x for x in range(1))')
2306-
self.assertEqual(err[-2], ' ^^^^^^^^^^^^^^^^^^^')
2306+
self.assertEqual(err[-2], ' ^^^')
23072307

23082308
# Check backwards tokenizer errors
23092309
source = '# -*- coding: ascii -*-\n\n(\n'

Lib/test/test_fstring.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import types
1616
import decimal
1717
import unittest
18+
import warnings
1819
from test import support
1920
from test.support.os_helper import temp_cwd
2021
from test.support.script_helper import assert_python_failure, assert_python_ok
@@ -1650,8 +1651,9 @@ def __repr__(self):
16501651
#self.assertEqual(f'X{x = }Y', 'Xx\t=\t'+repr(x)+'Y')
16511652

16521653
def test_debug_expressions_are_raw_strings(self):
1653-
1654-
self.assertEqual(f'{b"\N{OX}"=}', 'b"\\N{OX}"=b\'\\\\N{OX}\'')
1654+
with warnings.catch_warnings():
1655+
warnings.simplefilter('ignore', SyntaxWarning)
1656+
self.assertEqual(eval("""f'{b"\\N{OX}"=}'"""), 'b"\\N{OX}"=b\'\\\\N{OX}\'')
16551657
self.assertEqual(f'{r"\xff"=}', 'r"\\xff"=\'\\\\xff\'')
16561658
self.assertEqual(f'{r"\n"=}', 'r"\\n"=\'\\\\n\'')
16571659
self.assertEqual(f"{'\''=}", "'\\''=\"'\"")

Lib/test/test_peepholer.py

Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1261,6 +1261,202 @@ def test_optimize_if_const_set(self):
12611261
]
12621262
self.cfg_optimization_test(same, same, consts=[])
12631263

1264+
def test_optimize_literal_list_for_iter(self):
1265+
# for _ in [1, 2]: pass ==> for _ in (1, 2): pass
1266+
before = [
1267+
('LOAD_SMALL_INT', 1, 0),
1268+
('LOAD_SMALL_INT', 2, 0),
1269+
('BUILD_LIST', 2, 0),
1270+
('GET_ITER', None, 0),
1271+
start := self.Label(),
1272+
('FOR_ITER', end := self.Label(), 0),
1273+
('STORE_FAST', 0, 0),
1274+
('JUMP', start, 0),
1275+
end,
1276+
('END_FOR', None, 0),
1277+
('POP_ITER', None, 0),
1278+
('LOAD_CONST', 0, 0),
1279+
('RETURN_VALUE', None, 0),
1280+
]
1281+
after = [
1282+
('LOAD_CONST', 1, 0),
1283+
('GET_ITER', None, 0),
1284+
start := self.Label(),
1285+
('FOR_ITER', end := self.Label(), 0),
1286+
('STORE_FAST', 0, 0),
1287+
('JUMP', start, 0),
1288+
end,
1289+
('END_FOR', None, 0),
1290+
('POP_ITER', None, 0),
1291+
('LOAD_CONST', 0, 0),
1292+
('RETURN_VALUE', None, 0),
1293+
]
1294+
self.cfg_optimization_test(before, after, consts=[None], expected_consts=[None, (1, 2)])
1295+
1296+
# for _ in [1, x]: pass ==> for _ in (1, x): pass
1297+
before = [
1298+
('LOAD_SMALL_INT', 1, 0),
1299+
('LOAD_NAME', 0, 0),
1300+
('BUILD_LIST', 2, 0),
1301+
('GET_ITER', None, 0),
1302+
start := self.Label(),
1303+
('FOR_ITER', end := self.Label(), 0),
1304+
('STORE_FAST', 0, 0),
1305+
('JUMP', start, 0),
1306+
end,
1307+
('END_FOR', None, 0),
1308+
('POP_ITER', None, 0),
1309+
('LOAD_CONST', 0, 0),
1310+
('RETURN_VALUE', None, 0),
1311+
]
1312+
after = [
1313+
('LOAD_SMALL_INT', 1, 0),
1314+
('LOAD_NAME', 0, 0),
1315+
('BUILD_TUPLE', 2, 0),
1316+
('GET_ITER', None, 0),
1317+
start := self.Label(),
1318+
('FOR_ITER', end := self.Label(), 0),
1319+
('STORE_FAST', 0, 0),
1320+
('JUMP', start, 0),
1321+
end,
1322+
('END_FOR', None, 0),
1323+
('POP_ITER', None, 0),
1324+
('LOAD_CONST', 0, 0),
1325+
('RETURN_VALUE', None, 0),
1326+
]
1327+
self.cfg_optimization_test(before, after, consts=[None], expected_consts=[None])
1328+
1329+
def test_optimize_literal_set_for_iter(self):
1330+
# for _ in {1, 2}: pass ==> for _ in (1, 2): pass
1331+
before = [
1332+
('LOAD_SMALL_INT', 1, 0),
1333+
('LOAD_SMALL_INT', 2, 0),
1334+
('BUILD_SET', 2, 0),
1335+
('GET_ITER', None, 0),
1336+
start := self.Label(),
1337+
('FOR_ITER', end := self.Label(), 0),
1338+
('STORE_FAST', 0, 0),
1339+
('JUMP', start, 0),
1340+
end,
1341+
('END_FOR', None, 0),
1342+
('POP_ITER', None, 0),
1343+
('LOAD_CONST', 0, 0),
1344+
('RETURN_VALUE', None, 0),
1345+
]
1346+
after = [
1347+
('LOAD_CONST', 1, 0),
1348+
('GET_ITER', None, 0),
1349+
start := self.Label(),
1350+
('FOR_ITER', end := self.Label(), 0),
1351+
('STORE_FAST', 0, 0),
1352+
('JUMP', start, 0),
1353+
end,
1354+
('END_FOR', None, 0),
1355+
('POP_ITER', None, 0),
1356+
('LOAD_CONST', 0, 0),
1357+
('RETURN_VALUE', None, 0),
1358+
]
1359+
self.cfg_optimization_test(before, after, consts=[None], expected_consts=[None, frozenset({1, 2})])
1360+
1361+
# non constant literal set is not changed
1362+
# for _ in {1, x}: pass ==> for _ in {1, x}: pass
1363+
same = [
1364+
('LOAD_SMALL_INT', 1, 0),
1365+
('LOAD_NAME', 0, 0),
1366+
('BUILD_SET', 2, 0),
1367+
('GET_ITER', None, 0),
1368+
start := self.Label(),
1369+
('FOR_ITER', end := self.Label(), 0),
1370+
('STORE_FAST', 0, 0),
1371+
('JUMP', start, 0),
1372+
end,
1373+
('END_FOR', None, 0),
1374+
('POP_ITER', None, 0),
1375+
('LOAD_CONST', 0, 0),
1376+
('RETURN_VALUE', None, 0),
1377+
]
1378+
self.cfg_optimization_test(same, same, consts=[None], expected_consts=[None])
1379+
1380+
def test_optimize_literal_list_contains(self):
1381+
# x in [1, 2] ==> x in (1, 2)
1382+
before = [
1383+
('LOAD_NAME', 0, 0),
1384+
('LOAD_SMALL_INT', 1, 0),
1385+
('LOAD_SMALL_INT', 2, 0),
1386+
('BUILD_LIST', 2, 0),
1387+
('CONTAINS_OP', 0, 0),
1388+
('POP_TOP', None, 0),
1389+
('LOAD_CONST', 0, 0),
1390+
('RETURN_VALUE', None, 0),
1391+
]
1392+
after = [
1393+
('LOAD_NAME', 0, 0),
1394+
('LOAD_CONST', 1, 0),
1395+
('CONTAINS_OP', 0, 0),
1396+
('POP_TOP', None, 0),
1397+
('LOAD_CONST', 0, 0),
1398+
('RETURN_VALUE', None, 0),
1399+
]
1400+
self.cfg_optimization_test(before, after, consts=[None], expected_consts=[None, (1, 2)])
1401+
1402+
# x in [1, y] ==> x in (1, y)
1403+
before = [
1404+
('LOAD_NAME', 0, 0),
1405+
('LOAD_SMALL_INT', 1, 0),
1406+
('LOAD_NAME', 1, 0),
1407+
('BUILD_LIST', 2, 0),
1408+
('CONTAINS_OP', 0, 0),
1409+
('POP_TOP', None, 0),
1410+
('LOAD_CONST', 0, 0),
1411+
('RETURN_VALUE', None, 0),
1412+
]
1413+
after = [
1414+
('LOAD_NAME', 0, 0),
1415+
('LOAD_SMALL_INT', 1, 0),
1416+
('LOAD_NAME', 1, 0),
1417+
('BUILD_TUPLE', 2, 0),
1418+
('CONTAINS_OP', 0, 0),
1419+
('POP_TOP', None, 0),
1420+
('LOAD_CONST', 0, 0),
1421+
('RETURN_VALUE', None, 0),
1422+
]
1423+
self.cfg_optimization_test(before, after, consts=[None], expected_consts=[None])
1424+
1425+
def test_optimize_literal_set_contains(self):
1426+
# x in {1, 2} ==> x in (1, 2)
1427+
before = [
1428+
('LOAD_NAME', 0, 0),
1429+
('LOAD_SMALL_INT', 1, 0),
1430+
('LOAD_SMALL_INT', 2, 0),
1431+
('BUILD_SET', 2, 0),
1432+
('CONTAINS_OP', 0, 0),
1433+
('POP_TOP', None, 0),
1434+
('LOAD_CONST', 0, 0),
1435+
('RETURN_VALUE', None, 0),
1436+
]
1437+
after = [
1438+
('LOAD_NAME', 0, 0),
1439+
('LOAD_CONST', 1, 0),
1440+
('CONTAINS_OP', 0, 0),
1441+
('POP_TOP', None, 0),
1442+
('LOAD_CONST', 0, 0),
1443+
('RETURN_VALUE', None, 0),
1444+
]
1445+
self.cfg_optimization_test(before, after, consts=[None], expected_consts=[None, frozenset({1, 2})])
1446+
1447+
# non constant literal set is not changed
1448+
# x in {1, y} ==> x in {1, y}
1449+
same = [
1450+
('LOAD_NAME', 0, 0),
1451+
('LOAD_SMALL_INT', 1, 0),
1452+
('LOAD_NAME', 1, 0),
1453+
('BUILD_SET', 2, 0),
1454+
('CONTAINS_OP', 0, 0),
1455+
('POP_TOP', None, 0),
1456+
('LOAD_CONST', 0, 0),
1457+
('RETURN_VALUE', None, 0),
1458+
]
1459+
self.cfg_optimization_test(same, same, consts=[None], expected_consts=[None])
12641460

12651461
def test_conditional_jump_forward_const_condition(self):
12661462
# The unreachable branch of the jump is removed, the jump

0 commit comments

Comments
 (0)