// Find an overload that matches a subscript.
int qpycore_signal_index(qpycore_pyqtSignal *ps, PyObject *subscript, const char *context)
{
    // Make sure the subscript is a tuple.
    PyObject *args;

    if (PyTuple_Check(subscript))
    {
        args = subscript;
    }
    else
    {
        args = PyTuple_New(1);

        if (!args)
            return -1;

        PyTuple_SET_ITEM(args, 0, subscript);
    }

    Py_INCREF(subscript);

    // Parse the subscript as a tuple of types.
    Chimera::Signature *ss_signature = Chimera::parse(args, 0, context);

    Py_DECREF(args);

    if (!ss_signature)
        return -1;

    // Search for an overload with this signature.
    int idx = -1;

    for (int i = 0; i < ps->overloads->size(); ++i)
    {
        Chimera::Signature *oload = ps->overloads->at(i);

        if (oload->arguments() == ss_signature->signature)
        {
            idx = i;
            break;
        }
    }

    delete ss_signature;

    if (idx < 0)
    {
        PyErr_SetString(PyExc_KeyError,
                "there is no matching overloaded signal");

        return -1;
    }

    return idx;
}
// Disonnect a signal from a slot and handle any errors.
static PyObject *disconnect(qpycore_pyqtBoundSignal *bs, QObject *qrx,
        const char *slot)
{
    Chimera::Signature *signature = bs->unbound_signal->signature;
    bool ok;

    Py_BEGIN_ALLOW_THREADS
    ok = QObject::disconnect(bs->bound_qobject,
            signature->signature.constData(), qrx, slot);
    Py_END_ALLOW_THREADS

    if (!ok)
    {
        QByteArray tx_name = signature->name();

        if (slot)
        {
            QByteArray rx_name = Chimera::Signature::name(slot);

            PyErr_Format(PyExc_TypeError,
                    "disconnect() failed between '%s' and '%s'",
                    tx_name.constData() + 1, rx_name.constData() + 1);
        }
        else
        {
            PyErr_Format(PyExc_TypeError,
                    "disconnect() failed between '%s' and all its connections",
                    tx_name.constData() + 1);
        }

        return 0;
    }

    Py_INCREF(Py_None);
    return Py_None;
}
void qpycore_qmetaobject_connectslotsbyname(QObject *qobj,
        PyObject *qobj_wrapper)
{
    // Get the class attributes.
    PyObject *dir = PyObject_Dir((PyObject *)Py_TYPE(qobj_wrapper));

    if (!dir)
        return;

    PyObject *slot_obj = 0;

    for (SIP_SSIZE_T li = 0; li < PyList_GET_SIZE(dir); ++li)
    {
        PyObject *name_obj = PyList_GET_ITEM(dir, li);

        // Get the slot object.
        Py_XDECREF(slot_obj);
        slot_obj = PyObject_GetAttr(qobj_wrapper, name_obj);

        if (!slot_obj)
            continue;

        // Ignore it if it is not a callable.
        if (!PyCallable_Check(slot_obj))
            continue;

        // Use the signature attribute instead of the name if there is one.
        PyObject *sigattr = PyObject_GetAttr(slot_obj,
                qpycore_dunder_pyqtsignature);

        if (sigattr)
        {
            for (SIP_SSIZE_T i = 0; i < PyList_GET_SIZE(sigattr); ++i)
            {
                PyObject *decoration = PyList_GET_ITEM(sigattr, i);
                Chimera::Signature *sig = Chimera::Signature::fromPyObject(decoration);
                QByteArray args = sig->arguments();

                if (!args.isEmpty())
                    connect(qobj, slot_obj, sig->name(), args);
            }

            Py_DECREF(sigattr);
        }
        else
        {
            const char *ascii_name = sipString_AsASCIIString(&name_obj);

            if (!ascii_name)
                continue;

            PyErr_Clear();

            connect(qobj, slot_obj, QByteArray(ascii_name), QByteArray());

            Py_DECREF(name_obj);
        }
    }

    Py_XDECREF(slot_obj);
    Py_DECREF(dir);
}
// Return the full name and signature of a Qt slot that a signal can be
// connected to, taking the slot decorators into account.
static QByteArray slot_signature_from_decorations(Chimera::Signature *signal,
        PyObject *decorations, int nr_args)
{
    for (SIP_SSIZE_T i = 0; i < PyList_GET_SIZE(decorations); ++i)
    {
        Chimera::Signature *slot = Chimera::Signature::fromPyObject(
                PyList_GET_ITEM(decorations, i));

        if (slot->parsed_arguments.count() != nr_args)
            continue;

        int a;

        for (a = 0; a < nr_args; ++a)
        {
            const Chimera *sig_arg = signal->parsed_arguments.at(a);
            const Chimera *slot_arg = slot->parsed_arguments.at(a);

            // The same type names must be compatible.
            if (sig_arg->name() == slot_arg->name())
                continue;

            enum Match {
                // The type is PyQt_PyObject because it was explicitly
                // specified as such as a string.
                MatchesAll,

                // The type is PyQt_PyObject because it was specified as a type
                // object that needed wrapping.
                MatchesPyType,

                // The type is something other than PyQt_PyObject.
                MatchesName
            };

            Match sig_match, slot_match;

            if (sig_arg->name() != "PyQt_PyObject")
                sig_match = MatchesName;
            else
                sig_match = sig_arg->py_type() ? MatchesPyType : MatchesAll;

            if (slot_arg->name() != "PyQt_PyObject")
                slot_match = MatchesName;
            else
                slot_match = slot_arg->py_type() ? MatchesPyType : MatchesAll;

            // They are incompatible unless one is called PyQt_PyObject.
            if (sig_match == MatchesName || slot_match == MatchesName)
                break;

            // They are compatible if neither was a Python type.
            if (sig_match == MatchesAll || slot_match == MatchesAll)
                continue;

            // The signal type can be a sub-type of the slot type.
            if (!PyType_IsSubtype((PyTypeObject *)sig_arg->py_type(), (PyTypeObject *)slot_arg->py_type()))
                break;
        }

        if (a == nr_args)
            return slot_signature(signal, slot->name(), nr_args);
    }

    return QByteArray();
}