I want some Python 3 C coding style guidance.

Lennart Regebro <[email protected]>
Newsgroups gmane.comp.python.zope.interface,gmane.comp.web.zope.devel
Message-ID <[email protected]>
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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.