Adding new function in signal module

Hi,

I am trying to add a new function in signal module similar to function signal_signal_impl in signalmodule.c, say signal_mysignal.

I have added the function and made the necessary changes in signalmodule.c
in static PyMethodDef signal_methods and Modules/clinic/signalmodule.c.h to declare the function. My function takes in the same parameters as signal_signal_impl and have added changes analogous to signal_signal in signalmodule.c.h.

I am able to see my function in python binary, however there is a catch for me, when I pass signal.SIG_DFL as an argument to my custom function the check for PyLong_CheckExact in compare_handler(in file signalmodule.c) fails, I have designed my function as a replica of signal_signal_impl taking in same parameters and everything and yet signal.SIG_DFL passes the check for PyLong_CheckExact in signal.signal and not my function which is signal_mysignal.

Any tips on how to tackle this issue?

I’d have to see the code before making any suggestions. It might be something simple that you’re overlooking but that a second person would spot.

The following are my changes in signalmodule.c

static PyObject *
signal_mysignal_impl(PyObject *module, int signalnum, PyObject *handler)
/*[clinic end generated code: output=b44cfda43780f3a1 input=deee84af5fa0432c]*/
{
    _signal_module_state *modstate = get_signal_state(module);
    PyObject *old_handler;
    void (*func)(int);
#ifdef MS_WINDOWS
    /* Validate that signalnum is one of the allowable signals */
    switch (signalnum) {
        case SIGABRT: break;
#ifdef SIGBREAK
        /* Issue #10003: SIGBREAK is not documented as permitted, but works
           and corresponds to CTRL_BREAK_EVENT. */
        case SIGBREAK: break;
#endif
        case SIGFPE: break;
        case SIGILL: break;
        case SIGINT: break;
        case SIGSEGV: break;
        case SIGTERM: break;
        default:
            PyErr_SetString(PyExc_ValueError, "invalid signal value");
            return NULL;
    }
#endif

    PyThreadState *tstate = _PyThreadState_GET();
    if (!_Py_ThreadCanHandleSignals(tstate->interp)) {
        _PyErr_SetString(tstate, PyExc_ValueError,
                         "signal only works in main thread "
                         "of the main interpreter");
        return NULL;
    }
    if (signalnum < 1 || signalnum >= Py_NSIG) {
        _PyErr_SetString(tstate, PyExc_ValueError,
                         "signal number out of range");
        return NULL;
    }
    if (PyCallable_Check(handler)) {
        func = signal_handler;
    }
    else if (compare_handler(handler, modstate->ignore_handler))
	{
        func = SIG_IGN;
    }
    else if (compare_handler(handler, modstate->default_handler))
    {
        func = SIG_DFL;
    } else {
        _PyErr_SetString(tstate, PyExc_TypeError,
                         "signal handler must be signal.SIG_IGN, "
                         "signal.SIG_DFL, or a callable object");
        return NULL;
    }

    /* Check for pending signals before changing signal handler */
    if (_PyErr_CheckSignalsTstate(tstate)) {
        return NULL;
    }

    if (my_setsig(signalnum, func) == SIG_ERR) { // The only change with respect to signal_signal_impl is this function instead of PyOS_setsig
        PyErr_SetFromErrno(PyExc_OSError);
        return NULL;
    }

    old_handler = get_handler(signalnum);
    set_handler(signalnum, Py_NewRef(handler));

    if (old_handler != NULL) {
        return old_handler;
    }
    else {
        Py_RETURN_NONE;
    }
}

I have added this method’s inclusion in this struct

static PyMethodDef signal_methods[] = {
    SIGNAL_DEFAULT_INT_HANDLER_METHODDEF
    SIGNAL_ALARM_METHODDEF
    SIGNAL_SETITIMER_METHODDEF
    SIGNAL_GETITIMER_METHODDEF
    SIGNAL_SIGNAL_METHODDEF
    SIGNAL_MYSIGNAL_METHODDEF
    SIGNAL_RAISE_SIGNAL_METHODDEF
    SIGNAL_STRSIGNAL_METHODDEF
    SIGNAL_GETSIGNAL_METHODDEF
    {"set_wakeup_fd", _PyCFunction_CAST(signal_set_wakeup_fd), METH_VARARGS | METH_KEYWORDS, set_wakeup_fd_doc},
    SIGNAL_SIGINTERRUPT_METHODDEF
    SIGNAL_PAUSE_METHODDEF
    SIGNAL_PIDFD_SEND_SIGNAL_METHODDEF
    SIGNAL_PTHREAD_KILL_METHODDEF
    SIGNAL_PTHREAD_SIGMASK_METHODDEF
    SIGNAL_SIGPENDING_METHODDEF
    SIGNAL_SIGWAIT_METHODDEF
    SIGNAL_SIGWAITINFO_METHODDEF
    SIGNAL_SIGTIMEDWAIT_METHODDEF
#if defined(HAVE_SIGFILLSET) || defined(MS_WINDOWS)
    SIGNAL_VALID_SIGNALS_METHODDEF
#endif
    {NULL, NULL}           /* sentinel */
};

The following are my changes in Modules/clinic/signalmodule.c.h

PyDoc_STRVAR(signal_mysignal__doc__,
"mysignal($module, signalnum, handler, /)\n"
"--\n"
"\n"
"Set the action for the given signal.\n"
"\n"
"The action can be SIG_DFL, SIG_IGN, or a callable Python object.\n"
"The previous action is returned.  See getsignal() for possible return values.\n"
"\n"
"*** IMPORTANT NOTICE ***\n"
"A signal handler function is called with two arguments:\n"
"the first is the signal number, the second is the interrupted stack frame.");

#define SIGNAL_MYSIGNAL_METHODDEF    \
    {"mysignal", _PyCFunction_CAST(signal_mysignal), METH_FASTCALL, signal_mysignal__doc__},

static PyObject *
signal_mysignal_impl(PyObject *module, int signalnum, PyObject *handler);

static PyObject *
signal_mysignal(PyObject *module, PyObject *const *args, Py_ssize_t nargs)
{
    PyObject *return_value = NULL;
    int signalnum;
    PyObject *handler;

    if (!_PyArg_CheckPositional("mysignal", nargs, 2, 2)) {
        goto exit;
    }
    signalnum = _PyLong_AsInt(args[0]);
    if (signalnum == -1 && PyErr_Occurred()) {
        goto exit;
    }
    handler = args[1];
    return_value = signal_mysignal_impl(module, signalnum, handler);

exit:
    return return_value;
}