Create a python function from the given source code \param sourceCode A python string containing the core of the function. Might include the return statement (or not), definition of local functions, classes, etc. Indentation matters ! \param args The string representing the argument
(sourceCode, args="", additional_symbols=dict())
| 19 | |
| 20 | |
| 21 | def createFunction(sourceCode, args="", additional_symbols=dict()): |
| 22 | """ |
| 23 | Create a python function from the given source code |
| 24 | |
| 25 | \param sourceCode A python string containing the core of the |
| 26 | function. Might include the return statement (or not), definition of |
| 27 | local functions, classes, etc. Indentation matters ! |
| 28 | |
| 29 | \param args The string representing the arguments to put in the function's |
| 30 | prototype, such as "a, b", or "a=12, b", |
| 31 | or "a=12, b=dict(akey=42, another=5)" |
| 32 | |
| 33 | \param additional_symbols A dictionary variable name => |
| 34 | variable/funcion/object to include in the generated function's |
| 35 | closure |
| 36 | |
| 37 | The sourceCode will be executed in a restricted environment, |
| 38 | containing only the python builtins that are harmless (such as map, |
| 39 | hasattr, etc.). To allow the function to access other modules or |
| 40 | functions or objects, use the additional_symbols parameter. For |
| 41 | example, to allow the source code to access the re and sys modules, |
| 42 | as well as a global function F named afunction in the sourceCode and |
| 43 | an object OoO named ooo in the sourceCode, specify: |
| 44 | additional_symbols = dict(re=re, sys=sys, afunction=F, ooo=OoO) |
| 45 | |
| 46 | \return A python function implementing the source code. It can be |
| 47 | recursive: the (internal) name of the function being defined is: |
| 48 | __TheFunction__. Its docstring is the initial sourceCode string. |
| 49 | |
| 50 | Tests show that the resulting function does not have any calling |
| 51 | time overhead (-3% to +3%, probably due to system preemption aleas) |
| 52 | compared to normal python function calls. |
| 53 | """ |
| 54 | # Include the sourcecode as the code of a function __TheFunction__: |
| 55 | s = "def __TheFunction__(%s):\n" % args |
| 56 | s += "\t" + "\n\t".join(sourceCode.split('\n')) + "\n" |
| 57 | |
| 58 | # Byte-compilation (optional) |
| 59 | byteCode = compile(s, "<string>", 'exec') |
| 60 | |
| 61 | # Setup the local and global dictionaries of the execution |
| 62 | # environment for __TheFunction__ |
| 63 | bis = dict() # builtins |
| 64 | globs = dict() |
| 65 | locs = dict() |
| 66 | |
| 67 | # Setup a standard-compatible python environment |
| 68 | bis["locals"] = lambda: locs |
| 69 | bis["globals"] = lambda: globs |
| 70 | globs["__builtins__"] = bis |
| 71 | globs["__name__"] = "SUBENV" |
| 72 | globs["__doc__"] = sourceCode |
| 73 | |
| 74 | # Determine how the __builtins__ dictionary should be accessed |
| 75 | if type(__builtins__) is dict: |
| 76 | bi_dict = __builtins__ |
| 77 | else: |
| 78 | bi_dict = __builtins__.__dict__ |