Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 0ad1e03

Browse files
authored
bpo-43754: Eliminate bindings for partial pattern matches (GH-25229)
1 parent 7d2b83e commit 0ad1e03

10 files changed

Lines changed: 575 additions & 345 deletions

File tree

Doc/library/dis.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1256,6 +1256,15 @@ All of the following opcodes use their arguments.
12561256

12571257
.. versionadded:: 3.10
12581258

1259+
1260+
.. opcode:: ROT_N (count)
1261+
1262+
Lift the top *count* stack items one position up, and move TOS down to
1263+
position *count*.
1264+
1265+
.. versionadded:: 3.10
1266+
1267+
12591268
.. opcode:: HAVE_ARGUMENT
12601269

12611270
This is not really an opcode. It identifies the dividing line between

Include/opcode.h

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

Lib/importlib/_bootstrap_external.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -351,6 +351,7 @@ def _write_atomic(path, data, mode=0o666):
351351
# Python 3.10b1 3436 (Add GEN_START bytecode #43683)
352352
# Python 3.10b1 3437 (Undo making 'annotations' future by default - We like to dance among core devs!)
353353
# Python 3.10b1 3438 Safer line number table handling.
354+
# Python 3.10b1 3439 (Add ROT_N)
354355

355356
#
356357
# MAGIC must change whenever the bytecode emitted by the compiler may no
@@ -360,7 +361,7 @@ def _write_atomic(path, data, mode=0o666):
360361
# Whenever MAGIC_NUMBER is changed, the ranges in the magic_values array
361362
# in PC/launcher.c must also be updated.
362363

363-
MAGIC_NUMBER = (3438).to_bytes(2, 'little') + b'\r\n'
364+
MAGIC_NUMBER = (3439).to_bytes(2, 'little') + b'\r\n'
364365
_RAW_MAGIC_NUMBER = int.from_bytes(MAGIC_NUMBER, 'little') # For import.c
365366

366367
_PYCACHE = '__pycache__'

Lib/opcode.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ def jabs_op(name, op):
139139
name_op('DELETE_ATTR', 96) # ""
140140
name_op('STORE_GLOBAL', 97) # ""
141141
name_op('DELETE_GLOBAL', 98) # ""
142-
142+
def_op('ROT_N', 99)
143143
def_op('LOAD_CONST', 100) # Index in const list
144144
hasconst.append(100)
145145
name_op('LOAD_NAME', 101) # Index in name list

Lib/test/test_patma.py

