<commit_before>=======================
Python C API cheatsheet
=======================

.. contents:: Table of Contents
    :backlinks: none


Simple setup.py for c extension
----------------------------------

.. code-block:: python

    from distutils.core import setup, Extension

    ext = Extension('foo', sources=['foo.c'])
    setup(name="Foo", version="1.0", ext_modules=[ext])


Customize CFLAGS
-----------------

.. code-block:: python

    import sysconfig
    from distutils.core import setup, Extension

    cflags = sysconfig.get_config_var("CFLAGS")

    extra_compile_args = cflags.split()
    extra_compile_args += ["-Wextra"]

    ext = Extension(
        "foo", ["foo.c"],
        extra_compile_args=extra_compile_args
    )

    setup(name="foo", version="1.0", ext_modules=[ext])

Doc string
----------

.. code-block:: c

    PyDoc_STRVAR(doc_mod, "Module document\n");
    PyDoc_STRVAR(doc_foo, "foo() -> None\n\nFoo doc");

    static PyMethodDef methods[] = {
        {"foo", (PyCFunction)foo, METH_NOARGS, doc_foo},
        {NULL, NULL, 0, NULL}
    };

    static struct PyModuleDef module = {
        .m_base    = PyModuleDef_HEAD_INIT,
        .m_name    = "Foo",
        .m_doc     = doc_mod,
        .m_size    = -1,
        .m_methods = methods
    };


Simple C Extension
-------------------

foo.c

.. code-block:: c

    #include <Python.h>

    PyDoc_STRVAR(doc_mod, "Module document\n");
    PyDoc_STRVAR(doc_foo, "foo() -> None\n\nFoo doc");

    static PyObject* foo(PyObject* self)
    {
        PyObject* s = PyUnicode_FromString("foo");
        PyObject_Print(s, stdout, 0);
        Py_RETURN_NONE;
    }

    static PyMethodDef methods[] = {
        {"foo", (PyCFunction)foo, METH_NOARGS, doc_foo},
        {NULL, NULL, 0, NULL}
    };

    static struct PyModuleDef module = {
        PyModuleDef_HEAD_INIT, "Foo", doc_mod, -1, methods
    };

    PyMODINIT_FUNC PyInit_foo(void)
    {
        return PyModule_Create(&module);
    }

output:

.. code-block:: bash

    $ python setup.py -q build
    $ python setup.py -q install
    $ python -c "import foo; foo.foo()"
    'foo'

Get Reference Count
--------------------

.. code-block:: c

    #include <Python.h>

    static PyObject *
    getrefcount(PyObject *self, PyObject *a)
    {
        return PyLong_FromSsize_t(Py_REFCNT(a));
    }

    static PyMethodDef methods[] = {
        {"getrefcount", (PyCFunction)getrefcount, METH_O, NULL},
        {NULL, NULL, 0, NULL}
    };

    static struct PyModuleDef module = {
        PyModuleDef_HEAD_INIT, "foo", NULL, -1, methods
    };

    PyMODINIT_FUNC PyInit_foo(void)
    {
        return PyModule_Create(&module);
    }

output:

.. code-block:: bash

    $ python setup.py -q build
    $ python setup.py -q install
    $ python -q
    >>> import sys
    >>> import foo
    >>> l = [1, 2, 3]
    >>> sys.getrefcount(l[0])
    104
    >>> foo.getrefcount(l[0])
    104
    >>> i = l[0]
    >>> sys.getrefcount(l[0])
    105
    >>> foo.getrefcount(l[0])
    105

Parse Arguments
----------------

.. code-block:: c

    #include <Python.h>

    static PyObject *
    foo(PyObject *self)
    {
        return PyUnicode_FromString("no args");
    }

    static PyObject *
    bar(PyObject *self, PyObject *args)
    {
        int i = -1;
        const char *s = NULL;
        if (!PyArg_ParseTuple(args, "is", &i, &s)) return NULL;
        return PyUnicode_FromFormat("args(%d, %s)", i, s);
    }

    static PyMethodDef methods[] = {
        {"foo", (PyCFunction)foo, METH_NOARGS, NULL},
        {"bar", (PyCFunction)bar, METH_VARARGS, NULL},
        {NULL, NULL, 0, NULL}
    };

    static struct PyModuleDef module = {
        PyModuleDef_HEAD_INIT, "foo", NULL, -1, methods
    };

    PyMODINIT_FUNC PyInit_foo(void)
    {
        return PyModule_Create(&module);
    }

