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