Skip to content

Commit 72c3f74

Browse files
mrbean-bremenjcfr
authored andcommitted
[Backport] 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) (cherry picked from commit MeVisLab/pythonqt@f6e8ea7)
1 parent c622531 commit 72c3f74

File tree

3 files changed

+59
-0
lines changed

3 files changed

+59
-0
lines changed

src/PythonQt.cpp

+50
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+
#ifdef PY3K
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*>");
@@ -406,6 +416,46 @@ void PythonQtPrivate::setTaskDoneCallback(const PythonQtObjectPtr & callable)
406416
_pyTaskDoneCallback = callable;
407417
}
408418

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

src/PythonQt.h

+8
Original file line numberDiff line numberDiff line change
@@ -686,6 +686,12 @@ class PYTHONQT_EXPORT PythonQtPrivate : public QObject {
686686
//! created by checkAndRunCoroutine
687687
void setTaskDoneCallback(const PythonQtObjectPtr& callable);
688688

689+
//! Runs the given coroutine (via asyncio), returns a scheduled task if it object is a coroutine.
690+
PythonQtObjectPtr checkAndRunCoroutine(const PythonQtObjectPtr& object);
691+
692+
//! Creates a new asyncio.Future object
693+
PythonQtObjectPtr createAsyncioFuture();
694+
689695
//! get the suffixes that are used for shared libraries
690696
const QStringList& sharedLibrarySuffixes() { return _sharedLibrarySuffixes; }
691697

@@ -865,6 +871,8 @@ class PYTHONQT_EXPORT PythonQtPrivate : public QObject {
865871

866872
PythonQtObjectPtr _pySourceFileLoader;
867873
PythonQtObjectPtr _pySourcelessFileLoader;
874+
PythonQtObjectPtr _pyEnsureFuture;
875+
PythonQtObjectPtr _pyFutureClass;
868876

869877
PythonQtObjectPtr _pyTaskDoneCallback;
870878

src/PythonQtSignalReceiver.cpp

+1
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)