C / Python integration and local variables

Alexandre Courbot alexandrecourbot at linuxgames.com
Mon Sep 3 08:00:14 EDT 2001


Hello everybody,

In the project I'm working on, (written in C++) we use Python as a
scripting language, to control the behavior of game elements
(characters, etc...) from scripts. Here is a simplified representation
or what we used to do:

class character
{
    PyObject * locals;

    PyCodeObject * script;

    // Parse 'file' with PyParser_SimpleParseFile and create
    // the 'script' code object with PyNode_Compile
    void set_script (string file);

    // Function called once per game cycle, so the character
    // can do it's business
    void update ()
    {
        // data::globals is the game's globals namespace
	// which contains all wrapped methods and types
        PyEval_EvalCode (script, data::globals, locals);
    }


    // Control methods
    void go_north ();
    void go_south ();
    void go_east ();
    void go_west ();
  
    void speak (string t);
};

locals is built at constructor time, and is supposed to contain the
character's locales variables. You can assume it just contains a
'myself' member, which is a reference to the instance of the character
so it can be accessed from Python. That way, into the Python script set
with set_script () I can do something like myself.go_north () to control
the character from Python. So far, it works like a charm.

The problem is, that sometimes the script could have it's own variables.
For example, the following script would make the character utter some
random speech:

--------------------------------------------------------------------
speech = ["Hello!", "What time is it?", "The knights who say 'ni!'"]

myself.speak (speech[randint (0, 2)])
--------------------------------------------------------------------

That would make the character randomly say one of these 3 sentences each
time it's updated.

The problem is, that the speech list is created each time the script is
run. That's a waste of CPU time, and we'd like to avoid this. So we
tried a different approach: each script has 3 functions: init () which
is called when the script is set, cleanup () which is called when the
script is deleted (constructor/destructor behavior, actually) and run ()
which is the script itself (that is, what is run by update). Ideally,
our script would then be built this way:

---------------------------------------------------------------------
def init ():
     speech = ["Hello!", "What time is it?", \
               "The knights who say 'ni!'"]

def cleanup ():
     del speech

def run ():
     myself.speak (speech[randint (0, 2)])
---------------------------------------------------------------------

The PyCodeObject is built differently: First we import the script as a
module (PyImport_ImportModule), then we get the run () function by doing
a 'PyObject * function = PyObject_GetAttrString (module, "run")' and,
finally, we get the function code object by 'script = (PyCodeObject *)
PyObject_GetAttrString (function, "func_code");'. Idem for init () and
cleanup (). Then we run init by 'PyEval_Evalcode (initcode,
data::globals, locals) and we expect to have speech created into the
locals PyObject * (at least, that's what was done with the old code,
even though it wasn't necessary). And, when update () is called, the run
() function is called... raising a NameError because it cannot find
neither 'myself' nor 'speech'! And if I print the locals () inside the
function, it displays an enpty dictionary, even though the locals are
correctly passed from C++! That's where we are totally stuck. I've
browsed the archive of this list, and found that several people had
problems with locals () inside function calls, that's probably where the
problem lies.

What we would like to achieve is to keep the init () cleanup () run ()
structure (or anything equivalent) for our scripts and being able to
have persistant variables between scripts calls through the 'locals'
PyObject * so Python can communicate with C++ and vice versa, and mainly
so the script can have persistant variables. Speed is also a very
important issue for us, as the compiled scripts are run very often (70
times per second!). We'd be very thankfull to anyone who could help us
solving this issue. Any comment or suggestion are welcome.

Thanks for your time,

Alex.






More information about the Python-list mailing list