Skip to content

Commit

Permalink
bpo-34880: Add the LOAD_ASSERTION_ERROR opcode. (pythonGH-15073)
Browse files Browse the repository at this point in the history
Fix assert statement misbehavior if AssertionError is shadowed.
  • Loading branch information
ZackerySpytz authored and serhiy-storchaka committed Aug 25, 2019
1 parent 8371799 commit ce6a070
Show file tree
Hide file tree
Showing 14 changed files with 2,663 additions and 2,626 deletions.
8 changes: 8 additions & 0 deletions Doc/library/dis.rst
Original file line number Diff line number Diff line change
Expand Up @@ -752,6 +752,14 @@ iterations of the loop.
from the block stack.


.. opcode:: LOAD_ASSERTION_ERROR

Pushes :exc:`AssertionError` onto the stack. Used by the :keyword:`assert`
statement.

.. versionadded:: 3.9


.. opcode:: LOAD_BUILD_CLASS

Pushes :func:`builtins.__build_class__` onto the stack. It is later called
Expand Down
9 changes: 9 additions & 0 deletions Doc/whatsnew/3.9.rst
Original file line number Diff line number Diff line change
Expand Up @@ -226,3 +226,12 @@ Changes in the Python API

* The :mod:`venv` activation scripts no longer special-case when
``__VENV_PROMPT__`` is set to ``""``.


CPython bytecode changes
------------------------

* The :opcode:`LOAD_ASSERTION_ERROR` opcode was added for handling the
:keyword:`assert` statement. Previously, the assert statement would not work
correctly if the :exc:`AssertionError` exception was being shadowed.
(Contributed by Zackery Spytz in :issue:`34880`.)
1 change: 1 addition & 0 deletions Include/opcode.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion Lib/importlib/_bootstrap_external.py
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,7 @@ def _write_atomic(path, data, mode=0o666):
# Python 3.8b2 3412 (Swap the position of positional args and positional
# only args in ast.arguments #37593)
# Python 3.8b4 3413 (Fix "break" and "continue" in "finally" #37830)
# Python 3.9a0 3420 (add LOAD_ASSERTION_ERROR #34880)
#
# MAGIC must change whenever the bytecode emitted by the compiler may no
# longer be understood by older implementations of the eval loop (usually
Expand All @@ -279,7 +280,7 @@ def _write_atomic(path, data, mode=0o666):
# Whenever MAGIC_NUMBER is changed, the ranges in the magic_values array
# in PC/launcher.c must also be updated.

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

_PYCACHE = '__pycache__'
Expand Down
2 changes: 1 addition & 1 deletion Lib/opcode.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ def jabs_op(name, op):
def_op('LOAD_BUILD_CLASS', 71)
def_op('YIELD_FROM', 72)
def_op('GET_AWAITABLE', 73)

def_op('LOAD_ASSERTION_ERROR', 74)
def_op('INPLACE_LSHIFT', 75)
def_op('INPLACE_RSHIFT', 76)
def_op('INPLACE_AND', 77)
Expand Down
2 changes: 1 addition & 1 deletion Lib/test/test_dis.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ def bug1333982(x=[]):
dis_bug1333982 = """\
%3d 0 LOAD_CONST 1 (0)
2 POP_JUMP_IF_TRUE 26
4 LOAD_GLOBAL 0 (AssertionError)
4 LOAD_ASSERTION_ERROR
6 LOAD_CONST 2 (<code object <listcomp> at 0x..., file "%s", line %d>)
8 LOAD_CONST 3 ('bug1333982.<locals>.<listcomp>')
10 MAKE_FUNCTION 0
Expand Down
16 changes: 16 additions & 0 deletions Lib/test/test_exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -1285,6 +1285,22 @@ def g():
next(i)
next(i)

@unittest.skipUnless(__debug__, "Won't work if __debug__ is False")
def test_assert_shadowing(self):
# Shadowing AssertionError would cause the assert statement to
# misbehave.
global AssertionError
AssertionError = TypeError
try:
assert False, 'hello'
except BaseException as e:
del AssertionError
self.assertIsInstance(e, AssertionError)
self.assertEqual(str(e), 'hello')
else:
del AssertionError
self.fail('Expected exception')


class ImportErrorTests(unittest.TestCase):

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
The :keyword:`assert` statement now works properly if the
:exc:`AssertionError` exception is being shadowed.
Patch by Zackery Spytz.
7 changes: 7 additions & 0 deletions Python/ceval.c
Original file line number Diff line number Diff line change
Expand Up @@ -2242,6 +2242,13 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
}
}

case TARGET(LOAD_ASSERTION_ERROR): {
PyObject *value = PyExc_AssertionError;
Py_INCREF(value);
PUSH(value);
FAST_DISPATCH();
}

case TARGET(LOAD_BUILD_CLASS): {
_Py_IDENTIFIER(__build_class__);

Expand Down
10 changes: 3 additions & 7 deletions Python/compile.c
Original file line number Diff line number Diff line change
Expand Up @@ -1129,6 +1129,8 @@ stack_effect(int opcode, int oparg, int jump)
return (oparg & FVS_MASK) == FVS_HAVE_SPEC ? -1 : 0;
case LOAD_METHOD:
return 1;
case LOAD_ASSERTION_ERROR:
return 1;
default:
return PY_INVALID_STACK_EFFECT;
}
Expand Down Expand Up @@ -3253,16 +3255,10 @@ compiler_from_import(struct compiler *c, stmt_ty s)
static int
compiler_assert(struct compiler *c, stmt_ty s)
{
static PyObject *assertion_error = NULL;
basicblock *end;

if (c->c_optimize)
return 1;
if (assertion_error == NULL) {
assertion_error = PyUnicode_InternFromString("AssertionError");
if (assertion_error == NULL)
return 0;
}
if (s->v.Assert.test->kind == Tuple_kind &&
asdl_seq_LEN(s->v.Assert.test->v.Tuple.elts) > 0)
{
Expand All @@ -3277,7 +3273,7 @@ compiler_assert(struct compiler *c, stmt_ty s)
return 0;
if (!compiler_jump_if(c, s->v.Assert.test, end, 1))
return 0;
ADDOP_O(c, LOAD_GLOBAL, assertion_error, names);
ADDOP(c, LOAD_ASSERTION_ERROR);
if (s->v.Assert.msg) {
VISIT(c, expr, s->v.Assert.msg);
ADDOP_I(c, CALL_FUNCTION, 1);
Expand Down
Loading

0 comments on commit ce6a070

Please sign in to comment.