Beispiel #1
0
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;
}
Beispiel #2
0
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();
}
Beispiel #6
0
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;
}
Beispiel #7
0
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);
}