Scoping bugs in an embedded Python interpreter - Wizards please

M

morris.slutsky

So every now and then I like to mess around with hobby projects - I
often end up trying to write an OpenGL video game. My last attempt
aborted due to the difficulty of automating game elements and creating
a good level editor - I basically needed a scripting language to
control the C modules of the game and, after a half-assed attempt or
two to make my own, I just gave up. So naturally this seems like a job
for Python. Embedding Python into an OpenGL skeleton just seemed like
a great place to start for my next project. I can write what I need to
in C, and tie it all together with Python, the games "main loop" itself
doesn't even need to be in C, I can test things as I go, can reuse
rendering code for game and level editing applications with great ease,
Python just seems like a wonderful way to go.

And it is wonderful! I can just bring up an OpenGL window now, there's
a console in there, I can type interactive commands in there and they
get fed into a python interpreter, I can even type stuff like
glBegin(gl.LINES); glColor()...glVertex()....glEnd() and see stuff
appear on the screen, it's really amazing and lots of fun. I am sure
that someday I can be calling C functions like RenderGame, MoveCamera,
UpdatePhysicsModel(deltaT), all from Python, and life will be good.
Maybe I can bring bits of my last game in, piece by piece, and tie it
all together.

I'm doing all this on Windows, using Microsoft Visual C++. Which is a
nice compiler, and as an academic I get it free, always a bonus, and I
can put Python code into resources compiled into the executable and run
it the same way as I handle interactive commands (being sure to strip
out all 0x0D characters because for some reason Python believes that
the One True Line Terminator is 0x0A) and it's all good, it's really
great.

The embedding - I'm doing it the way I've seen in web FAQs and the
Python documentation. I handle output by creating an object "myIO"
with a "write()" method and having Python start up with "import sys;
import myIO; sys.stdout = myIO; sys.stderr = myIO" and C gets magically
called to display Python's output on my OpenGL console, it's grand!
And I just run all the Python statements, interactive or from
resources, by calling Py_CompileString() on the string containing
python code and then, if there are no errors, calling PyEval_EvalCode.
If I'm running interactive I check for an "unexpected EOF" error just
like I saw in some FAQ and if I get one I just display a continuation
prompt and keep accumulating interactive input until I get a blank
line.... it looks just like the interactive Python you get from a
command line, it's just in my OpenGL console, which totally rules.

But .... I'm suffering from serious scoping bugs. I hope someone here
can help me with them.

First of all, if I type in something like;

def f(x):
if x==1:
return x
else:
return x * f(x-1)

and then go
print f(5)

I get an error. It says 'global name 'f' is not defined'. So
recursion won't work because of some odd scoping bug.

Similarly, one of my Python startup files looks like this:

class gl:
.....
POINTS = 0x0000
LINES = 0x0001
LINE_LOOP = 0x0002
LINE_STRIP = 0x0003
TRIANGLES = 0x0004

Well, I can't do a "global gl" statement in any function or I get the
same error. I end up having to pass the "gl" object down and down and
down, just so I can type glBegin(gl.POINTS) or something, from within a
function. And I know that this just can't be proper behavior.

Did I screw up my scoping somehow? How can I fix this? Is there some
way to call the interpreter that I don't know how to do?

I appeal to a wizard to help me with this small, but very annoying,
issue with an otherwise beautiful language.
 
F

Fredrik Lundh

But .... I'm suffering from serious scoping bugs. I hope someone here
can help me with them.

First of all, if I type in something like;

def f(x):
if x==1:
return x
else:
return x * f(x-1)

and then go
print f(5)

I get an error. It says 'global name 'f' is not defined'. So
recursion won't work because of some odd scoping bug.

looks like the globals are all messed up. it would help to see the
actual C code.

</F>
 
M

morris.slutsky

Fredrik said:
looks like the globals are all messed up. it would help to see the
actual C code.

</F>

Thanks!

I initialize the interpreter like this:


static PyObject *glb, *loc;

void startPython() {
Py_Initialize();
loc = PyDict_New();
glb = PyDict_New();
PyDict_SetItemString (glb, "__builtins__", PyEval_GetBuiltins ());

Py_InitModule("myIO", myIOMethods);
...... other modules initialization ......

executeFromResource(IDR_STARTUP_PY);
....... other startup scripts ........
}

I pretty much do the same thing for executing text resources or for
interactive commands - (and get the same bug either way) - actual code
from executeFromResource follows ....


int executeFromResource(int nResourceID) {
PyObject * src, *exception, *value, *traceback, *object, *result;
......miscellaneous Win32 stuff to capture resource from ID
......and to strip out 0x0D characters and add NULL terminator.
......char * "no0x0D" points to executable Python text
......msg is something like <<resource #103>>

src = Py_CompileString(no0x0D, msg, Py_file_input);

free(no0x0D);

if (src != NULL) {
/* it compiled. run it! */
result = PyEval_EvalCode ((PyCodeObject *)src, glb, loc);
if (PyErr_Occurred()) PyErr_Print();
Py_XDECREF(result);
Py_XDECREF(src);
return 1;
}
/* report whatever error we got */
if (nResourceID != IDR_STARTUP_PY)
{
/* report it to the console */
PyErr_Print();
PyErr_Clear();
} else {
/* if startup script dies, we have no console */
PyErr_Fetch (&exception, &value, &traceback);
PyArg_ParseTuple (value, "sO", &msg, &object);
PyErr_Clear();
Py_XDECREF (exception);
Py_XDECREF(value);
Py_XDECREF(traceback);
outputLogDialog("Error in Python startup\r\n");
outputLogDialog(msg);
outputLogDialog("\r\n");
}
return 0;
}

I had figured that referencing PyObject * glb and * loc was sufficient
to provide proper variable scoping. Am I missing something here? I
must be.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,982
Messages
2,570,190
Members
46,738
Latest member
TiffinyHol

Latest Threads

Top