1 // This implements the public API provided by PyQt to external packages.
2 //
3 // Copyright (c) 2021 Riverbank Computing Limited <info@riverbankcomputing.com>
4 //
5 // This file is part of PyQt5.
6 //
7 // This file may be used under the terms of the GNU General Public License
8 // version 3.0 as published by the Free Software Foundation and appearing in
9 // the file LICENSE included in the packaging of this file. Please review the
10 // following information to ensure the GNU General Public License version 3.0
11 // requirements will be met: http://www.gnu.org/copyleft/gpl.html.
12 //
13 // If you do not wish to use this file under the terms of the GPL version 3.0
14 // then you may purchase a commercial license. For more information contact
15 // info@riverbankcomputing.com.
16 //
17 // This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
18 // WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
19
20
21 #include <QByteArray>
22 #include <QHash>
23
24 #include "qpycore_chimera.h"
25 #include "qpycore_event_handlers.h"
26 #include "qpycore_objectified_strings.h"
27 #include "qpycore_public_api.h"
28 #include "qpycore_pyqtboundsignal.h"
29 #include "qpycore_pyqtsignal.h"
30 #include "qpycore_types.h"
31
32 #include <QCoreApplication>
33 #include <QThread>
34
35
36 // The visitor called for each wrapper to clean up if it is a QObject.
cleanup_qobject(sipSimpleWrapper * sw,void * closure)37 static void cleanup_qobject(sipSimpleWrapper *sw, void *closure)
38 {
39 // Ignore any wrapper for QCoreApplication (or sub-class) instances or that
40 // have already been destroyed.
41 void *addr = sipGetAddress(sw);
42
43 if (!addr || addr == closure)
44 return;
45
46 // Ignore anything not owned by Python.
47 if (!sipIsOwnedByPython(sw))
48 return;
49
50 // Ignore non-QObjects.
51 if (!PyObject_TypeCheck((PyObject *)sw, sipTypeAsPyTypeObject(sipType_QObject)))
52 return;
53
54 // Ignore running threads.
55 if (PyObject_TypeCheck((PyObject *)sw, sipTypeAsPyTypeObject(sipType_QThread)))
56 {
57 QThread *thr = reinterpret_cast<QThread *>(addr);
58
59 if (thr->isRunning())
60 return;
61 }
62
63 sipTransferTo((PyObject *)sw, SIP_NULLPTR);
64
65 Py_BEGIN_ALLOW_THREADS
66 delete reinterpret_cast<QObject *>(addr);
67 Py_END_ALLOW_THREADS
68 }
69
70
71 // Call the C++ dtors of all QObject instances (except for QCoreApplication
72 // instances) owned by Python.
pyqt5_cleanup_qobjects()73 void pyqt5_cleanup_qobjects()
74 {
75 // Disable any monitoring.
76 PyQtMonitor::enabled = false;
77
78 sipVisitWrappers(cleanup_qobject, QCoreApplication::instance());
79 }
80
81
82 // A replacement for PyErr_Print() that passes the exception to qFatal().
pyqt5_err_print()83 void pyqt5_err_print()
84 {
85 #if PY_MAJOR_VERSION >= 3
86 #define CONST_CAST(s) s
87 #else
88 #define CONST_CAST(s) const_cast<char *>(s)
89 #endif
90
91 static bool recursing = false;
92
93 if (recursing)
94 return;
95
96 recursing = true;
97
98 // Save the exception in case of new exceptions raised here.
99 PyObject *exception, *value, *traceback;
100
101 PyErr_Fetch(&exception, &value, &traceback);
102
103 // Get the standard exception hook.
104 static PyObject *original_hook = 0;
105
106 if (!original_hook)
107 original_hook = PySys_GetObject(CONST_CAST("__excepthook__"));
108
109 // See if the application has installed its own hook.
110 PyObject *hook = PySys_GetObject(CONST_CAST("excepthook"));
111
112 if (hook != original_hook)
113 {
114 // This will invoke the application's hook.
115 PyErr_Restore(exception, value, traceback);
116 PyErr_Print();
117 }
118 else
119 {
120 // Make sure we have the StringIO ctor.
121 static PyObject *stringio_ctor = 0;
122
123 if (!stringio_ctor)
124 {
125 PyObject *io_module;
126
127 #if PY_MAJOR_VERSION >= 3
128 io_module = PyImport_ImportModule("io");
129 #else
130 PyErr_Clear();
131 io_module = PyImport_ImportModule("cStringIO");
132
133 if (!io_module)
134 {
135 PyErr_Clear();
136 io_module = PyImport_ImportModule("StringIO");
137 }
138 #endif
139
140 if (io_module)
141 {
142 stringio_ctor = PyObject_GetAttrString(io_module, "StringIO");
143 Py_DECREF(io_module);
144 }
145 }
146
147 // Create a StringIO object and replace sys.stderr with it.
148 PyObject *new_stderr = 0, *old_stderr;
149
150 if (stringio_ctor)
151 {
152 old_stderr = PySys_GetObject(CONST_CAST("stderr"));
153
154 if (old_stderr)
155 {
156 new_stderr = PyObject_CallObject(stringio_ctor, NULL);
157
158 if (new_stderr)
159 {
160 // Make sure the old stderr doesn't get garbage collected
161 // when it is replaced.
162 Py_INCREF(old_stderr);
163
164 if (PySys_SetObject(CONST_CAST("stderr"), new_stderr) < 0)
165 {
166 Py_DECREF(old_stderr);
167 Py_DECREF(new_stderr);
168 new_stderr = 0;
169 }
170 }
171 }
172 }
173
174 // Restore the exception and print it.
175 PyErr_Restore(exception, value, traceback);
176 PyErr_Print();
177
178 // This will be passed to qFatal() if we can't get the detailed text.
179 QByteArray message("Unhandled Python exception");
180
181 // Extract the detailed text if it was redirected.
182 if (new_stderr)
183 {
184 // Restore sys.stderr.
185 PySys_SetObject(CONST_CAST("stderr"), old_stderr);
186 Py_DECREF(old_stderr);
187
188 // Extract the text.
189 PyObject *text = PyObject_CallMethod(new_stderr,
190 CONST_CAST("getvalue"), NULL);
191
192 if (text)
193 {
194 // Strip the text as qFatal() likes to add a newline.
195 PyObject *stripped = PyObject_CallMethod(text,
196 CONST_CAST("strip"), NULL);
197
198 if (stripped)
199 {
200 Py_DECREF(text);
201 text = stripped;
202 }
203
204 // Encode the text using the encoding of the original
205 // sys.stderr.
206
207 #if PY_MAJOR_VERSION >= 3
208 PyObject *encoding = PyObject_GetAttrString(old_stderr,
209 "encoding");
210
211 if (encoding)
212 {
213 PyObject *encoding_bytes = PyUnicode_AsUTF8String(
214 encoding);
215
216 if (encoding_bytes)
217 {
218 Q_ASSERT(PyBytes_Check(encoding_bytes));
219
220 PyObject *bytes = PyUnicode_AsEncodedString(text,
221 PyBytes_AsString(encoding_bytes), "strict");
222
223 if (bytes)
224 {
225 Q_ASSERT(PyBytes_Check(bytes));
226
227 message = QByteArray(PyBytes_AsString(bytes),
228 PyBytes_Size(bytes));
229
230 Py_DECREF(bytes);
231 }
232
233 Py_DECREF(encoding_bytes);
234 }
235
236 Py_DECREF(encoding);
237 }
238 #else
239 char *buffer;
240 Py_ssize_t length;
241
242 if (PyString_AsStringAndSize(text, &buffer, &length) == 0)
243 message = QByteArray(buffer, length);
244 #endif
245
246 Py_DECREF(text);
247 }
248
249 Py_DECREF(new_stderr);
250 }
251
252 // qFatal() may not call abort.
253 Py_BEGIN_ALLOW_THREADS
254 qFatal("%s", message.data());
255 Py_END_ALLOW_THREADS
256 }
257
258 recursing = false;
259 }
260
261
262 // Convert a Python argv list to a conventional C argc count and argv array.
pyqt5_from_argv_list(PyObject * argv_list,int & argc)263 char **pyqt5_from_argv_list(PyObject *argv_list, int &argc)
264 {
265 argc = PyList_Size(argv_list);
266
267 // Allocate space for two copies of the argument pointers, plus the
268 // terminating NULL.
269 char **argv = new char *[2 * (argc + 1)];
270
271 // Convert the list.
272 for (int a = 0; a < argc; ++a)
273 {
274 PyObject *arg_obj = PyList_GetItem(argv_list, a);
275 char *arg;
276
277 if (PyUnicode_Check(arg_obj))
278 {
279 QByteArray ba_arg = qpycore_PyObject_AsQString(arg_obj).toLocal8Bit();
280 arg = qstrdup(ba_arg.constData());
281 }
282 else if (SIPBytes_Check(arg_obj))
283 {
284 arg = qstrdup(SIPBytes_AsString(arg_obj));
285 }
286 else
287 {
288 arg = const_cast<char *>("invalid");
289 }
290
291 argv[a] = argv[a + argc + 1] = arg;
292 }
293
294 argv[argc + argc + 1] = argv[argc] = NULL;
295
296 return argv;
297 }
298
299
300 // Get the receiver object and slot signature for a connection.
pyqt5_get_connection_parts(PyObject * slot,QObject * transmitter,const char * signal_signature,bool single_shot,QObject ** receiver,QByteArray & slot_signature)301 sipErrorState pyqt5_get_connection_parts(PyObject *slot, QObject *transmitter,
302 const char *signal_signature, bool single_shot, QObject **receiver,
303 QByteArray &slot_signature)
304 {
305 static QHash<QByteArray, const Chimera::Signature *> cache;
306
307 QByteArray key(signal_signature);
308 const Chimera::Signature *parsed_signal_signature = cache.value(key);
309
310 if (!parsed_signal_signature)
311 {
312 parsed_signal_signature = Chimera::parse(key, "a signal argument");
313
314 if (!parsed_signal_signature)
315 return sipErrorFail;
316
317 cache.insert(key, parsed_signal_signature);
318 }
319
320 return qpycore_get_receiver_slot_signature(slot, transmitter,
321 parsed_signal_signature, single_shot, receiver, slot_signature);
322 }
323
324
325
326 // Get the transmitter object and signal signature from a bound signal.
pyqt5_get_pyqtsignal_parts(PyObject * signal,QObject ** transmitter,QByteArray & signal_signature)327 sipErrorState pyqt5_get_pyqtsignal_parts(PyObject *signal,
328 QObject **transmitter, QByteArray &signal_signature)
329 {
330 if (PyObject_TypeCheck(signal, qpycore_pyqtBoundSignal_TypeObject))
331 {
332 qpycore_pyqtBoundSignal *bs = (qpycore_pyqtBoundSignal *)signal;
333
334 *transmitter = bs->bound_qobject;
335 signal_signature = bs->unbound_signal->parsed_signature->signature;
336
337 return sipErrorNone;
338 }
339
340 return sipErrorContinue;
341 }
342
343
344 // Get the receiver object and slot signature from a decorated callable.
pyqt5_get_pyqtslot_parts(PyObject * slot,QObject ** receiver,QByteArray & slot_signature)345 sipErrorState pyqt5_get_pyqtslot_parts(PyObject *slot, QObject **receiver,
346 QByteArray &slot_signature)
347 {
348 PyObject *py_receiver, *decorations;
349 int is_err;
350 void *qobj;
351 Chimera::Signature *sig;
352 sipMethodDef slot_m;
353
354 // Get the QObject.
355 if (!sipGetMethod(slot, &slot_m))
356 goto bad_callable;
357
358 py_receiver = slot_m.pm_self;
359 if (!py_receiver)
360 goto bad_callable;
361
362 is_err = 0;
363
364 qobj = sipForceConvertToType(py_receiver, sipType_QObject, 0,
365 SIP_NO_CONVERTORS, 0, &is_err);
366
367 if (is_err)
368 goto bad_callable;
369
370 *receiver = reinterpret_cast<QObject *>(qobj);
371
372 // Get the decoration.
373 decorations = PyObject_GetAttr(slot, qpycore_dunder_pyqtsignature);
374
375 if (!decorations)
376 goto bad_callable;
377
378 // Use the first one ignoring any others.
379 sig = Chimera::Signature::fromPyObject(PyList_GetItem(decorations, 0));
380 Py_DECREF(decorations);
381
382 slot_signature = sig->signature;
383 slot_signature.prepend('1');
384
385 return sipErrorNone;
386
387 bad_callable:
388 PyErr_SetString(PyExc_TypeError,
389 "callable must be a method of a QtCore.QObject instance decorated "
390 "by QtCore.pyqtSlot");
391
392 return sipErrorFail;
393 }
394
395
396 // Get the signature string for a bound or unbound signal.
pyqt5_get_signal_signature(PyObject * signal,const QObject * transmitter,QByteArray & signal_signature)397 sipErrorState pyqt5_get_signal_signature(PyObject *signal,
398 const QObject *transmitter, QByteArray &signal_signature)
399 {
400 qpycore_pyqtSignal *ps;
401
402 if (PyObject_TypeCheck(signal, qpycore_pyqtBoundSignal_TypeObject))
403 {
404 qpycore_pyqtBoundSignal *bs = (qpycore_pyqtBoundSignal *)signal;
405
406 if (transmitter && bs->bound_qobject != transmitter)
407 {
408 PyErr_SetString(PyExc_ValueError,
409 "signal is bound to a different QObject");
410 return sipErrorFail;
411 }
412
413 ps = bs->unbound_signal;
414 }
415 else if (PyObject_TypeCheck(signal, qpycore_pyqtSignal_TypeObject))
416 {
417 ps = (qpycore_pyqtSignal *)signal;
418 }
419 else
420 {
421 return sipErrorContinue;
422 }
423
424 signal_signature = ps->parsed_signature->signature;
425
426 return sipErrorNone;
427 }
428
429
430 // Register a convertor function that converts a QVariant to a Python object.
pyqt5_register_from_qvariant_convertor(bool (* convertor)(const QVariant &,PyObject **))431 void pyqt5_register_from_qvariant_convertor(
432 bool (*convertor)(const QVariant &, PyObject **))
433 {
434 Chimera::registeredFromQVariantConvertors.append(convertor);
435 }
436
437
438 // Register a convertor function that converts a Python object to a QVariant.
pyqt5_register_to_qvariant_convertor(bool (* convertor)(PyObject *,QVariant &,bool *))439 void pyqt5_register_to_qvariant_convertor(
440 bool (*convertor)(PyObject *, QVariant &, bool *))
441 {
442 Chimera::registeredToQVariantConvertors.append(convertor);
443 }
444
445
446 // Register a convertor function that converts a Python object to the
447 // pre-allocated data of a QVariant with a specific meta-type.
pyqt5_register_to_qvariant_data_convertor(bool (* convertor)(PyObject *,void *,int,bool *))448 void pyqt5_register_to_qvariant_data_convertor(
449 bool (*convertor)(PyObject *, void *, int, bool *))
450 {
451 Chimera::registeredToQVariantDataConvertors.append(convertor);
452 }
453
454
455 // Remove arguments from the Python argv list that have been removed from the
456 // C argv array.
pyqt5_update_argv_list(PyObject * argv_list,int argc,char ** argv)457 void pyqt5_update_argv_list(PyObject *argv_list, int argc, char **argv)
458 {
459 for (int a = 0, na = 0; a < argc; ++a)
460 {
461 // See if it was removed.
462 if (argv[na] == argv[a + argc + 1])
463 ++na;
464 else
465 PyList_SetSlice(argv_list, na, na + 1, 0);
466 }
467
468 // Assume that we have created a QCoreApplication instance.
469 qpycore_created_qapp = true;
470 }
471
472
473 // Get the QMetaObject instance for a Python type.
pyqt5_get_qmetaobject(PyTypeObject * type)474 const QMetaObject *pyqt5_get_qmetaobject(PyTypeObject *type)
475 {
476 return qpycore_get_qmetaobject((sipWrapperType *)type);
477 }
478