Zac
Zac

Reputation: 215

Call a function through a variable in Python

I'm trying to make a game engine in Python as a "weekend project" but I have a problem.

I'm trying to make it that a user can declare a keybind by typing the key and the function into a text file what they want to run but when I run the code from that sheet with exec it runs the function and I have no idea on how to call the function through the variable. (Also I don't want it to run the function when it runs the code.)

Here is the code I use for executing the code from binds.zdata

for line in bind:
    try:
        exec line
    except:
        try:
            error.write(localtime + " : " + "ERROR: Could not bind key from the bind file : " + line)
        except:
            pass

Here's the text in binds.zdata

_w = Functions.motion.move().forward()
_a = Functions.motion.move().left()
_s = Functions.motion.move().back()
_d = Functions.motion.move().right()

Upvotes: 6

Views: 372

Answers (4)

tlastowka
tlastowka

Reputation: 702

Reading the config like that with exec is going to open you to all kinds of pain at runtime, as the user's config file is going to be executable code that you are just trusting. Malicious or unintentionally just bad commands will get execute. You could template it like this:

def read_config(config_file):

    user_button_map = {}
    with open(config_file, 'r') as fh:
        for line in fh.readlines():
            key,func_key = line.strip().split(',')                

            assert(key in BUTTON_MAP_FUNCTIONS),"no such action"
            user_button_map[key] = BUTTON_MAP_FUNCTIONS[func_key]

    return user_button_map


BUTTON_MAP_FUNCTIONS = {
   "forward" : Functions.motion.move().forward,
   "left" : Functions.motion.move().left,
   # etc...
}

# sample config file
# w, forward
# a, left
# etc...

Upvotes: 2

martineau
martineau

Reputation: 123413

I'd first change the lines in the binds.zdata file like this so they assign a function to each of the variables:

_w = Functions.motion.move().forward
_a = Functions.motion.move().left
_s = Functions.motion.move().back
_d = Functions.motion.move().right

Then, assuming that Functions is defined, I'd execute all the lines in the file at once with something like:

try:
    execfile('binds.zdata')
except Exception as exc:
    error.write(localtime + " : " + "ERROR: executing binds file")

Afterwards, the _w, _a, _s, _d variables will have the desired function assigned to them, which you can call as usual — i.e. _w(), _a(), etc.

Upvotes: 3

RobertB
RobertB

Reputation: 1929

I'm not sure I recommend "exec" since somebody could put any code in there.

But here is your problem. _w = Functions.motion.move().forward() Calls the function 'forward' and puts the results in _w. _w = Functions.motion.move().forward Assigns the function 'forward' to the variable '_w'.

Since you asked what I would do, I would create a set of tokens that represent the various functions and then let them do the mapping in side a config file (see: ConfigParser). Then parse the config file. It is a little more work, but a lot more secure.

Upvotes: 4

Paul Evans
Paul Evans

Reputation: 27567

You want to lose the () at the end of each line:

_w = Functions.motion.move().forward
_a = Functions.motion.move().left
_s = Functions.motion.move().back
_d = Functions.motion.move().right

now you can call the function through the variable by simply applying parenthesis, such as:

_w()

Upvotes: 6

Related Questions