diff --git a/example_ScriptTester/bin/data/mytest.py b/example_ScriptTester/bin/data/mytest.py index 469b0e3..ca92e74 100644 --- a/example_ScriptTester/bin/data/mytest.py +++ b/example_ScriptTester/bin/data/mytest.py @@ -4,6 +4,9 @@ print "-"*30 +from threading import Thread +from time import sleep + class myApp(object): def __init__(self): self.x = 50.0 @@ -44,4 +47,17 @@ def keyReleased(self, key): pass def windowResized(self, width, height): - pass \ No newline at end of file + pass + + +#Testing threading capabilities + +def athread(): + n = 0 + while True: + print n + n += 1 + sleep(1) + +t = Thread(target=athread) +t.start() diff --git a/example_ScriptTester/src/ofApp.cpp b/example_ScriptTester/src/ofApp.cpp index 691044b..8375e73 100644 --- a/example_ScriptTester/src/ofApp.cpp +++ b/example_ScriptTester/src/ofApp.cpp @@ -3,10 +3,10 @@ //-------------------------------------------------------------- void ofApp::setup(){ python.init(); - python.executeScript("mytest.py"); - ofxPythonObject klass = python.getObject("myApp"); - if(klass) - script = klass(); + python.executeScript("mytest.py"); + ofxPythonObject klass = python.getObject("myApp"); + if(klass) + script = klass(); } //-------------------------------------------------------------- diff --git a/src/ofxPython.cpp b/src/ofxPython.cpp index eb53511..c5821f5 100644 --- a/src/ofxPython.cpp +++ b/src/ofxPython.cpp @@ -13,6 +13,7 @@ ofxPythonObject make_object_owned(PyObject * obj, bool errcheck); void PythonErrorCheck() { + ofxPythonOperation op; PyObject * ptype, * pvalue, * ptraceback; PyErr_Fetch(&ptype, &pvalue, &ptraceback); if(ptype) @@ -40,6 +41,7 @@ void PythonErrorCheck() ofxPythonObject make_object_owned(PyObject * obj, bool errcheck= true) { + ofxPythonOperation op; if (obj==NULL) ofLog()<< "WARNING! make_object_owned: creating ofxPythonObject with NULL"; if(errcheck) @@ -51,6 +53,7 @@ ofxPythonObject make_object_owned(PyObject * obj, bool errcheck= true) ofxPythonObject make_object_borrowed(PyObject * obj, bool errcheck= true) { + ofxPythonOperation op; if (obj==NULL) ofLog()<< "WARNING! make_object_borrowed: creating ofxPythonObject with NULL"; if(errcheck) @@ -85,6 +88,22 @@ ofxPython::~ofxPython() instances--; if(instances == 0) { + { + ofxPythonOperation op; + long main_thread_id = ofxPythonOperation::pstate->thread_id; + for (ofxPythonObject t : ofxPython::getObject("enumerate", "threading")().asVector()){ + long t_id = t.attr("ident").asInt(); + if( t_id != main_thread_id){ + ofxPythonObject exc = locals["__builtins__"]["SystemExit"]; + int r = PyThreadState_SetAsyncExc(t_id,exc->obj); + if (r != 1){ + ofLog() << "Failed attewmpt to shut down thread " << t_id; + } + } + } + } + PyEval_RestoreThread(ofxPythonOperation::pstate); + Py_Finalize(); } } @@ -97,6 +116,7 @@ int ofxPython::init() initialized = true; if(instances == 0) { + PyEval_InitThreads(); Py_Initialize(); init_openframeworks(); init_openframeworks_extra(); @@ -131,7 +151,8 @@ int ofxPython::init() "catcher = StdoutCatcher()\n" "sys.stdout = catcher\n" ); - PythonErrorCheck(); + ofxPythonOperation::pstate = PyEval_SaveThread(); + PythonErrorCheck(); } instances++; } @@ -141,6 +162,7 @@ int ofxPython::init() void ofxPython::reset() { + ofxPythonOperation op; // globals = make_object_owned(PyDict_New()); locals = make_object_owned(PyDict_New()); //deal with cyclic references @@ -151,28 +173,33 @@ void ofxPython::reset() void ofxPython::executeScript(const string& path) { + ofxPythonOperation op; executeString(ofBufferFromFile(path).getText()); PythonErrorCheck(); } void ofxPython::executeString(const string& script) { + ofxPythonOperation op; make_object_owned(PyRun_String(script.c_str(),Py_file_input,locals->obj,locals->obj)); PythonErrorCheck(); } ofxPythonObject ofxPython::executeStatement(const string& script) { + ofxPythonOperation op; return make_object_owned(PyRun_String(script.c_str(),Py_single_input,locals->obj,locals->obj)); } ofxPythonObject ofxPython::evalString(const string& expression) { + ofxPythonOperation op; return make_object_owned(PyRun_String(expression.c_str(),Py_eval_input,locals->obj,locals->obj)); } ofxPythonObject ofxPython::getObject(const string& name, const string& module) { + ofxPythonOperation op; ofxPythonObject pmodule = make_object_owned( PyImport_Import(ofxPythonObject::fromString(module)->obj)); @@ -183,16 +210,19 @@ ofxPythonObject ofxPython::getObject(const string& name, const string& module) ofxPythonObject ofxPython::getObject(const string& name) { + ofxPythonOperation op; return locals[name]; } ofxPythonObject ofxPython::getObjectOrNone(const string& name) { + ofxPythonOperation op; return locals.attr("get")(ofxPythonObject::fromString(name)); } void ofxPython::setObject(const string& name, ofxPythonObject o) { + ofxPythonOperation op; locals[name]=o; } @@ -207,38 +237,46 @@ void ofxPythonObject::insert_owned(PyObject * obj) void ofxPythonObject::insert_borrowed(PyObject * obj) { + ofxPythonOperation op; Py_XINCREF(obj); reset(new ofxPythonObjectManaged(obj)); } ofxPythonObjectManaged::ofxPythonObjectManaged(PyObject * o):obj(o) { + ofxPythonOperation op; } ofxPythonObjectManaged::~ofxPythonObjectManaged() { - if(ofxPython::instances > 0) - Py_XDECREF(obj); + if(ofxPython::instances > 0){ + ofxPythonOperation op; + Py_XDECREF(obj); + } } ofxPythonObject ofxPythonObject::method(const string &method_name) { + ofxPythonOperation op; return make_object_owned( PyObject_CallMethod(get()->obj,noconststring(method_name),NULL)); } ofxPythonObject ofxPythonObject::operator ()() { + ofxPythonOperation op; return make_object_owned(PyObject_CallObject(get()->obj,NULL)); } ofxPythonObject ofxPythonObject::operator ()(ofxPythonObject o1) { + ofxPythonOperation op; if(o1->obj == NULL) o1=ofxPythonObject::_None(); return make_object_owned(PyObject_CallFunctionObjArgs(get()->obj,o1->obj,NULL)); } ofxPythonObject ofxPythonObject::operator ()(ofxPythonObject o1, ofxPythonObject o2) { + ofxPythonOperation op; if(o1->obj == NULL) o1=ofxPythonObject::_None(); if(o2->obj == NULL) o2=ofxPythonObject::_None(); return make_object_owned(PyObject_CallFunctionObjArgs(get()->obj,o1->obj,o2->obj,NULL)); @@ -246,6 +284,7 @@ ofxPythonObject ofxPythonObject::operator ()(ofxPythonObject o1, ofxPythonObject ofxPythonObject ofxPythonObject::operator ()(ofxPythonObject o1, ofxPythonObject o2, ofxPythonObject o3) { + ofxPythonOperation op; if(o1->obj == NULL) o1=ofxPythonObject::_None(); if(o2->obj == NULL) o2=ofxPythonObject::_None(); if(o3->obj == NULL) o3=ofxPythonObject::_None(); @@ -269,6 +308,7 @@ ofxPythonAttrValue ofxPythonObject::attr(const string& attribute) const string ofxPythonObject::repr() { + ofxPythonOperation op; ofxPythonObject objectsRepresentation = make_object_owned( PyObject_Repr(get()->obj)); string s = string(PyString_AsString(objectsRepresentation->obj)); @@ -278,6 +318,7 @@ const string ofxPythonObject::repr() const string ofxPythonObject::str() { + ofxPythonOperation op; ofxPythonObject objectsRepresentation = make_object_owned( PyObject_Str(get()->obj)); string s = string(PyString_AsString(objectsRepresentation->obj)); @@ -287,51 +328,61 @@ const string ofxPythonObject::str() ofxPythonObject::operator bool() const { + ofxPythonOperation op; return get() && get()->obj && !isNone(); //TODO: check if evaluates to false (0,(,),[]) } bool ofxPythonObject::isNone() const { + ofxPythonOperation op; return get() && get()->obj == Py_None; } bool ofxPythonObject::isBool() const { + ofxPythonOperation op; return get() && PyBool_Check(get()->obj); } bool ofxPythonObject::isInt() const { + ofxPythonOperation op; return get() && PyInt_Check(get()->obj); } bool ofxPythonObject::isFloat() const { + ofxPythonOperation op; return get() && PyFloat_Check(get()->obj); } bool ofxPythonObject::isString() const { + ofxPythonOperation op; return get() && PyString_Check(get()->obj); } bool ofxPythonObject::isList() const { + ofxPythonOperation op; return get() && PyList_Check(get()->obj); } bool ofxPythonObject::isTuple() const { + ofxPythonOperation op; return get() && PyTuple_Check(get()->obj); } bool ofxPythonObject::isDict() const { + ofxPythonOperation op; return get() && PyDict_Check(get()->obj); } long int ofxPythonObject::asInt() const { + ofxPythonOperation op; if (isInt()) return PyInt_AsLong(get()->obj); return 0; @@ -339,6 +390,7 @@ long int ofxPythonObject::asInt() const bool ofxPythonObject::asBool() const { + ofxPythonOperation op; if (isBool()) return get()->obj == Py_True; return false; @@ -346,6 +398,7 @@ bool ofxPythonObject::asBool() const double ofxPythonObject::asFloat() const { + ofxPythonOperation op; if (isFloat()) return PyFloat_AsDouble(get()->obj); return 0.0; @@ -353,6 +406,7 @@ double ofxPythonObject::asFloat() const string ofxPythonObject::asString() const { + ofxPythonOperation op; if(isString()) return string(PyString_AsString(get()->obj)); return string(); @@ -360,6 +414,7 @@ string ofxPythonObject::asString() const vector ofxPythonObject::asVector() const { + ofxPythonOperation op; std::vector v; if(isList()) { @@ -382,6 +437,7 @@ vector ofxPythonObject::asVector() const std::map ofxPythonObject::asMap() const { + ofxPythonOperation op; std::map m; if(isDict()) { @@ -398,11 +454,13 @@ std::map ofxPythonObject::asMap() const ofxPythonObject ofxPythonObject::fromInt(long int i) { + ofxPythonOperation op; return make_object_owned(PyInt_FromLong(i)); } ofxPythonObject ofxPythonObject::fromBool(bool b) { + ofxPythonOperation op; if(b) return make_object_borrowed(Py_True); return make_object_borrowed(Py_False); @@ -410,16 +468,19 @@ ofxPythonObject ofxPythonObject::fromBool(bool b) ofxPythonObject ofxPythonObject::_None() { + ofxPythonOperation op; return make_object_borrowed(Py_None); } ofxPythonObject ofxPythonObject::fromFloat(double d) { + ofxPythonOperation op; return make_object_owned(PyFloat_FromDouble(d)); } ofxPythonObject ofxPythonObject::fromString(const string& s) { + ofxPythonOperation op; return make_object_owned(PyString_FromString(s.c_str())); } @@ -434,12 +495,14 @@ ofxPythonMappingValue::ofxPythonMappingValue(ofxPythonObject o, const string& k) ofxPythonMappingValue::operator ofxPythonObject() { + ofxPythonOperation op; return make_object_owned( PyMapping_GetItemString(object->obj, noconststring(key))); } ofxPythonMappingValue& ofxPythonMappingValue::operator =(ofxPythonObject o) { + ofxPythonOperation op; PyMapping_SetItemString(object->obj, noconststring(key) , o->obj); PythonErrorCheck(); return *this; @@ -450,12 +513,14 @@ ofxPythonAttrValue::ofxPythonAttrValue(ofxPythonObject o, const string& attr) ofxPythonAttrValue::operator ofxPythonObject() { + ofxPythonOperation op; return make_object_owned( PyObject_GetAttrString(object->obj,attribute.c_str())); } ofxPythonAttrValue & ofxPythonAttrValue::operator =(ofxPythonObject o) { + ofxPythonOperation op; PyObject_SetAttrString(object->obj, attribute.c_str(), o->obj); PythonErrorCheck(); return *this; @@ -473,129 +538,154 @@ ofxPythonAttrValue & ofxPythonAttrValue::operator =(ofxPythonAttrValue & o) ofxPythonObject ofxPythonObjectLike::method(const string &method_name) { + ofxPythonOperation op; ofxPythonObject PO = *this; return PO.method(method_name); } ofxPythonObject ofxPythonObjectLike::operator ()() { + ofxPythonOperation op; ofxPythonObject PO = *this; return PO(); } ofxPythonObject ofxPythonObjectLike::operator ()(ofxPythonObject o) { + ofxPythonOperation op; ofxPythonObject PO = *this; return PO(o); } ofxPythonObject ofxPythonObjectLike::operator ()(ofxPythonObject o1, ofxPythonObject o2) { + ofxPythonOperation op; ofxPythonObject PO = *this; return PO(o1,o2); } ofxPythonObject ofxPythonObjectLike::operator ()(ofxPythonObject o1, ofxPythonObject o2, ofxPythonObject o3) { + ofxPythonOperation op; ofxPythonObject PO = *this; return PO(o1,o2,o3); } ofxPythonAttrValue ofxPythonObjectLike::attr(const string& attribute) { + ofxPythonOperation op; ofxPythonObject PO = *this; return PO.attr(attribute); } ofxPythonMappingValue ofxPythonObjectLike::operator [](const string& key) { + ofxPythonOperation op; ofxPythonObject PO = *this; return PO[key]; } ofxPythonMappingValue ofxPythonObjectLike::operator [](const char * key) { + ofxPythonOperation op; ofxPythonObject PO = *this; return PO[key]; } bool ofxPythonObjectLike::isNone() { + ofxPythonOperation op; ofxPythonObject PO = *this; return PO.isNone(); } bool ofxPythonObjectLike::isBool() { + ofxPythonOperation op; ofxPythonObject PO = *this; return PO.isBool(); } bool ofxPythonObjectLike::isInt() { + ofxPythonOperation op; ofxPythonObject PO = *this; return PO.isInt(); } bool ofxPythonObjectLike::isFloat() { + ofxPythonOperation op; ofxPythonObject PO = *this; return PO.isFloat(); } bool ofxPythonObjectLike::isString() { + ofxPythonOperation op; ofxPythonObject PO = *this; return PO.isString(); } bool ofxPythonObjectLike::isList() { + ofxPythonOperation op; ofxPythonObject PO = *this; return PO.isList(); } bool ofxPythonObjectLike::isDict() { + ofxPythonOperation op; ofxPythonObject PO = *this; return PO.isDict(); } bool ofxPythonObjectLike::isTuple() { + ofxPythonOperation op; ofxPythonObject PO = *this; return PO.isTuple(); } bool ofxPythonObjectLike::asBool() { + ofxPythonOperation op; ofxPythonObject PO = *this; return PO.asBool(); } long int ofxPythonObjectLike::asInt() { + ofxPythonOperation op; ofxPythonObject PO = *this; return PO.asInt(); } double ofxPythonObjectLike::asFloat() { + ofxPythonOperation op; ofxPythonObject PO = *this; return PO.asFloat(); } string ofxPythonObjectLike::asString() { + ofxPythonOperation op; ofxPythonObject PO = *this; return PO.asString(); } vector ofxPythonObjectLike::asVector() { + ofxPythonOperation op; ofxPythonObject PO = *this; return PO.asVector(); } std::map ofxPythonObjectLike::asMap() { + ofxPythonOperation op; ofxPythonObject PO = *this; return PO.asMap(); } ofxPythonObjectLike::operator bool() { + ofxPythonOperation op; ofxPythonObject PO = *this; return (bool)PO; } const string ofxPythonObjectLike::repr() { + ofxPythonOperation op; ofxPythonObject PO = *this; return PO.repr(); } const string ofxPythonObjectLike::str() { + ofxPythonOperation op; ofxPythonObject PO = *this; return PO.str(); } @@ -608,6 +698,7 @@ ofxPythonTupleMaker& ofxPythonTupleMaker::operator<<(ofxPythonObject o) ofxPythonTupleMaker::operator ofxPythonObject() { + ofxPythonOperation op; ofxPythonObject tuple = make_object_owned( PyTuple_New(contents.size())); for (unsigned int i = 0; i < contents.size(); ++i) @@ -617,4 +708,21 @@ ofxPythonTupleMaker::operator ofxPythonObject() } PythonErrorCheck(); return tuple; +} + +PyThreadState * ofxPythonOperation::pstate = NULL; +unsigned int ofxPythonOperation::instances = 0; + +ofxPythonOperation::ofxPythonOperation(){ + if(instances == 0){ + PyEval_RestoreThread(pstate); + } + instances++; +} + +ofxPythonOperation::~ofxPythonOperation(){ + instances--; + if(instances ==0){ + pstate = PyEval_SaveThread(); + } } \ No newline at end of file diff --git a/src/ofxPython.h b/src/ofxPython.h index 855689a..6d8e5b7 100644 --- a/src/ofxPython.h +++ b/src/ofxPython.h @@ -81,7 +81,14 @@ class ofxPython friend class ofxPythonObjectManaged; }; - +class ofxPythonOperation +{ +public: + static PyThreadState * pstate; + static unsigned int instances; + ofxPythonOperation(); + ~ofxPythonOperation(); +}; class ofxPythonObjectManaged {