Over at this question there are some neat tricks for generating functions on the fly in Python.
In my use case, however, I need to make sure the generated function has a particular name and particularly-named arguments as well. I'll give an example.
Suppose I want to parse a Yaml file that has a format like this:
Root:
my_idea:
type: conditional
conditionals:
- foo
- bar
query: >
SELECT * FROM some_table
This needs to be interpreted as: create a function for me called "my_idea" which has arguments named "foo" and "bar" (in that order).
When the function is called, use some existing tool to connect to a database and prepare the query
element from the Yaml file. Then add a WHERE
clause based on the conditional names matching the values passed in to the function for that argument.
So, after this happens, a user should be able to call:
my_idea(10, 20)
and it would be equivalent to executing the query
SELECT * FROM some_table WHERE foo = 10 AND bar = 20
If I used def
to make the function, it might be something like this:
def my_idea(arg1, arg2):
query = (query_retrieved_from_file +
" WHERE {}={} AND {}={}".format(arg1_name_from_file,
arg1,
arg2_name_from_file,
arg2))
connection = ExistingLibraryConnectionMaker()
return connection.execute(query).fetchall()
This is a really simplified example -- I'm not endorsing the specifics of this little function, just trying to illustrate the idea.
The question is: how to create this on-the-fly, where the name of the function and the name of the positional arguments are extracted from a text file?
In the other question, there is some example code:
import types
def create_function(name, args):
def y(): pass
y_code = types.CodeType(args,
y.func_code.co_nlocals,
y.func_code.co_stacksize,
y.func_code.co_flags,
y.func_code.co_code,
y.func_code.co_consts,
y.func_code.co_names,
y.func_code.co_varnames,
y.func_code.co_filename,
name,
y.func_code.co_firstlineno,
y.func_code.co_lnotab)
return types.FunctionType(y_code, y.func_globals, name)
but it's not clear how to have the positional args reflect what I want them to semantically reflect.
The other solution I found was like this:
import types
import sys,imp
code = """def f(a,b,c):
print a+b+c, "really WoW"
"""
module = imp.new_module('myfunctions')
exec code in module.__dict__
module.f('W', 'o', 'W')
Output:
WoW really WoW
This is much closer, but requires all of the code to be embedded in a string format. I'm looking to build up a function programmatically, and across a reasonably large set of options, so handling them all deep in strings is not doable.