output:

.. code-block:: bash

    $ python setup.py -q build
    $ python setup.py -q install
    $ python -c 'import foo; print(foo.foo())'
    no args
    $ python -c 'import foo; print(foo.bar(1, "s"))'
    args(1, s)

Calling Python Functions from C
--------------------------------

.. code-block:: c

    #include <Python.h>

    static PyObject *
    foo(PyObject *self, PyObject *args)
    {
        PyObject *py_callback = NULL;
        PyObject *rv = NULL;

        if (!PyArg_ParseTuple(args, "O:callback", &py_callback))
            return NULL;

        if (!PyCallable_Check(py_callback)) {
            PyErr_SetString(PyExc_TypeError, "should be callable");
            return NULL;
        }

        // similar to py_callback("Awesome Python!")
        rv = PyObject_CallFunction(py_callback, "s", "Awesome Python!");
        return rv;
    }

    static PyMethodDef methods[] = {
        {"foo", (PyCFunction)foo, METH_VARARGS, NULL},
        {NULL, NULL, 0, NULL}
    };

    static struct PyModuleDef module = {
        PyModuleDef_HEAD_INIT, "foo", NULL, -1, methods
    };

    PyMODINIT_FUNC PyInit_foo(void)
    {
        return PyModule_Create(&module);
    }

output:

.. code-block:: bash

    $ python setup.py -q build
    $ python setup.py -q install
    $ python -c "import foo; foo.foo(print)"
    Awesome Python!

Raise Exception
----------------

.. code-block:: c

    #include <Python.h>

    PyDoc_STRVAR(doc_mod, "Module document\n");
    PyDoc_STRVAR(doc_foo, "foo() -> None\n\nFoo doc");

    static PyObject*
    foo(PyObject* self)
    {
        // raise NotImplementedError
        PyErr_SetString(PyExc_NotImplementedError, "Not implemented");
        return NULL;
    }

    static PyMethodDef methods[] = {
        {"foo", (PyCFunction)foo, METH_NOARGS, doc_foo},
        {NULL, NULL, 0, NULL}
    };

    static struct PyModuleDef module = {
        PyModuleDef_HEAD_INIT, "Foo", doc_mod, -1, methods
    };

    PyMODINIT_FUNC PyInit_foo(void)
    {
        return PyModule_Create(&module);
    }

output:

.. code-block::

    $ python setup.py -q build
    $ python setup.py -q install
    $ python -c "import foo; foo.foo(print)"
    $ python -c "import foo; foo.foo()"
    Traceback (most recent call last):
      File "<string>", line 1, in <module>
    NotImplementedError: Not implemented

Customize Exception
--------------------

.. code-block:: c

    #include <stdio.h>
    #include <Python.h>

    static PyObject *FooError;

    PyDoc_STRVAR(doc_foo, "foo() -> void\n\n"
        "Equal to the following example:\n\n"
        "def foo():\n"
        "    raise FooError(\"Raise exception in C\")"
    );

    static PyObject *
    foo(PyObject *self __attribute__((unused)))
    {
        PyErr_SetString(FooError, "Raise exception in C");
        return NULL;
    }

    static PyMethodDef methods[] = {
        {"foo", (PyCFunction)foo, METH_NOARGS, doc_foo},
        {NULL, NULL, 0, NULL}
    };

    static struct PyModuleDef module = {
        PyModuleDef_HEAD_INIT, "foo", "doc", -1, methods
    };

    PyMODINIT_FUNC PyInit_foo(void)
    {
        PyObject *m = NULL;
        m = PyModule_Create(&module);
        if (!m) return NULL;

        FooError = PyErr_NewException("foo.FooError", NULL, NULL);
        Py_INCREF(FooError);
        PyModule_AddObject(m, "FooError", FooError);
        return m;
    }


output:

.. code-block:: bash

    $ python setup.py -q build
    $ python setup.py -q install
    $ python -c "import foo; foo.foo()"
    Traceback (most recent call last):
      File "<string>", line 1, in <module>
    foo.FooError: Raise exception in C

Iterate a List
---------------

