Repository: numpy/numpy
File: numpy/_core/src/multiarray/stringdtype/dtype.c (C)

Code before:
        return Py_NotImplemented;
    }

    // we know both are instances of PyArray_StringDType so this is safe
    PyArray_StringDTypeObject *sself = (PyArray_StringDTypeObject *)self;
    PyArray_StringDTypeObject *sother = (PyArray_StringDTypeObject *)other;

    int eq = _eq_comparison(sself->coerce, sother->coerce, sself->na_object,
                            sother->na_object);

    if (eq == -1) {
        return NULL;
    }

    if ((op == Py_EQ && eq) || (op == Py_NE && !eq)) {
        Py_RETURN_TRUE;
    }
    Py_RETURN_FALSE;
}

static Py_hash_t
PyArray_StringDType_hash(PyObject *self)
{
    PyArray_StringDTypeObject *sself = (PyArray_StringDTypeObject *)self;
    PyObject *hash_tup = NULL;
    if (sself->na_object != NULL) {
        hash_tup = Py_BuildValue("(iO)", sself->coerce, sself->na_object);
    }
    else {
        hash_tup = Py_BuildValue("(i)", sself->coerce);
    }

    Py_hash_t ret = PyObject_Hash(hash_tup);
    Py_DECREF(hash_tup);
    return ret;
}

/*
 * This is the basic things that you need to create a Python Type/Class in C.
 * However, there is a slight difference here because we create a
 * PyArray_DTypeMeta, which is a larger struct than a typical type.
 * (This should get a bit nicer eventually with Python >3.11.)
 */
PyArray_DTypeMeta PyArray_StringDType = {
        {{
                PyVarObject_HEAD_INIT(NULL, 0).tp_name =
                        "numpy.dtypes.StringDType",
                .tp_basicsize = sizeof(PyArray_StringDTypeObject),
                .tp_new = stringdtype_new,
                .tp_dealloc = (destructor)stringdtype_dealloc,
                .tp_repr = (reprfunc)stringdtype_repr,

Review comment (suggestion):
For my more out-there suggestion of wrapping a new ArrayMethod loop, we would here add `{_NPY_METH_static_data, &_sort_compare}`, and that would then be used inside the new method loop.

Code after:
        {{
                PyVarObject_HEAD_INIT(NULL, 0).tp_name =
                        "numpy.dtypes.StringDType",
                .tp_basicsize = sizeof(PyArray_StringDTypeObject),
                .tp_new = stringdtype_new,
                .tp_dealloc = (destructor)stringdtype_dealloc,
                .tp_repr = (reprfunc)stringdtype_repr,
                .tp_str = (reprfunc)stringdtype_repr,
                .tp_methods = PyArray_StringDType_methods,
                .tp_members = PyArray_StringDType_members,
                .tp_richcompare = PyArray_StringDType_richcompare,
                .tp_hash = PyArray_StringDType_hash,
        }},
        /* rest, filled in during DTypeMeta initialization */
};

NPY_NO_EXPORT int
init_stringdtype_sorts(void)
{
    PyArray_DTypeMeta *stringdtype = &PyArray_StringDType;

    PyArray_DTypeMeta *sort_dtypes[2] = {stringdtype, stringdtype};
    PyType_Slot sort_slots[3] = {
            {NPY_METH_resolve_descriptors, &stringdtype_sort_resolve_descriptors},
            {NPY_METH_get_loop, &stringdtype_get_sort_loop},
            {0, NULL}
    };
    PyArrayMethod_Spec sort_spec = {
            .name = "stringdtype_sort",
            .nin = 1,
            .nout = 1,
            .dtypes = sort_dtypes,
            .slots = sort_slots,
            .flags = NPY_METH_NO_FLOATINGPOINT_ERRORS,
    };

    PyBoundArrayMethodObject *sort_method = PyArrayMethod_FromSpec_int(
            &sort_spec, 1);
    if (sort_method == NULL) {
        return -1;
    }
    NPY_DT_SLOTS(stringdtype)->sort_meth = sort_method->method;
    Py_INCREF(sort_method->method);
    Py_DECREF(sort_method);

    PyArray_DTypeMeta *argsort_dtypes[2] = {stringdtype, &PyArray_IntpDType};
    PyType_Slot argsort_slots[2] = {
            {NPY_METH_get_loop, &stringdtype_get_argsort_loop},
            {0, NULL}
    };
    PyArrayMethod_Spec argsort_spec = {