[Interface-dev] I want some Python 3 C coding style guidance.
Lennart Regebro
regebro at gmail.com
Thu Nov 25 05:31:37 EST 2010
I've been porting some zope.* modules that have C-extensions to Python
3, and with the C-preprocessor you have so many possibilities that I
get all confused. So I'd like some opinions. Or onions. Or something.
The big issue is the module definition, which is quite different in
Python 2 and Python 3. What I ended up with in zope.proxy was
something like this:
----------------------------------
#if PY_MAJOR_VERSION >= 3
static struct PyModuleDef moduledef = {
PyModuleDef_HEAD_INIT,
"_zope_proxy_proxy", /* m_name */
module___doc__, /* m_doc */
-1, /* m_size */
module_functions, /* m_methods */
NULL, /* m_reload */
NULL, /* m_traverse */
NULL, /* m_clear */
NULL, /* m_free */
};
#endif
static PyObject *
moduleinit(void)
{
PyObject *m;
#if PY_MAJOR_VERSION >= 3
m = PyModule_Create(&moduledef);
#else
m = Py_InitModule3("_zope_proxy_proxy",
module_functions, module___doc__);
#endif
if (m == NULL)
return NULL;
if (empty_tuple == NULL)
empty_tuple = PyTuple_New(0);
ProxyType.tp_free = _PyObject_GC_Del;
if (PyType_Ready(&ProxyType) < 0)
return NULL;
Py_INCREF(&ProxyType);
PyModule_AddObject(m, "ProxyBase", (PyObject *)&ProxyType);
if (api_object == NULL) {
api_object = PyCObject_FromVoidPtr(&wrapper_capi, NULL);
if (api_object == NULL)
return NULL;
}
Py_INCREF(api_object);
PyModule_AddObject(m, "_CAPI", api_object);
return m;
}
#if PY_MAJOR_VERSION < 3
PyMODINIT_FUNC
init_zope_proxy_proxy(void)
{
moduleinit();
}
#else
PyMODINIT_FUNC
PyInit__zope_proxy_proxy(void)
{
return moduleinit();
}
#endif
----------------------------------
As you see, there are loads of #if PY_MAJOR_VERSION >= 3 in there. And
three methods (I took this from Martin v Löwis work on zope.interface)
for the module init, as they have different profiles and names in
Python 2 and Python3.
This may be seen as quite messy, and many other compatibility issues
between 2 and 3 can be handled by defining macros and using #ifndefs.
So why not do the same for the module initialization? Said and done.
This is from zope.hookable:
----------------------------------
#if PY_MAJOR_VERSION >= 3
#define MOD_ERROR_VAL NULL
#define MOD_SUCCESS_VAL(val) val
#define MOD_INIT(name) PyMODINIT_FUNC PyInit_##name(void)
#define MOD_DEF(ob, name, doc, methods) \
static struct PyModuleDef moduledef = { \
PyModuleDef_HEAD_INIT, name, doc, -1, methods, }; \
ob = PyModule_Create(&moduledef);
#else
#define MOD_ERROR_VAL
#define MOD_SUCCESS_VAL(val)
#define MOD_INIT(name) void init##name(void)
#define MOD_DEF(ob, name, doc, methods) \
ob = Py_InitModule3(name, methods, doc);
#endif
MOD_INIT(_zope_hookable)
{
PyObject *m;
hookabletype.tp_new = PyType_GenericNew;
hookabletype.tp_free = _PyObject_GC_Del;
if (PyType_Ready(&hookabletype) < 0)
return MOD_ERROR_VAL;
MOD_DEF(m, "_zope_hookable",
"Provide an efficient implementation for hookable objects",
module_methods)
if (m == NULL) return MOD_ERROR_VAL;
if (PyModule_AddObject(m, "hookable", \
(PyObject *)&hookabletype) < 0)
return MOD_ERROR_VAL;
return MOD_SUCCESS_VAL(m);
}
----------------------------------
As you see, there is one block of macro definitions in the start, and
then just one function at the bottom. Benefits are that if you have
many C-extensions you can extract the macro definitions to a separate
file. Drawbacks are that MOD_INIT looks like a function, when it is in
fact a function definition. But I don't know, maybe C-programmers are
used to that sort of thing. :-) Also, it's far from complete, MOD_DEF
doesn't support the new module_reload and module_traverse things for
example, bt maybe that's fixable.
Which style do you prefer? I'll make zope.hookable, zope.i18nmessage
and zope.proxy use the same style if we can agree on one.
//Lennart
More information about the Interface-dev
mailing list