.. code-block:: c

    #include <Python.h>

    #define PY_PRINTF(o) \
        PyObject_Print(o, stdout, 0); printf("\n");

    static PyObject *
    iter_list(PyObject *self, PyObject *args)
    {
        PyObject *list = NULL, *item = NULL, *iter = NULL;
        PyObject *result = NULL;

        if (!PyArg_ParseTuple(args, "O", &list))
            goto error;

        if (!PyList_Check(list))
            goto error;

        // Get iterator
        iter = PyObject_GetIter(list);
        if (!iter)
            goto error;

        // for i in arr: print(i)
        while ((item = PyIter_Next(iter)) != NULL) {
            PY_PRINTF(item);
            Py_XDECREF(item);
        }

        Py_XINCREF(Py_None);
        result = Py_None;
    error:
        Py_XDECREF(iter);
        return result;
    }

    static PyMethodDef methods[] = {
        {"iter_list", (PyCFunction)iter_list, METH_VARARGS, NULL},
        {NULL, NULL, 0, NULL}
    };

    static struct PyModuleDef module = {
        PyModuleDef_HEAD_INIT, "foo", NULL, -1, methods
    };

    PyMODINIT_FUNC PyInit_foo(void)
    {
        return PyModule_Create(&module);
    }

output:

.. code-block:: bash

    $ python setup.py -q build
    $ python setup.py -q install
    $ python -c "import foo; foo.iter_list([1,2,3])"
    1
    2
    3

Iterate a Dictionary
---------------------

.. code-block:: c

    #include <Python.h>

    #define PY_PRINTF(o) \
        PyObject_Print(o, stdout, 0); printf("\n");

    static PyObject *
    iter_dict(PyObject *self, PyObject *args)
    {
        PyObject *dict = NULL;
        PyObject *key = NULL, *val = NULL;
        PyObject *o = NULL, *result = NULL;
        Py_ssize_t pos = 0;

        if (!PyArg_ParseTuple(args, "O", &dict))
            goto error;

        // for k, v in d.items(): print(f"({k}, {v})")
        while (PyDict_Next(dict, &pos, &key, &val)) {
            o = PyUnicode_FromFormat("(%S, %S)", key, val);
            if (!o) continue;
            PY_PRINTF(o);
            Py_XDECREF(o);
        }

        Py_INCREF(Py_None);
        result = Py_None;
    error:
        return result;
    }

    static PyMethodDef methods[] = {
        {"iter_dict", (PyCFunction)iter_dict, METH_VARARGS, NULL},
        {NULL, NULL, 0, NULL}
    };

    static struct PyModuleDef module = {
        PyModuleDef_HEAD_INIT, "foo", NULL, -1, methods
    };

    PyMODINIT_FUNC PyInit_foo(void)
    {
        return PyModule_Create(&module);
    }

output:

.. code-block:: bash

    $ python setup.py -q build
    $ python setup.py -q install
    $ python -c "import foo; foo.iter_dict({'k': 'v'})"
    '(k, v)'

Run a Python command from C
----------------------------

.. code-block:: c

    #include <stdio.h>
    #include <Python.h>

    int
    main(int argc, char *argv[])
    {
        int rc = -1;
        Py_Initialize();
        rc = PyRun_SimpleString(argv[1]);
        Py_Finalize();
        return rc;
    }

output:

.. code-block:: bash

    $ clang `python3-config --cflags` -c foo.c -o foo.o
    $ clang `python3-config --ldflags` foo.o -o foo
    $ ./foo "print('Hello Python')"
    Hello Python

Run a Python file from C
-------------------------

.. code-block:: c

    #include <stdio.h>
    #include <Python.h>

    int
    main(int argc, char *argv[])
    {
        int rc = -1, i = 0;
        wchar_t **argv_copy = NULL;
        const char *filename = NULL;
        FILE *fp = NULL;
        PyCompilerFlags cf = {.cf_flags = 0};

        filename = argv[1];
        fp = fopen(filename, "r");
        if (!fp)
            goto error;

        // copy argv
        argv_copy = PyMem_RawMalloc(sizeof(wchar_t*) * argc);
        if (!argv_copy)
            goto error;

        for (i = 0; i < argc; i++) {
            argv_copy[i] = Py_DecodeLocale(argv[i], NULL);
            if (argv_copy[i]) continue;
            fprintf(stderr, "Unable to decode the argument");
            goto error;
        }

        Py_Initialize();
        Py_SetProgramName(argv_copy[0]);
        PySys_SetArgv(argc, argv_copy);
        rc = PyRun_AnyFileExFlags(fp, filename, 0, &cf);

    error:
        if (argv_copy) {
            for (i = 0; i < argc; i++)
                PyMem_RawFree(argv_copy[i]);
            PyMem_RawFree(argv_copy);
        }
        if (fp) fclose(fp);
        Py_Finalize();
        return rc;
    }

