7. Setting and Getting Module Globals¶
This section describes how you create and access module globals from Python C Extensions.
In this module, written as a Python extension in C, we are going to have a string, int, list, tuple and dict in global scope. In the C code we firstly define names for them:
const char *NAME_INT = "INT";
const char *NAME_STR = "STR";
const char *NAME_LST = "LST";
const char *NAME_TUP = "TUP";
const char *NAME_MAP = "MAP";
These are the names of the objects that will appear in the Python module:
>>> import cModuleGlobals
>>> dir(cModuleGlobals)
['INT', 'LST', 'MAP', 'STR', 'TUP', '__doc__', '__file__', '__loader__', '__name__', '__package__', 'print']
7.1. Initialising Module Globals¶
This is the module declaration, it will be called cModuleGlobals
and has just one function; print()
that will access the module globals from C:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | static PyMethodDef cModuleGlobals_methods[] = {
{"print", (PyCFunction)_print_globals, METH_NOARGS,
"Access and print out th globals."
},
{NULL, NULL, 0, NULL} /* Sentinel */
};
static PyModuleDef cModuleGlobals_module = {
PyModuleDef_HEAD_INIT,
"cModuleGlobals",
"Examples of global values in a module.",
-1,
cModuleGlobals_methods, /* cModuleGlobals_methods */
NULL, /* inquiry m_reload */
NULL, /* traverseproc m_traverse */
NULL, /* inquiry m_clear */
NULL, /* freefunc m_free */
};
|
The module initialisation code is next, this uses the Python C API to create the various global objects:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | PyMODINIT_FUNC
PyInit_cModuleGlobals(void)
{
PyObject *m = NULL;
m = PyModule_Create(&cModuleGlobals_module);
if (m == NULL) {
goto except;
}
/* Adding module globals */
if (PyModule_AddIntConstant(m, NAME_INT, 42)) {
goto except;
}
if (PyModule_AddStringConstant(m, NAME_STR, "String value")) {
goto except;
}
if (PyModule_AddObject(m, NAME_TUP, Py_BuildValue("iii", 66, 68, 73))) {
goto except;
}
if (PyModule_AddObject(m, NAME_LST, Py_BuildValue("[iii]", 66, 68, 73))) {
goto except;
}
/* An invented convenience function for this dict. */
if (_add_map_to_module(m)) {
goto except;
}
goto finally;
except:
Py_XDECREF(m);
m = NULL;
finally:
return m;
}
|
The dict is added in a separate C function merely for readability:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | /* Add a dict of {str : int, ...}.
* Returns 0 on success, 1 on failure.
*/
int _add_map_to_module(PyObject *module) {
int ret = 0;
PyObject *pMap = NULL;
pMap = PyDict_New();
if (! pMap) {
goto except;
}
/* Load map. */
if (PyDict_SetItem(pMap, PyBytes_FromString("66"), PyLong_FromLong(66))) {
goto except;
}
if (PyDict_SetItem(pMap, PyBytes_FromString("123"), PyLong_FromLong(123))) {
goto except;
}
/* Add map to module. */
if (PyModule_AddObject(module, NAME_MAP, pMap)) {
goto except;
}
ret = 0;
goto finally;
except:
Py_XDECREF(pMap);
ret = 1;
finally:
return ret;
}
|
7.2. Getting and Setting Module Globals¶
7.2.1. From Python¶
Once the module is built we can access the globals from Python as usual:
1 2 3 4 5 6 7 8 9 10 11 12 13 | >>> import cModuleGlobals
>>> dir(cModuleGlobals)
['INT', 'LST', 'MAP', 'STR', 'TUP', '__doc__', '__file__', '__loader__', '__name__', '__package__', 'print']
>>> cModuleGlobals.STR
'String value'
>>> cModuleGlobals.STR = 'F'
>>> cModuleGlobals.STR
'F'
>>> cModuleGlobals.MAP
{b'123': 123, b'66': 66}
>>> cModuleGlobals.MAP[b'asd'] = 9
>>> cModuleGlobals.MAP
{b'123': 123, b'asd': 9, b'66': 66}
|
7.2.2. Getting Module Globals From C¶
Accessing Python module globals from C is a little bit more tedious as we are getting borrowed references from the modules __dict__
and we should increment and decrement them appropriately. Here we print out the global INT
as both a Python object and a ‘C’ long
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | static PyObject *_print_global_INT(PyObject *pMod) {
PyObject *ret = NULL;
PyObject *pItem = NULL;
long val;
/* Sanity check. */
assert(pMod);
assert(PyModule_CheckExact(pMod));
assert(! PyErr_Occurred());
/* NOTE: PyModule_GetDict(pMod); never fails and returns a borrowed
* reference. pItem is NULL or a borrowed reference.
*/
pItem = PyDict_GetItemString(PyModule_GetDict(pMod), NAME_INT);
if (! pItem) {
PyErr_Format(PyExc_AttributeError,
"Module '%s' has no attibute '%s'.", \
PyModule_GetName(pMod), NAME_INT
);
goto except;
}
Py_INCREF(pItem);
fprintf(stdout, "Integer: \"%s\" ", NAME_INT);
PyObject_Print(pItem, stdout, 0);
val = PyLong_AsLong(pItem);
fprintf(stdout, " C long: %ld ", val);
fprintf(stdout, "\n");
assert(! PyErr_Occurred());
Py_INCREF(Py_None);
ret = Py_None;
goto finally;
except:
assert(PyErr_Occurred());
Py_XDECREF(ret);
ret = NULL;
finally:
Py_DECREF(pItem);
return ret;
}
|
From Python we would see this (C’s _print_global_INT()
is mapped to Python’s cModuleGlobals.printINT()
):
>>> import cModuleGlobals
>>> cModuleGlobals.printINT()
Module:
<module 'cModuleGlobals' from './cModuleGlobals.so'>
Integer: "INT" 42 C long: 42
7.2.3. Setting Module Globals From C¶
This is similar to the get code above but using int PyDict_SetItemString(PyObject *p, const char *key, PyObject *val)
where val will be a stolen reference:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | static PyObject *some_set_function(PyObject *pMod) {
PyObject *ret = NULL;
long val = ...; /* Some computed value. */
if (PyDict_SetItemString(PyModule_GetDict(pMod), NAME_INT, PyLong_FromLong(val))) {
PyErr_Format(PyExc_AttributeError,
"Can not set Module '%s' attibute '%s'.", \
PyModule_GetName(pMod), NAME_INT
);
goto except;
}
assert(! PyErr_Occurred());
Py_INCREF(Py_None);
ret = Py_None;
goto finally;
except:
assert(PyErr_Occurred());
Py_XDECREF(ret);
ret = NULL;
finally:
return ret;
}
|