Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

python equivalent of '#define func() ' or how to comment out a function call in python

my python code is interlaced with lots of function calls used for (debugging|profiling|tracing etc.) for example:

import logging

logging.root.setLevel(logging.DEBUG)
logging.debug('hello')
j = 0
for i in range(10):
    j += i
    logging.debug('i %d j %d' % (i,j))
print(j)
logging.debug('bye')

i want to #define these resource consuming functions out of the code. something like the c equivalent

#define logging.debug(val)

yes, i know the logging module logging level mechanism can be used to mask out loggings below set log level. but, im asking for a general way to have the python interpreter skip functions (that take time to run even if they dont do much)

one idea is to redefine the functions i want to comment out into empty functions:

def lazy(*args): pass
logging.debug = lazy

the above idea still calls a function, and may create a myriad of other problems

like image 319
random guy Avatar asked Jan 05 '10 13:01

random guy


People also ask

What is the Python equivalent of?

Python's "object-based" subset is roughly equivalent to JavaScript. Like JavaScript (and unlike Java), Python supports a programming style that uses simple functions and variables without engaging in class definitions. However, for JavaScript, that's all there is.

What is the IN operator in Python?

The 'in' Operator in Python The in operator works with iterable types, such as lists or strings, in Python. It is used to check if an element is found in the iterable. The in operator returns True if an element is found. It returns False if not.


3 Answers

Python does not have a preprocessor, although you could run your python source through an external preprocessor to get the same effect - e.g. sed "/logging.debug/d" will strip out all the debug logging commands. This is not very elegant though - you will end up needing some sort of build system to run all your modules through the preprocessor and perhaps create a new directory tree of the processed .py files before running the main script.

Alternatively if you put all your debug statements in an if __debug__: block they will get optimised out when python is run with the -O (optimise) flag.

As an aside, I checked the code with the dis module to ensure that it did get optimised away. I discovered that both

if __debug__: doStuff()

and

if 0: doStuff()

are optimised, but

if False: doStuff()

is not. This is because False is a regular Python object, and you can in fact do this:

>>> False = True
>>> if False: print "Illogical, captain"
Illogical, captain

Which seems to me a flaw in the language - hopefully it is fixed in Python 3.

Edit:

This is fixed in Python 3: Assigning to True or False now gives a SyntaxError. Since True and False are constants in Python 3, it means that if False: doStuff() is now optimised:

>>> def f():
...     if False: print( "illogical")
... 
>>> dis.dis(f)
  2           0 LOAD_CONST               0 (None) 
              3 RETURN_VALUE         
like image 115
Dave Kirby Avatar answered Oct 16 '22 01:10

Dave Kirby


Although I think the question is perfectly clear and valid (notwithstanding the many responses that suggest otherwise), the short answer is "there's no support in Python for this".

The only potential solution other than the preprocessor suggestion would be to use some bytecode hacking. I won't even begin to imagine how this should work in terms of the high-level API, but at a low level you could imagine examining code objects for particular sequences of instructions and re-writing them to eliminate them.

For example, look at the following two functions:

>>> def func():
...    if debug:  # analogous to if __debug__:
...       foo
>>> dis.dis(func)
  2           0 LOAD_GLOBAL              0 (debug)
              3 JUMP_IF_FALSE            8 (to 14)
              6 POP_TOP

  3           7 LOAD_GLOBAL              1 (foo)
             10 POP_TOP
             11 JUMP_FORWARD             1 (to 15)
        >>   14 POP_TOP
        >>   15 LOAD_CONST               0 (None)
             18 RETURN_VALUE

Here you could scan for the LOAD_GLOBAL of debug, and eliminate it and everything up to the JUMP_IF_FALSE target.

This one is the more traditional C-style debug() function that gets nicely obliterated by a preprocessor:

>>> def func2():
...    debug('bar', baz)
>>> dis.dis(func2)
  2           0 LOAD_GLOBAL              0 (debug)
              3 LOAD_CONST               1 ('bar')
              6 LOAD_GLOBAL              1 (baz)
              9 CALL_FUNCTION            2
             12 POP_TOP
             13 LOAD_CONST               0 (None)
             16 RETURN_VALUE

Here you would look for LOAD_GLOBAL of debug and wipe everything up to the corresponding CALL_FUNCTION.

Of course, both of those descriptions of what you would do are far simpler than what you'd really need for all but the most simplistic patterns of use, but I think it would be feasible. Would make a cute project, if nobody's already done it.

like image 27
Peter Hansen Avatar answered Oct 16 '22 00:10

Peter Hansen


Well, you can always implement your own simple preprocessor that does the trick. Or, even better, you can use an already existing one. Say http://code.google.com/p/preprocess/

like image 29
Tomas Brambora Avatar answered Oct 16 '22 00:10

Tomas Brambora