output:

.. code-block:: bash

    $ clang `python3-config --cflags` -c foo.c -o foo.o
    $ clang `python3-config --ldflags` foo.o -o foo
    $ echo "import sys; print(sys.argv)" > foo.py
    $ ./foo foo.py arg1 arg2 arg3
    ['./foo', 'foo.py', 'arg1', 'arg2', 'arg3']

Import a Python Module from C
------------------------------

.. code-block:: c

    #include <stdio.h>
    #include <Python.h>

    #define PYOBJECT_CHECK(obj, label) \
        if (!obj) { \
            PyErr_Print(); \
            goto label; \
        }

    int
    main(int argc, char *argv[])
    {
        int rc = -1;
        wchar_t *program = NULL;
        PyObject *json_module = NULL, *json_dict = NULL;
        PyObject *json_dumps = NULL;
        PyObject *dict = NULL;
        PyObject *result = NULL;

        program = Py_DecodeLocale(argv[0], NULL);
        if (!program) {
            fprintf(stderr, "unable to decode the program name");
            goto error;
        }

        Py_SetProgramName(program);
        Py_Initialize();

        // import json
        json_module = PyImport_ImportModule("json");
        PYOBJECT_CHECK(json_module, error);

        // get json.__dict__
        json_dict = PyModule_GetDict(json_module);
        PYOBJECT_CHECK(json_dict, error);

        // get json.__dict__['dumps']
        json_dumps = PyDict_GetItemString(json_dict, "dumps");
        PYOBJECT_CHECK(json_dumps, error);

        // dict = {'foo': 'Foo', 'bar': 123}
        dict = Py_BuildValue("({sssi})", "foo", "Foo", "bar", 123);
        PYOBJECT_CHECK(dict, error);

        // json.dumps(dict)
        result = PyObject_CallObject(json_dumps, dict);
        PYOBJECT_CHECK(result, error);
        PyObject_Print(result, stdout, 0);
        printf("\n");
    error:
        Py_XDECREF(result);
        Py_XDECREF(dict);
        Py_XDECREF(json_dumps);
        Py_XDECREF(json_dict);
        Py_XDECREF(json_module);

        PyMem_RawFree(program);
        Py_Finalize();
        return rc;
    }

output:

.. code-block:: bash

    $ clang `python3-config --cflags` -c foo.c -o foo.o
    $ clang `python3-config --ldflags` foo.o -o foo
    $ ./foo
    '{"foo": "Foo", "bar": 123}'

Performance of c api
---------------------<commit_msg>add access attributes from c<commit_after>=======================
Python C API cheatsheet
=======================

.. contents:: Table of Contents
    :backlinks: none


Simple setup.py for c extension
----------------------------------

.. code-block:: python

    from distutils.core import setup, Extension

    ext = Extension('foo', sources=['foo.c'])
    setup(name="Foo", version="1.0", ext_modules=[ext])


Customize CFLAGS
-----------------

.. code-block:: python

    import sysconfig
    from distutils.core import setup, Extension

    cflags = sysconfig.get_config_var("CFLAGS")

    extra_compile_args = cflags.split()
    extra_compile_args += ["-Wextra"]

    ext = Extension(
        "foo", ["foo.c"],
        extra_compile_args=extra_compile_args
    )

    setup(name="foo", version="1.0", ext_modules=[ext])

Doc string
----------

.. code-block:: c

    PyDoc_STRVAR(doc_mod, "Module document\n");
    PyDoc_STRVAR(doc_foo, "foo() -> None\n\nFoo doc");

    static PyMethodDef methods[] = {
        {"foo", (PyCFunction)foo, METH_NOARGS, doc_foo},
        {NULL, NULL, 0, NULL}
    };

    static struct PyModuleDef module = {
        .m_base    = PyModuleDef_HEAD_INIT,
        .m_name    = "Foo",
        .m_doc     = doc_mod,
        .m_size    = -1,
        .m_methods = methods
    };


