Reputation: 654
I am trying to make sure that an assert is not executed by python when using -O. My test program however indicates that it is always executed. I specifically use -O on the command line and I used -O when I ran setup.py with both build and install. Before I submit a bug report I wanted to make sure I did not do any rookie mistakes...
So do I need to do something else or different so that the assert is not executed?
My simple script:
print __debug__
if __debug__:
print "True branch"
else:
print "False branch"
assert(False)
works when run standalone. Prints:
False
False branch
When I copy this snippet in the main program (that I cannot include here...) I get:
False
True branch
AssertionError
I am utterly confused how that can come about. This Python 2.7.6 on a Mac. (Sorry about the Mac, I have to use it for work.)
Upvotes: 4
Views: 1111
Reputation: 30561
You can achieve the effect you describe by running a .pyc
file directly with the -O
flag. That's an abuse of the way things are supposed to work. You want to either:
.py
file with or without the -O
flag (the usual approach), or.pyc
file without the -O
flag, or.pyo
file with the -O
flag.If you run a .pyc
file with the -O
flag, or a .pyo
file without it, you're going to get surprises like this one.
What's happening is that at compile time the peephole optimiser has optimised away the if __debug__
branches, so the .pyc
or .pyo
files will execute the appropriate branch unconditionally. Then when you run with the wrong -O
specification, you'll be running with a value of __debug__
that doesn't match the optimisation that was applied at compile time.
There was a similar issue reported on the Python issue tracker a while back, though that was the opposite situation: someone running a .pyo
file without using the -O
flag.
A quick example: suppose I've got a file somewhat like yours named "debug_example.py" sitting in my current directory:
noether:Desktop mdickinson$ cat debug_example.py
def main():
print "__debug__ is {}".format(__debug__)
if __debug__:
print "__debug__ is True"
else:
print "__debug__ is False"
if __name__ == '__main__':
main()
If we execute the file directly, with or without the -O
flag, we see the expected results:
noether:Desktop mdickinson$ python2 debug_example.py
__debug__ is True
__debug__ is True
noether:Desktop mdickinson$ python2 -O debug_example.py
__debug__ is False
__debug__ is False
Now let's compile this file to a "debug_example.pyc" file using the handy py_compile
module. (In your case this compilation is likely being performed as part of the setup.py
installation.):
noether:Desktop mdickinson$ python2 -m py_compile debug_example.py
noether:Desktop mdickinson$ ls -l debug_example.pyc
-rw-r--r-- 1 mdickinson staff 350 24 Mar 21:41 debug_example.pyc
Now we execute the debug_example.pyc
file, but (wrongly) using the -O
flag, and Python gets confused:
noether:Desktop mdickinson$ python2 -O debug_example.pyc
__debug__ is False
__debug__ is True
We can use Python's dis
module to see the bytecode inside the module:
Python 2.7.6 (default, Nov 18 2013, 15:12:51)
[GCC 4.2.1 Compatible Apple LLVM 5.0 (clang-500.2.79)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import debug_example
>>> import dis
>>> dis.dis(debug_example)
Disassembly of main:
2 0 LOAD_CONST 1 ('__debug__ is {}')
3 LOAD_ATTR 0 (format)
6 LOAD_GLOBAL 1 (__debug__)
9 CALL_FUNCTION 1
12 PRINT_ITEM
13 PRINT_NEWLINE
4 14 LOAD_CONST 2 ('__debug__ is True')
17 PRINT_ITEM
18 PRINT_NEWLINE
19 LOAD_CONST 0 (None)
22 RETURN_VALUE
Note that there's no bytecode corresponding to the if
statement there at all: we see an unconditional printing of '__debug__ is True'
.
Solution: don't execute the .pyc
or the .pyo
files directly: execute the .py
file and let Python figure out whether to use the .pyc
or .pyo
as appropriate.
Upvotes: 4