Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Running Python code contained in a string

I'm writing a game engine using pygame and box2d, and in the character builder, I want to be able to write the code that will be executed on keydown events.

My plan was to have a text editor in the character builder that let you write code similar to:

if key == K_a:
    ## Move left
    pass
elif key == K_d:
    ## Move right
    pass

I will retrieve the contents of the text editor as a string, and I want the code to be run in a method in this method of Character:

def keydown(self, key):
    ## Run code from text editor

What's the best way to do that?

like image 417
Brad Zeis Avatar asked Jun 18 '09 20:06

Brad Zeis


People also ask

How do you run a dynamic code in Python?

To execute dynamically generated Python code, use Python's exec function; to evaluate a dynamically generated Python expression, use Python's eval function. Beware: using these functions (or any of the below) with input from the user potentially allows the user to execute arbitrary code.

How do you run text in Python?

To run Python scripts with the python command, you need to open a command-line and type in the word python , or python3 if you have both versions, followed by the path to your script, just like this: $ python3 hello.py Hello World! If everything works okay, after you press Enter , you'll see the phrase Hello World!

How do you search for a part of a string in Python?

The find() string method is built into Python's standard library. It takes a substring as input and finds its index - that is, the position of the substring inside the string you call the method on.


2 Answers

You can use the eval(string) method to do this.

Definition

eval(code, globals=None, locals=None)
The code is just standard Python code - this means that it still needs to be properly indented.

The globals can have a custom __builtins__ defined, which could be useful for security purposes.

Example

eval("print('Hello')")

Would print hello to the console. You can also specify local and global variables for the code to use:

eval("print('Hello, %s'%name)", {}, {'name':'person-b'})

Security Concerns

Be careful, though. Any user input will be executed. Consider:

eval("import os;os.system('sudo rm -rf /')")

There are a number of ways around that. The easiest is to do something like:

eval("import os;...", {'os':None})

Which will throw an exception, rather than erasing your hard drive. While your program is desktop, this could be a problem if people redistributed scripts, which I imagine is intended.

Strange Example

Here's an example of using eval rather strangely:

def hello() : print('Hello')
def world() : print('world')
CURRENT_MOOD = 'happy'

eval(get_code(), {'contrivedExample':__main__}, {'hi':hello}.update(locals()))

What this does on the eval line is:

  1. Gives the current module another name (it becomes contrivedExample to the script). The consumer can call contrivedExample.hello() now.)
  2. It defines hi as pointing to hello
  3. It combined that dictionary with the list of current globals in the executing module.

FAIL

It turns out (thanks commenters!) that you actually need to use the exec statement. Big oops. The revised examples are as follows:


exec Definition

(This looks familiar!) Exec is a statement:
exec "code" [in scope] Where scope is a dictionary of both local and global variables. If this is not specified, it executes in the current scope.

The code is just standard Python code - this means that it still needs to be properly indented.

exec Example

exec "print('hello')"

Would print hello to the console. You can also specify local and global variables for the code to use:

eval "print('hello, '+name)" in {'name':'person-b'}

exec Security Concerns

Be careful, though. Any user input will be executed. Consider:

exec "import os;os.system('sudo rm -rf /')"

Print Statement

As also noted by commenters, print is a statement in all versions of Python prior to 3.0. In 2.6, the behaviour can be changed by typing from __future__ import print_statement. Otherwise, use:

print "hello"

Instead of :

print("hello")
like image 65
Lucas Jones Avatar answered Sep 20 '22 15:09

Lucas Jones


As others have pointed out, you can load the text into a string and use exec "codestring". If contained in a file already, using execfile will avoid having to load it.

One performance note: You should avoid execing the code multiple times, as parsing and compiling the python source is a slow process. ie. don't have:

def keydown(self, key):
    exec user_code

You can improve this a little by compiling the source into a code object (with compile() and exec that, or better, by constructing a function that you keep around, and only build once. Either require the user to write "def my_handler(args...)", or prepend it yourself, and do something like:

user_source = "def user_func(args):\n" + '\n'.join("    "+line for line in user_source.splitlines())

d={}
exec user_source in d
user_func = d['user_func']

Then later:

if key == K_a:
   user_func(args)
like image 26
Brian Avatar answered Sep 19 '22 15:09

Brian