Simple C Extension
-------------------

foo.c

.. code-block:: c

    #include <Python.h>

    PyDoc_STRVAR(doc_mod, "Module document\n");
    PyDoc_STRVAR(doc_foo, "foo() -> None\n\nFoo doc");

    static PyObject* foo(PyObject* self)
    {
        PyObject* s = PyUnicode_FromString("foo");
        PyObject_Print(s, stdout, 0);
        Py_RETURN_NONE;
    }

    static PyMethodDef methods[] = {
        {"foo", (PyCFunction)foo, METH_NOARGS, doc_foo},
        {NULL, NULL, 0, NULL}
    };

    static struct PyModuleDef module = {
        PyModuleDef_HEAD_INIT, "Foo", doc_mod, -1, methods
    };

    PyMODINIT_FUNC PyInit_foo(void)
    {
        return PyModule_Create(&module);
    }

output:

.. code-block:: bash

    $ python setup.py -q build
    $ python setup.py -q install
    $ python -c "import foo; foo.foo()"
    'foo'

Get Reference Count
--------------------

.. code-block:: c

    #include <Python.h>

    static PyObject *
    getrefcount(PyObject *self, PyObject *a)
    {
        return PyLong_FromSsize_t(Py_REFCNT(a));
    }

    static PyMethodDef methods[] = {
        {"getrefcount", (PyCFunction)getrefcount, METH_O, NULL},
        {NULL, NULL, 0, NULL}
    };

    static struct PyModuleDef module = {
        PyModuleDef_HEAD_INIT, "foo", NULL, -1, methods
    };

    PyMODINIT_FUNC PyInit_foo(void)
    {
        return PyModule_Create(&module);
    }

output:

.. code-block:: bash

    $ python setup.py -q build
    $ python setup.py -q install
    $ python -q
    >>> import sys
    >>> import foo
    >>> l = [1, 2, 3]
    >>> sys.getrefcount(l[0])
    104
    >>> foo.getrefcount(l[0])
    104
    >>> i = l[0]
    >>> sys.getrefcount(l[0])
    105
    >>> foo.getrefcount(l[0])
    105

Parse Arguments
----------------

.. code-block:: c

    #include <Python.h>

    static PyObject *
    foo(PyObject *self)
    {
        return PyUnicode_FromString("no args");
    }

    static PyObject *
    bar(PyObject *self, PyObject *args)
    {
        int i = -1;
        const char *s = NULL;
        if (!PyArg_ParseTuple(args, "is", &i, &s)) return NULL;
        return PyUnicode_FromFormat("args(%d, %s)", i, s);
    }

    static PyMethodDef methods[] = {
        {"foo", (PyCFunction)foo, METH_NOARGS, NULL},
        {"bar", (PyCFunction)bar, METH_VARARGS, NULL},
        {NULL, NULL, 0, NULL}
    };

    static struct PyModuleDef module = {
        PyModuleDef_HEAD_INIT, "foo", NULL, -1, methods
    };

    PyMODINIT_FUNC PyInit_foo(void)
    {
        return PyModule_Create(&module);
    }

output:

.. code-block:: bash

    $ python setup.py -q build
    $ python setup.py -q install
    $ python -c 'import foo; print(foo.foo())'
    no args
    $ python -c 'import foo; print(foo.bar(1, "s"))'
    args(1, s)

Calling Python Functions from C
--------------------------------

.. code-block:: c

    #include <Python.h>

    static PyObject *
    foo(PyObject *self, PyObject *args)
    {
        PyObject *py_callback = NULL;
        PyObject *rv = NULL;

        if (!PyArg_ParseTuple(args, "O:callback", &py_callback))
            return NULL;

        if (!PyCallable_Check(py_callback)) {
            PyErr_SetString(PyExc_TypeError, "should be callable");
            return NULL;
        }

        // similar to py_callback("Awesome Python!")
        rv = PyObject_CallFunction(py_callback, "s", "Awesome Python!");
        return rv;
    }

    static PyMethodDef methods[] = {
        {"foo", (PyCFunction)foo, METH_VARARGS, NULL},
        {NULL, NULL, 0, NULL}
    };

    static struct PyModuleDef module = {
        PyModuleDef_HEAD_INIT, "foo", NULL, -1, methods
    };

    PyMODINIT_FUNC PyInit_foo(void)
    {
        return PyModule_Create(&module);
    }

