aboutsummaryrefslogtreecommitdiffstats
path: root/examples/scriptableapplication/pythonutils.cpp
blob: 8104bb167534ba010ab0568f40ab3bc8399ceaa1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause

#include "pythonutils.h"

#include <QtCore/QByteArray>
#include <QtCore/QCoreApplication>
#include <QtCore/QDebug>
#include <QtCore/QOperatingSystemVersion>
#include <QtCore/QStringList>
#include <QtCore/QTemporaryFile>
#include <QtCore/QDir>

#include <sbkpython.h>
#include <sbkconverter.h>
#include <sbkmodule.h>

/* from AppLib bindings */

extern "C" PyObject *PyInit_AppLib();
static const char moduleName[] = "AppLib";

// This variable stores all Python types exported by this module.
extern PyTypeObject **SbkAppLibTypes;

// This variable stores all type converters exported by this module.
extern SbkConverter **SbkAppLibTypeConverters;

namespace PythonUtils {

static State state = PythonUninitialized;

static void cleanup()
{
    if (state > PythonUninitialized) {
        Py_Finalize();
        state = PythonUninitialized;
    }
}

static const char virtualEnvVar[] = "VIRTUAL_ENV";

// If there is an active python virtual environment, use that environment's
// packages location.
static void initVirtualEnvironment()
{
    // As of Python 3.8, Python is no longer able to run stand-alone in a
    // virtualenv due to missing libraries. Add the path to the modules instead.
    if (QOperatingSystemVersion::currentType() == QOperatingSystemVersion::Windows
        && (PY_MAJOR_VERSION > 3 || (PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION >= 8))) {
        const QByteArray virtualEnvPath = qgetenv(virtualEnvVar);
        qputenv("PYTHONPATH", virtualEnvPath + "\\Lib\\site-packages");
    }
}

State init()
{
    if (state > PythonUninitialized)
        return state;

    if (qEnvironmentVariableIsSet(virtualEnvVar))
        initVirtualEnvironment();

    if (PyImport_AppendInittab(moduleName, PyInit_AppLib) == -1) {
        qWarning("Failed to add the module '%s' to the table of built-in modules.", moduleName);
        return state;
    }

    Py_Initialize();
    qAddPostRoutine(cleanup);
    state = PythonInitialized;
    const bool pythonInitialized = PyInit_AppLib() != nullptr;
    const bool pyErrorOccurred = PyErr_Occurred() != nullptr;
    if (pythonInitialized && !pyErrorOccurred) {
        state = AppModuleLoaded;
    } else {
        if (pyErrorOccurred)
            PyErr_Print();
        qWarning("Failed to initialize the module.");
    }
    return state;
}

bool bindAppObject(const QString &moduleName, const QString &name,
                      int index, QObject *o)
{
    if (init() != AppModuleLoaded)
        return false;
    PyTypeObject *typeObject = SbkAppLibTypes[index];

    PyObject *po = Shiboken::Conversions::pointerToPython(typeObject, o);
    if (!po) {
        qWarning() << __FUNCTION__ << "Failed to create wrapper for" << o;
        return false;
    }
    Py_INCREF(po);

    PyObject *module = PyImport_AddModule(moduleName.toLocal8Bit().constData());
    if (!module) {
        Py_DECREF(po);
        if (PyErr_Occurred())
            PyErr_Print();
        qWarning() << __FUNCTION__ << "Failed to locate module" << moduleName;
        return false;
    }

    if (PyModule_AddObject(module, name.toLocal8Bit().constData(), po) < 0) {
        if (PyErr_Occurred())
            PyErr_Print();
        qWarning() << __FUNCTION__ << "Failed add object" << name << "to" << moduleName;
        return false;
    }

    return true;
}

bool runScript(const QString &script)
{
    if (init() == PythonUninitialized)
        return false;

    // Executing the whole script as one line
    bool result = true;
    const QByteArray line = script.toUtf8();
    if (PyRun_SimpleString(line.constData()) == -1) {
        if (PyErr_Occurred())
            PyErr_Print();
        result = false;
    }

    return result;
}

} // namespace PythonUtils