bool QPython::importModule_sync(QString name) { // Lesson learned: name.toUtf8().constData() doesn't work, as the // temporary QByteArray will be destroyed after constData() has // returned, so we need to save the toUtf8() result in a local // variable that doesn't get destroyed until the function returns. QByteArray utf8bytes = name.toUtf8(); const char *moduleName = utf8bytes.constData(); ENSURE_GIL_STATE; bool use_api_10 = (api_version_major == 1 && api_version_minor == 0); PyObjectRef module; if (use_api_10) { // PyOtherSide API 1.0 behavior (star import) module = PyObjectRef(PyImport_ImportModule(moduleName), true); } else { // PyOtherSide API 1.2 behavior: "import x.y.z" PyObjectRef fromList(PyList_New(0), true); module = PyObjectRef(PyImport_ImportModuleEx(const_cast<char *>(moduleName), NULL, NULL, fromList.borrow()), true); } if (!module) { emitError(QString("Cannot import module: %1 (%2)").arg(name).arg(priv->formatExc())); return false; } if (!use_api_10) { // PyOtherSide API 1.2 behavior: "import x.y.z" // If "x.y.z" is imported, we need to set "x" in globals if (name.indexOf('.') != -1) { name = name.mid(0, name.indexOf('.')); utf8bytes = name.toUtf8(); moduleName = utf8bytes.constData(); } } PyDict_SetItemString(priv->globals.borrow(), moduleName, module.borrow()); return true; }
QVariant QPython::call_sync(QVariant func, QVariant args) { ENSURE_GIL_STATE; PyObjectRef callable; QString name; if (SINCE_API_VERSION(1, 4)) { if (static_cast<QMetaType::Type>(func.type()) == QMetaType::QString) { // Using version >= 1.4, but func is a string callable = PyObjectRef(priv->eval(func.toString()), true); name = func.toString(); } else { // Try to interpret "func" as a Python object callable = PyObjectRef(convertQVariantToPyObject(func), true); PyObjectRef repr = PyObjectRef(PyObject_Repr(callable.borrow()), true); name = convertPyObjectToQVariant(repr.borrow()).toString(); } } else { // Versions before 1.4 only support func as a string callable = PyObjectRef(priv->eval(func.toString()), true); name = func.toString(); } if (!callable) { emitError(QString("Function not found: '%1' (%2)").arg(name).arg(priv->formatExc())); return QVariant(); } QVariant v; QString errorMessage = priv->call(callable.borrow(), name, args, &v); if (!errorMessage.isNull()) { emitError(errorMessage); } return v; }
PythonProxyHandle::PythonProxyHandle(std::shared_ptr<PythonProxyEnvironment> env, PyObject *obj, const bool borrowed): env(env), obj(obj) { PyGilStateLock lock; ref = PyObjectRef(obj, borrowed); }
Pothos::Proxy PythonProxyHandle::call(const std::string &name, const Pothos::Proxy *args, const size_t numArgs) { PyGilStateLock lock; if (this->obj == nullptr) throw Pothos::ProxyHandleCallError( "PythonProxyHandle::call("+name+")", "cant call on a null object"); /******************************************************************* * Step 0) handle field accessors and mutators ******************************************************************/ const auto colon = name.find(":"); if (colon != std::string::npos) { PyObjectRef result(Py_None, REF_BORROWED); if (name.substr(0, colon) == "set" and numArgs == 1) { PyObject_SetAttrString(this->obj, name.substr(colon+1).c_str(), env->getHandle(args[0])->obj); } else if (name.substr(0, colon) == "get" and numArgs == 0) { result = PyObjectRef(PyObject_GetAttrString(this->obj, name.substr(colon+1).c_str()), REF_NEW); } else throw Pothos::ProxyHandleCallError( "PythonProxyHandle::call("+name+")", "unknown operation"); auto errorMsg = getErrorString(); if (not errorMsg.empty()) { throw Pothos::ProxyExceptionMessage(errorMsg); } return env->makeHandle(result); } /******************************************************************* * Step 1) locate the callable object ******************************************************************/ PyObjectRef attrObj; if (name.empty() or name == "()") attrObj = PyObjectRef(ref); else attrObj = PyObjectRef(PyObject_GetAttrString(this->obj, name.c_str()), REF_NEW); if (attrObj.obj == nullptr) { throw Pothos::ProxyHandleCallError( "PythonProxyHandle::call("+name+")", Poco::format("no attribute on %s", this->toString())); } if (not PyCallable_Check(attrObj.obj)) { if (numArgs == 0) return env->makeHandle(attrObj); //access a field else throw Pothos::ProxyHandleCallError( "PythonProxyHandle::call("+name+")", Poco::format("cant call on %s", this->toString())); } /******************************************************************* * Step 2) create tuple of arguments ******************************************************************/ PyObjectRef argsObj(PyTuple_New(numArgs), REF_NEW); std::vector<std::shared_ptr<PythonProxyHandle>> argHandles(numArgs); for (size_t i = 0; i < numArgs; i++) { argHandles[i] = env->getHandle(args[i]); PyTuple_SetItem(argsObj.obj, i, argHandles[i]->ref.newRef()); } /******************************************************************* * Step 4) call into the callable object ******************************************************************/ PyObjectRef result(PyObject_CallObject(attrObj.obj, argsObj.obj), REF_NEW); /******************************************************************* * Step 5) exception handling and reporting ******************************************************************/ auto errorMsg = getErrorString(); if (not errorMsg.empty()) { throw Pothos::ProxyExceptionMessage(errorMsg); } auto x = env->makeHandle(result); if (x.getClassName() == "PothosProxy") return x.convert<Pothos::Proxy>(); return x; }
PythonProxyHandle::~PythonProxyHandle(void) { PyGilStateLock lock; ref = PyObjectRef(); }
void test_converter_for(Converter<V> *conv) { V v, w, x; /* Convert from/to Integer */ v = conv->fromInteger(123); QVERIFY(conv->type(v) == Converter<V>::INTEGER); QVERIFY(conv->integer(v) == 123); /* Convert from/to Float */ v = conv->fromFloating(42.23); QVERIFY(conv->type(v) == Converter<V>::FLOATING); QVERIFY(conv->floating(v) == 42.23); /* Convert from/to Bool */ v = conv->fromBoolean(true); QVERIFY(conv->type(v) == Converter<V>::BOOLEAN); QVERIFY(conv->boolean(v)); v = conv->fromBoolean(false); QVERIFY(conv->type(v) == Converter<V>::BOOLEAN); QVERIFY(!conv->boolean(v)); /* Convert from/to String */ v = conv->fromString("Hello World"); QVERIFY(conv->type(v) == Converter<V>::STRING); QVERIFY(strcmp(conv->string(v), "Hello World") == 0); /* Convert from/to List */ ListBuilder<V> *builder = conv->newList(); v = conv->fromInteger(444); builder->append(v); v = conv->fromString("Hello"); builder->append(v); v = builder->value(); delete builder; ListIterator<V> *iterator = conv->list(v); QVERIFY(iterator->next(&w)); QVERIFY(conv->type(w) == Converter<V>::INTEGER); QVERIFY(conv->integer(w) == 444); QVERIFY(iterator->next(&w)); QVERIFY(conv->type(w) == Converter<V>::STRING); QVERIFY(strcmp(conv->string(w), "Hello") == 0); delete iterator; /* Convert from/to Dict */ DictBuilder<V> *builder2 = conv->newDict(); v = conv->fromBoolean(true); builder2->set(conv->fromString("a"), v); v = builder2->value(); delete builder2; DictIterator<V> *iterator2 = conv->dict(v); QVERIFY(iterator2->next(&w, &x)); QVERIFY(conv->type(w) == Converter<V>::STRING); QVERIFY(strcmp(conv->string(w), "a") == 0); QVERIFY(conv->type(x) == Converter<V>::BOOLEAN); QVERIFY(conv->boolean(x) == true); delete iterator2; /* Convert from/to generic PyObject */ PyObject *obj = PyCapsule_New(conv, "test", NULL); v = conv->fromPyObject(PyObjectRef(obj)); QVERIFY(conv->type(v) == Converter<V>::PYOBJECT); // Check if getting a new reference works PyObject *o = conv->pyObject(v).newRef(); QVERIFY(o == obj); Py_DECREF(o); Py_CLEAR(obj); delete conv; }
void TestPyOtherSide::testPyObjectRefAssignment() { // Test assignment operator of PyObjectRef bool destructor_called_foo = false; PyObject *foo = PyCapsule_New(&destructor_called_foo, "test", destruct); bool destructor_called_bar = false; PyObject *bar = PyCapsule_New(&destructor_called_bar, "test", destruct); QVERIFY(foo); QVERIFY(foo->ob_refcnt == 1); QVERIFY(bar); QVERIFY(bar->ob_refcnt == 1); { PyObjectRef a(foo); PyObjectRef b(bar); PyObjectRef c; // empty // foo got a new reference in a QVERIFY(foo->ob_refcnt == 2); // bar got a new reference in b QVERIFY(bar->ob_refcnt == 2); // Overwrite empty reference with reference to bar c = b; // bar got a new reference in c QVERIFY(bar->ob_refcnt == 3); // reference count for foo is unchanged QVERIFY(foo->ob_refcnt == 2); // Overwrite reference to bar with reference to foo b = a; // bar lost a reference in b QVERIFY(bar->ob_refcnt == 2); // foo got a new reference in b QVERIFY(foo->ob_refcnt == 3); // Overwrite reference to foo with empty reference a = PyObjectRef(); // foo lost a reference in a QVERIFY(foo->ob_refcnt == 2); Py_DECREF(foo); // there is still a reference to foo in b QVERIFY(foo->ob_refcnt == 1); QVERIFY(!destructor_called_foo); // a falls out of scope (but is empty) // b falls out of scope, foo loses a reference // c falls out of scope, bar loses a reference } // Now that b fell out of scope, foo was destroyed QVERIFY(destructor_called_foo); // But we still have a single reference to bar QVERIFY(!destructor_called_bar); QVERIFY(bar->ob_refcnt == 1); Py_CLEAR(bar); // Now bar is also gone QVERIFY(destructor_called_bar); }