output:

.. code-block:: bash

    $ python setup.py -q build
    $ python setup.py -q install
    $ python -c "import foo; foo.foo(print)"
    Awesome Python!

Raise Exception
----------------

.. code-block:: c

    #include <Python.h>

    PyDoc_STRVAR(doc_mod, "Module document\n");
    PyDoc_STRVAR(doc_foo, "foo() -> None\n\nFoo doc");

    static PyObject*
    foo(PyObject* self)
    {
        // raise NotImplementedError
        PyErr_SetString(PyExc_NotImplementedError, "Not implemented");
        return NULL;
    }

    static PyMethodDef methods[] = {
        {"foo", (PyCFunction)foo, METH_NOARGS, doc_foo},
        {NULL, NULL, 0, NULL}
    };

    static struct PyModuleDef module = {
        PyModuleDef_HEAD_INIT, "Foo", doc_mod, -1, methods
    };

    PyMODINIT_FUNC PyInit_foo(void)
    {
        return PyModule_Create(&module);
    }

output:

.. code-block::

    $ python setup.py -q build
    $ python setup.py -q install
    $ python -c "import foo; foo.foo(print)"
    $ python -c "import foo; foo.foo()"
    Traceback (most recent call last):
      File "<string>", line 1, in <module>
    NotImplementedError: Not implemented

Customize Exception
--------------------

.. code-block:: c

    #include <stdio.h>
    #include <Python.h>

    static PyObject *FooError;

    PyDoc_STRVAR(doc_foo, "foo() -> void\n\n"
        "Equal to the following example:\n\n"
        "def foo():\n"
        "    raise FooError(\"Raise exception in C\")"
    );

    static PyObject *
    foo(PyObject *self __attribute__((unused)))
    {
        PyErr_SetString(FooError, "Raise exception in C");
        return NULL;
    }

    static PyMethodDef methods[] = {
        {"foo", (PyCFunction)foo, METH_NOARGS, doc_foo},
        {NULL, NULL, 0, NULL}
    };

    static struct PyModuleDef module = {
        PyModuleDef_HEAD_INIT, "foo", "doc", -1, methods
    };

    PyMODINIT_FUNC PyInit_foo(void)
    {
        PyObject *m = NULL;
        m = PyModule_Create(&module);
        if (!m) return NULL;

        FooError = PyErr_NewException("foo.FooError", NULL, NULL);
        Py_INCREF(FooError);
        PyModule_AddObject(m, "FooError", FooError);
        return m;
    }


output:

.. code-block:: bash

    $ python setup.py -q build
    $ python setup.py -q install
    $ python -c "import foo; foo.foo()"
    Traceback (most recent call last):
      File "<string>", line 1, in <module>
    foo.FooError: Raise exception in C

Iterate a List
---------------

.. code-block:: c

    #include <Python.h>

    #define PY_PRINTF(o) \
        PyObject_Print(o, stdout, 0); printf("\n");

    static PyObject *
    iter_list(PyObject *self, PyObject *args)
    {
        PyObject *list = NULL, *item = NULL, *iter = NULL;
        PyObject *result = NULL;

        if (!PyArg_ParseTuple(args, "O", &list))
            goto error;

        if (!PyList_Check(list))
            goto error;

        // Get iterator
        iter = PyObject_GetIter(list);
        if (!iter)
            goto error;

        // for i in arr: print(i)
        while ((item = PyIter_Next(iter)) != NULL) {
            PY_PRINTF(item);
            Py_XDECREF(item);
        }

        Py_XINCREF(Py_None);
        result = Py_None;
    error:
        Py_XDECREF(iter);
        return result;
    }

    static PyMethodDef methods[] = {
        {"iter_list", (PyCFunction)iter_list, METH_VARARGS, NULL},
        {NULL, NULL, 0, NULL}
    };

    static struct PyModuleDef module = {
        PyModuleDef_HEAD_INIT, "foo", NULL, -1, methods
    };

    PyMODINIT_FUNC PyInit_foo(void)
    {
        return PyModule_Create(&module);
    }

