59

In C we write code like

#ifdef DEBUG
printf("Some debug log... This could probably be achieved by python logging.Logger");
/* Do some sanity check code */
assert someCondition
/* More complex sanitycheck */
while(list->next){
assert fooCheck(list)
}

#endif

Is there a way to do this in python?

Edit: I got my answer, and more :) Paolo, Steven Rumbalski and J Sebastian gave me the information I was looking for. Thanks das for the detailed answer, although I'll probably not use a preprocessor right now.

J Sebastian, whose comment got deleted because the answer in which he posted his comment, deleted his answer I think. He said I could use the isEnabledFor() method in Logger to feed a conditional.

Thanks everyone for your inputs. This is my first question. I wish I could accept paolo, or j sebastian's answers. But since those were offered as comments, I'll accept das' answer.

I will probably use either http://nestedinfiniteloops.wordpress.com/2012/01/15/if-debug-python-flavoured/ or Logger.isEnabledFor()

Spundun
  • 3,936
  • 2
  • 23
  • 36
  • 2
    http://stackoverflow.com/questions/1593274/how-do-i-check-if-the-python-debug-option-is-set-from-within-a-script and http://nestedinfiniteloops.wordpress.com/2012/01/15/if-debug-python-flavoured/ – Paolo Moretti Nov 12 '12 at 22:39
  • 4
    What's wrong with setting a global `DEBUG` and using a simple if-statment `if DEBUG: ...`? – Steven Rumbalski Nov 12 '12 at 22:42

6 Answers6

116

Use __debug__ in your code:

if __debug__:
    print 'Debug ON'
else:
    print 'Debug OFF'

Create a script abc.py with the above code and then

  1. Run with python -O abc.py
  2. Run with python abc.py

Observe the difference.

Nick Chapman
  • 4,402
  • 1
  • 27
  • 41
Mohammad Shahid Siddiqui
  • 3,730
  • 2
  • 27
  • 12
  • 3
    In fact, Python completely removes the `if` statement if the expression is a static value (such as `True`, `False`, `None`, `__debug__`, `0`, and `0.0`), making `if __debug__` a compile-time directive rather than a runtime check. (EDIT: apparently can't include code blocks in comments; guess I'll add another answer) – doctaphred Aug 22 '17 at 15:26
  • Does *NOT* work for me in Python 3.7.4 on Windows 7 and 10 for Python programs executed by filename from cmd.exe or Console 2. `__debug__` is always `True` there. – PointedEars Aug 12 '19 at 12:56
  • 6
    Why is ```__debug__``` true by default? Wouldn't it be rather better ```__debug__``` false by default and python -O setting it to true? – Edu Oct 10 '19 at 08:18
  • 4
    Because the O in -O stands for optimise. It sets `__debug__` to false, which optimises those sections away, as well as removing assert statements. -OO also removes docstrings. – Sam Rockett May 14 '20 at 16:41
  • This is another reason why if you upvote a post that is not the accepted answer, then that post should show above the accepted answer. – Jeremy Thompson Oct 07 '20 at 05:48
  • Agreed that it would be more intuitive for `__debug__` to be `False` by default. – ijoseph Jan 24 '21 at 20:20
  • How does this work in MicroPython / Thonny? Can't find where to pass in -O – MathCrackExchange Jun 08 '23 at 21:42
37

Mohammad's answer is the right approach: use if __debug__.

In fact, Python completely removes the if statement if the expression is a static constant (such as True, False, None, __debug__, 0, and 0.0), making if __debug__ a compile-time directive rather than a runtime check:

>>> def test():
...     if __debug__:
...         return 'debug'
...     return 'not debug'
...
>>> import dis
>>> dis.dis(test)
  3           0 LOAD_CONST               1 ('debug')
              2 RETURN_VALUE

The -O option is explained in detail in the python documentation for command line options, and there is similar optimization for assert statements.

So don't use an external preprocessor—for this purpose, you have one built in!

Julian Fortune
  • 172
  • 1
  • 7
