Get the code of an unnamed lambda used as a function argument
Opened this issue · 7 comments
Inspired by https://stackoverflow.com/q/75787845, I tried dill.source
, and it appears to have the same issue(?) as inspect
:
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(lambda: True)
def function() -> None:
pass # This should not be printed as part of lambda_argument!
function()
Output:
Printing lambda_argument ...
<function <lambda> at 0x000001E3CD2F04A0>
Printing source of lambda_argument ...
@decorator_with_lambda(lambda: True)
def function() -> None:
pass # This should not be printed as part of lambda_argument!
What I would like to be printed is lambda: True
or even True
, but instead I am getting the code of all of function
.
Others have pointed out my lambda_argument
wasn't a lambda at all, but I believe the output shows it is one.
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
>>>
Thanks! Is there a chance that dill.source
will be able to handle this any better than inspect
, or are the two so closely knit together that I should not expect a resolution unless it comes through the Python standard library?
I'm not sure what you mean. inspect
can't get the source correctly while dill.source
does. If inspect
begins to handle this case, we will certainly leverage it -- but otherwise, I'd expect new development to be done in dill
.
>>> foo = lambda : True
>>> import inspect
>>> def decorator_with_lambda(lambda_argument):
... print("Printing lambda_argument ...")
... print(lambda_argument)
... print("Printing source of lambda_argument ...")
... print(inspect.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 0x106d8bee0>
Printing source of lambda_argument ...
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 5, in decorator_with_lambda
File "/opt/local/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/inspect.py", line 997, in getsource
lines, lnum = getsourcelines(object)
File "/opt/local/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/inspect.py", line 979, in getsourcelines
lines, lnum = findsource(object)
File "/opt/local/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/inspect.py", line 798, in findsource
raise OSError('could not get source code')
OSError: could not get source code
>>>
Ah, now I see what you mean.
inspect
can't get the source correctly whiledill.source
does
I think this holds true for working on the REPL. My original code in https://stackoverflow.com/q/75787845, saved as some bug.py
, works fine when using python bug.py
.
So there may be a couple of intertwined issues, getting code from unnamed lambdas and getting code from unnamed lambdas on the REPL. My use case has nothing to with the REPL, so I am primarily interested in whether there is chance we will be able to extract an unnamed lambda's body inside a Python code file, be it using dill.source
or inspect
. Having looked at inspect
, the current implementation seems to very much line-based, and I wonder if the problem can be tackled at all (consider an example with two lambdas being defined on the same line). I'd hope that something like PEP 657 may allow closer inspection at one point, compare https://docs.python.org/3/whatsnew/3.11.html#whatsnew311-pep657
If I remember correctly, I probably didn't account for the case of extracting the source of an unnamed lambda where the reference to the lambda was extracted from a containing object. I assume this case needs to be added to the source parser (potentially using some dummy name for the lambda).
Note that this case (an unnamed lambda) should also fail:
>>> import dill
>>>
>>> class Foo(object):
... def __init__(self, func):
... self.func = func
... def __call__(self, *args, **kwds):
... return self.func(*args, **kwds)
...
>>> f = Foo(lambda x:x*x)
>>> f(2)
4
>>> dill.source.getsource(f.func)
However, if the lambda is named, it works.