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 = {
        "_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 */

    static PyObject *
        PyObject *m;
    #if PY_MAJOR_VERSION >= 3
        m = PyModule_Create(&moduledef);
        m = Py_InitModule3("_zope_proxy_proxy",
                            module_functions, module___doc__);

        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;

        PyModule_AddObject(m, "ProxyBase", (PyObject *)&ProxyType);

        if (api_object == NULL) {
            api_object = PyCObject_FromVoidPtr(&wrapper_capi, NULL);
            if (api_object == NULL)
            return NULL;
        PyModule_AddObject(m, "_CAPI", api_object);
      return m;

    #if PY_MAJOR_VERSION < 3
            return moduleinit();


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);
        #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);

        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",

        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.

Zope-Dev maillist  -  Zope-Dev@zope.org
**  No cross posts or HTML encoding!  **
(Related lists - 
 https://mail.zope.org/mailman/listinfo/zope )

Reply via email to