doctaphred
  • 2,504
  • 1
  • 23
  • 26
  • Gave +1 for the reply :) – Mohammad Shahid Siddiqui Aug 23 '17 at 17:36
  • This seems to fail on anything slightly more complicated, e.g. ```def test(): if not __debug__: return 'not debug' else: return 'debug' import dis dis.dis(test)``` Results in: 2 0 LOAD_CONST 1 (True) 2 POP_JUMP_IF_TRUE 8 3 4 LOAD_CONST 2 ('not debug') 6 RETURN_VALUE 5 >> 8 LOAD_CONST 3 ('debug') 10 RETURN_VALUE 12 LOAD_CONST 0 (None) 14 RETURN_VALUE – Gajo Petrovic Jan 24 '19 at 04:35
  • That's true in Python 2 (which is EOL on January 1 -- time to upgrade!), but in Python 3 many statically computable expressions (including `not __debug__`) are evaluated at compile time, and bytecode in unreachable branches is elided. – doctaphred Feb 18 '19 at 14:20
  • Does *NOT* work for me in Python 3.7.4 on Windows 7 and 10 for Python programs executed by filename from cmd.exe or Console 2. `__debug__` is always `True` there. – PointedEars Aug 12 '19 at 12:55
  • @PointedEars are you passing the `-O` option, as explained in Mohammad's answer and the linked docs? On Unix-like systems, if you want to execute a Python script directly and provide interpreter options, you can add them to the shebang line at the start of the script: `#!/usr/bin/env python -O`. Not sure what the equivalent would be for Windows. But unless you somehow give the interpreter that flag, `__debug__` will indeed always be true. – doctaphred Aug 21 '19 at 20:43
  • @doctaphred I did not know what to make of the mention of “the `-O` flag”, and I did not pass it in my script. Makes sense now, thanks. I will try later. – PointedEars Aug 22 '19 at 16:22
  • If you use compileall to create pyc, optimize=1 and optimize=2 correspond to -O and -OO – Eli Burke Jun 30 '20 at 20:38
11

What you are looking for is a preprocessor for python. Generally you have three options:

  1. Write a selfmade script/program which replaces parts of your sourcecode based on certain templates before passing the result on to the interpreter (May be difficult)
  2. Use a special purpose python preprocessor like pppp - Poor's Python Pre-Processor
  3. Use a general purpose preprocessor like GPP

I recommend trying pppp first ;)

The main advantage of a preprocessor compared to setting a DEBUG flag and running code if (DEBUG == True) is that conditional checks also cost CPU cycles, so it is better to remove code that does not need to be run (if the python interpreter doesn't do that anyway), instead of skipping it.

christopher.online
  • 2,614
  • 3
  • 28
  • 52
das_weezul
  • 6,082
  • 2
  • 28
  • 33
  • 2
    cog.py is another nice preprocesor (where the preprocessor language itself is Python): http://nedbatchelder.com/code/cog/ – Mr Fooz Sep 24 '14 at 14:44
  • It looks like the link to pppp - Poor's Python Pre-Processor above is dead. Here's a link to the code on [github](https://github.com/pinard/Pymacs/blob/master/pppp.rst.in) – user2070305 Apr 30 '16 at 15:12
  • 2
    For the simple case of `#ifdef DEBUG`, `if __debug__` is totally sufficient, and incurs **no runtime cost**. See https://stackoverflow.com/a/45821863/1752050 – doctaphred Aug 22 '17 at 15:42
2

If you are looking for assertions in Python, assert is an actual valid python statement. http://docs.python.org/2/reference/simple_stmts.html#assert

Julien Vivenot
  • 2,230
  • 13
  • 17
2

check the result of sys.gettrace() is None. That will mean that there is no debugger

import sys
if sys.gettrace():
    print("debug mode!")
else:
   print("debug mode is off!")
majid lesani
  • 189
  • 9
0

I use another approach - to test module as standalone/direct running:

import ...

DEBUG = False

....

def main():
    pass

if __name__ == '__main__':
    DEBUG = True
    main()

It gives - flexibility to run as module inside project DEBUG will False, when standalone DEBUG will True. This suitable for example to load data from local file or from request:

def load_csv():
    fn = get_csv_filename()  # will return path depending DEBUG
    if DEBUG:
        if not exists(fn):
            store_df(request_bond_list(), fn)
        return pd.read_csv(fn)
    return request_bond_list()

It also help me to control a path depending DEBUG mode. As you know when module placed in different directory from a main code the path will be 'module' directory in case we run module as standalone (for tests), but when we implements methods from this module inside 'main' then path will set to root of your project. So you can use DEBUG to tune the path for different situations

(or you can directly change 'path' in "if __name__ == '__main__':")

DenisSh
  • 21
  • 1