[pygtk] Can't run PyGTK more than once from embedded Python

Patrick K. O'Brien pobrien at orbtech.com
Tue Jan 10 05:26:29 WST 2006


Romain Behar wrote:
> 
> Why running the script using:
> 
>  PyRun_String(my_script, 0, 0, 0);
> 
> or:
> 
>  PyObject* main_module =
> PyImport_AddModule("__main__");
>  PyObject* main_dict = PyModule_GetDict(main_module);
>  PyRun_String(my_script, 0, main_dict, main_dict);
> 
> does nothing? The script runs  and ends, but printed
> messages don't show in the console and PyGTK windows
> don't open. Is that what you call "using different
> namespaces"?

Here is a pile of code that represents the extent to which I was able to
create a decent environment for executing Python scripts.  I wouldn't
hold this up as the best code in the world, but it might help you out:

int PythonInit()
{
	if (!Py_IsInitialized())
	{
		Py_Initialize();  // No-op if already initialized.

		// Allow subinterpreters.
		PyEval_InitThreads();
		/// Start a subinterpreter.
		Py_NewInterpreter();
	}
	return(GOOD);
}


int PythonClose()
{
	if (Py_IsInitialized())
		Py_Finalize();
	return(GOOD);
}


// Set a flag saying that we need to reset the environment.
void PythonNewProgramInit()
{
	mRunReset = true;
}


// Create a fresh environment within which to execute test script code.
int PythonReset()
{
	if (!Py_IsInitialized())
	{
		PythonInit();
	}
	else
	{
		Py_EndInterpreter(PyThreadState_Get());
		Py_NewInterpreter();
	}

	return(GOOD);
}


// PythonTrace is a C function that will be called by Python for every event
// that takes place in its code evaluation loop.  Basically, this
function will
// get called each time Python executes another line of code.  The
arguments are
// required by Python, but we don't do anything with them.
//
// This is a hack wherein we tap into Python's debugging capabilities so
that we
// can keep the app GUI alive without resorting to threading.  This will
allow
// us to stop the execution of any Python code appearing in a file, with one
// important exception: this gimic cannot protect against an infinite loop
// coded as a single line of Python, such as:
//
// while True: pass
//
static int PythonTrace(PyObject *obj, PyFrameObject *frame, int what,
PyObject *arg)
{
	ptheApp->PeekAndPump();
	return 0;
}


// Run the Python code, typically extracted from a TE file.
int PythonRunCodeString(LPCSTR apstrCode)
{
	int errorCode = 0;
	char *cstrTmp = NULL;
	PyObject *pmod, *pdict, *pstr;

	PythonInit();  // No-op if already initialized.
	if (mRunReset)
	{
		PythonReset();
		mRunReset = false;
	}

	// Get the main Python dictionary to use as global and local namespace.
	pmod = PyImport_ImportModule("__main__");
	pdict = PyModule_GetDict(pmod);
	Py_DECREF(pmod);

	try
	{
		// Set default return code value - assume success.
		errorCode = GOOD;

		cstrTmp = (char*)malloc(strlen(apstrCode)+1);
		if (!cstrTmp)
			return(MALLOC_ERROR);
		strcpy(cstrTmp,apstrCode);

		// Run the Python script using the main dictionary as the global and
		// local namespace.  This form of PyRun allows us to catch Python
		// errors (PyRun_SimpleString does not).

		// Tap into Python's debugging mode to keep the app GUI alive.
		PyEval_SetTrace(PythonTrace, NULL);
		// Keep track of the fact that we are now running code from a TE file.
		mRunningCode = true;
		// Run the Python code that was in the file.
		pstr = PyRun_String(cstrTmp, Py_file_input, pdict, pdict);
		// Set this back to false since we only want PythonStop to stop Python
		// when it is running code that came from a file.
		mRunningCode = false;
		// Stop tracing every single line of Python code.
		PyEval_SetTrace(NULL, NULL);

		free(cstrTmp);

		if (PyErr_Occurred())
		{
			ptheApp->DebugAddLine(ptheApp->DEBUG_INFO_MSG,"The following error
occured in the Python script:");
			// Print a Python traceback to stderr.
			// If you call PyErr_Print() when there was NOT an error
			// you will cause a fatal error, so don't do it.
			PyErr_Print();
			errorCode = SCRIPT_EXECUTION_ERROR;
		}
		else
		{
			Py_DECREF(pstr);
		}

		return(errorCode);
	}
	catch (...)
	{
		return(SCRIPT_EXECUTION_ERROR);
	}
}


// Send a keyboard interrupt to stop Python while it is running a script.
int PythonStop()
{
	if (mRunningCode)
	{
		PyErr_SetInterrupt();
	}
	return(GOOD);
}

Hope that helps.  I removed some proprietary bits and pieces, so please
test before using any of this code as is.  :-)

-- 
Patrick K. O'Brien
Orbtech       http://www.orbtech.com
Schevo        http://www.schevo.org
Louie         http://louie.berlios.de



More information about the pygtk mailing list