1 // This contains the implementation of the pyqtSignal type.
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 <Python.h>
22
23 #include <QtGlobal>
24 #include <QByteArray>
25 #include <QMetaObject>
26
27 #include "qpycore_chimera.h"
28 #include "qpycore_misc.h"
29 #include "qpycore_pyqtboundsignal.h"
30 #include "qpycore_pyqtsignal.h"
31
32
33 // The type object.
34 PyTypeObject *qpycore_pyqtSignal_TypeObject;
35
36
37 // Forward declarations.
38 extern "C" {
39 static PyObject *pyqtSignal_call(PyObject *self, PyObject *args, PyObject *kw);
40 static void pyqtSignal_dealloc(PyObject *self);
41 static PyObject *pyqtSignal_descr_get(PyObject *self, PyObject *obj,
42 PyObject *type);
43 static int pyqtSignal_init(PyObject *self, PyObject *args, PyObject *kwd_args);
44 static PyObject *pyqtSignal_repr(PyObject *self);
45 static PyObject *pyqtSignal_mp_subscript(PyObject *self, PyObject *subscript);
46 static PyObject *pyqtSignal_get_doc(PyObject *self, void *);
47 static PyObject *pyqtSignal_get_signatures(PyObject *self, void *);
48 }
49
50 static int init_signal_from_types(qpycore_pyqtSignal *ps, const char *name,
51 const QList<QByteArray> *parameter_names, int revision,
52 PyObject *types);
53 static void append_overload(qpycore_pyqtSignal *ps);
54 static bool is_signal_name(const char *sig, const QByteArray &name);
55
56
57 // Doc-strings.
58 PyDoc_STRVAR(pyqtSignal_doc,
59 "pyqtSignal(*types, name: str = ..., revision: int = ..., arguments: Sequence = ...) -> PYQT_SIGNAL\n"
60 "\n"
61 "types is normally a sequence of individual types. Each type is either a\n"
62 "type object or a string that is the name of a C++ type. Alternatively\n"
63 "each type could itself be a sequence of types each describing a different\n"
64 "overloaded signal.\n"
65 "name is the optional C++ name of the signal. If it is not specified then\n"
66 "the name of the class attribute that is bound to the signal is used.\n"
67 "revision is the optional revision of the signal that is exported to QML.\n"
68 "If it is not specified then 0 is used.\n"
69 "arguments is the optional sequence of the names of the signal's arguments.\n");
70
71 PyDoc_STRVAR(pyqtSignal_signatures_doc,
72 "The signatures of each of the overloaded signals");
73
74
75 // The getters/setters.
76 static PyGetSetDef pyqtSignal_getset[] = {
77 {const_cast<char *>("__doc__"), pyqtSignal_get_doc, NULL, NULL, NULL},
78 {const_cast<char *>("signatures"), pyqtSignal_get_signatures, NULL,
79 const_cast<char *>(pyqtSignal_signatures_doc), NULL},
80 {NULL, NULL, NULL, NULL, NULL}
81 };
82
83
84 #if PY_VERSION_HEX >= 0x03040000
85 // Define the slots.
86 static PyType_Slot qpycore_pyqtSignal_Slots[] = {
87 {Py_tp_new, (void *)PyType_GenericNew},
88 {Py_tp_init, (void *)pyqtSignal_init},
89 {Py_tp_dealloc, (void *)pyqtSignal_dealloc},
90 {Py_tp_doc, (void *)pyqtSignal_doc},
91 {Py_tp_repr, (void *)pyqtSignal_repr},
92 {Py_tp_call, (void *)pyqtSignal_call},
93 {Py_tp_descr_get, (void *)pyqtSignal_descr_get},
94 {Py_mp_subscript, (void *)pyqtSignal_mp_subscript},
95 {Py_tp_getset, pyqtSignal_getset},
96 {0, 0}
97 };
98
99
100 // Define the type.
101 static PyType_Spec qpycore_pyqtSignal_Spec = {
102 "PyQt5.QtCore.pyqtSignal",
103 sizeof (qpycore_pyqtSignal),
104 0,
105 Py_TPFLAGS_DEFAULT,
106 qpycore_pyqtSignal_Slots
107 };
108 #else
109 // Define the mapping methods.
110 static PyMappingMethods pyqtSignal_as_mapping = {
111 0, /* mp_length */
112 pyqtSignal_mp_subscript, /* mp_subscript */
113 0, /* mp_ass_subscript */
114 };
115
116
117 // Define the type.
118 static PyTypeObject qpycore_pyqtSignal_Type = {
119 PyVarObject_HEAD_INIT(NULL, 0)
120 #if PY_VERSION_HEX >= 0x02050000
121 "PyQt5.QtCore.pyqtSignal", /* tp_name */
122 #else
123 const_cast<char *>("PyQt5.QtCore.pyqtSignal"), /* tp_name */
124 #endif
125 sizeof (qpycore_pyqtSignal), /* tp_basicsize */
126 0, /* tp_itemsize */
127 pyqtSignal_dealloc, /* tp_dealloc */
128 0, /* tp_print */
129 0, /* tp_getattr */
130 0, /* tp_setattr */
131 0, /* tp_compare */
132 pyqtSignal_repr, /* tp_repr */
133 0, /* tp_as_number */
134 0, /* tp_as_sequence */
135 &pyqtSignal_as_mapping, /* tp_as_mapping */
136 0, /* tp_hash */
137 pyqtSignal_call, /* tp_call */
138 0, /* tp_str */
139 0, /* tp_getattro */
140 0, /* tp_setattro */
141 0, /* tp_as_buffer */
142 Py_TPFLAGS_DEFAULT, /* tp_flags */
143 pyqtSignal_doc, /* tp_doc */
144 0, /* tp_traverse */
145 0, /* tp_clear */
146 0, /* tp_richcompare */
147 0, /* tp_weaklistoffset */
148 0, /* tp_iter */
149 0, /* tp_iternext */
150 0, /* tp_methods */
151 0, /* tp_members */
152 pyqtSignal_getset, /* tp_getset */
153 0, /* tp_base */
154 0, /* tp_dict */
155 pyqtSignal_descr_get, /* tp_descr_get */
156 0, /* tp_descr_set */
157 0, /* tp_dictoffset */
158 pyqtSignal_init, /* tp_init */
159 0, /* tp_alloc */
160 PyType_GenericNew, /* tp_new */
161 0, /* tp_free */
162 0, /* tp_is_gc */
163 0, /* tp_bases */
164 0, /* tp_mro */
165 0, /* tp_cache */
166 0, /* tp_subclasses */
167 0, /* tp_weaklist */
168 0, /* tp_del */
169 0, /* tp_version_tag */
170 #if PY_VERSION_HEX >= 0x03040000
171 0, /* tp_finalize */
172 #endif
173 };
174 #endif
175
176
177 // The __doc__ getter.
pyqtSignal_get_doc(PyObject * self,void *)178 static PyObject *pyqtSignal_get_doc(PyObject *self, void *)
179 {
180 qpycore_pyqtSignal *ps = (qpycore_pyqtSignal *)self;
181
182 // Make sure we have the default signal.
183 ps = ps->default_signal;
184
185 QByteArray doc;
186
187 // Get any docstrings from any non-signal overloads.
188 if (ps->non_signals && ps->non_signals->ml_doc)
189 {
190 doc.append('\n');
191 doc.append(ps->non_signals->ml_doc);
192 }
193
194 // Get any docstrings from the signals.
195 do
196 {
197 const char *docstring = ps->docstring;
198
199 if (docstring)
200 {
201 if (*docstring == '\1')
202 ++docstring;
203
204 doc.append('\n');
205 doc.append(docstring);
206 doc.append(" [signal]");
207 }
208
209 ps = ps->next;
210 }
211 while (ps);
212
213 if (doc.isEmpty())
214 {
215 Py_INCREF(Py_None);
216 return Py_None;
217 }
218
219 return
220 #if PY_MAJOR_VERSION >= 3
221 PyUnicode_FromString
222 #else
223 PyString_FromString
224 #endif
225 (doc.constData() + 1);
226 }
227
228
229 // The 'signatures' getter.
pyqtSignal_get_signatures(PyObject * self,void *)230 static PyObject *pyqtSignal_get_signatures(PyObject *self, void *)
231 {
232 qpycore_pyqtSignal *head = ((qpycore_pyqtSignal *)self)->default_signal;
233 qpycore_pyqtSignal *ps;
234 PyObject *sigs;
235 Py_ssize_t sig_nr;
236
237 // Count the number of overloads.
238 for (sig_nr = 0, ps = head; ps; ps = ps->next, ++sig_nr)
239 ;
240
241 sigs = PyTuple_New(sig_nr);
242 if (!sigs)
243 return 0;
244
245 for (sig_nr = 0, ps = head; ps; ps = ps->next, ++sig_nr)
246 {
247 PyObject *sig =
248 #if PY_MAJOR_VERSION >= 3
249 PyUnicode_FromString
250 #else
251 PyString_FromString
252 #endif
253 (ps->parsed_signature->signature.constData() + 1);
254
255 if (!sig || PyTuple_SetItem(sigs, sig_nr, sig))
256 {
257 Py_DECREF(sigs);
258 return 0;
259 }
260 }
261
262 return sigs;
263 }
264
265
266 // The type repr slot.
pyqtSignal_repr(PyObject * self)267 static PyObject *pyqtSignal_repr(PyObject *self)
268 {
269 qpycore_pyqtSignal *ps = (qpycore_pyqtSignal *)self;
270
271 return
272 #if PY_MAJOR_VERSION >= 3
273 PyUnicode_FromFormat
274 #else
275 PyString_FromFormat
276 #endif
277 ("<unbound PYQT_SIGNAL %s>", ps->parsed_signature->signature.constData() + 1);
278 }
279
280
281 // The type call slot.
pyqtSignal_call(PyObject * self,PyObject * args,PyObject * kw)282 static PyObject *pyqtSignal_call(PyObject *self, PyObject *args, PyObject *kw)
283 {
284 return qpycore_call_signal_overload((qpycore_pyqtSignal *)self, 0, args,
285 kw);
286 }
287
288
289 // The type dealloc slot.
pyqtSignal_dealloc(PyObject * self)290 static void pyqtSignal_dealloc(PyObject *self)
291 {
292 qpycore_pyqtSignal *ps = (qpycore_pyqtSignal *)self;
293
294 delete ps->parsed_signature;
295
296 if (ps->parameter_names)
297 delete ps->parameter_names;
298
299 // If we are the default then we own the overloads references.
300 if (ps == ps->default_signal)
301 {
302 qpycore_pyqtSignal *next = ps->next;
303
304 while (next)
305 {
306 ps = next;
307 next = ps->next;
308
309 Py_DECREF((PyObject *)ps);
310 }
311 }
312
313 PyObject_Del(self);
314 }
315
316
317 // The type descriptor get slot.
pyqtSignal_descr_get(PyObject * self,PyObject * obj,PyObject *)318 static PyObject *pyqtSignal_descr_get(PyObject *self, PyObject *obj,
319 PyObject *)
320 {
321 qpycore_pyqtSignal *ps = (qpycore_pyqtSignal *)self;
322
323 // Return the unbound signal if there is nothing to bind it to.
324 if (obj == NULL || obj == Py_None)
325 {
326 Py_INCREF(self);
327 return self;
328 }
329
330 // Get the QObject.
331 int is_err = 0;
332 void *qobject = sipForceConvertToType(obj, sipType_QObject, 0,
333 SIP_NO_CONVERTORS, 0, &is_err);
334
335 if (is_err)
336 return 0;
337
338 // Return the bound signal.
339 return qpycore_pyqtBoundSignal_New(ps, obj,
340 reinterpret_cast<QObject *>(qobject));
341 }
342
343
344 // The type init slot.
pyqtSignal_init(PyObject * self,PyObject * args,PyObject * kwd_args)345 static int pyqtSignal_init(PyObject *self, PyObject *args, PyObject *kwd_args)
346 {
347 qpycore_pyqtSignal *ps = (qpycore_pyqtSignal *)self;
348
349 // Get the keyword arguments.
350 PyObject *name_obj = 0;
351 const char *name = 0;
352 int revision = 0;
353 QList<QByteArray> *parameter_names = 0;
354
355 if (kwd_args)
356 {
357 Py_ssize_t pos = 0;
358 PyObject *key, *value;
359
360 while (PyDict_Next(kwd_args, &pos, &key, &value))
361 {
362 #if PY_MAJOR_VERSION >= 3
363 if (PyUnicode_CompareWithASCIIString(key, "name") == 0)
364 #else
365 Q_ASSERT(PyString_Check(key));
366
367 if (qstrcmp(PyString_AsString(key), "name") == 0)
368 #endif
369 {
370 name_obj = value;
371 name = sipString_AsASCIIString(&name_obj);
372
373 if (!name)
374 {
375 PyErr_Format(PyExc_TypeError,
376 "signal 'name' must be a str, not %s",
377 sipPyTypeName(Py_TYPE(value)));
378
379 return -1;
380 }
381 }
382 #if PY_MAJOR_VERSION >= 3
383 else if (PyUnicode_CompareWithASCIIString(key, "revision") == 0)
384 #else
385 else if (qstrcmp(PyString_AsString(key), "revision") == 0)
386 #endif
387 {
388 revision = sipLong_AsInt(value);
389
390 if (PyErr_Occurred())
391 {
392 if (PyErr_ExceptionMatches(PyExc_TypeError))
393 PyErr_Format(PyExc_TypeError,
394 "signal 'revision' must be an int, not %s",
395 sipPyTypeName(Py_TYPE(value)));
396
397 Py_XDECREF(name_obj);
398 return -1;
399 }
400 }
401 #if PY_MAJOR_VERSION >= 3
402 else if (PyUnicode_CompareWithASCIIString(key, "arguments") == 0)
403 #else
404 else if (qstrcmp(PyString_AsString(key), "arguments") == 0)
405 #endif
406 {
407 bool ok = true;
408
409 if (PySequence_Check(value))
410 {
411 Py_ssize_t len = PySequence_Size(value);
412
413 parameter_names = new QList<QByteArray>;
414
415 for (Py_ssize_t i = 0; i < len; ++i)
416 {
417 PyObject *py_attr = PySequence_GetItem(value, i);
418
419 if (!py_attr)
420 {
421 ok = false;
422 break;
423 }
424
425 PyObject *py_ascii_attr = py_attr;
426 const char *attr = sipString_AsASCIIString(
427 &py_ascii_attr);
428
429 Py_DECREF(py_attr);
430
431 if (!attr)
432 {
433 ok = false;
434 break;
435 }
436
437 parameter_names->append(QByteArray(attr));
438
439 Py_DECREF(py_ascii_attr);
440 }
441 }
442 else
443 {
444 ok = false;
445 }
446
447 if (!ok)
448 {
449 PyErr_Format(PyExc_TypeError,
450 "signal 'attribute_names' must be a sequence of str, not %s",
451 sipPyTypeName(Py_TYPE(value)));
452
453 if (parameter_names)
454 delete parameter_names;
455
456 Py_XDECREF(name_obj);
457 return -1;
458 }
459 }
460 else
461 {
462 #if PY_MAJOR_VERSION >= 3
463 PyErr_Format(PyExc_TypeError,
464 "pyqtSignal() got an unexpected keyword argument '%U'",
465 key);
466 #else
467 PyErr_Format(PyExc_TypeError,
468 "pyqtSignal() got an unexpected keyword argument '%s'",
469 PyString_AsString(key));
470 #endif
471
472 Py_XDECREF(name_obj);
473 return -1;
474 }
475 }
476 }
477
478 // If there is at least one argument and it is a sequence then assume all
479 // arguments are sequences. Unfortunately a string is also a sequence so
480 // check for tuples and lists explicitly.
481 if (PyTuple_Size(args) > 0 && (PyTuple_Check(PyTuple_GetItem(args, 0)) || PyList_Check(PyTuple_GetItem(args, 0))))
482 {
483 for (Py_ssize_t i = 0; i < PyTuple_Size(args); ++i)
484 {
485 PyObject *types = PySequence_Tuple(PyTuple_GetItem(args, i));
486
487 if (!types)
488 {
489 PyErr_SetString(PyExc_TypeError,
490 "pyqtSignal() argument expected to be sequence of types");
491
492 if (name)
493 {
494 Py_DECREF(name_obj);
495 }
496
497 return -1;
498 }
499
500 int rc;
501
502 if (i == 0)
503 {
504 // The first is the default.
505 rc = init_signal_from_types(ps, name, parameter_names,
506 revision, types);
507 }
508 else
509 {
510 qpycore_pyqtSignal *overload = (qpycore_pyqtSignal *)PyType_GenericNew(qpycore_pyqtSignal_TypeObject, 0, 0);
511
512 if (!overload)
513 {
514 rc = -1;
515 }
516 else if ((rc = init_signal_from_types(overload, name, 0, revision, types)) < 0)
517 {
518 Py_DECREF((PyObject *)overload);
519 }
520 else
521 {
522 overload->default_signal = ps;
523 append_overload(overload);
524 }
525 }
526
527 Py_DECREF(types);
528
529 if (rc < 0)
530 {
531 if (name)
532 {
533 Py_DECREF(name_obj);
534 }
535
536 return -1;
537 }
538 }
539 }
540 else if (init_signal_from_types(ps, name, parameter_names, revision, args) < 0)
541 {
542 if (name)
543 {
544 Py_DECREF(name_obj);
545 }
546
547 return -1;
548 }
549
550 if (name)
551 {
552 Py_DECREF(name_obj);
553 }
554
555 return 0;
556 }
557
558
559 // The mapping subscript slot.
pyqtSignal_mp_subscript(PyObject * self,PyObject * subscript)560 static PyObject *pyqtSignal_mp_subscript(PyObject *self, PyObject *subscript)
561 {
562 qpycore_pyqtSignal *ps = (qpycore_pyqtSignal *)self;
563
564 PyObject *sig = (PyObject *)qpycore_find_signal(ps, subscript,
565 "an unbound signal type argument");
566
567 Py_XINCREF(sig);
568
569 return sig;
570 }
571
572
573 // Initialise the type and return true if there was no error.
qpycore_pyqtSignal_init_type()574 bool qpycore_pyqtSignal_init_type()
575 {
576 #if PY_VERSION_HEX >= 0x03040000
577 qpycore_pyqtSignal_TypeObject = (PyTypeObject *)PyType_FromSpec(
578 &qpycore_pyqtSignal_Spec);
579
580 return qpycore_pyqtSignal_TypeObject;
581 #else
582 if (PyType_Ready(&qpycore_pyqtSignal_Type) < 0)
583 return false;
584
585 qpycore_pyqtSignal_TypeObject = &qpycore_pyqtSignal_Type;
586
587 return true;
588 #endif
589 }
590
591
592 // Create a new signal instance.
qpycore_pyqtSignal_New(const char * signature,bool * fatal)593 qpycore_pyqtSignal *qpycore_pyqtSignal_New(const char *signature, bool *fatal)
594 {
595 // Assume any error is fatal.
596 if (fatal)
597 *fatal = true;
598
599 // See if we have two variants of the signature, ie. one with namespaces
600 // stripped that we can use with connect() and another that we can parse.
601 const char *full_sig = strchr(signature, '|');
602
603 Chimera::Signature *parsed_signature = Chimera::parse(
604 (full_sig ? full_sig + 1 : signature), "a signal argument");
605
606 // At first glance the parse should never fail because the signature
607 // originates from the .sip file. However it might if it includes a type
608 // that has been forward declared, but not yet defined. The only example
609 // in PyQt is the declaration of QWidget by QSignalMapper. Therefore we
610 // assume the error isn't fatal.
611 if (!parsed_signature)
612 {
613 if (fatal)
614 *fatal = false;
615
616 return 0;
617 }
618
619 if (full_sig)
620 // Use the variant with namespaces stripped.
621 parsed_signature->signature = QByteArray(signature,
622 full_sig - signature);
623
624 parsed_signature->signature.prepend('2');
625
626 // Create and initialise the signal.
627 qpycore_pyqtSignal *ps = (qpycore_pyqtSignal *)PyType_GenericNew(
628 qpycore_pyqtSignal_TypeObject, 0, 0);
629
630 if (!ps)
631 {
632 delete parsed_signature;
633 return 0;
634 }
635
636 ps->default_signal = ps;
637 ps->next = 0;
638 ps->docstring = 0;
639 ps->parameter_names = 0;
640 ps->revision = 0;
641 ps->parsed_signature = parsed_signature;
642 ps->emitter = 0;
643 ps->non_signals = 0;
644
645 return ps;
646 }
647
648
649 // Find an overload that matches a subscript.
qpycore_find_signal(qpycore_pyqtSignal * ps,PyObject * subscript,const char * context)650 qpycore_pyqtSignal *qpycore_find_signal(qpycore_pyqtSignal *ps,
651 PyObject *subscript, const char *context)
652 {
653 // Make sure the subscript is a tuple.
654 PyObject *args;
655
656 if (PyTuple_Check(subscript))
657 {
658 args = subscript;
659 }
660 else
661 {
662 args = PyTuple_New(1);
663
664 if (!args)
665 return 0;
666
667 PyTuple_SetItem(args, 0, subscript);
668 }
669
670 Py_INCREF(subscript);
671
672 // Parse the subscript as a tuple of types.
673 Chimera::Signature *ss_signature = Chimera::parse(args, 0, context);
674
675 Py_DECREF(args);
676
677 if (!ss_signature)
678 return 0;
679
680 // Search for an overload with this signature.
681 qpycore_pyqtSignal *overload = ps->default_signal;
682
683 do
684 {
685 if (overload->parsed_signature->arguments() == ss_signature->signature)
686 break;
687
688 overload = overload->next;
689 }
690 while (overload);
691
692 delete ss_signature;
693
694 if (!overload)
695 PyErr_SetString(PyExc_KeyError,
696 "there is no matching overloaded signal");
697
698 return overload;
699 }
700
701
702 // Initialise a signal when given a tuple of types.
init_signal_from_types(qpycore_pyqtSignal * ps,const char * name,const QList<QByteArray> * parameter_names,int revision,PyObject * types)703 static int init_signal_from_types(qpycore_pyqtSignal *ps, const char *name,
704 const QList<QByteArray> *parameter_names, int revision,
705 PyObject *types)
706 {
707 Chimera::Signature *parsed_signature = Chimera::parse(types, name,
708 "a pyqtSignal() type argument");
709
710 if (!parsed_signature)
711 return -1;
712
713 if (name)
714 parsed_signature->signature.prepend('2');
715
716 ps->default_signal = ps;
717 ps->next = 0;
718 ps->docstring = 0;
719 ps->parameter_names = parameter_names;
720 ps->revision = revision;
721 ps->parsed_signature = parsed_signature;
722 ps->emitter = 0;
723 ps->non_signals = 0;
724
725 return 0;
726 }
727
728
729 // Append an overload to the default signal's list.
append_overload(qpycore_pyqtSignal * ps)730 static void append_overload(qpycore_pyqtSignal *ps)
731 {
732 // Append to the list of overloads.
733 qpycore_pyqtSignal **tailp = &ps->default_signal->next;
734
735 while (*tailp)
736 tailp = &(*tailp)->next;
737
738 *tailp = ps;
739 }
740
741
742 // Give a signal a name if it hasn't already got one.
qpycore_set_signal_name(qpycore_pyqtSignal * ps,const char * type_name,const char * name)743 void qpycore_set_signal_name(qpycore_pyqtSignal *ps, const char *type_name,
744 const char *name)
745 {
746 ps = ps->default_signal;
747
748 // If the signature already has a name then they all have and there is
749 // nothing more to do.
750 if (!ps->parsed_signature->signature.startsWith('('))
751 return;
752
753 do
754 {
755 QByteArray &sig = ps->parsed_signature->signature;
756
757 sig.prepend(name);
758 sig.prepend('2');
759
760 QByteArray &py_sig = ps->parsed_signature->py_signature;
761
762 py_sig.prepend(name);
763 py_sig.prepend('.');
764 py_sig.prepend(type_name);
765
766 ps = ps->next;
767 }
768 while (ps);
769 }
770
771
772 // Handle the getting of a lazy attribute, ie. a native Qt signal.
qpycore_get_lazy_attr(const sipTypeDef * td,PyObject * dict)773 int qpycore_get_lazy_attr(const sipTypeDef *td, PyObject *dict)
774 {
775 const pyqt5QtSignal *sigs = reinterpret_cast<const pyqt5ClassPluginDef *>(
776 sipTypePluginData(td))->qt_signals;
777
778 // Handle the trvial case.
779 if (!sigs)
780 return 0;
781
782 QByteArray default_name;
783 qpycore_pyqtSignal *default_signal = 0;
784
785 do
786 {
787 // See if we have come to the end of the current signal.
788 if (default_signal && !is_signal_name(sigs->signature, default_name))
789 {
790 if (PyDict_SetItemString(dict, default_name.constData(), (PyObject *)default_signal) < 0)
791 return -1;
792
793 default_signal = 0;
794 }
795
796 bool fatal;
797
798 qpycore_pyqtSignal *sig = qpycore_pyqtSignal_New(sigs->signature,
799 &fatal);
800
801 if (!sig)
802 {
803 if (fatal)
804 return -1;
805
806 PyErr_Clear();
807 continue;
808 }
809
810 sig->docstring = sigs->docstring;
811 sig->emitter = sigs->emitter;
812
813 // See if this is a new default.
814 if (default_signal)
815 {
816 sig->default_signal = default_signal;
817 append_overload(sig);
818 }
819 else
820 {
821 sig->non_signals = sigs->non_signals;
822
823 default_signal = sig->default_signal = sig;
824
825 default_name = sigs->signature;
826 default_name.truncate(default_name.indexOf('('));
827 }
828 }
829 while ((++sigs)->signature);
830
831 // Save the last one, if any (in case of a non-fatal error).
832 if (!default_signal)
833 return 0;
834
835 return PyDict_SetItemString(dict, default_name.constData(),
836 (PyObject *)default_signal);
837 }
838
839
840 // Return true if a signal signatures has a particular name.
is_signal_name(const char * sig,const QByteArray & name)841 static bool is_signal_name(const char *sig, const QByteArray &name)
842 {
843 return (qstrncmp(sig, name.constData(), name.size()) == 0 && sig[name.size()] == '(');
844 }
845
846
847 // Call a signal's overloaded method (if there is one).
qpycore_call_signal_overload(qpycore_pyqtSignal * ps,PyObject * bound,PyObject * args,PyObject * kw)848 PyObject *qpycore_call_signal_overload(qpycore_pyqtSignal *ps, PyObject *bound,
849 PyObject *args, PyObject *kw)
850 {
851 if (!ps->non_signals)
852 {
853 PyErr_SetString(PyExc_TypeError, "native Qt signal is not callable");
854 return 0;
855 }
856
857 PyObject *func = PyCFunction_New(ps->non_signals, bound);
858
859 if (!func)
860 return 0;
861
862 PyObject *result = PyObject_Call(func, args, kw);
863
864 Py_DECREF(func);
865
866 return result;
867 }
868