about summary refs log tree commit diff
path: root/extra/python
diff options
context:
space:
mode:
Diffstat (limited to 'extra/python')
-rw-r--r--extra/python/CMakeLists.txt46
-rw-r--r--extra/python/README.rst42
-rw-r--r--extra/python/benchmark/test_benchmarks.py45
-rwxr-xr-xextra/python/example.py21
-rw-r--r--extra/python/immer/__init__.py2
m---------extra/python/lib/pybind110
-rw-r--r--extra/python/src/immer-boost.cpp80
-rw-r--r--extra/python/src/immer-pybind.cpp77
-rw-r--r--extra/python/src/immer-raw.cpp402
9 files changed, 715 insertions, 0 deletions
diff --git a/extra/python/CMakeLists.txt b/extra/python/CMakeLists.txt
new file mode 100644
index 000000000000..11d5fff209ad
--- /dev/null
+++ b/extra/python/CMakeLists.txt
@@ -0,0 +1,46 @@
+
+option(USE_PYBIND "bind with pybind1" off)
+option(USE_BOOST_PYTHON  "bind with boost::python" off)
+
+if (USE_PYBIND)
+  set(PYBIND11_CPP_STANDARD -std=c++14)
+  find_package(Boost 1.56 REQUIRED)
+  add_subdirectory(lib/pybind11)
+  pybind11_add_module(immer_python_module src/immer-pybind.cpp)
+  target_link_libraries(immer_python_module PUBLIC
+    immer)
+
+elseif(USE_BOOST_PYTHON)
+  find_package(PythonInterp)
+  find_package(PythonLibs)
+  find_package(Boost 1.56 COMPONENTS python)
+  python_add_module(immer_python_module src/immer-boost.cpp)
+  include_directories(immer_python_module PUBLIC
+    ${immer_include_dir}
+    ${Boost_INCLUDE_DIRS}
+    ${PYTHON_INCLUDE_DIRS})
+  target_link_libraries(immer_python_module PUBLIC
+    immer
+    ${Boost_LIBRARIES}
+    ${PYTHON_LIBRARIES})
+
+else()
+  find_package(PythonInterp)
+  find_package(PythonLibs)
+
+  if (NOT PYTHONLIBS_FOUND)
+    message(STATUS "Disabling Python modules")
+    return()
+  endif()
+
+  python_add_module(immer_python_module EXCLUDE_FROM_ALL
+    src/immer-raw.cpp)
+  target_include_directories(immer_python_module PUBLIC
+    ${PYTHON_INCLUDE_DIRS})
+  target_link_libraries(immer_python_module PUBLIC
+    immer
+    ${PYTHON_LIBRARIES})
+
+endif()
+
+add_custom_target(python DEPENDS immer_python_module)
diff --git a/extra/python/README.rst b/extra/python/README.rst
new file mode 100644
index 000000000000..7447e99e6281
--- /dev/null
+++ b/extra/python/README.rst
@@ -0,0 +1,42 @@
+
+Python bindings
+===============
+
+This library includes experimental bindings bring efficient immutable
+vectors for the Python language.  They were developed as part of the
+research for the `ICFP'17 paper`_.  The interface is quite
+**incomplete**, yet you can already do some things like:
+
+.. literalinclude:: ../extra/python/example.py
+   :language: python
+   :start-after: intro/start
+   :end-before:  intro/end
+..
+
+    **Do you want to help** making these bindings complete and production
+    ready?  Drop a line at `immer@sinusoid.al
+    <mailto:immer@sinusoid.al>`_ or `open an issue on Github
+    <https://github.com/arximboldi/immer>`_
+
+Installation
+------------
+::
+
+    pip install --user git+https://github.com/arximboldi/immer.git
+
+Benchmarks
+----------
+
+The library includes a set of benchmarks that compare it to
+`pyrsistent <https://github.com/tobgu/pyrsistent>`_.  You can see the
+results in the `ICFP'17 paper`_.  If you want to run them yourself,
+you need to install some dependencies::
+
+     pip install --user pytest-benchmark pyrsistent
+
+Then you need to clone the `project repository
+<https://github.com/arximboldi/immer>`_ and from its root, run::
+
+     pytest extra/python/benchmark
+
+.. _ICFP'17 paper: https://public.sinusoid.es/misc/immer/immer-icfp17.pdf
diff --git a/extra/python/benchmark/test_benchmarks.py b/extra/python/benchmark/test_benchmarks.py
new file mode 100644
index 000000000000..11e4817c3256
--- /dev/null
+++ b/extra/python/benchmark/test_benchmarks.py
@@ -0,0 +1,45 @@
+
+# immer: immutable data structures for C++
+# Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+#
+# This software is distributed under the Boost Software License, Version 1.0.
+# See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+
+##
+
+import immer
+import pyrsistent
+
+BENCHMARK_SIZE = 1000
+
+def push(v, n=BENCHMARK_SIZE):
+    for x in xrange(n):
+        v = v.append(x)
+    return v
+
+def assoc(v):
+    for i in xrange(len(v)):
+        v = v.set(i, i+1)
+    return v
+
+def index(v):
+    for i in xrange(len(v)):
+        v[i]
+
+def test_push_immer(benchmark):
+    benchmark(push, immer.Vector())
+
+def test_push_pyrsistent(benchmark):
+    benchmark(push, pyrsistent.pvector())
+
+def test_assoc_immer(benchmark):
+    benchmark(assoc, push(immer.Vector()))
+
+def test_assoc_pyrsistent(benchmark):
+    benchmark(assoc, push(pyrsistent.pvector()))
+
+def test_index_immer(benchmark):
+    benchmark(index, push(immer.Vector()))
+
+def test_index_pyrsistent(benchmark):
+    benchmark(index, push(pyrsistent.pvector()))
diff --git a/extra/python/example.py b/extra/python/example.py
new file mode 100755
index 000000000000..36f7333c4f0f
--- /dev/null
+++ b/extra/python/example.py
@@ -0,0 +1,21 @@
+#!/usr/bin/env python##
+
+# immer: immutable data structures for C++
+# Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+#
+# This software is distributed under the Boost Software License, Version 1.0.
+# See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+
+
+# include:intro/start
+import immer
+
+v0 = immer.Vector().append(13).append(42)
+assert v0[0] == 13
+assert v0[1] == 42
+assert len(v0) == 2
+
+v1 = v0.set(0, 12)
+assert v0.tolist() == [13, 42]
+assert v1.tolist() == [12, 42]
+# include:intro/end
diff --git a/extra/python/immer/__init__.py b/extra/python/immer/__init__.py
new file mode 100644
index 000000000000..e4702b40e4a1
--- /dev/null
+++ b/extra/python/immer/__init__.py
@@ -0,0 +1,2 @@
+
+from immer_python_module import *
diff --git a/extra/python/lib/pybind11 b/extra/python/lib/pybind11
new file mode 160000
+Subproject 1eaacd19f6de9a053570c21de6d173efc2304bc
diff --git a/extra/python/src/immer-boost.cpp b/extra/python/src/immer-boost.cpp
new file mode 100644
index 000000000000..0ea42354b6b1
--- /dev/null
+++ b/extra/python/src/immer-boost.cpp
@@ -0,0 +1,80 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include <boost/python.hpp>
+#include <boost/python/suite/indexing/vector_indexing_suite.hpp>
+
+#include <immer/vector.hpp>
+#include <immer/refcount/unsafe_refcount_policy.hpp>
+
+namespace {
+
+struct heap_t
+{
+    template <typename ...Tags>
+    static void* allocate(std::size_t size, Tags...)
+    {
+        return PyMem_Malloc(size);
+    }
+
+    template <typename ...Tags>
+    static void deallocate(std::size_t, void* obj, Tags...)
+    {
+        PyMem_Free(obj);
+    }
+};
+
+using memory_t = immer::memory_policy<
+    immer::unsafe_free_list_heap_policy<heap_t>,
+    immer::unsafe_refcount_policy>;
+
+template <typename Vector>
+struct immer_vector_indexing_suite : boost::python::vector_indexing_suite<
+    Vector, true, immer_vector_indexing_suite<Vector>>
+{
+    using value_t = typename Vector::value_type;
+    using index_t = typename Vector::size_type;
+    using object_t = boost::python::object;
+
+    static void forbidden() { throw std::runtime_error{"immutable!"}; }
+    static void todo() { throw std::runtime_error{"TODO!"}; }
+
+    static const value_t& get_item(const Vector& v, index_t i) { return v[i]; }
+    static object_t get_slice(const Vector&, index_t, index_t) { todo(); }
+
+    static void set_item(const Vector&, index_t, const value_t&) { forbidden(); }
+    static void delete_item(const Vector&, index_t) { forbidden(); }
+    static void set_slice(const Vector&, index_t, index_t, const value_t&) { forbidden(); }
+    static void delete_slice(const Vector&, index_t, index_t) { forbidden(); }
+    template <typename Iter>
+    static void set_slice(const Vector&, index_t, index_t, Iter, Iter) { forbidden(); }
+    template <class Iter>
+    static void extend(const Vector& container, Iter, Iter) { forbidden(); }
+};
+
+} // anonymous namespace
+
+
+BOOST_PYTHON_MODULE(immer_python_module)
+{
+    using namespace boost::python;
+
+    using vector_t = immer::vector<object, memory_t>;
+
+    class_<vector_t>("Vector")
+        .def(immer_vector_indexing_suite<vector_t>())
+        .def("append",
+             +[] (const vector_t& v, object x) {
+                 return v.push_back(std::move(x));
+              })
+        .def("set",
+             +[] (const vector_t& v, std::size_t i, object x) {
+                 return v.set(i, std::move(x));
+              })
+        ;
+}
diff --git a/extra/python/src/immer-pybind.cpp b/extra/python/src/immer-pybind.cpp
new file mode 100644
index 000000000000..8f8aab1231f8
--- /dev/null
+++ b/extra/python/src/immer-pybind.cpp
@@ -0,0 +1,77 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+#include <pybind11/pybind11.h>
+
+#include <immer/vector.hpp>
+#include <immer/refcount/unsafe_refcount_policy.hpp>
+
+namespace {
+
+struct heap_t
+{
+    template <typename ...Tags>
+    static void* allocate(std::size_t size, Tags...)
+    {
+        return PyMem_Malloc(size);
+    }
+
+    template <typename ...Tags>
+    static void deallocate(std::size_t, void* obj, Tags...)
+    {
+        PyMem_Free(obj);
+    }
+};
+
+using memory_t = immer::memory_policy<
+    immer::unsafe_free_list_heap_policy<heap_t>,
+    immer::unsafe_refcount_policy>;
+
+} // anonymous namespace
+
+namespace py = pybind11;
+
+PYBIND11_PLUGIN(immer_python_module)
+{
+    py::module m("immer", R"pbdoc(
+        Immer
+        -----
+        .. currentmodule:: immer
+        .. autosummary::
+           :toctree: _generate
+           Vector
+    )pbdoc");
+
+    using vector_t = immer::vector<py::object, memory_t>;
+
+    py::class_<vector_t>(m, "Vector")
+        .def(py::init<>())
+        .def("__len__", &vector_t::size)
+        .def("__getitem__",
+             [] (const vector_t& v, std::size_t i) {
+                 if (i > v.size())
+                     throw py::index_error{"Index out of range"};
+                 return v[i];
+             })
+        .def("append",
+             [] (const vector_t& v, py::object x) {
+                 return v.push_back(std::move(x));
+             })
+        .def("set",
+             [] (const vector_t& v, std::size_t i, py::object x) {
+                 return v.set(i, std::move(x));
+             });
+
+#ifdef VERSION_INFO
+    m.attr("__version__") = py::str(VERSION_INFO);
+#else
+    m.attr("__version__") = py::str("dev");
+#endif
+
+    return m.ptr();
+}
diff --git a/extra/python/src/immer-raw.cpp b/extra/python/src/immer-raw.cpp
new file mode 100644
index 000000000000..283f973c8d0f
--- /dev/null
+++ b/extra/python/src/immer-raw.cpp
@@ -0,0 +1,402 @@
+//
+// immer: immutable data structures for C++
+// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
+//
+// This software is distributed under the Boost Software License, Version 1.0.
+// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
+//
+
+extern "C" {
+#include <Python.h>
+#include <structmember.h>
+}
+
+#include <immer/vector.hpp>
+#include <immer/algorithm.hpp>
+#include <immer/refcount/unsafe_refcount_policy.hpp>
+
+#include <iostream>
+
+namespace {
+
+struct heap_t
+{
+    template <typename ...Tags>
+    static void* allocate(std::size_t size, Tags...)
+    {
+        return PyMem_Malloc(size);
+    }
+
+    template <typename ...Tags>
+    static void deallocate(std::size_t, void* obj, Tags...)
+    {
+        PyMem_Free(obj);
+    }
+};
+
+struct object_t
+{
+    struct wrap_t {};
+    struct adopt_t {};
+
+    PyObject* ptr_ = nullptr;
+
+    object_t() = delete;
+    ~object_t() { Py_XDECREF(ptr_); }
+
+    explicit object_t(PyObject* p, wrap_t)  : ptr_{p} {}
+    explicit object_t(PyObject* p, adopt_t) : ptr_{p}
+    {
+        assert(p);
+        Py_INCREF(p);
+    }
+
+    static object_t wrap(PyObject* p)  { return object_t{p, wrap_t{}}; }
+    static object_t adopt(PyObject* p) { return object_t{p, adopt_t{}}; }
+
+    object_t(const object_t& o) : ptr_(o.ptr_) { Py_INCREF(ptr_); }
+    object_t(object_t&& o) { std::swap(ptr_, o.ptr_); }
+
+    object_t& operator=(const object_t& o)
+    {
+        Py_XINCREF(o.ptr_);
+        Py_XDECREF(ptr_);
+        ptr_ = o.ptr_;
+        return *this;
+    }
+    object_t& operator=(object_t&& o)
+    {
+        std::swap(ptr_, o.ptr_);
+        return *this;
+    }
+
+    PyObject* release()
+    {
+        auto p = ptr_;
+        ptr_ = nullptr;
+        return p;
+    }
+
+    PyObject* get() const { return ptr_; }
+};
+
+using memory_t = immer::memory_policy<
+    immer::unsafe_free_list_heap_policy<heap_t>,
+    immer::unsafe_refcount_policy>;
+
+using vector_impl_t = immer::vector<object_t, memory_t>;
+
+struct vector_t
+{
+    PyObject_HEAD
+    vector_impl_t impl;
+    PyObject*     in_weakreflist;
+
+    static PyTypeObject type;
+};
+
+vector_t* empty_vector = nullptr;
+
+vector_t* make_vector()
+{
+    auto* v = PyObject_GC_New(vector_t, &vector_t::type);
+    new (&v->impl) vector_impl_t{};
+    v->in_weakreflist = nullptr;
+    PyObject_GC_Track((PyObject*)v);
+    return v;
+}
+
+vector_t* make_vector(vector_impl_t&& impl)
+{
+    auto v = PyObject_GC_New(vector_t, &vector_t::type);
+    new (&v->impl) vector_impl_t{std::move(impl)};
+    v->in_weakreflist = nullptr;
+    PyObject_GC_Track((PyObject*)v);
+    return v;
+}
+
+auto todo()
+{
+    PyErr_SetString(PyExc_RuntimeError, "immer: todo!");
+    return nullptr;
+}
+
+void vector_dealloc(vector_t* self)
+{
+    if (self->in_weakreflist != nullptr)
+        PyObject_ClearWeakRefs((PyObject*)self);
+
+    PyObject_GC_UnTrack((PyObject*)self);
+    Py_TRASHCAN_SAFE_BEGIN(self);
+
+    self->impl.~vector_impl_t();
+
+    PyObject_GC_Del(self);
+    Py_TRASHCAN_SAFE_END(self);
+}
+
+PyObject* vector_to_list(vector_t* self)
+{
+    auto list = PyList_New(self->impl.size());
+    auto idx  = 0;
+    immer::for_each(self->impl, [&] (auto&& obj) {
+            auto o = obj.get();
+            Py_INCREF(o);
+            PyList_SET_ITEM(list, idx, o);
+            ++idx;
+        });
+    return list;
+}
+
+PyObject* vector_repr(vector_t *self)
+{
+    auto list = vector_to_list(self);
+    auto list_repr = PyObject_Repr(list);
+    Py_DECREF(list);
+
+    if (!list_repr) return nullptr;
+
+#if PY_MAJOR_VERSION >= 3
+    auto s = PyUnicode_FromFormat("%s%U%s", "immer.vector(", list_repr, ")");
+    Py_DECREF(list_repr);
+#else
+    auto s = PyString_FromString("immer.vector(");
+    PyString_ConcatAndDel(&s, list_repr);
+    PyString_ConcatAndDel(&s, PyString_FromString(")"));
+#endif
+    return s;
+}
+
+Py_ssize_t vector_len(vector_t* self)
+{
+    return self->impl.size();
+}
+
+PyObject* vector_extend(vector_t* self, PyObject* iterable)
+{
+    return todo();
+}
+
+vector_t* vector_append(vector_t* self, PyObject *obj)
+{
+    assert(obj != nullptr);
+    return make_vector(self->impl.push_back(object_t::adopt(obj)));
+}
+
+vector_t* vector_set(vector_t* self, PyObject* args)
+{
+    PyObject* obj = nullptr;
+    Py_ssize_t pos;
+    // the n parses for size, the O parses for a Python object
+    if(!PyArg_ParseTuple(args, "nO", &pos, &obj)) {
+        return nullptr;
+    }
+    if (pos < 0)
+        pos += self->impl.size();
+    if (pos < 0 || pos > (Py_ssize_t)self->impl.size()) {
+        PyErr_Format(PyExc_IndexError, "Index out of range: %zi", pos);
+        return nullptr;
+    }
+    return make_vector(self->impl.set(pos, object_t::adopt(obj)));
+}
+
+PyObject* vector_new(PyTypeObject* subtype, PyObject *args, PyObject *kwds)
+{
+    Py_INCREF(empty_vector);
+    return (PyObject*)empty_vector;
+}
+
+long vector_hash(vector_t* self)
+{
+    todo();
+    return 0;
+}
+
+PyObject* vector_get_item(vector_t* self, Py_ssize_t pos)
+{
+    if (pos < 0)
+        pos += self->impl.size();
+    if (pos < 0 || pos >= (Py_ssize_t)self->impl.size()) {
+        PyErr_Format(PyExc_IndexError, "Index out of range: %zi", pos);
+        return nullptr;
+    }
+    auto r = self->impl[pos];
+    return r.release();
+}
+
+PyObject* vector_subscript(vector_t* self, PyObject* item)
+{
+    if (PyIndex_Check(item)) {
+        auto i = PyNumber_AsSsize_t(item, PyExc_IndexError);
+        if (i == -1 && PyErr_Occurred())
+            return nullptr;
+        return vector_get_item(self, i);
+    } else if (PySlice_Check(item)) {
+        return todo();
+    } else {
+        PyErr_Format(PyExc_TypeError,
+                     "vector indices must be integers, not %.200s",
+                     Py_TYPE(item)->tp_name);
+        return nullptr;
+    }
+}
+
+PyObject* vector_repeat(vector_t* self, Py_ssize_t n)
+{
+    return todo();
+}
+
+int vector_traverse(vector_t* self, visitproc visit, void* arg)
+{
+    auto result = 0;
+    immer::all_of(self->impl, [&] (auto&& o) {
+            return 0 == (result = [&] {
+                Py_VISIT(o.get());
+                return 0;
+            }());
+        });
+    return result;
+}
+
+PyObject* vector_richcompare(PyObject* v, PyObject* w, int op)
+{
+    return todo();
+}
+
+PyObject* vector_iter(PyObject* self)
+{
+    return todo();
+}
+
+PyMethodDef vector_methods[] =
+{
+    {"append",      (PyCFunction)vector_append, METH_O, "Appends an element"},
+    {"set",         (PyCFunction)vector_set, METH_VARARGS, "Inserts an element at the specified position"},
+    {"extend",      (PyCFunction)vector_extend, METH_O|METH_COEXIST, "Extend"},
+    {"tolist",      (PyCFunction)vector_to_list, METH_NOARGS, "Convert to list"},
+    {0}
+};
+
+PyMemberDef vector_members[] =
+{
+    {0}  /* sentinel */
+};
+
+PySequenceMethods vector_sequence_methods =
+{
+    (lenfunc)vector_len,             /* sq_length */
+    (binaryfunc)vector_extend,       /* sq_concat */
+    (ssizeargfunc)vector_repeat,     /* sq_repeat */
+    (ssizeargfunc)vector_get_item,   /* sq_item */
+    0,                               /* sq_slice */
+    0,                               /* sq_ass_item */
+    0,                               /* sq_ass_slice */
+    0,                               /* sq_contains */
+    0,                               /* sq_inplace_concat */
+    0,                               /* sq_inplace_repeat */
+};
+
+PyMappingMethods vector_mapping_methods =
+{
+    (lenfunc)vector_len,
+    (binaryfunc)vector_subscript,
+    0
+};
+
+PyTypeObject vector_t::type =
+{
+    PyVarObject_HEAD_INIT(NULL, 0)
+    "immer.Vector",                             /* tp_name        */
+    sizeof(vector_t),                           /* tp_basicsize   */
+    0,		                                /* tp_itemsize    */
+    (destructor)vector_dealloc,                 /* tp_dealloc     */
+    0,                                          /* tp_print       */
+    0,                                          /* tp_getattr     */
+    0,                                          /* tp_setattr     */
+    0,                                          /* tp_compare     */
+    (reprfunc)vector_repr,                      /* tp_repr        */
+    0,                                          /* tp_as_number   */
+    &vector_sequence_methods,                   /* tp_as_sequence */
+    &vector_mapping_methods,                    /* tp_as_mapping  */
+    (hashfunc)vector_hash,                      /* tp_hash        */
+    0,                                          /* tp_call        */
+    0,                                          /* tp_str         */
+    0,                                          /* tp_getattro    */
+    0,                                          /* tp_setattro    */
+    0,                                          /* tp_as_buffer   */
+    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,    /* tp_flags       */
+    "",                                         /* tp_doc         */
+    (traverseproc)vector_traverse,              /* tp_traverse       */
+    0,                                          /* tp_clear          */
+    vector_richcompare,                         /* tp_richcompare    */
+    offsetof(vector_t, in_weakreflist),         /* tp_weaklistoffset */
+    vector_iter,                                /* tp_iter           */
+    0,                                          /* tp_iternext       */
+    vector_methods,                             /* tp_methods        */
+    vector_members,                             /* tp_members        */
+    0,                                          /* tp_getset         */
+    0,                                          /* tp_base           */
+    0,                                          /* tp_dict           */
+    0,                                          /* tp_descr_get      */
+    0,                                          /* tp_descr_set      */
+    0,                                          /* tp_dictoffset     */
+    0,                                          /* tp_init           */
+    0,                                          /* tp_alloc */
+    vector_new,                                 /* tp_new   */
+};
+
+#if PY_MAJOR_VERSION >= 3
+struct PyModuleDef module_def =
+{
+    PyModuleDef_HEAD_INIT,
+    "immer_python_module", /* m_name */
+    "",                  /* m_doc */
+    -1,                  /* m_size */
+    /module_methods,     /* m_methods */
+    0,                   /* m_reload */
+    0,                   /* m_traverse */
+    0,                   /* m_clear */
+    0,                   /* m_free */
+};
+#endif
+
+PyMethodDef module_methods[] = {
+    {0, 0, 0, 0}
+};
+
+PyObject* module_init()
+{
+    if (PyType_Ready(&vector_t::type) < 0)
+        return nullptr;
+
+#if PY_MAJOR_VERSION >= 3
+    auto m = PyModule_Create(&module_def);
+#else
+    auto m = Py_InitModule3("immer_python_module", module_methods, "");
+#endif
+    if (!m)
+        return nullptr;
+
+    if (!empty_vector)
+        empty_vector = make_vector();
+
+    Py_INCREF(&vector_t::type);
+    PyModule_AddObject(m, "Vector", (PyObject*) &vector_t::type);
+    return m;
+}
+
+} // anonymous namespace
+
+extern "C" {
+#if PY_MAJOR_VERSION >= 3
+PyMODINIT_FUNC PyInit_immer_python_module()
+{
+    return module_init();
+}
+#else
+PyMODINIT_FUNC initimmer_python_module()
+{
+    module_init();
+}
+#endif
+}