views:

240

answers:

4

This seems like it should be easy, but I can't find the answer anywhere - nor able to derive one myself. How do you turn an unquoted python function/lambda into an AST?

Here is what I'd like to be able to do.

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

Your lambda expression is a function, which has lots of information, but I don't think it still has source code associated with. I'm not sure you can get what you want.

Ned Batchelder
+4  A: 

In general, you can't. For example, 2 + 2 is an expression -- but if you pass it to any function or method, the argument being passed is just the number 4, no way to recover what expression it was computed from. Function source code can sometimes be recovered (though not for a lambda), but "an unquoted Python expression" gets evaluated so what you get is just the object that's the expression's value.

What problem are you trying to solve? There may be other, viable approaches.

Edit: tx to the OP for clarifying. There's no way to do it for lambda or some other corner cases, but as I mention function source code can sometimes be recovered...:

import ast
import inspect

def f():
  return 23

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

print ast.dump(tree)

inspect.getsource raises IOError if it can't get the source code for whatever object you're passing it. I suggest you wrap the parsing and getsource call into an auxiliary function that can accept a string (and just parses it) OR a function (and tries getsource on it, possibly giving better errors in the IOError case).

Alex Martelli
I'm sorry - I see that expression is the wrong term to use. It has been removed from the question.In general, I'm trying transform an AST into another grammar. Specific examples: from the given func/lambda, generate a SQL statement or a javascript couchdb map/reduce view or a mongodb query dict, etc. I suppose unquoted isn't a strict requirement, but would be cleaner.
Chris
Thanks for your suggestions and code. I was was not aware of insepect.getsource. I may attempt both the bytecode approach and this inspect.getsource approach to compare.
Chris
+1  A: 

You can't generate AST from compiled bytecode. You need the source code.

nosklo
+3  A: 

If you only get access to the function/lambda you only have the compiled python bytecode. The exact Python AST can't be reconstructed from the bytecode because there is information loss in the compilation process. But you can analyze the bytecode and create AST's for that. There is one such analyzer in GeniuSQL. I also have a small proof of concept that analyzes bytecode and creates SQLAlchemy clauseelements from this.

The process I used for analyzing is the following:

  1. Split the code into a list of opcodes with potential arguments.
  2. Find the basic blocks in the code by going through the opcodes and for every jump create a basic block boundary after the jump and before the jump target
  3. Create a control flow graph from the basic blocks.
  4. Go through all the basic blocks with abstract interpretation tracking stack and variable assignments in SSA form.
  5. To create the output expression just get the calculated SSA return value.

I have pasted my proof of concept and example code using it. This is non-clean quickly hacked together code, but you're free to build on it if you like. Leave a note if you decide to make something useful from it.

Ants Aasma
Ah, I see. So you take the compiled bytcode and create your own AST suitable for generating SQL or other grammars. That is pretty brilliant (and far beyond my skill level:). I will take a further look at your code and GenuiuSQL. I didn't realize there was another route other than the standard lib ``ast`` module, and I think that was blinding me. Thanks.
Chris