2

I made a simple test program to test the functionality of assigning to __debug__ using globals()["__debug__"] = value (__debug__ = value is a SyntaxError). It basically tries to raise an AssertionError and prints if the error was raised and whether or not it was expected. I did this as I ran in to issues with __debug__ changing mid-program.

print("__debug__ is", __debug__)
exp = ["(expected)", "(not expected)"]
if __debug__:
    exp = exp[::-1]
try:
    assert False
    print("No AssertionError", exp[0])
except AssertionError:
    print("AssertionError", exp[1])
exp = exp[::-1]
globals()["__debug__"] = not __debug__
print("__debug__ is", __debug__)
try:
    assert False
    print("No AssertionError", exp[0])
except AssertionError:
    print("AssertionError", exp[1])

It produces unexpected results when run from command prompt, with and without the -O flag.

C:\Test>python assert.py
__debug__ is True
AssertionError (expected)
__debug__ is False
AssertionError (not expected)
C:\Test>python -O assert.py
__debug__ is False
No AssertionError (expected)
__debug__ is True
No AssertionError (not expected)

It seems that __debug__ is changing, but assert is not actually checking if it has.

1 Answers1

13

You are not meant to change the value of __debug__
As the note under here states:

Note: The names None, False, True and __debug__ cannot be reassigned (assignments to them, even as an attribute name, raise SyntaxError), so they can be considered “true” constants

The reason this is happening is because __debug__ is not evaluated at runtime, rather the -O command line flag is (At compile time). See also Runtime vs Compile time.

While you can change the value of __debug__ through the hack globals()["__debug__"], it does nothing, as the assert expression1, expression2 doesn't really check for the value of __debug__. Supplying the -O flag assigns False to __debug__ and removes all assert statements. That is to say, assert statements are removed by the -O flag, not the __debug__ variable.

You can see this with dis() from dis. Using the following code:

import dis
dis.dis("assert False")

Without the -O flag (path\to\file>python file.py):

  1           0 LOAD_CONST               0 (False)
              3 POP_JUMP_IF_TRUE        12
              6 LOAD_GLOBAL              0 (AssertionError)
              9 RAISE_VARARGS            1
        >>   12 LOAD_CONST               1 (None)
             15 RETURN_VALUE

With the -O flag (path\to\file>python -O file.py):

  1           0 LOAD_CONST               0 (None)
              3 RETURN_VALUE

As you can see, the assert statement is essentially removed from the code. Lines 0 to 3 with the -O flag are identical to lines 12 to 15 without. No where does it check for the value of __debug__.

Community
  • 1
  • 1