Skip to content

Commit 1f74b31

Browse files
committed
Add initial support for coroutines
- when a signal calls a Python function and it is a coroutine, it will be scheduled (patch by Florian Link)
1 parent 1c2efce commit 1f74b31

File tree

4 files changed

+60
-1
lines changed

4 files changed

+60
-1
lines changed

.github/workflows/dockerfiles/Dockerfile_centos7

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ WORKDIR work
2525
ARG QT_SELECT=qt5
2626
RUN uname -a; gcc --version | grep "gcc"; python --version; qmake-qt5 --version
2727

28-
RUN qmake-qt5 -r PythonQt.pro \
28+
RUN qmake-qt5 DEFINES+=PYTHON2 -r PythonQt.pro \
2929
PYTHON_VERSION=$(python --version | cut -d " " -f 2 | cut -d "." -f1,2) \
3030
PYTHON_DIR=$(which python | xargs dirname | xargs dirname)
3131

src/PythonQt.cpp

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,16 @@ void PythonQt::init(int flags, const QByteArray& pythonQtModuleName)
8888
_self->_p->_pySourcelessFileLoader = importlib.getVariable("SourcelessFileLoader");
8989
}
9090

91+
#ifndef PYTHON2
92+
PythonQtObjectPtr asyncio;
93+
asyncio.setNewRef(PyImport_ImportModule("asyncio"));
94+
if (asyncio)
95+
{
96+
_self->_p->_pyEnsureFuture = asyncio.getVariable("ensure_future");
97+
_self->_p->_pyFutureClass = asyncio.getVariable("Future");
98+
}
99+
#endif
100+
91101
PythonQt::priv()->setupSharedLibrarySuffixes();
92102

93103
PythonQtMethodInfo::addParameterTypeAlias("QObjectList", "QList<QObject*>");
@@ -402,6 +412,46 @@ void PythonQtPrivate::setTaskDoneCallback(const PythonQtObjectPtr & callable)
402412
_pyTaskDoneCallback = callable;
403413
}
404414

415+
PythonQtObjectPtr PythonQtPrivate::checkAndRunCoroutine(const PythonQtObjectPtr& object)
416+
{
417+
PythonQtObjectPtr result;
418+
#ifndef PYTHON2
419+
if (!PyCoro_CheckExact(object))
420+
{
421+
return result;
422+
}
423+
424+
if (!_pyEnsureFuture)
425+
{
426+
std::cerr << "PythonQt: ensure_future not initialized" << std::endl;
427+
return PythonQtObjectPtr();
428+
}
429+
PyObject* args = PyTuple_New(1);
430+
PyObject* coro = object.object();
431+
Py_INCREF(coro);
432+
PyTuple_SetItem(args, 0, coro);
433+
PyObject* r = PyObject_CallObject(_pyEnsureFuture, args);
434+
result.setNewRef(r);
435+
if (_pyTaskDoneCallback) {
436+
PyObject* methodName = PyUnicode_FromString("add_done_callback");
437+
PyObject_CallMethodObjArgs(r, methodName, _pyTaskDoneCallback.object(), nullptr);
438+
Py_XDECREF(methodName);
439+
}
440+
Py_XDECREF(args);
441+
#endif
442+
return result;
443+
}
444+
445+
PythonQtObjectPtr PythonQtPrivate::createAsyncioFuture()
446+
{
447+
if (!_pyFutureClass)
448+
{
449+
std::cerr << "PythonQt: _pyFutureClass not initialized" << std::endl;
450+
return nullptr;
451+
}
452+
return _pyFutureClass.call();
453+
}
454+
405455
void PythonQt::setRedirectStdInCallback(PythonQtInputChangedCB* callback, void * callbackData)
406456
{
407457
if (!callback) {

src/PythonQt.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -674,6 +674,12 @@ class PYTHONQT_EXPORT PythonQtPrivate : public QObject {
674674
//! created by checkAndRunCoroutine
675675
void setTaskDoneCallback(const PythonQtObjectPtr& callable);
676676

677+
//! Runs the given coroutine (via asyncio), returns a scheduled task if it object is a coroutine.
678+
PythonQtObjectPtr checkAndRunCoroutine(const PythonQtObjectPtr& object);
679+
680+
//! Creates a new asyncio.Future object
681+
PythonQtObjectPtr createAsyncioFuture();
682+
677683
//! get the suffixes that are used for shared libraries
678684
const QStringList& sharedLibrarySuffixes() { return _sharedLibrarySuffixes; }
679685

@@ -853,6 +859,8 @@ class PYTHONQT_EXPORT PythonQtPrivate : public QObject {
853859

854860
PythonQtObjectPtr _pySourceFileLoader;
855861
PythonQtObjectPtr _pySourcelessFileLoader;
862+
PythonQtObjectPtr _pyEnsureFuture;
863+
PythonQtObjectPtr _pyFutureClass;
856864

857865
PythonQtObjectPtr _pyTaskDoneCallback;
858866

src/PythonQtSignalReceiver.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ void PythonQtSignalTarget::call(void **arguments) const {
5555
PYTHONQT_GIL_SCOPE
5656
PyObject* result = call(_callable, methodInfo(), arguments);
5757
if (result) {
58+
PythonQt::priv()->checkAndRunCoroutine(result);
5859
Py_DECREF(result);
5960
}
6061
}

0 commit comments

Comments
 (0)