Lines changed: 77 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2905,6 +2905,79 @@ def test_patma_288(self):
29052905
pass
29062906
""")
29072907

2908+
def test_patma_289(self):
2909+
x = {"y": 1}
2910+
match x:
2911+
case {"y": (0 as y) | (1 as y)}:
2912+
z = 0
2913+
self.assertEqual(x, {"y": 1})
2914+
self.assertEqual(y, 1)
2915+
self.assertEqual(z, 0)
2916+
2917+
@no_perf
2918+
def test_patma_290(self):
2919+
self.assert_syntax_error("""
2920+
match ...:
2921+
case [a, [b] | [c] | [d]]:
2922+
pass
2923+
""")
2924+
2925+
@no_perf
2926+
def test_patma_291(self):
2927+
# Hunting for leaks using -R doesn't catch leaks in the compiler itself,
2928+
# just the code under test. This test ensures that if there are leaks in
2929+
# the pattern compiler, those runs will fail:
2930+
with open(__file__) as file:
2931+
compile(file.read(), __file__, "exec")
2932+
2933+
def test_patma_292(self):
2934+
def f(x):
2935+
match x:
2936+
case ((a, b, c, d, e, f, g, h, i, 9) |
2937+
(h, g, i, a, b, d, e, c, f, 10) |
2938+
(g, b, a, c, d, -5, e, h, i, f) |
2939+
(-1, d, f, b, g, e, i, a, h, c)):
2940+
w = 0
2941+
out = locals()
2942+
del out["x"]
2943+
return out
2944+
alts = [
2945+
dict(a=0, b=1, c=2, d=3, e=4, f=5, g=6, h=7, i=8, w=0),
2946+
dict(h=1, g=2, i=3, a=4, b=5, d=6, e=7, c=8, f=9, w=0),
2947+
dict(g=0, b=-1, a=-2, c=-3, d=-4, e=-6, h=-7, i=-8, f=-9, w=0),
2948+
dict(d=-2, f=-3, b=-4, g=-5, e=-6, i=-7, a=-8, h=-9, c=-10, w=0),
2949+
dict(),
2950+
]
2951+
self.assertEqual(f(range(10)), alts[0])
2952+
self.assertEqual(f(range(1, 11)), alts[1])
2953+
self.assertEqual(f(range(0, -10, -1)), alts[2])
2954+
self.assertEqual(f(range(-1, -11, -1)), alts[3])
2955+
self.assertEqual(f(range(10, 20)), alts[4])
2956+
2957+
def test_patma_293(self):
2958+
def f(x):
2959+
match x:
2960+
case [y, (a, b, c, d, e, f, g, h, i, 9) |
2961+
(h, g, i, a, b, d, e, c, f, 10) |
2962+
(g, b, a, c, d, -5, e, h, i, f) |
2963+
(-1, d, f, b, g, e, i, a, h, c), z]:
2964+
w = 0
2965+
out = locals()
2966+
del out["x"]
2967+
return out
2968+
alts = [
2969+
dict(a=0, b=1, c=2, d=3, e=4, f=5, g=6, h=7, i=8, w=0, y=False, z=True),
2970+
dict(h=1, g=2, i=3, a=4, b=5, d=6, e=7, c=8, f=9, w=0, y=False, z=True),
2971+
dict(g=0, b=-1, a=-2, c=-3, d=-4, e=-6, h=-7, i=-8, f=-9, w=0, y=False, z=True),
2972+
dict(d=-2, f=-3, b=-4, g=-5, e=-6, i=-7, a=-8, h=-9, c=-10, w=0, y=False, z=True),
2973+
dict(),
2974+
]
2975+
self.assertEqual(f((False, range(10), True)), alts[0])
2976+
self.assertEqual(f((False, range(1, 11), True)), alts[1])
2977+
self.assertEqual(f((False, range(0, -10, -1), True)), alts[2])
2978+
self.assertEqual(f((False, range(-1, -11, -1), True)), alts[3])
2979+
self.assertEqual(f((False, range(10, 20), True)), alts[4])
2980+
29082981

29092982
class PerfPatma(TestPatma):
29102983

@@ -2936,7 +3009,8 @@ def setUpClass():
29363009

29373010

29383011
"""
2939-
sudo ./python -m pyperf system tune && \
2940-
./python -m pyperf timeit --rigorous --setup "from test.test_patma import PerfPatma; p = PerfPatma()" "p.run_perf()"; \
2941-
sudo ./python -m pyperf system reset
3012+
# From inside venv pointing to this Python, with pyperf installed:
3013+
sudo $(which python) -m pyperf system tune && \
3014+
$(which python) -m pyperf timeit --rigorous --setup "from test.test_patma import PerfPatma; p = PerfPatma()" "p.run_perf()"; \
3015+
sudo $(which python) -m pyperf system reset
29423016
"""
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
When performing structural pattern matching (:pep:`634`), captured names are
2+
now left unbound until the *entire* pattern has matched successfully.

Python/ceval.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4399,6 +4399,14 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, PyFrameObject *f, int throwflag)
43994399
DISPATCH();
44004400
}
44014401

4402+
case TARGET(ROT_N): {
4403+
PyObject *top = TOP();
4404+
memmove(&PEEK(oparg - 1), &PEEK(oparg),
4405+
sizeof(PyObject*) * (oparg - 1));
4406+
PEEK(oparg) = top;
4407+
DISPATCH();
4408+
}
4409+
44024410
case TARGET(EXTENDED_ARG): {
44034411
int oldoparg = oparg;
44044412
NEXTOPARG();

0 commit comments

Comments
 (0)