Question: I have the following function def foo(): for _ in range(1): print("hello")  Now I want to add another print statement to print "Loop iterated" after every loop iteration. For this I define a new function that transforms foo into an ast tree, inserts the corresponding print node and then compiles the ast tree into an executable function: def modify(func): def wrapper(): source_code = inspect.getsource(func) ast_tree = ast.parse(source_code) # insert new node into ast tree for node in ast.walk(ast_tree): if isinstance(node, ast.For): node.body += ast.parse("print('Loop iterated')").body # get the compiled function new_func = compile(ast_tree, '<ast>', 'exec') namespace = {} exec(new_func, globals(), namespace) new_func = namespace[func.__name__] return new_func() return wrapper  This works fine as expected when using: foo = modify(foo) foo()  However, if I decide to use modify as a decorator: @modify def foo(): for _ in range(1): print("hello") foo()  I get the following error: Traceback (most recent call last): File "c:\Users\noinn\Documents\decorator_test\test.py", line 34, in <module> foo() File "c:\Users\noinn\Documents\decorator_test\test.py", line 25, in wrapper return new_func() File "c:\Users\noinn\Documents\decorator_test\test.py", line 11, in wrapper source_code = inspect.getsource(func) File "C:\Users\noinn\AppData\Local\Programs\Python\Python39\lib\inspect.py", line 1024, in getsource lines, lnum = getsourcelines(object) File "C:\Users\noinn\AppData\Local\Programs\Python\Python39\lib\inspect.py", line 1006, in getsourcelines lines, lnum = findsource(object) File "C:\Users\noinn\AppData\Local\Programs\Python\Python39\lib\inspect.py", line 835, in findsource raise OSError('could not get source code') OSError: could not get source code  Does anyone know why that error appears? Note that this does not happen If I return the original function and the error only appears once new_func() is called. ------------------------- Solution ---------------------- Simply remove the decorator from the function in the decorator itself using: ast_tree.body[0].decorator_list = [] 

Answer: After some experimenting, I found out my initial hypothesis is incorrect: inspect.getsource is smart enough to retrieve the source code even if the function name is not yet set in the module globals. (surprisingly). What happens is that the source code is retrieved along with the decorator calls as well, and when the function is called, the decorator runs again - so, it gets some re-entrancy in inspect.getsource, at which points it fails. The solution bellow work: I just strip decorators at the module level in the retrieved source code, before feeding it to ast.parse I also rearranged your decorator, as it would re-read the source code, and reparse the AST at each time the decorated function would be called - the way this example is, there is no need for an inner wrapper function at all. If you happen to need to parametrize your decorator, and need the inner wrapper, all the function re-writting parts, up to the creation of new_func, should be outside the wrapper, so that they run only once import inspect import ast import functools def modify(func): func.__globals__[func.__name__] = func source_lines = inspect.getsource(func).splitlines() # strip decorators from the source itself source_code = "\n".join(line for line in source_lines if not line.startswith('@')) ast_tree = ast.parse(source_code) # insert new node into ast tree for node in ast.walk(ast_tree): if isinstance(node, ast.For): node.body += ast.parse("print('Loop iterated')").body # get the compiled function new_func = compile(ast_tree, '<ast>', 'exec') namespace = {} exec(new_func, func.__globals__, namespace) new_func = namespace[func.__name__] return functools.wraps(func)(new_func) @modify def foo(): for _ in range(1): print("hello") #foo = modify(foo) foo()  initial answer Left here for the reasoning and simpler workaround: it can't get the source of <module>.foo function due to the simple fact that the name foo will only be defined and bound after the function definition, including all its decorators, is executed. The name foo simply does not exist as a variable in the module at the point the decorator code is run, although it is set as the __name__ attribute in func at this point. There is nothing inspect.getsource can do about it. In other words, decorator syntax is altogether off-limits if one is using inspect.getsource because there is nothing yet getsource can get the source of. The good news is the workaround is simple: one have to give-up the @ decorator syntax, and apply the decorator "in the old way" (as it was before Python 2.3): one declares the function as usual, and reassign its name to the return of manually calling the decorator - this is the same as is written in your working example: def foo(...): ... foo = modify(foo)