{"id":2227561,"url":"http://patchwork.ozlabs.org/api/1.2/patches/2227561/?format=json","web_url":"http://patchwork.ozlabs.org/project/linux-gpio/patch/20260423222125.29097-9-vfazio@gmail.com/","project":{"id":42,"url":"http://patchwork.ozlabs.org/api/1.2/projects/42/?format=json","name":"Linux GPIO development","link_name":"linux-gpio","list_id":"linux-gpio.vger.kernel.org","list_email":"linux-gpio@vger.kernel.org","web_url":"","scm_url":"","webscm_url":"","list_archive_url":"","list_archive_url_format":"","commit_url_format":""},"msgid":"<20260423222125.29097-9-vfazio@gmail.com>","list_archive_url":null,"date":"2026-04-23T22:21:25","name":"[libgpiod,v2,8/8] bindings: python: tests: migrate the gpiosim module to multi-phase init","commit_ref":null,"pull_url":null,"state":"new","archived":false,"hash":"f62357c786a9b4c646e7cfc01e18781fdeca08e7","submitter":{"id":78694,"url":"http://patchwork.ozlabs.org/api/1.2/people/78694/?format=json","name":"Vincent Fazio","email":"vfazio@gmail.com"},"delegate":null,"mbox":"http://patchwork.ozlabs.org/project/linux-gpio/patch/20260423222125.29097-9-vfazio@gmail.com/mbox/","series":[{"id":501257,"url":"http://patchwork.ozlabs.org/api/1.2/series/501257/?format=json","web_url":"http://patchwork.ozlabs.org/project/linux-gpio/list/?series=501257","date":"2026-04-23T22:21:18","name":"bindings: python: modernize C extensions","version":2,"mbox":"http://patchwork.ozlabs.org/series/501257/mbox/"}],"comments":"http://patchwork.ozlabs.org/api/patches/2227561/comments/","check":"pending","checks":"http://patchwork.ozlabs.org/api/patches/2227561/checks/","tags":{},"related":[],"headers":{"Return-Path":"\n <linux-gpio+bounces-35440-incoming=patchwork.ozlabs.org@vger.kernel.org>","X-Original-To":["incoming@patchwork.ozlabs.org","linux-gpio@vger.kernel.org"],"Delivered-To":"patchwork-incoming@legolas.ozlabs.org","Authentication-Results":["legolas.ozlabs.org;\n\tdkim=pass (2048-bit key;\n unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256\n header.s=20251104 header.b=neA4dhi2;\n\tdkim-atps=neutral","legolas.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=vger.kernel.org\n (client-ip=172.234.253.10; helo=sea.lore.kernel.org;\n envelope-from=linux-gpio+bounces-35440-incoming=patchwork.ozlabs.org@vger.kernel.org;\n receiver=patchwork.ozlabs.org)","smtp.subspace.kernel.org;\n\tdkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com\n header.b=\"neA4dhi2\"","smtp.subspace.kernel.org;\n arc=none smtp.client-ip=209.85.210.45","smtp.subspace.kernel.org;\n dmarc=pass (p=none dis=none) header.from=gmail.com","smtp.subspace.kernel.org;\n spf=pass smtp.mailfrom=gmail.com"],"Received":["from sea.lore.kernel.org (sea.lore.kernel.org [172.234.253.10])\n\t(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n\t key-exchange x25519)\n\t(No client certificate requested)\n\tby legolas.ozlabs.org (Postfix) with ESMTPS id 4g1rDW4WlPz1y2d\n\tfor <incoming@patchwork.ozlabs.org>; Fri, 24 Apr 2026 08:22:03 +1000 (AEST)","from smtp.subspace.kernel.org (conduit.subspace.kernel.org\n [100.90.174.1])\n\tby sea.lore.kernel.org (Postfix) with ESMTP id AB398301F4AD\n\tfor <incoming@patchwork.ozlabs.org>; Thu, 23 Apr 2026 22:21:57 +0000 (UTC)","from localhost.localdomain (localhost.localdomain [127.0.0.1])\n\tby smtp.subspace.kernel.org (Postfix) with ESMTP id 5885537C916;\n\tThu, 23 Apr 2026 22:21:57 +0000 (UTC)","from mail-ot1-f45.google.com (mail-ot1-f45.google.com\n [209.85.210.45])\n\t(using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits))\n\t(No client certificate requested)\n\tby smtp.subspace.kernel.org (Postfix) with ESMTPS id A8A3A36165E\n\tfor <linux-gpio@vger.kernel.org>; Thu, 23 Apr 2026 22:21:55 +0000 (UTC)","by mail-ot1-f45.google.com with SMTP id\n 46e09a7af769-7d4be94eeacso7244902a34.2\n        for <linux-gpio@vger.kernel.org>;\n Thu, 23 Apr 2026 15:21:55 -0700 (PDT)","from Zephyrus.localdomain ([131.93.209.211])\n        by smtp.gmail.com with ESMTPSA id\n 586e51a60fabf-42b930afa63sm17784598fac.5.2026.04.23.15.21.53\n        (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n        Thu, 23 Apr 2026 15:21:53 -0700 (PDT)"],"ARC-Seal":"i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116;\n\tt=1776982917; cv=none;\n b=kvmp9NHcvoD8iJgrOvHKuiS0u0Fi4bLCB0XS0aKwoITyvp+uRplZnihWuLo/4VWtGVoUmvsVimSwW/OtcbOhD5lmHe9ruCfr9F3cggTRGVAj847Op4G3nztBdRLYknhg6A+O478bvnNvebVMW/r82+1MEpAHcJ9RDFjXK49tjSE=","ARC-Message-Signature":"i=1; a=rsa-sha256; d=subspace.kernel.org;\n\ts=arc-20240116; t=1776982917; c=relaxed/simple;\n\tbh=nDW3zQJrJEzfq2EQ1zRjT1Yq/XLcigrgez47P0cyZR0=;\n\th=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References:\n\t MIME-Version;\n b=UZ6PMik28zfxuP8LQgR2FCpZjLvTXy/UY5lgW9AKIVPqzA9MLeT0i6v/Nhtf5D+pKEIml6n66mvaqVuIh4/2fjM617Z+WGh4hxb5Vl7L+dK2EyeWbO3S8TSmWq3MNMak45tEzr48h5gwiHIrnGAgSYRKVMo2xkxQyUsFmp/nYrU=","ARC-Authentication-Results":"i=1; smtp.subspace.kernel.org;\n dmarc=pass (p=none dis=none) header.from=gmail.com;\n spf=pass smtp.mailfrom=gmail.com;\n dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com\n header.b=neA4dhi2; arc=none smtp.client-ip=209.85.210.45","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n        d=gmail.com; s=20251104; t=1776982914; x=1777587714;\n darn=vger.kernel.org;\n        h=content-transfer-encoding:mime-version:references:in-reply-to\n         :message-id:date:subject:cc:to:from:from:to:cc:subject:date\n         :message-id:reply-to;\n        bh=QLcmhIcwdWxUpVcuACLh1E0xbICxTuenu22WdCqYPJw=;\n        b=neA4dhi2kCtvRAT8J/bwZSgumwS4FP1y28pnuyTptJ20GYss0nGnSAKt67cP2m88d5\n         DeVJ42z88foZwDDQzY51zWkOn15lr+FCGdToZwz5eiri3kiwcb75ZhWTUcyjQw6yB+Eb\n         JHK6bOrJV1Zu0FOHlD0GfCBujAkYoJi7TX3YvdWwZo1HmM9o2FAvw87q9fwumucZgoAM\n         +SeE8NdpZuY2fweu9uSsen5z6tLknOsumwJfcAWl5raTaM1ooyfWHuWieClD7JCL+eFK\n         dqV1rOSAxKVhjF9Uy4ogxDLDceSVMtsqaTYfsNkE9Z0OojX9BlCrxdGBzXGusSIhC4Qj\n         8m4g==","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n        d=1e100.net; s=20251104; t=1776982914; x=1777587714;\n        h=content-transfer-encoding:mime-version:references:in-reply-to\n         :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from\n         :to:cc:subject:date:message-id:reply-to;\n        bh=QLcmhIcwdWxUpVcuACLh1E0xbICxTuenu22WdCqYPJw=;\n        b=GXKOeoGG68t0jkLJQRrkGKSjJLqDPv5xmIFq4S3VvwGtHGcqzMgEBFl/ZtFcTL9BQr\n         6S1/b8QNShu/aNTfz3Irwa8uvb9OW4CPDKmsNZYE0QBSqoaSJ2pHycn9uMV+m8BDJ04e\n         nNWRiGhXi/gCXQWpQ8QZWjLj1nACOR4TEdmQkiwEftdiEZasDnkEjTkGwZyN2TfhJ1BT\n         NhXm1xcYYFNleB+HPmXPJsA7OswuBsKfRjdkUe0VxiOEMxnu03fVEIIsVT2ouRlsoGTx\n         uvQ2SGdaDvwutkquIdOsacWzbsoS74CPZN3sp22U5nRqZXbz2Wmj8MuMFL1mZPsyDiN8\n         6TPQ==","X-Gm-Message-State":"AOJu0Yx+L6pQQhyvX9GwFl3jW19u2aXNYvAAcV+PRa4OZFtEWlm7W6jN\n\tH5nqjoma85OxOw82+vtR73cjy3b3PQr+hn6mQc1GuWrM/YanmRAnOYGoBpRoNQ==","X-Gm-Gg":"AeBDietFBZKtXMUR3m5GXztPpqgzddVO09C+UbxRgGZCfSl39Dv9u2psuhDLCGvmmlf\n\tMNmx7hfBhXAH0AQim9rAoFXxWNEuitrlUVjiiDGoMXRnfgAkpU4dqlX0ym62svde3wNY1fzFMjh\n\tlK051Ux+yyui45xPSP+UNjFjaZa6hVtZd+Nivm0iPS+QdA4oy02LOB9q9cFApDWpYx2inJ9BRhw\n\tWsFSrqEsCaQUKUqKGXeDCNQDTQfKdPkyl8ykm8UQofAj5LqTO3HR6YnBOjvfs8pdbMYrAlhW38q\n\tfZ6ms0TWAfNPtcmC9Q7D4+y+EAS8HotnlD4vkZoXgHtLFSya1426/dlfI9/Anks3hkBHex3xpbd\n\tCFEbeh3T4QjBMFhXt0ai8RGZmuTPNplD0JPjEuHiCnHv1GsBO1A39TR+MDQMFgJZCIkuCAOl9UD\n\td7zlwtGMi/T8si/bAAedGJRaaF/uh6GPSkd4HSJdTm0H9UUwGUeuMydKHWkqv3GDNDEB8LFh2K","X-Received":"by 2002:a05:6820:982:b0:694:9070:8227 with SMTP id\n 006d021491bc7-6949070840dmr9705081eaf.41.1776982914378;\n        Thu, 23 Apr 2026 15:21:54 -0700 (PDT)","From":"Vincent Fazio <vfazio@gmail.com>","To":"linux-gpio@vger.kernel.org","Cc":"brgl@kernel.org,\n\tVincent Fazio <vfazio@gmail.com>","Subject":"[libgpiod][PATCH v2 8/8] bindings: python: tests: migrate the gpiosim\n module to multi-phase init","Date":"Thu, 23 Apr 2026 17:21:25 -0500","Message-ID":"<20260423222125.29097-9-vfazio@gmail.com>","X-Mailer":"git-send-email 2.43.0","In-Reply-To":"<20260423222125.29097-1-vfazio@gmail.com>","References":"<20260423222125.29097-1-vfazio@gmail.com>","Precedence":"bulk","X-Mailing-List":"linux-gpio@vger.kernel.org","List-Id":"<linux-gpio.vger.kernel.org>","List-Subscribe":"<mailto:linux-gpio+subscribe@vger.kernel.org>","List-Unsubscribe":"<mailto:linux-gpio+unsubscribe@vger.kernel.org>","MIME-Version":"1.0","Content-Transfer-Encoding":"8bit"},"content":"Single-phase initialization has been classified as legacy within CPython\ndocumentation [0] with multi-phase being its successor [1].\n\nAs such, switch to the new methodology.\n\nSwitching requires converting `gpiosim.Chip` to be a heap-type [2] in\norder to access the module's state [3].\n\n[0]: https://docs.python.org/3/c-api/extension-modules.html#legacy-single-phase-initialization\n[1]: https://docs.python.org/3/c-api/extension-modules.html#multi-phase-initialization\n[2]: https://docs.python.org/3/c-api/typeobj.html#heap-types\n[3]: https://docs.python.org/3/howto/isolating-extensions.html#heap-types\n\nSigned-off-by: Vincent Fazio <vfazio@gmail.com>\n---\n bindings/python/tests/gpiosim/ext.c | 151 +++++++++++++++++-----------\n 1 file changed, 94 insertions(+), 57 deletions(-)","diff":"diff --git a/bindings/python/tests/gpiosim/ext.c b/bindings/python/tests/gpiosim/ext.c\nindex 1ebd5af..9954dd5 100644\n--- a/bindings/python/tests/gpiosim/ext.c\n+++ b/bindings/python/tests/gpiosim/ext.c\n@@ -45,21 +45,6 @@ struct module_state {\n \tstruct gpiosim_ctx *sim_ctx;\n };\n \n-static void free_module_state(void *mod)\n-{\n-\tstruct module_state *state = PyModule_GetState((PyObject *)mod);\n-\n-\tif (state->sim_ctx)\n-\t\tgpiosim_ctx_unref(state->sim_ctx);\n-}\n-\n-static PyModuleDef module_def = {\n-\tPyModuleDef_HEAD_INIT,\n-\t.m_name = \"gpiosim._ext\",\n-\t.m_size = sizeof(struct module_state),\n-\t.m_free = free_module_state,\n-};\n-\n typedef struct {\n \tPyObject_HEAD\n \tstruct gpiosim_dev *dev;\n@@ -71,13 +56,11 @@ static int chip_init(chip_object *self,\n \t\t     PyObject *Py_UNUSED(ignored1))\n {\n \tstruct module_state *state;\n-\tPyObject *mod;\n \n-\tmod = PyState_FindModule(&module_def);\n-\tif (!mod)\n-\t\treturn -1;\n+\tstate = PyType_GetModuleState(Py_TYPE(self));\n \n-\tstate = PyModule_GetState(mod);\n+\tif (!state)\n+\t\treturn -1;\n \n \tself->dev = gpiosim_dev_new(state->sim_ctx);\n \tif (!self->dev) {\n@@ -111,12 +94,14 @@ static void chip_finalize(chip_object *self)\n static void chip_dealloc(PyObject *self)\n {\n \tint ret;\n+\tPyTypeObject *tp = Py_TYPE(self);\n \n \tret = PyObject_CallFinalizerFromDealloc(self);\n \tif (ret < 0)\n \t\treturn;\n \n \tPyObject_Free(self);\n+\tPy_DECREF(tp);\n }\n \n static PyObject *chip_dev_path(chip_object *self, void *Py_UNUSED(ignored))\n@@ -289,58 +274,110 @@ static PyMethodDef chip_methods[] = {\n \t{ }\n };\n \n-static PyTypeObject chip_type = {\n-\tPyVarObject_HEAD_INIT(NULL, 0)\n-\t.tp_name = \"gpiosim.Chip\",\n-\t.tp_basicsize = sizeof(chip_object),\n-\t.tp_flags = Py_TPFLAGS_DEFAULT,\n-\t.tp_new = PyType_GenericNew,\n-\t.tp_init = (initproc)chip_init,\n-\t.tp_finalize = (destructor)chip_finalize,\n-\t.tp_dealloc = (destructor)chip_dealloc,\n-\t.tp_methods = chip_methods,\n-\t.tp_getset = chip_getset,\n+static PyType_Slot chip_type_slots[] = {\n+\t{Py_tp_new, PyType_GenericNew},\n+\t{Py_tp_init, (initproc)chip_init},\n+\t{Py_tp_finalize, (destructor)chip_finalize},\n+\t{Py_tp_dealloc, (destructor)chip_dealloc},\n+\t{Py_tp_methods, chip_methods},\n+\t{Py_tp_getset, chip_getset},\n+\t{0, 0}\n };\n \n-PyMODINIT_FUNC PyInit__ext(void)\n+/*\n+See xxlimited.c and _bz2module.c for inspiration.\n+\n+As part of transitioning to multi-phase module initialization, the\n+gpiosim.Chip type needs to become heap allocated so that it can access\n+module state.\n+\n+We disallow subclassing by not specifying Py_TPFLAGS_BASETYPE. This\n+allows the module to use PyType_GetModuleState() since it may otherwise\n+not return the proper module if a subclass is invoking the method.\n+\n+Note:\n+We do not hold PyObject references so no reference cycles should exist. As such,\n+we do not set Py_TPFLAGS_HAVE_GC nor define either tp_traverse or tp_clear.\n+\n+There is still some ongoing debate about this this use case however:\n+  https://github.com/python/cpython/issues/116946\n+\n+Note:\n+If we allow subclassing in the future, reconsider use of PyObject_Free vs using\n+the function defined in the tp_free slot.\n+\n+See also:\n+  https://github.com/python/cpython/pull/138329#issuecomment-3242079564\n+  https://github.com/python/cpython/issues/116946#issuecomment-3242135537\n+  https://github.com/python/cpython/pull/139073\n+  https://github.com/python/cpython/commit/ec689187957cc80af56b9a63251bbc295bafd781\n+*/\n+static PyType_Spec chip_type_spec = {\n+\t.name = \"gpiosim.Chip\",\n+\t.basicsize = sizeof(chip_object),\n+\t.flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_IMMUTABLETYPE),\n+\t.slots = chip_type_slots,\n+};\n+\n+static int module_exec(PyObject *module)\n {\n \tconst struct module_const *modconst;\n \tstruct module_state *state;\n-\tPyObject *module;\n+\tPyObject *chip_type_obj;\n \tint ret;\n \n-\tmodule = PyModule_Create(&module_def);\n-\tif (!module)\n-\t\treturn NULL;\n-\n-\tret = PyState_AddModule(module, &module_def);\n-\tif (ret) {\n-\t\tPy_DECREF(module);\n-\t\treturn NULL;\n-\t}\n-\n \tstate = PyModule_GetState(module);\n \n \tstate->sim_ctx = gpiosim_ctx_new();\n \tif (!state->sim_ctx) {\n-\t\tPy_DECREF(module);\n-\t\treturn PyErr_SetFromErrno(PyExc_OSError);\n+\t\tPyErr_SetFromErrno(PyExc_OSError);\n+\t\treturn -1;\n \t}\n \n-\tret = PyModule_AddType(module, &chip_type);\n-\tif (ret) {\n-\t\tPy_DECREF(module);\n-\t\treturn NULL;\n-\t}\n+\tchip_type_obj = PyType_FromModuleAndSpec(module, &chip_type_spec, NULL);\n+\n+\tif (!chip_type_obj)\n+\t\treturn -1;\n+\n+\tret = PyModule_AddType(module, (PyTypeObject*)chip_type_obj);\n+\tPy_DECREF(chip_type_obj);\n+\tif (ret < 0)\n+\t\treturn -1;\n \n \tfor (modconst = module_constants; modconst->name; modconst++) {\n-\t\tret = PyModule_AddIntConstant(module,\n-\t\t\t\t\t      modconst->name, modconst->val);\n-\t\tif (ret) {\n-\t\t\tPy_DECREF(module);\n-\t\t\treturn NULL;\n-\t\t}\n+\t\tret = PyModule_AddIntConstant(module, modconst->name,\n+\t\t\t\t\t      modconst->val);\n+\t\tif (ret < 0)\n+\t\t\treturn -1;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static void free_module_state(void *mod)\n+{\n+\tstruct module_state *state = PyModule_GetState((PyObject *)mod);\n+\n+\tif (state->sim_ctx) {\n+\t\tgpiosim_ctx_unref(state->sim_ctx);\n+\t\tstate->sim_ctx = NULL;\n \t}\n+}\n+\n+static struct PyModuleDef_Slot module_slots[] = {\n+\t{Py_mod_exec, module_exec},\n+\t{0, NULL},\n+};\n+\n+static PyModuleDef module_def = {\n+\tPyModuleDef_HEAD_INIT,\n+\t.m_name = \"gpiosim._ext\",\n+\t.m_size = sizeof(struct module_state),\n+\t.m_free = free_module_state,\n+\t.m_slots = module_slots,\n+};\n \n-\treturn module;\n+PyMODINIT_FUNC PyInit__ext(void)\n+{\n+\treturn PyModuleDef_Init(&module_def);\n }\n","prefixes":["libgpiod","v2","8/8"]}