Using PyModule_AddIntConstant() in an extension

Simon Source

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.

pythoncpython-3.xpython-c-api

Answers

answered 1 week ago CristiFati #1

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.

  • Close every running interpreter that imported your module (to "unlock" WinUtils.cp36-win32.pyd)
  • Build (this time, the linker will be able to overwrite the file)
  • Test (run your Python code)

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

@EDIT0: (regardng the 2nd problem)

As I pecified in my one of my comments, use PyModule_Create (as according to [Python]: 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;
}

comments powered by Disqus