Convert to Cython.
authorW. Trevor King <wking@tremily.us>
Fri, 19 Oct 2012 03:30:30 +0000 (23:30 -0400)
committerW. Trevor King <wking@tremily.us>
Fri, 19 Oct 2012 03:34:29 +0000 (23:34 -0400)
With Cython we get easier memory handling and Python 3 compatibility.

Signed-off-by: W. Trevor King <wking@tremily.us>
13 files changed:
.gitignore [new file with mode: 0644]
kmod/__init__.py [new file with mode: 0644]
kmod/_libkmod_h.pxd [new file with mode: 0644]
kmod/error.py [new file with mode: 0644]
kmod/kmod.pxd [new file with mode: 0644]
kmod/kmod.pyx [new file with mode: 0644]
kmod/list.pxd [new file with mode: 0644]
kmod/list.pyx [new file with mode: 0644]
kmod/module.pxd [new file with mode: 0644]
kmod/module.pyx [new file with mode: 0644]
kmod/version.py [new file with mode: 0644]
libkmod.c [deleted file]
setup.py

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..c074348
--- /dev/null
@@ -0,0 +1,6 @@
+__pycache__
+build
+dist
+*.c
+*.pyc
+*.so
diff --git a/kmod/__init__.py b/kmod/__init__.py
new file mode 100644 (file)
index 0000000..5d3f129
--- /dev/null
@@ -0,0 +1,17 @@
+# Copyright (C) 2012 Red Hat, Inc. All rights reserved.
+#               2012 W. Trevor King
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU Lesser General Public License v.2.1.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+#
+# Author: Andy Grover <agrover redhat com>
+
+"Libkmod -- Python interface to kmod API."
+
+from .version import __version__
+from .kmod import Kmod
diff --git a/kmod/_libkmod_h.pxd b/kmod/_libkmod_h.pxd
new file mode 100644 (file)
index 0000000..975dfa3
--- /dev/null
@@ -0,0 +1,77 @@
+cdef extern from *:
+    ctypedef char* const_char_ptr 'const char *'
+    ctypedef char* const_char_const_ptr 'const char const *'
+    ctypedef void* const_void_ptr 'const void *'
+
+cdef extern from 'errno.h':
+    enum: EEXIST
+
+cdef extern from 'stdbool.h':
+    ctypedef struct bool:
+        pass
+
+
+cdef extern from 'libkmod.h':
+    # library user context - reads the config and system
+    # environment, user variables, allows custom logging
+    cdef struct kmod_ctx:
+        pass
+
+    kmod_ctx *kmod_new(
+        const_char_ptr dirname, const_char_const_ptr config_paths)
+    kmod_ctx *kmod_ref(kmod_ctx *ctx)
+    kmod_ctx *kmod_unref(kmod_ctx *ctx)
+
+    # Management of libkmod's resources
+    int kmod_load_resources(kmod_ctx *ctx)
+    void kmod_unload_resources(kmod_ctx *ctx)
+
+    # access to kmod generated lists
+    cdef struct kmod_list:
+        pass
+    ctypedef kmod_list* const_kmod_list_ptr 'const struct kmod_list *'
+    kmod_list *kmod_list_next(
+        const_kmod_list_ptr list, const_kmod_list_ptr curr)
+    kmod_list *kmod_list_prev(
+        const_kmod_list_ptr list, const_kmod_list_ptr curr)
+    kmod_list *kmod_list_last(const_kmod_list_ptr list)
+
+    # Operate on kernel modules
+    cdef struct kmod_module:
+        pass
+    ctypedef kmod_module* const_kmod_module_ptr 'const struct kmod_module *'
+    int kmod_module_new_from_name(
+        kmod_ctx *ctx, const_char_ptr name, kmod_module **mod)
+    int kmod_module_new_from_lookup(
+        kmod_ctx *ctx, const_char_ptr given_alias, kmod_list **list)
+    int kmod_module_new_from_loaded(kmod_ctx *ctx, kmod_list **list)
+
+    kmod_module *kmod_module_ref(kmod_module *mod)
+    kmod_module *kmod_module_unref(kmod_module *mod)
+    int kmod_module_unref_list(kmod_list *list)
+    kmod_module *kmod_module_get_module(kmod_list *entry)
+
+    # Flags to kmod_module_probe_insert_module
+    # codes below can be used in return value, too
+    enum: KMOD_PROBE_APPLY_BLACKLIST
+
+    #ctypedef int (*install_callback_t)(
+    #    kmod_module *m, const_char_ptr cmdline, const_void_ptr data)
+    #ctypedef void (*print_action_callback_t)(
+    #    kmod_module *m, bool install, const_char_ptr options)
+
+    int kmod_module_remove_module(
+        kmod_module *mod, unsigned int flags)
+    int kmod_module_insert_module(
+        kmod_module *mod, unsigned int flags, const_char_ptr options)
+    int kmod_module_probe_insert_module(
+        kmod_module *mod, unsigned int flags, const_char_ptr extra_options,
+        int (*run_install)(
+            kmod_module *m, const_char_ptr cmdline, void *data),
+        const_void_ptr data,
+        void (*print_action)(
+            kmod_module *m, bool install, const_char_ptr options),
+        )
+
+    const_char_ptr kmod_module_get_name(const_kmod_module_ptr mod)
+    long kmod_module_get_size(const_kmod_module_ptr mod)
diff --git a/kmod/error.py b/kmod/error.py
new file mode 100644 (file)
index 0000000..d976bb6
--- /dev/null
@@ -0,0 +1,2 @@
+class KmodError (Exception):
+    pass
diff --git a/kmod/kmod.pxd b/kmod/kmod.pxd
new file mode 100644 (file)
index 0000000..2933724
--- /dev/null
@@ -0,0 +1,5 @@
+cimport _libkmod_h
+
+cdef class Kmod (object):
+    cdef _libkmod_h.kmod_ctx *_kmod_ctx
+    cdef object mod_dir
diff --git a/kmod/kmod.pyx b/kmod/kmod.pyx
new file mode 100644 (file)
index 0000000..fdba4c6
--- /dev/null
@@ -0,0 +1,101 @@
+# Copyright (C) 2012 Red Hat, Inc. All rights reserved.
+#               2012 W. Trevor King
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU Lesser General Public License v.2.1.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+cimport cython as _cython
+cimport _libkmod_h
+from error import KmodError as _KmodError
+cimport module as _module
+import module as _module
+cimport list as _list
+import list as _list
+
+
+cdef class Kmod (object):
+    def __cinit__(self):
+        self._kmod_ctx = NULL
+
+    def __dealloc__(self):
+        self._cleanup()
+
+    def __init__(self, mod_dir=None):
+        self.set_mod_dir(mod_dir=mod_dir)
+
+    def set_mod_dir(self, mod_dir=None):
+        self.mod_dir = mod_dir
+        self._setup()
+
+    def _setup(self):
+        cdef char *mod_dir = NULL
+        self._cleanup()
+        if self.mod_dir:
+            mod_dir = self.mod_dir
+        self._kmod_ctx = _libkmod_h.kmod_new(mod_dir, NULL);
+        if self._kmod_ctx is NULL:
+            raise _KmodError('Could not initialize')
+        _libkmod_h.kmod_load_resources(self._kmod_ctx)
+
+    def _cleanup(self):
+        if self._kmod_ctx is not NULL:
+            _libkmod_h.kmod_unload_resources(self._kmod_ctx);
+            self._kmod_ctx = NULL
+
+    def loaded(self):
+        "iterate through currently loaded modules"
+        cdef _list.ModList ml = _list.ModList()
+        cdef _list.ModListItem mli
+        err = _libkmod_h.kmod_module_new_from_loaded(self._kmod_ctx, &ml.list)
+        if err < 0:
+            raise _KmodError('Could not get loaded modules')
+        for item in ml:
+            mli = <_list.ModListItem> item
+            mod = _module.Module()
+            mod.from_mod_list_item(item)
+            yield mod
+
+    def lookup(self, alias_name, flags=_libkmod_h.KMOD_PROBE_APPLY_BLACKLIST):
+        "iterate through modules matching `alias_name`"
+        cdef _list.ModList ml = _list.ModList()
+        cdef _list.ModListItem mli
+        if hasattr(alias_name, 'encode'):
+            alias_name = alias_name.encode('ascii')
+        err = _libkmod_h.kmod_module_new_from_lookup(
+            self._kmod_ctx, alias_name, &ml.list)
+        if err < 0:
+            raise _KmodError('Could not modprobe')
+        for item in ml:
+            mli = <_list.ModListItem> item
+            mod = _module.Module()
+            mod.from_mod_list_item(item)
+            yield mod
+
+    @_cython.always_allow_keywords(True)
+    def module_from_name(self, name):
+        cdef _module.Module mod = _module.Module()
+        if hasattr(name, 'encode'):
+            name = name.encode('ascii')
+        err = _libkmod_h.kmod_module_new_from_name(
+            self._kmod_ctx, name, &mod.module)
+        if err < 0:
+            raise _KmodError('Could not get module')
+        return mod
+
+    def list(self):
+        "iterate through currently loaded modules and sizes"
+        for mod in self.loaded():
+            yield (mod.name, mod.size)
+
+    def modprobe(self, alias_name, *args, **kwargs):
+        for mod in self.lookup(alias_name=alias_name):
+            mod.insert(*args, **kwargs)
+
+    def rmmod(self, module_name, *args, **kwargs):
+       mod = self.module_from_name(name=module_name)
+       mod.remove(*args, **kwargs)
diff --git a/kmod/list.pxd b/kmod/list.pxd
new file mode 100644 (file)
index 0000000..6c3725b
--- /dev/null
@@ -0,0 +1,9 @@
+cimport _libkmod_h
+
+
+cdef class ModListItem (object):
+    cdef _libkmod_h.kmod_list *list
+
+
+cdef class ModList (ModListItem):
+    cdef _libkmod_h.kmod_list *_next
diff --git a/kmod/list.pyx b/kmod/list.pyx
new file mode 100644 (file)
index 0000000..e2bf04f
--- /dev/null
@@ -0,0 +1,29 @@
+cimport _libkmod_h
+
+
+cdef class ModListItem (object):
+    "Wrap a struct kmod_list* list item"
+    def __cinit__(self):
+        self.list = NULL
+
+
+cdef class ModList (ModListItem):
+    "Wrap a struct kmod_list* list with iteration"
+    def __cinit__(self):
+        self._next = NULL
+
+    def __dealloc__(self):
+        if self.list is not NULL:
+            _libkmod_h.kmod_module_unref_list(self.list)
+
+    def __iter__(self):
+        self._next = self.list
+        return self
+
+    def __next__(self):
+        if self._next is NULL:
+            raise StopIteration()
+        mli = ModListItem()
+        mli.list = self._next
+        self._next = _libkmod_h.kmod_list_next(self.list, self._next)
+        return mli
diff --git a/kmod/module.pxd b/kmod/module.pxd
new file mode 100644 (file)
index 0000000..3cc4d63
--- /dev/null
@@ -0,0 +1,8 @@
+cimport _libkmod_h
+cimport list as _list
+
+
+cdef class Module (object):
+    cdef _libkmod_h.kmod_module *module
+
+    cpdef from_mod_list_item(self, _list.ModListItem item)
diff --git a/kmod/module.pyx b/kmod/module.pyx
new file mode 100644 (file)
index 0000000..1e66772
--- /dev/null
@@ -0,0 +1,65 @@
+import sys as _sys
+
+cimport _libkmod_h
+from error import KmodError as _KmodError
+cimport list as _list
+import list as _list
+
+
+cdef class Module (object):
+    "Wrap a struct kmod_module* item"
+    def __cinit__(self):
+        self.module = NULL
+
+    def __dealloc__(self):
+        self._cleanup()
+
+    def _cleanup(self):
+        if self.module is not NULL:
+            _libkmod_h.kmod_module_unref(self.module)
+            self.module = NULL
+
+    cpdef from_mod_list_item(self, _list.ModListItem item):
+        self._cleanup()
+        self.module = _libkmod_h.kmod_module_get_module(item.list)
+
+    def _name_get(self):
+        name = _libkmod_h.kmod_module_get_name(self.module)
+        if _sys.version_info >= (3,):  # Python 3
+            name = str(name, 'ascii')
+        else:  # Python 2
+            name = unicode(name, 'ascii')
+        return name
+    name = property(fget=_name_get)
+
+    def _size_get(self):
+        return _libkmod_h.kmod_module_get_size(self.module)
+    size = property(fget=_size_get)
+
+    def insert(self, flags=0, extra_options=None, install_callback=None,
+               data=None, print_action_callback=None):
+        cdef char *opt = NULL
+        #cdef _libkmod_h.install_callback_t install = NULL
+        cdef int (*install)(
+            _libkmod_h.kmod_module *, _libkmod_h.const_char_ptr, void *)
+        install = NULL
+        cdef void *d = NULL
+        #cdef _libkmod_h.print_action_callback_t print_action = NULL
+        cdef void (*print_action)(
+            _libkmod_h.kmod_module *, _libkmod_h.bool,
+            _libkmod_h.const_char_ptr)
+        print_action = NULL
+        if extra_options:
+            opt = extra_options
+        # TODO: convert callbacks and data from Python object to C types
+        err = _libkmod_h.kmod_module_probe_insert_module(
+            self.module, flags, opt, install, d, print_action)
+        if err == -_libkmod_h.EEXIST:
+            raise _KmodError('Module already loaded')
+        elif err < 0:
+            raise _KmodError('Could not load module')
+
+    def remove(self, flags=0):
+        err = _libkmod_h.kmod_module_remove_module(self.module, flags)
+        if err < 0:
+            raise _KmodError('Could not remove module')
diff --git a/kmod/version.py b/kmod/version.py
new file mode 100644 (file)
index 0000000..11d27f8
--- /dev/null
@@ -0,0 +1 @@
+__version__ = '0.1'
diff --git a/libkmod.c b/libkmod.c
deleted file mode 100644 (file)
index 82f0de8..0000000
--- a/libkmod.c
+++ /dev/null
@@ -1,269 +0,0 @@
-/*
- * Libkmod -- Python interface to kmod API.
- *
- * Copyright (C) 2012 Red Hat, Inc. All rights reserved.
- *
- * This copyrighted material is made available to anyone wishing to use,
- * modify, copy, or redistribute it subject to the terms and conditions
- * of the GNU Lesser General Public License v.2.1.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- *
- * Author: Andy Grover <agrover redhat com>
- *
- */
-
-#include <Python.h>
-#include <libkmod.h>
-
-typedef struct {
-    PyObject_HEAD
-    struct kmod_ctx *ctx;
-} kmodobject;
-
-static PyTypeObject KmodObjType;
-
-static PyObject *KmodError;
-
-/* ----------------------------------------------------------------------
- * kmod toplevel module methods
- */
-static PyObject *
-kmod_library_get_version(PyObject *self, PyObject *unused_args)
-{
-    return Py_BuildValue("s", "0.1");
-}
-
-/* ----------------------------------------------------------------------
- * Kmod object initialization/deallocation
- */
-static int
-kmod_obj_init(PyObject *self, PyObject *args, PyObject *kwds)
-{
-    kmodobject *kmod = (kmodobject *)self;
-    char *mod_dir = NULL;
-
-    if (!PyArg_ParseTuple(args, "|s", &mod_dir))
-       return -1;
-
-    /* init can be called multiple times */
-    if (kmod->ctx) {
-        kmod_unload_resources(kmod->ctx);
-        kmod_unref(kmod->ctx);
-    }
-
-    kmod->ctx = kmod_new(mod_dir, NULL);
-    if (!kmod->ctx) {
-        PyErr_SetString(KmodError, "Could not initialize");
-        return -1;
-    }
-
-    kmod_load_resources(kmod->ctx);
-
-    return 0;
-}
-
-static void
-kmod_obj_dealloc(PyObject *self)
-{
-    kmodobject *kmod = (kmodobject *)self;
-
-    kmod_unload_resources(kmod->ctx);
-
-    /* if already closed, don't reclose it */
-    if (kmod->ctx != NULL){
-            kmod_unref(kmod->ctx);
-    }
-    //self->ob_type->tp_free((PyObject*)self);
-    PyObject_Del(self);
-}
-
-/*
- * list currently loaded modules and sizes
- */
-static PyObject *
-kmod_obj_loaded_modules(PyObject *self, PyObject *unused_args)
-{
-    kmodobject *kmod = (kmodobject *)self;
-    struct kmod_list *list, *itr;
-    int err;
-
-    err = kmod_module_new_from_loaded(kmod->ctx, &list);
-    if (err < 0) {
-        PyErr_SetString(KmodError, "Could not get loaded modules");
-        return NULL;
-    }
-
-    PyObject *pylist = PyList_New(0);
-    if (!pylist) {
-        kmod_module_unref_list(list);
-        return PyErr_NoMemory();
-    }
-
-    /* refcountapallooza. */
-    kmod_list_foreach(itr, list) {
-        struct kmod_module *mod = kmod_module_get_module(itr);
-        const char *name = kmod_module_get_name(mod);
-        long size = kmod_module_get_size(mod);
-
-        PyObject *entry = Py_BuildValue("(sl)", name, size);
-        if (!entry) {
-            Py_DECREF(pylist);
-            kmod_module_unref(mod);
-            kmod_module_unref_list(list);
-            return NULL;
-        }
-
-        if (PyList_Append(pylist, entry) == -1) {
-            Py_DECREF(entry);
-            Py_DECREF(pylist);
-            kmod_module_unref(mod);
-            kmod_module_unref_list(list);
-            return NULL;
-        }
-
-        Py_DECREF(entry);
-        kmod_module_unref(mod);
-    }
-    kmod_module_unref_list(list);
-
-    return pylist;
-}
-
-static PyObject *
-kmod_obj_modprobe(PyObject *self, PyObject *args)
-{
-    kmodobject *kmod = (kmodobject *)self;
-    struct kmod_list *list = NULL, *itr;
-    struct kmod_module *mod;
-    char *alias_name;
-    unsigned int flags = KMOD_PROBE_APPLY_BLACKLIST;
-    int err;
-
-    if (!PyArg_ParseTuple(args, "s", &alias_name))
-       return NULL;
-
-    err = kmod_module_new_from_lookup(kmod->ctx, alias_name, &list);
-    if (err < 0) {
-        PyErr_SetString(KmodError, "Could not modprobe");
-        return NULL;
-    }
-
-    kmod_list_foreach(itr, list) {
-        mod = kmod_module_get_module(itr);
-
-        err = kmod_module_probe_insert_module(mod, flags,
-            NULL, NULL, NULL, NULL);
-
-        if (err < 0) {
-            if (err == -EEXIST) {
-                PyErr_SetString(KmodError, "Module already loaded");
-                    goto err;
-            } else {
-                PyErr_SetString(KmodError, "Could not load module");
-                    goto err;
-            }
-        }
-
-        kmod_module_unref(mod);
-    }
-    kmod_module_unref_list(list);
-
-    Py_INCREF(Py_None);
-    return Py_None;
-
-err:
-    kmod_module_unref(mod);
-    kmod_module_unref_list(list);
-    return NULL;
-}
-
-static PyObject *
-kmod_obj_rmmod(PyObject *self, PyObject *args)
-{
-    kmodobject *kmod = (kmodobject *)self;
-    struct kmod_module *mod;
-    char *module_name;
-    int err;
-
-    if (!PyArg_ParseTuple(args, "s", &module_name))
-        return NULL;
-
-    err = kmod_module_new_from_name(kmod->ctx, module_name, &mod);
-    if (err < 0) {
-        PyErr_SetString(KmodError, "Could  get module");
-        return NULL;
-    }
-
-    err = kmod_module_remove_module(mod, 0);
-    if (err < 0) {
-        PyErr_SetString(KmodError, "Could not remove module");
-        kmod_module_unref(mod);
-        return NULL;
-    }
-    kmod_module_unref(mod);
-
-    Py_INCREF(Py_None);
-    return Py_None;
-}
-
-/* ----------------------------------------------------------------------
- * Method tables and other bureaucracy
- */
-
-static PyMethodDef kmod_lib_methods[] = {
-    {"getVersion", kmod_library_get_version, METH_NOARGS },
-    {NULL, NULL}           /* sentinel */
-};
-
-static PyMethodDef kmod_obj_methods[] = {
-    {"loaded_modules", kmod_obj_loaded_modules, METH_NOARGS,
-    "List loaded kernel modules, and their sizes"},
-    {"modprobe", kmod_obj_modprobe, METH_VARARGS,
-    "Load a kernel module"},
-    {"rmmod", kmod_obj_rmmod, METH_VARARGS,
-    "Unload a kernel module"},
-    {NULL, NULL}           /* sentinel */
-};
-
-
-static PyTypeObject KmodObjType = {
-    PyObject_HEAD_INIT(NULL)
-    .tp_name = "kmod.Kmod",
-    .tp_basicsize = sizeof(kmodobject),
-    .tp_new = PyType_GenericNew,
-    .tp_init = kmod_obj_init,
-    .tp_dealloc = kmod_obj_dealloc,
-    .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
-    .tp_doc = "kmod.Kmod object",
-    .tp_methods = kmod_obj_methods,
-};
-
-#ifndef PyMODINIT_FUNC    /* declarations for DLL import/export */
-#define PyMODINIT_FUNC void
-#endif
-PyMODINIT_FUNC
-initkmod(void)
-{
-    PyObject *m;
-
-    if (PyType_Ready(&KmodObjType) < 0)
-        return;
-
-    m = Py_InitModule3("kmod", kmod_lib_methods, "kmod module");
-    if (!m)
-        return;
-
-    Py_INCREF(&KmodObjType);
-    PyModule_AddObject(m, "Kmod", (PyObject *)&KmodObjType);
-
-    KmodError = PyErr_NewException("kmod.KmodError",
-                                   NULL, NULL);
-    if (KmodError) {
-        /* Each call to PyModule_AddObject decrefs it; compensate: */
-        Py_INCREF(KmodError);
-        PyModule_AddObject(m, "KmodError", KmodError);
-    }
-}
index 09c6675daeee9fa2823bec6c92ef31e8b85d92e3..301967c481b77751ffc81f8604dea14cd5addc9a 100644 (file)
--- a/setup.py
+++ b/setup.py
@@ -1,10 +1,38 @@
-from distutils.core import setup, Extension
+from distutils.core import setup
+from distutils.extension import Extension as _Extension
+import os as _os
+import sys as _sys
 
-libkmod = Extension('kmod',
-                    sources = ['libkmod.c'],
-                    libraries= ['kmod'])
+from Cython.Distutils import build_ext as _build_ext
 
-setup (name = 'kmod',
-       version = '0.1',
-       description = 'Python binding for kmod',
-       ext_modules = [libkmod])
+
+package_name = 'kmod'
+
+# read version from local kmod/version.py without pulling in
+# kmod/__init__.py
+_sys.path.insert(0, package_name)
+from version import __version__
+
+
+_this_dir = _os.path.dirname(__file__)
+
+ext_modules = []
+for filename in sorted(_os.listdir(package_name)):
+    basename,extension = _os.path.splitext(filename)
+    if extension == '.pyx':
+        ext_modules.append(
+            _Extension(
+                '{}.{}'.format(package_name, basename),
+                [_os.path.join(package_name, filename)],
+                libraries=['kmod'],
+                ))
+
+setup(
+    name=package_name,
+    version=__version__,
+    description='Python binding for kmod',
+    packages=[package_name],
+    provides=[package_name],
+    cmdclass = {'build_ext': _build_ext},
+    ext_modules=ext_modules,
+    )