{"id":6,"date":"2016-05-30T13:25:43","date_gmt":"2016-05-30T13:25:43","guid":{"rendered":"http:\/\/blogs.gentoo.org\/gsoc2016-libebuild\/?p=6"},"modified":"2017-03-07T19:31:21","modified_gmt":"2017-03-07T19:31:21","slug":"my-first-gsoc-week-at-gentoo-foundation","status":"publish","type":"post","link":"https:\/\/blogs.gentoo.org\/gsoc2016-libebuild\/2016\/05\/30\/my-first-gsoc-week-at-gentoo-foundation\/","title":{"rendered":"My first GSoC week at Gentoo Foundation"},"content":{"rendered":"<p>First, some brief notion about the project I am working on &#8211; libebuild.The idea is to\u00a0implement efficient and well-tested C library which provides functionality for working with Gentoo ebuilds and intended\u00a0to be used in package managers.<\/p>\n<p><a href=\"https:\/\/github.com\/den4ix\/libebuild\">Libebuild repo<\/a><\/p>\n<p>The initial thought is to provide pure C implementation with well defined API, so that it also could be used in external tool development. As well as we might provide bindings for various languages and use library e.g with python.<\/p>\n<p>Here are some difficulties that I&#8217;ve faced while trying to add python bindings based on my C code.<\/p>\n<p>Let&#8217;s say I have my C struct for working with category\/package\/version:<\/p>\n<pre><code>\r\ntypedef struct {\r\n    char *cat;\r\n    char *pkg;\r\n    char *ver;\r\n} cpv;\r\n<\/code><\/pre>\n<p>and some C code for allocating memory and filling up this struct:<\/p>\n<pre><code>\r\ncpv *cpv_alloc(char *cpv_string) {\r\n    cpv *c = malloc(sizeof(cpv)+strlen(cpv_string)+1);\r\n    char *ptr = (char*)c;\r\n    c-&gt;cat = ptr + sizeof(cpv);\r\n    strcpy(c-&gt;cat, cpv_string);\r\n    c-&gt;pkg = strchr(c-&gt;cat, '\/');\r\n    *(c-&gt;pkg) = '\\0';\r\n    c-&gt;pkg++;\r\n    c-&gt;ver = strrchr(c-&gt;pkg, '-');\r\n    *(c-&gt;ver) = '\\0';\r\n    c-&gt;ver++;\r\n    return c;\r\n}\r\n<\/code><\/pre>\n<p>To define new python CPV type I have to implement some python object type layout:<\/p>\n<pre><code>\r\ntypedef struct {\r\n    PyObject_HEAD\r\n    char *cat;\r\n    char *pkg;\r\n    char *ver;\r\n} cpv_CPVObject;\r\n\r\nstatic PyTypeObject cpv_CPVType = {\r\n    PyObject_HEAD_INIT(NULL)\r\n    0,                     \/*ob_size*\/\r\n    \"cpv.CPV\",             \/*tp_name*\/\r\n    sizeof(cpv_CPVObject), \/*tp_basicsize*\/\r\n    0,                     \/*tp_itemsize*\/\r\n    0,                     \/*tp_dealloc*\/\r\n    0,                     \/*tp_print*\/\r\n    0,                     \/*tp_getattr*\/\r\n    0,                     \/*tp_setattr*\/\r\n    0,                     \/*tp_compare*\/\r\n    0,                     \/*tp_repr*\/\r\n    0,                     \/*tp_as_number*\/\r\n    0,                     \/*tp_as_sequence*\/\r\n    0,                     \/*tp_as_mapping*\/\r\n    0,                     \/*tp_hash *\/\r\n    0,                     \/*tp_call*\/\r\n    0,                     \/*tp_str*\/\r\n    0,                     \/*tp_getattro*\/\r\n    0,                     \/*tp_setattro*\/\r\n    0,                     \/*tp_as_buffer*\/\r\n    Py_TPFLAGS_DEFAULT,    \/*tp_flags*\/\r\n    \"CPV objects\",         \/* tp_doc *\/\r\n};\r\n\r\nstatic PyMethodDef cpv_methods[] = {\r\n    {NULL} \/* Sentinel *\/\r\n};\r\n\r\nPyMODINIT_FUNC\r\ninitcpv(void)\r\n{\r\n    PyObject* m;\r\n\r\n    cpv_Type.tp_new = PyType_GenericNew;\r\n    if (PyType_Ready(cpv_CPVType) &lt; 0)\r\n        return;\r\n\r\n    m = Py_InitModule3(\"cpv\", cpv_methods,\r\n        \"Example module that creates an extension type.\");\r\n\r\n    Py_INCREF(&amp;cpv_CPVType);\r\n    PyModule_AddObject(m, \"CPV\", (PyObject *)&amp;cpv_CPVType);\r\n}\r\n<\/code><\/pre>\n<p>Now my struct should include PyObject_HEAD, which basically provides object type and reference counting information and also I specify sizeof(cpv_CPVObject), so that python knows how much memory to allocate when creating an instance.<\/p>\n<p>So now it&#8217;s clear that I can&#8217;t just use my initial C struct to manage CPV objects in python. One solution I see is to call cpv_alloc while executing tp_init, then copy corresponding fields from struct cpv to the struct cpv_CPVObject and free struct cpv, but that seems too much of a hassle. Let&#8217;s say we want to compare two atoms, from python view we receive two PyObject* which again should be converted to pure C cpv structs to perform actual operations.<br \/>\nMaybe it&#8217;s possible to somehow redefine tp_new, tp_alloc.<\/p>\n<p>Alright, I&#8217;ve came up with smth that seems an appropriate solution, this way when we have to use Python\/C API, let python do allocations and just pass self to the cpv function, also substitute alloc family functions with python specific to use python private heap.<\/p>\n<pre><code>\r\n#ifdef PYTHONC\r\n#define malloc  PyMem_Malloc\r\n#define realloc PyMem_Realloc\r\n#define free    PyMem_Free\r\n#endif\r\n\r\ntypedef struct {\r\n#ifdef PYTHONC\r\n    PyObject_HEAD\r\n#endif\r\n    char *cat;\r\n    char *pkg;\r\n    char *ver;\r\n} cpv;\r\n\r\n#include \"cpv.h\"\r\n\r\ncpv *cpv_alloc(char *cpv_string\r\n#ifdef PYTHONC\r\n, cpv *self\r\n#endif\r\n) {\r\n    char *ptr;\r\n#ifdef PYTHONC\r\n    cpv *c = self;\r\n    ptr = malloc(strlen(cpv_string)+1);\r\n    c-&gt;cat = ptr;\r\n#else\r\n    cpv *c = malloc(sizeof(cpv)+strlen(cpv_string)+1);\r\n    ptr = (char*)c;\r\n    c-&gt;cat = ptr + sizeof(cpv);\r\n#endif\r\n    strcpy(c-&gt;cat, cpv_string);\r\n    c-&gt;pkg = strchr(c-&gt;cat, '\/');\r\n    *(c-&gt;pkg) = '\\0';\r\n    c-&gt;pkg++;\r\n    c-&gt;ver = strrchr(c-&gt;pkg, '-');\r\n    *(c-&gt;ver) = '\\0';\r\n    c-&gt;ver++;\r\n    return c;\r\n}\r\n\r\nstatic int\r\ncpv_init(cpv *self, PyObject *args, PyObject *kwds)\r\n{\r\n    char *cpvstr;\r\n    if (!PyArg_ParseTuple(args, \"s\", &amp;cpvstr))\r\n        return -1;\r\n    if (!cpv_alloc(cpvstr, self)) {\r\n        printf(\"Invalid cpv\\n\");\r\n        return -1; \r\n    }\r\n}\r\n\r\nstatic PyMethodDef cpv_methods[] = {\r\n    {NULL}  \/* Sentinel *\/\r\n};\r\n\r\nstatic PyMemberDef cpv_members[] = {\r\n    {\"category\", T_STRING, offsetof(cpv, cat), READONLY},\r\n    {\"package\",  T_STRING, offsetof(cpv, pkg), READONLY},\r\n    {\"version\",  T_STRING, offsetof(cpv, ver), READONLY},\r\n    {NULL}\r\n};\r\n\r\n\r\nstatic PyTypeObject cpv_cpvType = {\r\n    PyObject_HEAD_INIT(NULL)\r\n    0,                         \/*ob_size*\/\r\n    \"cpv.CPV\",                 \/*tp_name*\/\r\n    sizeof(cpv),               \/*tp_basicsize*\/\r\n    0,                         \/*tp_itemsize*\/\r\n    0,                         \/*tp_dealloc*\/\r\n    0,                         \/*tp_print*\/\r\n    0,                         \/*tp_getattr*\/\r\n    0,                         \/*tp_setattr*\/\r\n    0,                         \/*tp_compare*\/\r\n    0,                         \/*tp_repr*\/\r\n    0,                         \/*tp_as_number*\/\r\n    0,                         \/*tp_as_sequence*\/\r\n    0,                         \/*tp_as_mapping*\/\r\n    0,                         \/*tp_hash *\/\r\n    0,                         \/*tp_call*\/\r\n    0,                         \/*tp_str*\/\r\n    0,                         \/*tp_getattro*\/\r\n    0,                         \/*tp_setattro*\/\r\n    0,                         \/*tp_as_buffer*\/\r\n    Py_TPFLAGS_DEFAULT,        \/*tp_flags*\/\r\n    \"cpv objects\",             \/* tp_doc *\/\r\n    0,                     \/* tp_traverse *\/\r\n    0,                     \/* tp_clear *\/\r\n    0,                     \/* tp_richcompare *\/\r\n    0,                     \/* tp_weaklistoffset *\/\r\n    0,                     \/* tp_iter *\/\r\n    0,                     \/* tp_iternext *\/\r\n    cpv_methods,             \/* tp_methods *\/\r\n    cpv_members,             \/* tp_members *\/\r\n    0,                         \/* tp_getset *\/\r\n    0,                         \/* tp_base *\/\r\n    0,                         \/* tp_dict *\/\r\n    0,                         \/* tp_descr_get *\/\r\n    0,                         \/* tp_descr_set *\/\r\n    0,                         \/* tp_dictoffset *\/\r\n    (initproc)cpv_init,      \/* tp_init *\/\r\n};\r\n\r\nPyMODINIT_FUNC\r\ninitcpv(void)\r\n{\r\n    PyObject* m;\r\n\r\n    cpv_cpvType.tp_new = PyType_GenericNew;\r\n    if (PyType_Ready(&amp;cpv_cpvType) &lt; 0)\r\n        return;\r\n\r\n    m = Py_InitModule3(&quot;cpv&quot;, cpv_methods,\r\n                       &quot;Example module that creates an extension type.&quot;);\r\n\r\n    Py_INCREF(&amp;cpv_cpvType);\r\n    PyModule_AddObject(m, &quot;CPV&quot;, (PyObject *)&amp;cpv_cpvType);\r\n}\r\n<\/code><\/pre>\n<p>Though the question remains open in case I want to access struct member which is itself struct since for python it should be PyObject *. I believe it&#8217;s possible to somehow hack into python memory allocation and do the right things without ugly ifdefs.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>First, some brief notion about the project I am working on &#8211; libebuild.The idea is to\u00a0implement efficient and well-tested C library which provides functionality for working with Gentoo ebuilds and intended\u00a0to be used in package managers. Libebuild repo The initial thought is to provide pure C implementation with well defined API, so that it also &hellip; <a href=\"https:\/\/blogs.gentoo.org\/gsoc2016-libebuild\/2016\/05\/30\/my-first-gsoc-week-at-gentoo-foundation\/\" class=\"more-link\">Continue reading <span class=\"screen-reader-text\">My first GSoC week at Gentoo Foundation<\/span><\/a><\/p>\n","protected":false},"author":162,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":[],"categories":[1],"tags":[],"jetpack_featured_media_url":"","_links":{"self":[{"href":"https:\/\/blogs.gentoo.org\/gsoc2016-libebuild\/wp-json\/wp\/v2\/posts\/6"}],"collection":[{"href":"https:\/\/blogs.gentoo.org\/gsoc2016-libebuild\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blogs.gentoo.org\/gsoc2016-libebuild\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blogs.gentoo.org\/gsoc2016-libebuild\/wp-json\/wp\/v2\/users\/162"}],"replies":[{"embeddable":true,"href":"https:\/\/blogs.gentoo.org\/gsoc2016-libebuild\/wp-json\/wp\/v2\/comments?post=6"}],"version-history":[{"count":20,"href":"https:\/\/blogs.gentoo.org\/gsoc2016-libebuild\/wp-json\/wp\/v2\/posts\/6\/revisions"}],"predecessor-version":[{"id":27,"href":"https:\/\/blogs.gentoo.org\/gsoc2016-libebuild\/wp-json\/wp\/v2\/posts\/6\/revisions\/27"}],"wp:attachment":[{"href":"https:\/\/blogs.gentoo.org\/gsoc2016-libebuild\/wp-json\/wp\/v2\/media?parent=6"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blogs.gentoo.org\/gsoc2016-libebuild\/wp-json\/wp\/v2\/categories?post=6"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blogs.gentoo.org\/gsoc2016-libebuild\/wp-json\/wp\/v2\/tags?post=6"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}