2009-09-23 5 views
7

Cela semble être facile, mais je ne trouve nulle part la réponse, et je ne peux pas en trouver une moi-même. Comment transformer une fonction python sans guillemets/lambda en AST?Comment transformer une fonction Python non datée/lambda en AST? 2.6

Voici ce que j'aimerais pouvoir faire.

import ast 
class Walker(ast.NodeVisitor): 
    pass 
    # ... 

# note, this doesnt work as ast.parse wants a string 
tree = ast.parse(lambda x,y: x+y) 

Walker().visit(tree) 
+0

@Ants suggestion Aasma était plus proche de ce que j'espérais, bien qu'il semble être beaucoup plus impliqué que je ne pensais et j'imagine plus fragiles wrt différentes versions de Python (si des changements bytecode). GeniuSQL semble prometteur aussi. Désactivé pour faire des tests! – Chris

+0

Aussi, juste trouvé ceci: http://code.activestate.com/recipes/442447/ – Chris

Répondre

6

Si vous n'avez accès qu'à la fonction/lambda, vous n'avez que le bytecode python compilé. L'AST Python exact ne peut pas être reconstruit à partir du bytecode car il y a une perte d'information dans le processus de compilation. Mais vous pouvez analyser le bytecode et créer des AST pour cela. Il y a un tel analyseur dans GeniuSQL. J'ai aussi une petite preuve de concept qui analyse le bytecode et crée des éléments de clause SQLAlchemy à partir de cela.

Le processus j'ai utilisé pour l'analyse est la suivante:

  1. de Split le code dans une liste des opcodes avec des arguments potentiels.
  2. Trouvez les blocs de base dans le code en passant par les opcodes et créez pour chaque saut une limite de bloc de base après le saut et avant la cible de saut
  3. Créez un graphique de flux de contrôle à partir des blocs de base.
  4. Parcourez tous les blocs de base avec la pile de suivi d'interprétation abstraite et les affectations de variables sous forme SSA.
  5. Pour créer l'expression de sortie, il suffit d'obtenir la valeur de retour SSA calculée.

J'ai collé mes proof of concept et example code using it. C'est du code piraté rapidement et non-nettoyé, mais vous pouvez en tirer parti si vous le souhaitez. Laissez une note si vous décidez d'en faire quelque chose d'utile.

+0

Ah, je vois. Donc, vous prenez le bytcode compilé et créez votre propre AST approprié pour générer des grammaires SQL ou autres. C'est assez brillant (et bien au-delà de mon niveau de compétence :). Je vais regarder de plus près votre code et GenuiuSQL. Je ne me suis pas rendu compte qu'il y avait une autre route que le module standard '' '' '' ''' '' '' '' '' '' '' '' '' '' ''' '' '' '' '' '' '' '' ' Merci. – Chris

+0

Les liens vers dpaste sont maintenant périmés :( –

0

Votre expression lambda est une fonction qui a beaucoup d'informations, mais je ne pense pas qu'il a toujours le code source associé à. Je ne suis pas sûr que tu puisses obtenir ce que tu veux.

10

En général, vous ne pouvez pas. Par exemple, 2 + 2 est une expression - mais si vous la passez à une fonction ou à une méthode quelconque, l'argument transmis est simplement le nombre 4, aucun moyen de récupérer l'expression à partir de laquelle elle a été calculée. Le code source de fonction peut parfois être récupéré (mais pas pour lambda), mais "une expression Python non protégée" obtient évalué donc ce que vous obtenez est juste l'objet qui est la valeur de l'expression.

Quel problème tentez-vous de résoudre? Il peut y avoir d'autres approches viables.

Modifier: tx à l'OP pour la clarification. Il n'y a aucun moyen de le faire pour lambda ou d'autres cas d'angle, mais comme je mentionne le code source de la fonction peut parfois être récupéré ...:

import ast 
import inspect 

def f(): 
    return 23 

tree = ast.parse(inspect.getsource(f)) 

print ast.dump(tree) 

inspect.getsource soulève IOError si elle ne peut pas obtenir le code source pour tout objet que vous le passez. Je suggère que vous enveloppiez l'appel d'analyse et getsource dans une fonction auxiliaire qui peut accepter une chaîne (et l'analyse juste) OU une fonction (et essaye getsource dessus, donnant probablement de meilleures erreurs dans le cas IOError).

+0

Je suis désolé - je vois cette expression est le mauvais terme à utiliser. Il a été retiré de la question. En général, j'essaie de transformer un AST en une autre grammaire. Exemples spécifiques: à partir du func/lambda donné, générer une instruction SQL ou une vue couchdb javascript/réduire une vue ou un dict de requête mongodb, etc Je suppose que non cité n'est pas une exigence stricte, mais serait plus propre. – Chris

+0

Merci pour vos suggestions et votre code. Je n'étais pas au courant de insepect.getsource. Je peux essayer à la fois l'approche bytecode et cette approche inspect.getsource à comparer. – Chris

1

Vous ne pouvez pas générer d'AST à partir du bytecode compilé. Vous avez besoin du code source.

4

The Meta libraryThe Meta library vous permet de récupérer la source dans de nombreux cas, avec quelques exceptions telles que les compréhensions et les lambdas.

import meta, ast 
source = ''' 
a = 1 
b = 2 
c = (a ** b) 
''' 

mod = ast.parse(source, '<nofile>', 'exec') 
code = compile(mod, '<nofile>', 'exec') 

mod2 = meta.decompile(code) 
source2 = meta.dump_python_source(mod2) 

assert source == source2