1

In the following example code, inspect.getsource(lambda_argument) seems to include the source code of the function call surrounding the definition of the lambda. How can I prevent that?

import inspect

def fun_of_lambda(lambda_argument):
    print(inspect.getsource(lambda_argument))

fun_of_lambda(lambda x, y, z: x + y + z)

Output:

fun_of_lambda(lambda x, y, z: x + y + z)

Previous version of the question:

import inspect

def decorator_with_lambda(lambda_argument):
    def decorator(inner_fun):
        print("Printing lambda_argument ...")
        print(inspect.getsource(lambda_argument))
        return inner_fun
    return decorator

@decorator_with_lambda(lambda: True)
def function() -> None:
    pass  # This should not be printed as part of lambda_argument!

function()

Output:

Printing lambda_argument ...
@decorator_with_lambda(lambda: True)
def function() -> None:
    pass  # This should not be printed as part of lambda_argument!

Update: dill.source seems to have the same issue, and I have reported https://github.com/uqfoundation/dill/issues/583.

bers
  • 4,817
  • 2
  • 40
  • 59
  • By no means your `lambda_argument` is a lambda. Actually it's just `function`, since that is in fact what you are decorating using the `@` syntax. And I think it won't even work at all. The parenthesis (the "call" will be executed before the syntax sugar of `@`). Did you try to execute that? What was the output? – Gameplay Mar 20 '23 at 08:27
  • @Gameplay `print(lambda_argument)` prints ` at 0x0000023F6BF696C0>`, so I would say it is a lambda. Also, I did execute it, the output is just below the code. Maybe you want to read how decorators with arguments work, there is an extra layer of functions involved there: "the decorator with arguments should return a function that will take a function and return another function", https://stackoverflow.com/a/5929165/880783 – bers Mar 20 '23 at 08:36
  • That's a good read indeed. Have a 2nd look yourself please. You are not on the level of "decorator_factory", you are on the level of "decorator" from that example. It's not gonna work imho. You never tried to call `function()` in your code. If you do you will see the error. Ask youself this: If my `decorator_with_lambda` takes one argument and I pass `lambda : True`, then what does the `@` do when I decorate a function. – Gameplay Mar 20 '23 at 08:47
  • @Gameply I don't have to follow all your (incorrect, IMHO) arguments, but *assuming* you are right, why does the output of `print(lambda_argument)` contain ``? – bers Mar 20 '23 at 08:49
  • @Gameplay the `@` calls `decorator` with `function` as an argument... – juanpa.arrivillaga Mar 20 '23 at 08:55
  • I am not entirely sure how `getsource` works, but it does not appear to be fine-grained enough to extract substrings from a single line of code. Write the function call as three lines (using implicit line breaking inside the parentheses) and you can get just the middle line containing the lambda expression. – chepner Mar 20 '23 at 13:02
  • @chepner: yes, I have debugged through `inspect.getsource` and that seems to be a fundamental limitation - everything is in terms of complete lines of code. – bers Mar 20 '23 at 14:38

1 Answers1

1

What I've found in other cases is dill.source can't get the source of "unnamed" lambdas. If you name them (i.e. give them an entry in the namespace), then dill.source works as expected. The same seems to apply in your case:

>>> foo = lambda : True
>>> import dill.source
>>> def decorator_with_lambda(lambda_argument):
...     print("Printing lambda_argument ...")
...     print(lambda_argument)
...     print("Printing source of lambda_argument ...")
...     print(dill.source.getsource(lambda_argument))
...     def decorator(inner_fun):
...         return inner_fun
...     return decorator
... 
>>> @decorator_with_lambda(foo)
... def function() -> None:
...   pass
... 
Printing lambda_argument ...
<function <lambda> at 0x100b903a0>
Printing source of lambda_argument ...
foo = lambda : True

>>> 
Mike McKerns
  • 33,715
  • 8
  • 119
  • 139
  • And the same if true for `inspect.getsource`. I wonder if there is a better solution, though. – bers Mar 20 '23 at 12:49
  • Your answer made me realize that the problem has nothing do with decorators, so I modified the original question. – bers Mar 20 '23 at 12:52