2010-01-05 8 views
4

Existe-t-il un moyen de déboguer une fonction définie dynamiquement en cours d'exécution?Comment déboguer des fonctions définies dynamiquement en Python?

Ou au moins existe-t-il un moyen facile de savoir où cette fonction est produite?

mise à jour pour donner plus de détails:

I utilisé 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' 

Ici inspect montre que la méthode get_thumbnail_url de photos.models.Image classe de pinax est produit par django.utils.functional.curry._curried fonction. Mais il ne montre toujours pas où la méthode est produite, à savoir où est la fonction _curried appelée. Cette information est nécessaire pour savoir comment get_thumbnail_url est implémenté.

Je peux mettre pdb à l'intérieur de _curried fonction, mais alors il casse beaucoup de fois là parce que c'est un appel de fonction très fréquemment utilisé. Je dois avoir une caractéristique distinctive pour utiliser la condition de point de rupture.

Mise à jour sur la solution:

Merci pour toutes les suggestions. J'ai trouvé la solution. Laissez-moi vous expliquer comment je l'ai trouvé. Peut-être que cela va aider les autres:

D'abord, j'ai cherché le terme 'get_thumbnail_url' dans le code source de pinax. Pas de résultat. Deuxièmement, j'ai cherché le terme «miniature» dans le code source de pinax. Pas de résultat utile Enfin, j'ai cherché le terme 'curry' dans le code source de pinax. Ce qui suit a été l'un de plusieurs résultats:

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)) 

méthode get_thumbnail_url est produit par cet appel: curry(self._get_SIZE_url, size=size)).

Mais bien sûr, ce n'est pas une méthode de solution générale. Si vous pouvez partager des moyens alternatifs pour savoir où une fonction définie dynamiquement est réellement produite, ce serait très utile.

Edit:

Le meilleur general solution est écrit ci-dessous par Jason Orendorff.

+0

Que voulez-vous dire par "fonction définie dynamiquement"? Est-ce une fonction 'lambda' ou une fonction générée par' exec ("def foo(): barre de retour") 'ou quelque chose de similaire? –

+0

Plus précisément, la fonction que j'essaie d'introspecter maintenant est une fonction au curry. Je ne pense pas que ce soit une fonction lambda ou une fonction générée par exec. Je ne sais pas comment il est défini exactement parce que je ne sais pas où il est produit. Mais je suppose que l'auteur a utilisé une sorte de dispatching. –

+0

Pouvez-vous poster un exemple de code? – richo

Répondre

1

Compte tenu d'une fonction f, dans CPython, vous pouvez imprimer f.func_code.co_filename et f.func_code.co_firstlineno pour obtenir le fichier et le numéro de la première ligne. Cela n'aidera pas si la fonction a été créée en utilisant eval ou exec.

Les retraits contiennent également des informations sur les fichiers et les lignes.

Si vous import dis vous pouvez utiliser dis.dis(f) pour voir les bytecodes CPython; Cela peut ne pas être utile, mais il peut montrer des chaînes qui vous aident à trouver votre chemin au bon endroit.

Une autre chose à regarder est PDB, le débogueur en mode texte Python. Après une exception, import pdb; pdb.pm() lance PDB. C'est primitif, mais utile; tapez help pour obtenir la liste des commandes. where montre la pile.

EDIT: Vous mentionnez ceci est une fonction au curry. S'il a été créé en utilisant functools.partial, alors il a un attribut .func qui fait référence à la fonction sous-jacente.

EDIT 2: La méthode générale consiste à définir un point d'arrêt sur im.get_thumbnail_url, puis de l'appeler immédiatement. Votre point d'arrêt devrait frapper tout de suite. Ensuite, l'étape jusqu'à ce que vous atteignez le code que vous êtes intéressé par

Puisque la fonction cari était, dans ce cas, produit par code comme:.

def curry(_curried_func, *args, **kwargs): 
    def _curried(*moreargs, **morekwargs): 
     return _curried_func(*(args+moreargs), **dict(kwargs, **morekwargs)) 
    return _curried 

Une autre approche consiste à examiner f.func_closure, comme ceci:

>>> 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>) 

La fonction se ferme sur trois variables: un tuple, un dict et une fonction. Évidemment, la fonction est celle qui nous intéresse (mais ces trois cellules correspondent aux variables args, kwargs et _curried_func qui sont définies dans la fonction englobante curry mais utilisées par la fermeture _curried).

>>> f.func_closure[2].cell_contents 
<function say at 0xb77d1d84> 
>>> import inspect 
>>> inspect.getsource(_) 
'def say(x):\n print x\n' 
1

Je ne sais pas, mais vous pouvez obtenir une fonction pour imprimer le fichier source et le numéro de la ligne qu'elle a été définie dans l'utilisation de l'inspection Module:

import inspect 
f = lambda: inspect.currentframe().f_code.co_filename + ':' + \ 
    str(inspect.currentframe().f_lineno); 
print f() 

Il imprime:

script.py:2 
Questions connexes