Skip to content

Commit ce6a070

Browse files
ZackerySpytzserhiy-storchaka
authored andcommitted
bpo-34880: Add the LOAD_ASSERTION_ERROR opcode. (GH-15073)
Fix assert statement misbehavior if AssertionError is shadowed.
1 parent 8371799 commit ce6a070

File tree

14 files changed

+2663
-2626
lines changed

14 files changed

+2663
-2626
lines changed

Doc/library/dis.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -752,6 +752,14 @@ iterations of the loop.
752752
from the block stack.
753753

754754

755+
.. opcode:: LOAD_ASSERTION_ERROR
756+
757+
Pushes :exc:`AssertionError` onto the stack. Used by the :keyword:`assert`
758+
statement.
759+
760+
.. versionadded:: 3.9
761+
762+
755763
.. opcode:: LOAD_BUILD_CLASS
756764

757765
Pushes :func:`builtins.__build_class__` onto the stack. It is later called

Doc/whatsnew/3.9.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,3 +226,12 @@ Changes in the Python API
226226

227227
* The :mod:`venv` activation scripts no longer special-case when
228228
``__VENV_PROMPT__`` is set to ``""``.
229+
230+
231+
CPython bytecode changes
232+
------------------------
233+
234+
* The :opcode:`LOAD_ASSERTION_ERROR` opcode was added for handling the
235+
:keyword:`assert` statement. Previously, the assert statement would not work
236+
correctly if the :exc:`AssertionError` exception was being shadowed.
237+
(Contributed by Zackery Spytz in :issue:`34880`.)

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
@@ -271,6 +271,7 @@ def _write_atomic(path, data, mode=0o666):
271271
# Python 3.8b2 3412 (Swap the position of positional args and positional
272272
# only args in ast.arguments #37593)
273273
# Python 3.8b4 3413 (Fix "break" and "continue" in "finally" #37830)
274+
# Python 3.9a0 3420 (add LOAD_ASSERTION_ERROR #34880)
274275
#
275276
# MAGIC must change whenever the bytecode emitted by the compiler may no
276277
# longer be understood by older implementations of the eval loop (usually
@@ -279,7 +280,7 @@ def _write_atomic(path, data, mode=0o666):
279280
# Whenever MAGIC_NUMBER is changed, the ranges in the magic_values array
280281
# in PC/launcher.c must also be updated.
281282

282-
MAGIC_NUMBER = (3413).to_bytes(2, 'little') + b'\r\n'
283+
MAGIC_NUMBER = (3420).to_bytes(2, 'little') + b'\r\n'
283284
_RAW_MAGIC_NUMBER = int.from_bytes(MAGIC_NUMBER, 'little') # For import.c
284285

285286
_PYCACHE = '__pycache__'

Lib/opcode.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ def jabs_op(name, op):
109109
def_op('LOAD_BUILD_CLASS', 71)
110110
def_op('YIELD_FROM', 72)
111111
def_op('GET_AWAITABLE', 73)
112-
112+
def_op('LOAD_ASSERTION_ERROR', 74)
113113
def_op('INPLACE_LSHIFT', 75)
114114
def_op('INPLACE_RSHIFT', 76)
115115
def_op('INPLACE_AND', 77)

Lib/test/test_dis.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ def bug1333982(x=[]):
147147
dis_bug1333982 = """\
148148
%3d 0 LOAD_CONST 1 (0)
149149
2 POP_JUMP_IF_TRUE 26
150-
4 LOAD_GLOBAL 0 (AssertionError)
150+
4 LOAD_ASSERTION_ERROR
151151
6 LOAD_CONST 2 (<code object <listcomp> at 0x..., file "%s", line %d>)
152152
8 LOAD_CONST 3 ('bug1333982.<locals>.<listcomp>')
153153
10 MAKE_FUNCTION 0

Lib/test/test_exceptions.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1285,6 +1285,22 @@ def g():
12851285
next(i)
12861286
next(i)
12871287

1288+
@unittest.skipUnless(__debug__, "Won't work if __debug__ is False")
1289+
def test_assert_shadowing(self):
1290+
# Shadowing AssertionError would cause the assert statement to
1291+
# misbehave.
1292+
global AssertionError
1293+
AssertionError = TypeError
1294+
try:
1295+
assert False, 'hello'
1296+
except BaseException as e:
1297+
del AssertionError
1298+
self.assertIsInstance(e, AssertionError)
1299+
self.assertEqual(str(e), 'hello')
1300+
else:
1301+
del AssertionError
1302+
self.fail('Expected exception')
1303+
12881304

12891305
class ImportErrorTests(unittest.TestCase):
12901306

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
The :keyword:`assert` statement now works properly if the
2+
:exc:`AssertionError` exception is being shadowed.
3+
Patch by Zackery Spytz.

Python/ceval.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2242,6 +2242,13 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
22422242
}
22432243
}
22442244

2245+
case TARGET(LOAD_ASSERTION_ERROR): {
2246+
PyObject *value = PyExc_AssertionError;
2247+
Py_INCREF(value);
2248+
PUSH(value);
2249+
FAST_DISPATCH();
2250+
}
2251+
22452252
case TARGET(LOAD_BUILD_CLASS): {
22462253
_Py_IDENTIFIER(__build_class__);
22472254

Python/compile.c

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1129,6 +1129,8 @@ stack_effect(int opcode, int oparg, int jump)
11291129
return (oparg & FVS_MASK) == FVS_HAVE_SPEC ? -1 : 0;
11301130
case LOAD_METHOD:
11311131
return 1;
1132+
case LOAD_ASSERTION_ERROR:
1133+
return 1;
11321134
default:
11331135
return PY_INVALID_STACK_EFFECT;
11341136
}
@@ -3253,16 +3255,10 @@ compiler_from_import(struct compiler *c, stmt_ty s)
32533255
static int
32543256
compiler_assert(struct compiler *c, stmt_ty s)
32553257
{
3256-
static PyObject *assertion_error = NULL;
32573258
basicblock *end;
32583259

32593260
if (c->c_optimize)
32603261
return 1;
3261-
if (assertion_error == NULL) {
3262-
assertion_error = PyUnicode_InternFromString("AssertionError");
3263-
if (assertion_error == NULL)
3264-
return 0;
3265-
}
32663262
if (s->v.Assert.test->kind == Tuple_kind &&
32673263
asdl_seq_LEN(s->v.Assert.test->v.Tuple.elts) > 0)
32683264
{
@@ -3277,7 +3273,7 @@ compiler_assert(struct compiler *c, stmt_ty s)
32773273
return 0;
32783274
if (!compiler_jump_if(c, s->v.Assert.test, end, 1))
32793275
return 0;
3280-
ADDOP_O(c, LOAD_GLOBAL, assertion_error, names);
3276+
ADDOP(c, LOAD_ASSERTION_ERROR);
32813277
if (s->v.Assert.msg) {
32823278
VISIT(c, expr, s->v.Assert.msg);
32833279
ADDOP_I(c, CALL_FUNCTION, 1);

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy