Reputation: 10143
Is there a way to debug a function that is defined dynamically in run time?
Or at least is there an easy way to find out where this function is produced?
Update to give more detail:
I used inspect
module:
ipdb> inspect.getmodule(im.get_thumbnail_url)
Out[0]: <module 'django.utils.functional' from 'C:\java\python\Python25\Lib\site
-packages\django\utils\functional.pyc'>
ipdb> inspect.getsource(im.get_thumbnail_url)
Out[0]: ' def _curried(*moreargs, **morekwargs):\n return _curried_fun
c(*(args+moreargs), **dict(kwargs, **morekwargs))\n'
Here inspect
shows that the get_thumbnail_url
method of photos.models.Image
class of pinax is produced by django.utils.functional.curry._curried
function. But it still doesn't show where the method is produced, namely where is _curried
function called. This information is necessary to find out how get_thumbnail_url
is implemented.
I can put pdb
inside _curried
function, but then it breaks lots of times there because this is a very frequently used function call. I need to have some distinguishing feature to use breakpoint condition.
Update about solution:
Thanks for all suggestions. I found out the solution. Let me explain how I found it. Maybe it will help other people:
First, I searched for 'get_thumbnail_url' term in pinax source code. No result. Second, I searched for 'thumbnail' term in pinax source code. No useful result. Lastly, I searched for 'curry' term in pinax source code. The following was one of several results:
def add_accessor_methods(self, *args, **kwargs):
for size in PhotoSizeCache().sizes.keys():
setattr(self, 'get_%s_size' % size,
curry(self._get_SIZE_size, size=size))
setattr(self, 'get_%s_photosize' % size,
curry(self._get_SIZE_photosize, size=size))
setattr(self, 'get_%s_url' % size,
curry(self._get_SIZE_url, size=size))
setattr(self, 'get_%s_filename' % size,
curry(self._get_SIZE_filename, size=size))
get_thumbnail_url
method is produced by this call: curry(self._get_SIZE_url, size=size))
.
But of course this is not a general solution method. If you can share alternative ways to find out where a dynamically defined function is actually produced, this would be of great use.
Edit:
The best general solution is written below by Jason Orendorff.
Upvotes: 4
Views: 3909
Reputation: 45116
Given a function f
, in CPython you can print f.func_code.co_filename
and f.func_code.co_firstlineno
to get the file and first line number. This will not help if the function was created using eval
or exec
.
Tracebacks contain file and line information too.
If you import dis
you can use dis.dis(f)
to see the CPython bytecodes; this might not be useful, but it might show some strings that help you find your way to the right place.
Another thing to look at is PDB, the Python text-mode debugger. After an exception, import pdb; pdb.pm()
launches PDB. It's primitive, but useful; type help
for a list of commands. where
shows the stack.
EDIT: You mention this is a curried function. If it was created using functools.partial
, then it has a .func
attribute that refers to the underlying function.
EDIT 2: The general way would be to set a breakpoint on im.get_thumbnail_url
and then immediately call it. Your breakpoint should hit right away. Then step until you reach the code you're interested in.
Since the curried function was, in this case, produced by code like:
def curry(_curried_func, *args, **kwargs):
def _curried(*moreargs, **morekwargs):
return _curried_func(*(args+moreargs), **dict(kwargs, **morekwargs))
return _curried
another approach is to examine f.func_closure
, like this:
>>> f
<function _curried at 0xb77d64fc>
>>> f.func_closure
(<cell at 0xb77eb44c: tuple object at 0xb77dfa0c>, <cell at 0xb77eb5e4: dict object at 0xb77d93e4>, <cell at 0xb77eb5cc: function object at 0xb77d1d84>)
The function closes on three variables: a tuple, a dict, and a function. Obviously the function is the one we're interested in (but fyi these three cells correspond to the variables args
, kwargs
, and _curried_func
that are defined in the enclosing function curry
but used by the closure _curried
).
>>> f.func_closure[2].cell_contents
<function say at 0xb77d1d84>
>>> import inspect
>>> inspect.getsource(_)
'def say(x):\n print x\n'
Upvotes: 1
Reputation: 51924
Not sure, but you can get a function to print out the source file and line number it was defined in using the inspect module:
import inspect
f = lambda: inspect.currentframe().f_code.co_filename + ':' + \
str(inspect.currentframe().f_lineno);
print f()
It prints:
script.py:2
Upvotes: 1