Archived

This topic is now archived and is closed to further replies.

Toni Petrina

(Yet) another python problem

Recommended Posts

Toni Petrina    123
Why doesn't this work? Can anybody help?
#include <python.h>

int main() {
	Py_Initialize();

	FILE *p = fopen("test.py", "r");
	PyRun_SimpleFile(p, "test.py");
	fclose(p);

	if (Py_IsInitialized())
		Py_Finalize();
	return 0;
}
If you are using Boost.Python, tell me how are you actually calling python files within your application. It just crashes. [edited by - ffx on August 8, 2003 3:13:19 PM]

Share this post


Link to post
Share on other sites
thedustbustr    191
I dont see your problem, but I can share with you how I call Python.

In my engine, I use only the Very High Level Embedding to call python. I wrap PyRun_SimpleString myself to provide error checking, but that is unnecessary here.


PyRun_SimpleString("import sys");
PyRun_SimpleString("sys.path.append("./scripts");

PyRun_SimpleString("import scheduler");
PyRun_SimpleString("secheduler.load(''ascript'')");
while (true)
PyRun_SimpleString("scheduler.update()");


In other words, I have a class written in Python that calls out to other scripts.

Then, in the scripts, I do

import engine
def main()
...
yield None #all my scripts have a generator function called main

where engine is a static python extension module (this is where I''m having trouble myself, but I know it can be done).

No boost.python necessary until the static extension part.

HTH,
Dustin

Share this post


Link to post
Share on other sites
M_R_Ducs    122
I beleive that's a bug in Python. You have to use the multi-threaded dll runtime.

edit:
Alternatively you could load the whole script into a string and use PyRun_SimpleString.

[edited by - M_R_Ducs on August 8, 2003 4:14:38 PM]

Share this post


Link to post
Share on other sites
c t o a n    163
Whoa, you guys really do use the high level stuff huh? If you want to have more control over your scripts, like I do, then use more lower level functions like so (this is my actual in-engine script class, that pretty much wraps all this good stuff up for me). First the header:

#ifndef _SCRIPT_H_
#define _SCRIPT_H_

#include "unknown.h"

#include "singleton.h"

#ifndef _GHOST_MODULE_PATH_
#define _GHOST_MODULE_PATH_ "C:/Developer/Scripts/"
#endif

typedef class boost::python::object BstObject;
typedef class boost::python::handle<> BstHandle;

enum KPyObjectType
{
PYTYPE_INVALID = 0,
PYTYPE_FUNCTION,
PYTYPE_VARIABLE
};


#define CallScript PyObject_CallFunction


class GstScript : public GstUnknown
{
private:

// the module object


BstObject module, dict;


// the module name (without path or extension)


char name[ 32 ];


// declare our manager as a friend


friend class GstScriptMgr;


// the bind function


void Bind( BstObject& module, char* name );

public:

// default constructor


GstScript() {}


// gets an object (could be function or variable)


PyObject* Get( char* object );


// gets the script''s name


const char* getName() { return name; }


// checks what sort of object is under this name


KPyObjectType Type( char* object );


// operator overloads


PyObject* operator[]( char* variable );
};


class GstScriptMgr : public GstSingleton< GstScriptMgr >
{
private:

// loads the Python interpreter


bool LoadPython( char* modPath );

// frees the Python interpreter


bool FreePython();

public:

// default constructor


GstScriptMgr( char* modPath = _GHOST_MODULE_PATH_ );

// destructor


~GstScriptMgr();


// retrieves a script


bool LoadModule( char* modName, GstScript& script );
};


// pre-definition of the ''script'' Python module


extern "C"
{
extern void __declspec(dllexport) initscript();
}

#endif


And now the implementation:


#ifdef _DEBUG
#undef _DEBUG
#define _ISDEBUG_
#include <Python.h>
#include <Boost/Python.h>
#else
#include <Python.h>
#include <Boost/Python.h>
#endif
#ifdef _ISDEBUG_
#pragma warning( disable : 4005 )
#define _DEBUG
#endif

#include <iostream.h>
#include <stdarg.h>

#include "types.h"

#include "hash.h"

#include "script.h"

#pragma comment( lib, "boost_python.lib" )

using namespace boost::python;

BOOST_PYTHON_MODULE(script)
{
class_<GstScript>( "KScript" )
.def( "Get", &GstScript::Get )
.def( "Type", &GstScript::Type )
;

class_<GstScriptMgr>( "KScriptMgr", no_init )
.def( "LoadModule", &GstScriptMgr::LoadModule )
;

def( "get", &GstScriptMgr::get, return_value_policy< reference_existing_object >() );
}


// the bind function


void GstScript :: Bind( BstObject& mod, char* str )
{
// copy the passed objects into this variable


module = mod;

strcpy( name, str );


// get the dictionary (borrowed reference!)


dict = BstObject( BstHandle( boost::python::borrowed( PyModule_GetDict( module.ptr() ) ) ) );
}

// calls a function


PyObject* GstScript :: Get( char* obj )
{
// get the requested function (borrowed!)


PyObject* tmp = PyDict_GetItemString( dict.ptr(), obj );


// check if it''s valid


if( !tmp )
{
PyErr_Print();

// Log Error Here

cout << "Couldn''t Find The Object: " << obj << "\n";
cout.flush();
}


// it''s valid


return tmp;
}


// checks what sort of object is under this name


KPyObjectType GstScript :: Type( char* object )
{
PyObject* ptr = Get( object );

if( !dict || !module || !ptr )
return PYTYPE_INVALID;

if( PyCallable_Check( ptr ) )
return PYTYPE_FUNCTION;

return PYTYPE_VARIABLE;
}


// operator overloads


////////////////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////////////////


// loads the Python interpreter


bool GstScriptMgr :: LoadPython( char* modPath )
{
// initialize the interpreter


Py_Initialize();


// copy the script location into Python


char buffer[ 128 ];

sprintf( buffer, "sys.path.append(''%s'')", modPath );

PyRun_SimpleString( "import sys" );
PyRun_SimpleString( buffer );

sprintf( buffer, "sys.path.append(''%s'' + ''Events/'')", modPath );

PyRun_SimpleString( buffer );


// return success


return true;
}

// frees the Python interpreter


bool GstScriptMgr :: FreePython()
{
// free the interpreter


Py_Finalize();


return true;
}

// default constructor


GstScriptMgr :: GstScriptMgr( char* modPath )
{
bool res;


// load the python interpreter


res = LoadPython( modPath );

assert( res && "Unable To Start The Python Interpreter" );
}

// destructor


GstScriptMgr :: ~GstScriptMgr()
{
bool res;


// free the Python interpreter


res = FreePython();

assert( res && "Unable To Free Python Interpreter" );
}

// retrieves a script


bool GstScriptMgr :: LoadModule( char* modName, GstScript& script )
{
PyObject* ptr = PyImport_Import( PyString_FromString( modName ) );

if( !ptr )
{
PyErr_Print();

// print an error message

cout << "Couldn''t GstScriptMgr::LoadModule() The Requested Module ''" << modName << "''\n";
cout.flush();

return false;
}

BstObject& mod = BstObject( BstHandle( ptr ) );

script.Bind( mod, modName );

return true;
}


And I''m sorry if it''s not really legible, the gamedev.net parser likes to take out empty lines... But as you can see, I ''import'' the module, and then grab it''s dictionary. This way, you know all the functions in that particular module. I originally had it such that I scanned through a directory and added all functions to a list, but then I realized you can have functions named the same with same arguments, but just different script files, so I trashed the idea... But here''s an example of the script object working. This is where I grab an API from each of the scripts so that I can call them without regard to which file or whatnot.


// load the API


scriptAPI.InitActive = (void*) Script.Get( "InitActive" );
scriptAPI.InitPriority = (void*) Script.Get( "InitPriority" );
scriptAPI.Create = (void*) Script.Get( "Create" );
scriptAPI.Start = (void*) Script.Get( "Start" );
scriptAPI.Update = (void*) Script.Get( "Update" );
scriptAPI.Stop = (void*) Script.Get( "Stop" );
scriptAPI.Destroy = (void*) Script.Get( "Destroy" );


// get the default values from the script


active = extract<u16>( BstObject( BstHandle( CallScript( (PyObject*) scriptAPI.InitActive, "" ) ) ) );
priority = extract<u16>( BstObject( BstHandle( CallScript( (PyObject*) scriptAPI.InitPriority, "
" ) ) ) );


Oh yeah, and I couldn''t tell you why you''re codes not working. I just checked the API docs and it appears to be correct. Why not test your file pointer to make sure it''s valid? Technically, I''m assuming the test.py file is in the same directory as the workspace?

Chris Pergrossi
My Realm | "Good Morning, Dave"

Share this post


Link to post
Share on other sites
Toni Petrina    123
Thanks c t o a n, I''ll try that. Whole Python API documentation is pretty good but it gives you no real examples of embeding. Also, there are no real examples of using Boost.Python, just detailed docs.

thedustbustr: I see where you are going, but when you execute one PyRun_SimpleString, the following one doesn''t know anything about it. Maybe I should concatenate more lines into one.

Share this post


Link to post
Share on other sites
thedustbustr    191
"thedustbustr: I see where you are going, but when you execute one PyRun_SimpleString, the following one doesn't know anything about it. Maybe I should concatenate more lines into one. "

wrong. As far as I can tell, PyRun_SimpleString is like running a command in the python interpreter. Not only that, but take this for example:

PyRun_SimpleString("import katana"); //thats my static module
PyRun_SimpleString("katana.foo=42"); //create a new variable in katana module namespace

and now every python script that does 'import katana' can access 'katana.foo'. It sounds like it wouldnt work, but try it, its great. I believe this works because katana is a static built-in (like 'sys'), I expect it would not work (and if it does, thats a security problem) for a normal pure python module (but I've never tried).

Now that I got my static python extension module working (thanks ctoan!) everything is beautiful. I use none of the python api functions except for PyRun_SimpleString, PyErr_Occurred, and PyErr_Print (and a whole bunch of boost.python wrapper stuff). Then I have a script handler class written in Python that gives each script execution time using generators (sort of like threads but, imo, much much better (simpler, more control, none of that typical threadsafe hassle)). And now that I fixed my static extension module I'm flying my camera around a 3d terrain mesh using bezier curves and circular orbits and its awesome.

The boost.python documentation is not for the faint of heart (they were way over my head), but at the end of each section there is usually an example (with c++ and python code), and the tutorial is great. For python embedding docs, python ships with a great tutorial (its seperate from the api reference).

cameras following Bezier curves are the shit, let me tell you

Enough ranting.
Dustin

[edited by - thedustbustr on August 10, 2003 7:33:57 PM]

Share this post


Link to post
Share on other sites