The Assert Statement

Author: Dean Hall
Id:AssertStatement.txt 410 2010-01-18 22:46:12Z dwhall256

Purpose

This document describes the implementation and use of the assert statement in the PyMite virtual machine (VM). In doing so, it serves as a design document for the PyMite developer and a user manual for the PyMite user.

Overview

PyMite shall support the simple form of assert statements and may support the extended form. The assert statement is useful in unit tests for the PyMite VM itself and is thus worth implementing. The assert statement inherently depends on having exceptions available. When an assertion fails, an AssertionError exception is raised. To read about exceptions in PyMite, see ErrorsAndExceptions.

Background

A simple example assert statement looks like this:

assert arg != None

The above line of code compiles to the following bytecode in Python 2.4:

>>> co = compile("assert arg != None","fn","single")
>>> dis.disco(co)
  1           0 LOAD_NAME                0 (arg)
              3 LOAD_CONST               0 (None)
              6 COMPARE_OP               3 (!=)
              9 JUMP_IF_TRUE             7 (to 19)
             12 POP_TOP
             13 LOAD_GLOBAL              2 (AssertionError)
             16 RAISE_VARARGS            1
        >>   19 POP_TOP
             20 LOAD_CONST               0 (None)
             23 RETURN_VALUE

All of the bytecodes listed above exist in PyMite release 02 except for RAISE_VARARGS. The RAISE_VARARGS bytecode is described here in these words:

RAISE_VARARGS    argc
    Raises an exception. argc indicates the number of parameters to the
    raise statement, ranging from 0 to 3. The handler will find the
    traceback as TOS2, the parameter as TOS1, and the exception as TOS.

Implementation

The Python compiler changed the way it generates bytecode for the assert statement in Python 2.4. Prior to 2.4, Python looked to the __debug__ name to determine if assert code should be executed. After 2.4, the compiler includes the code in the assert block by default or excludes it if -O optimization is turned on. The implementation that handles assert statements in PyMite requires that the user compile the application source code with Python 2.4 or later. Since PyMite system and unit tests use the assert statement, using Python 2.4 or later shall be a requirement for building PyMite.

To support the assert statement a built-in named AssertionError must exist and the RAISE_VARARGS (1) bytecode must be implemented. Since revision 369 (2009/05/20), AssertionError is an actual class that inherits from the Exception class. Prior to r369, AssertionError was its own exception datatype. The AssertionError object also has an integer attribute, code, that corresponds to the value of the PmReturn_t code that represents that exception. In this case, PM_RET_EX_ASSRT. So, the code added to __bi.py looks like this:

class AssertionError(Exception):
    pass
AssertionError.code = 0xE4

The first step to support RAISE_VARARGS is to make pmImgCreator.py aware that this bytecode is supported. Then, the bytecode implementation shall do the following:

  • pop the top of the stack and expect an exception object (OBJ_TYPE_EXN).
  • push the None object as the traceback
  • push the None object as the parameter (???)
  • push the exception object

The above steps will get the stack ready for any exception handling if it is present. The "try/except" structure that would setup exception handling is not yet supported in PyMite, but it may be in the future.

Finally, tests shall be written to exercise the assert statement. Full testing would require "try/except" support to catch expected exceptions, so that shall have to wait.