Skip to content Skip to sidebar Skip to footer

Replacing Parts Of The Function Code On-the-fly

Here I came up with the solution to the other question asked by me on how to remove all costly calling to debug output function scattered over the function code (slowdown was 25 ti

Solution 1:

A decorator can return either a wrapper, or the decorated function unaltered. Use it to create a better debugger:

from functools import wraps

defdebug(enabled=False):
    ifnot enabled:
        returnlambda x: x  # Noop, returns decorated function unaltereddefdebug_decorator(f):
        @wraps(f)defprint_start(*args, **kw):
            print('{0}() started'.format(f.__name__))
            try:
                return f(*args, **kw)
            finally:
                print('{0}() completed'.format(f.__name__))
        return print_start
    return debug_decorator

The debug function is a decorator factory, when called it produces a decorator function. If debugging is disabled, it simply returns a lambda that returns it argument unchanged, a no-op decorator. When debugging is enabled, it returns a debugging decorator that prints when a decorated function has started and prints again when it returns.

The returned decorator is then applied to the decorated function.

Usage:

DEBUG = True@debug(DEBUG)defmy_function_to_be_tested():
    print('Hello world!')

To reiterate: when DEBUG is set to false, the my_function_to_be_tested remains unaltered, so runtime performance is not affected at all.

Solution 2:

Here is the solution I came up with after composing answers from another questions asked by me here on StackOverflow.

This solution don't comment anything and just deletes standalone dprint statements. It uses ast module and works with Abstract Syntax Tree, it lets us avoid parsing source code. This idea was written in the comment here.

Writing to temp_f.py is replaced with execution f in necessary environment. This solution was offered here.

Also, the last solution addresses the problem of decorator recursive application. It's solved by using _blocked global variable.

This code solves the problem asked to be solved in the question. But still, it's suggested not to be used in real projects:

You are correct, you should never resort to this, there are so many ways it can go wrong. First, Python is not a language designed for source-level transformations, and it's hard to write it a transformer such as comment_1 without gratuitously breaking valid code. Second, this hack would break in all kinds of circumstances - for example, when defining methods, when defining nested functions, when used in Cython, when inspect.getsource fails for whatever reason. Python is dynamic enough that you really don't need this kind of hack to customize its behavior.

from __future__ import print_function

DEBUG = Falsedefdprint(*args,**kwargs):
    '''Debug print'''print(*args,**kwargs)

_blocked = Falsedefnodebug(name='dprint'):
    '''Decorator to remove all functions with name 'name' being a separate expressions'''defhelper(f):      
        global _blocked
        if _blocked:
            return f

        import inspect, ast, sys

        source = inspect.getsource(f)        
        a = ast.parse(source) #get ast tree of fclassTransformer(ast.NodeTransformer):
            '''Will delete all expressions containing 'name' functions at the top level'''defvisit_Expr(self, node): #visit all expressionstry:
                    if node.value.func.id == name: #if expression consists of function with name areturnNone#delete itexcept(ValueError):
                    passreturn node #return node unchanged
        transformer = Transformer()
        a_new = transformer.visit(a)
        f_new_compiled = compile(a_new,'<string>','exec')

        env = sys.modules[f.__module__].__dict__
        _blocked = Truetry:
            exec(f_new_compiled,env)
        finally:
            _blocked = Falsereturn env[f.__name__]         
    return helper


@nodebug('dprint')        deff():
    dprint('f() started')
    print('Important output')
    dprint('f() ended')
    print('Important output2')


f()

Other relevant links:

Post a Comment for "Replacing Parts Of The Function Code On-the-fly"