output:

.. code-block:: bash

    $ python setup.py -q build
    $ python setup.py -q install
    $ python -c "import foo; foo.iter_list([1,2,3])"
    1
    2
    3

Iterate a Dictionary
---------------------

.. code-block:: c

    #include <Python.h>

    #define PY_PRINTF(o) \
        PyObject_Print(o, stdout, 0); printf("\n");

    static PyObject *
    iter_dict(PyObject *self, PyObject *args)
    {
        PyObject *dict = NULL;
        PyObject *key = NULL, *val = NULL;
        PyObject *o = NULL, *result = NULL;
        Py_ssize_t pos = 0;

        if (!PyArg_ParseTuple(args, "O", &dict))
            goto error;

        // for k, v in d.items(): print(f"({k}, {v})")
        while (PyDict_Next(dict, &pos, &key, &val)) {
            o = PyUnicode_FromFormat("(%S, %S)", key, val);
            if (!o) continue;
            PY_PRINTF(o);
            Py_XDECREF(o);
        }

        Py_INCREF(Py_None);
        result = Py_None;
    error:
        return result;
    }

    static PyMethodDef methods[] = {
        {"iter_dict", (PyCFunction)iter_dict, METH_VARARGS, NULL},
        {NULL, NULL, 0, NULL}
    };

    static struct PyModuleDef module = {
        PyModuleDef_HEAD_INIT, "foo", NULL, -1, methods
    };

    PyMODINIT_FUNC PyInit_foo(void)
    {
        return PyModule_Create(&module);
    }

output:

.. code-block:: bash

    $ python setup.py -q build
    $ python setup.py -q install
    $ python -c "import foo; foo.iter_dict({'k': 'v'})"
    '(k, v)'

Run a Python command from C
----------------------------

.. code-block:: c

    #include <stdio.h>
    #include <Python.h>

    int
    main(int argc, char *argv[])
    {
        int rc = -1;
        Py_Initialize();
        rc = PyRun_SimpleString(argv[1]);
        Py_Finalize();
        return rc;
    }

output:

.. code-block:: bash

    $ clang `python3-config --cflags` -c foo.c -o foo.o
    $ clang `python3-config --ldflags` foo.o -o foo
    $ ./foo "print('Hello Python')"
    Hello Python

Run a Python file from C
-------------------------

.. code-block:: c

    #include <stdio.h>
    #include <Python.h>

    int
    main(int argc, char *argv[])
    {
        int rc = -1, i = 0;
        wchar_t **argv_copy = NULL;
        const char *filename = NULL;
        FILE *fp = NULL;
        PyCompilerFlags cf = {.cf_flags = 0};

        filename = argv[1];
        fp = fopen(filename, "r");
        if (!fp)
            goto error;

        // copy argv
        argv_copy = PyMem_RawMalloc(sizeof(wchar_t*) * argc);
        if (!argv_copy)
            goto error;

        for (i = 0; i < argc; i++) {
            argv_copy[i] = Py_DecodeLocale(argv[i], NULL);
            if (argv_copy[i]) continue;
            fprintf(stderr, "Unable to decode the argument");
            goto error;
        }

        Py_Initialize();
        Py_SetProgramName(argv_copy[0]);
        PySys_SetArgv(argc, argv_copy);
        rc = PyRun_AnyFileExFlags(fp, filename, 0, &cf);

    error:
        if (argv_copy) {
            for (i = 0; i < argc; i++)
                PyMem_RawFree(argv_copy[i]);
            PyMem_RawFree(argv_copy);
        }
        if (fp) fclose(fp);
        Py_Finalize();
        return rc;
    }

output:

.. code-block:: bash

    $ clang `python3-config --cflags` -c foo.c -o foo.o
    $ clang `python3-config --ldflags` foo.o -o foo
    $ echo "import sys; print(sys.argv)" > foo.py
    $ ./foo foo.py arg1 arg2 arg3
    ['./foo', 'foo.py', 'arg1', 'arg2', 'arg3']

Import a Python Module from C
------------------------------

