pythoncpython-3.xpython-c-api

Using PyModule_AddIntConstant() in an extension


I have seen Adding symbolic constants with hex values to Python extension module and I am trying to reproduce this effect:

#include <Python.h>
#include <Windows.h>

static PyObject * sys_shutdown(PyObject *self, PyObject *args) {
    int val;

    if (!PyArg_ParseTuple(args, "i", &val))
        val = SHTDN_REASON_MINOR_OTHER; // Provide failsafe

    ExitWindowsEx(EWX_POWEROFF, val); // Shutdown
    return Py_BuildValue("");
}

static PyObject * sys_restart(PyObject *self, PyObject *args) {
    int val;

    if (!PyArg_ParseTuple(args, "i", &val))
        val = SHTDN_REASON_MINOR_OTHER; // Provide failsafe
    ExitWindowsEx(EWX_REBOOT, val); // Restart
    return Py_BuildValue("");
}

static PyObject * sys_log_out(PyObject *self, PyObject *args) {
    int val;

    if (!PyArg_ParseTuple(args, "i", &val))
        val = SHTDN_REASON_MINOR_OTHER; // Provide failsafe

    ExitWindowsEx(EWX_LOGOFF, val); // Log out
    return Py_BuildValue("");
}

static PyMethodDef localMethods[] = {
    {"shutdown", (PyCFunction)sys_shutdown, METH_VARARGS, "..."},
    {"restart", (PyCFunction)sys_restart, METH_VARARGS, "..."},
    {"log_out", (PyCFunction)sys_log_out, METH_VARARGS, "..."},
    {NULL, NULL, 0, NULL}
};

static struct PyModuleDef func = {
    PyModuleDef_HEAD_INIT,
    "utilities",
    "...",
    -1,
    localMethods,
};

PyMODINIT_FUNC PyInit_utilities(void) {
    PyObject *value;

    value = PyModule_New(&func);

    PyModule_AddIntConstant(value, "DEFINED", AF_INET);

    return PyModule_Create(&func);
}

Setup Script:

from distutils.core import setup, Extension

module = Extension(
    "utilities", 
    sources = ["main.c"],
        libraries = ["user32"]
)

setup (
    name = "Utilities",
    version = "1.0",
    ext_modules = [module])

Everything builds as expected, however I cannot use DEFINED in my extension:

import utilities
for i in utilities.__dict__: print(i)
utilities.DEFINED # AttributeError: module 'utilities' has no attribute 'DEFINED'

Returns:

__name__
__doc__
__package__
__loader__
__spec__
shutdown
restart
log_out
__file__

I thought of returning value like so:

return PyModule_Create(&value);

But that returns:

LINK : fatal error LNK1104: cannot open file 'build\lib.win32-3.6\WinUtils.cp36-win32.pyd' error: command 'C:\Program Files (x86)\Microsoft Visual Studio\2017\WDExpress\VC\Tools\MSVC\14.14.26428\bin\HostX86\x86\link.exe' failed with exit status 1104

How can I add the DEFINED value to my extension (so that I can run utilities.DEFINED)?

Edit:

As mentioned in the answer below closing everything and trying again builds the extension successfully, however using return PyModule_Create(&value); still crashes.


Solution

  • PyModule_AddIntConstant(value, "DEFINED", DEFINED_VALUE); is correct (assuming that DEFINED_VALUE is a (C) long).

    That, combined with the linker error at the end (and with the fact that you're writing code then testing, and so on ...) tells me that the linker is not able to write the new .pyd file (that contains the latest changes - including DEFINED variable), because it's in use by a previously started python.exe process that imported your module.

    Note: you could check the function return value ([Python.Docs]: int PyModule_AddIntConstant(PyObject *module, const char *name, long value)).

    Update #0 (regarding the 2nd problem):

    As I specified in my one of my comments, use PyModule_Create (as according to [Python.Docs]: PyObject* PyModule_New(const char *name), you're getting Undefined Behavior):

    PyMODINIT_FUNC PyInit_utilities()
    {
        PyObject *mod = PyModule_Create(&func);
        PyModule_AddIntConstant(mod, "DEFINED", AF_INET);
        return mod;
    }