.. code-block:: c

    #include <stdio.h>
    #include <Python.h>

    #define PYOBJECT_CHECK(obj, label) \
        if (!obj) { \
            PyErr_Print(); \
            goto label; \
        }

    int
    main(int argc, char *argv[])
    {
        int rc = -1;
        wchar_t *program = NULL;
        PyObject *json_module = NULL, *json_dict = NULL;
        PyObject *json_dumps = NULL;
        PyObject *dict = NULL;
        PyObject *result = NULL;

        program = Py_DecodeLocale(argv[0], NULL);
        if (!program) {
            fprintf(stderr, "unable to decode the program name");
            goto error;
        }

        Py_SetProgramName(program);
        Py_Initialize();

        // import json
        json_module = PyImport_ImportModule("json");
        PYOBJECT_CHECK(json_module, error);

        // get json.__dict__
        json_dict = PyModule_GetDict(json_module);
        PYOBJECT_CHECK(json_dict, error);

        // get json.__dict__['dumps']
        json_dumps = PyDict_GetItemString(json_dict, "dumps");
        PYOBJECT_CHECK(json_dumps, error);

        // dict = {'foo': 'Foo', 'bar': 123}
        dict = Py_BuildValue("({sssi})", "foo", "Foo", "bar", 123);
        PYOBJECT_CHECK(dict, error);

        // json.dumps(dict)
        result = PyObject_CallObject(json_dumps, dict);
        PYOBJECT_CHECK(result, error);
        PyObject_Print(result, stdout, 0);
        printf("\n");
    error:
        Py_XDECREF(result);
        Py_XDECREF(dict);
        Py_XDECREF(json_dumps);
        Py_XDECREF(json_dict);
        Py_XDECREF(json_module);

        PyMem_RawFree(program);
        Py_Finalize();
        return rc;
    }

output:

.. code-block:: bash

    $ clang `python3-config --cflags` -c foo.c -o foo.o
    $ clang `python3-config --ldflags` foo.o -o foo
    $ ./foo
    '{"foo": "Foo", "bar": 123}'

Access Attributes
------------------

.. code-block:: c

    #include <stdio.h>
    #include <Python.h>

    #define PYOBJECT_CHECK(obj, label) \
        if (!obj) { \
            PyErr_Print(); \
            goto label; \
        }

    int
    main(int argc, char *argv[])
    {
        int rc = -1;
        wchar_t *program = NULL;
        PyObject *json_module = NULL;
        PyObject *json_dumps = NULL;
        PyObject *dict = NULL;
        PyObject *result = NULL;

        program = Py_DecodeLocale(argv[0], NULL);
        if (!program) {
            fprintf(stderr, "unable to decode the program name");
            goto error;
        }

        Py_SetProgramName(program);
        Py_Initialize();

        // import json
        json_module = PyImport_ImportModule("json");
        PYOBJECT_CHECK(json_module, error);

        // json_dumps = json.dumps
        json_dumps = PyObject_GetAttrString(json_module, "dumps");
        PYOBJECT_CHECK(json_dumps, error);

        // dict = {'foo': 'Foo', 'bar': 123}
        dict = Py_BuildValue("({sssi})", "foo", "Foo", "bar", 123);
        PYOBJECT_CHECK(dict, error);

        // json.dumps(dict)
        result = PyObject_CallObject(json_dumps, dict);
        PYOBJECT_CHECK(result, error);
        PyObject_Print(result, stdout, 0);
        printf("\n");
    error:
        Py_XDECREF(result);
        Py_XDECREF(dict);
        Py_XDECREF(json_dumps);
        Py_XDECREF(json_module);

        PyMem_RawFree(program);
        Py_Finalize();
        return rc;
    }

output:

.. code-block:: bash

    $ clang `python3-config --cflags` -c foo.c -o foo.o
    $ clang `python3-config --ldflags` foo.o -o foo
    $ ./foo
    '{"foo": "Foo", "bar": 123}'

Performance of c api
---------------------
{'fraction_non_alphanumeric': 0.1568476661762739, 'fraction_numerical': 0.006553430520262137, 'mean_word_length': 1.985526053104412, 'pattern_counts': {'":': 6, '<': 47, '<?xml version=': 0, '>': 101, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2853306', 'n_tokens_mistral': 10143, 'n_tokens_neox': 9834, 'n_words': 2397}