get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

GET /api/patches/2231423/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 2231423,
    "url": "http://patchwork.ozlabs.org/api/patches/2231423/?format=api",
    "web_url": "http://patchwork.ozlabs.org/project/qemu-devel/patch/20260430172204.1006673-18-pbonzini@redhat.com/",
    "project": {
        "id": 14,
        "url": "http://patchwork.ozlabs.org/api/projects/14/?format=api",
        "name": "QEMU Development",
        "link_name": "qemu-devel",
        "list_id": "qemu-devel.nongnu.org",
        "list_email": "qemu-devel@nongnu.org",
        "web_url": "",
        "scm_url": "",
        "webscm_url": "",
        "list_archive_url": "",
        "list_archive_url_format": "",
        "commit_url_format": ""
    },
    "msgid": "<20260430172204.1006673-18-pbonzini@redhat.com>",
    "list_archive_url": null,
    "date": "2026-04-30T17:21:23",
    "name": "[PULL,17/58] minikconf: add mypy annotations",
    "commit_ref": null,
    "pull_url": null,
    "state": "new",
    "archived": false,
    "hash": "4fc697b7571db16b04b5a4c56e2cadfed76d041f",
    "submitter": {
        "id": 2701,
        "url": "http://patchwork.ozlabs.org/api/people/2701/?format=api",
        "name": "Paolo Bonzini",
        "email": "pbonzini@redhat.com"
    },
    "delegate": null,
    "mbox": "http://patchwork.ozlabs.org/project/qemu-devel/patch/20260430172204.1006673-18-pbonzini@redhat.com/mbox/",
    "series": [
        {
            "id": 502347,
            "url": "http://patchwork.ozlabs.org/api/series/502347/?format=api",
            "web_url": "http://patchwork.ozlabs.org/project/qemu-devel/list/?series=502347",
            "date": "2026-04-30T17:21:16",
            "name": "[PULL,01/58] pythondeps: bump to meson 1.11.1",
            "version": 1,
            "mbox": "http://patchwork.ozlabs.org/series/502347/mbox/"
        }
    ],
    "comments": "http://patchwork.ozlabs.org/api/patches/2231423/comments/",
    "check": "pending",
    "checks": "http://patchwork.ozlabs.org/api/patches/2231423/checks/",
    "tags": {},
    "related": [],
    "headers": {
        "Return-Path": "<qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org>",
        "X-Original-To": "incoming@patchwork.ozlabs.org",
        "Delivered-To": "patchwork-incoming@legolas.ozlabs.org",
        "Authentication-Results": [
            "legolas.ozlabs.org;\n\tdkim=pass (1024-bit key;\n unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256\n header.s=mimecast20190719 header.b=IKPTmJQS;\n\tdkim=pass (2048-bit key;\n unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256\n header.s=google header.b=oVNbRkhQ;\n\tdkim-atps=neutral",
            "legolas.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=nongnu.org\n (client-ip=209.51.188.17; helo=lists1p.gnu.org;\n envelope-from=qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org;\n receiver=patchwork.ozlabs.org)"
        ],
        "Received": [
            "from lists1p.gnu.org (lists1p.gnu.org [209.51.188.17])\n\t(using TLSv1.2 with cipher ECDHE-ECDSA-AES256-GCM-SHA384 (256/256 bits))\n\t(No client certificate requested)\n\tby legolas.ozlabs.org (Postfix) with ESMTPS id 4g61Jm3fBvz1yHZ\n\tfor <incoming@patchwork.ozlabs.org>; Fri, 01 May 2026 03:25:12 +1000 (AEST)",
            "from localhost ([::1] helo=lists1p.gnu.org)\n\tby lists1p.gnu.org with esmtp (Exim 4.90_1)\n\t(envelope-from <qemu-devel-bounces@nongnu.org>)\n\tid 1wIV6V-0004nW-R1; Thu, 30 Apr 2026 13:23:11 -0400",
            "from eggs.gnu.org ([2001:470:142:3::10])\n by lists1p.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)\n (Exim 4.90_1) (envelope-from <pbonzini@redhat.com>)\n id 1wIV6U-0004mz-86\n for qemu-devel@nongnu.org; Thu, 30 Apr 2026 13:23:10 -0400",
            "from us-smtp-delivery-124.mimecast.com ([170.10.129.124])\n by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)\n (Exim 4.90_1) (envelope-from <pbonzini@redhat.com>)\n id 1wIV6R-0004QD-90\n for qemu-devel@nongnu.org; Thu, 30 Apr 2026 13:23:09 -0400",
            "from mail-qk1-f200.google.com (mail-qk1-f200.google.com\n [209.85.222.200]) by relay.mimecast.com with ESMTP with STARTTLS\n (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id\n us-mta-203-nC_HnT4wPTaMKjYGMKRNAA-1; Thu, 30 Apr 2026 13:23:04 -0400",
            "by mail-qk1-f200.google.com with SMTP id\n af79cd13be357-8f9e55c40a5so148864985a.3\n for <qemu-devel@nongnu.org>; Thu, 30 Apr 2026 10:23:04 -0700 (PDT)",
            "from [192.168.10.48] ([151.49.85.67])\n by smtp.gmail.com with ESMTPSA id\n af79cd13be357-8fbb0fe8572sm25691185a.14.2026.04.30.10.23.00\n (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n Thu, 30 Apr 2026 10:23:01 -0700 (PDT)"
        ],
        "DKIM-Signature": [
            "v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com;\n s=mimecast20190719; t=1777569786;\n h=from:from:reply-to:subject:subject:date:date:message-id:message-id:\n to:to:cc:cc:mime-version:mime-version:content-type:content-type:\n content-transfer-encoding:content-transfer-encoding:\n in-reply-to:in-reply-to:references:references;\n bh=5CzYwYWcXL3BbbDZ2e85ReFWfQVcm5wxQ2rv5xTDKng=;\n b=IKPTmJQSE+/j25nSvS8hhzLt+52DvrPTvYJ4LhumeGQu7Hgb6rU5K+Ude+hWPs5OHJf7GC\n UZ3bErU2N/NuPli1ejhsSlvqjtQT/vu/hjzkcapBM8nUH9cwl4kznChuIUV1Ji6pxN3qjU\n mpNk0dOEvJ2A/RI1/CrJd+wGP2Mufvs=",
            "v=1; a=rsa-sha256; c=relaxed/relaxed;\n d=redhat.com; s=google; t=1777569783; x=1778174583; darn=nongnu.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=5CzYwYWcXL3BbbDZ2e85ReFWfQVcm5wxQ2rv5xTDKng=;\n b=oVNbRkhQ1nRFy3lIUmMbT2g/SY7b+A/s9QaPpqmTb6L1I49NrZrDdEgX6RKIXh9WXa\n 4XC9Uun2S2/32yxLklcArArGyLagMJwI7pxR2cc/MOgTia7Spm82bPmVlSSPNEMJVBVF\n 00Ru6WQKvu4lc77yPUehgtQjW4Igif4v546FJrQjMRiaj18xvDgFMtWVQUk3bddvryx/\n tW/PXToVuQNLSRcRbhGHd4dx51dwdwzbkBZbFsTGHxctHu1XnVQFYAV/+ch7qQ9Q92d2\n Z6apXdSaVSKZA1t/3CZk4yGORaEBWHswzSPaoECaXc3KuyNfeAnEbpFzVYM2nPeOaGgP\n M78g=="
        ],
        "X-MC-Unique": "nC_HnT4wPTaMKjYGMKRNAA-1",
        "X-Mimecast-MFC-AGG-ID": "nC_HnT4wPTaMKjYGMKRNAA_1777569784",
        "X-Google-DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n d=1e100.net; s=20251104; t=1777569783; x=1778174583;\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=5CzYwYWcXL3BbbDZ2e85ReFWfQVcm5wxQ2rv5xTDKng=;\n b=HnFyCxCPZobWll/srEazjfOzJwAqqo3qJQqrlZfkawWgGOasBEbreZ4RzjcCLIiHoh\n FFsB1JOD3lAH43mpufmVBOeBFnIxQRztPtVg9MSnPzDfYGBZOqY6WwkG7PzfFkHw6Ft9\n CXlFA9XGkXMueeas0J4FyFMs3/W0Pcq4b5wF2rUwG1/jN+9Lial19dhYKCxvsLTUq+VO\n /Ut8sbHBABhmt2f5byHdCL34sGnge2WQAwJz+7MiVa97BsN3MVKSDUoLvRz3iH27rGV8\n 82cU5IRGxe1fAfuCy7NK0w+c9YPQxK5fId/mGN7TA2rB9KBP0c+9dPjcYk7LVBjC8PKd\n p0Cw==",
        "X-Gm-Message-State": "AOJu0Yz9diWbLFqx2aP0Ju0SEB+tHiHqmM5N+aqBCy1NVX//rC2R2kCC\n DZMmGpww0CuWGIg/fvDF6ZcUB2PYCTqPbhJHo/KxsCA5/GNpjSsB3x3z7XIhcbXoC3oQOHkDWrB\n lVckYm14V3e8acTYKPiQ5892uxtdPAuiWx2Fvy+oVsZZEmjuhs1F4FRebSDBWalustOax2AQ7JV\n QSf4TvJOSByUvPIG3vGPfBYezeujmFJS8YCkXma79D",
        "X-Gm-Gg": "AeBDietrZmR8ZS7sdtvDCv4PoLGa26XZaEwa2iixcNX7EgfOwFZh9H8Pjgrc3F/IwPq\n t3n8ncFIolBa7DFCUG7W7oqa939eAYXl/6FMObi0mtFap94llp3MP5KsjH5bZkp/4jM436Lwhcl\n 5hX4vF6h51jtxKz4mmCv6CHTvSI4QM1GmyssP33iPZtM1JTvpLZIaEyXOrLCKj4nXb0L9dYCVma\n AnSS3COyx06cWy/+ryIdyUTWJHxTOaSe+vrv8vAkqM/+kKYOO0AX4/CSnSxJ4tVp+TOYdl40jjZ\n dANGBOcD4xPlyzBjeKgy2WOxgFhrskGGE/otn5FHOrUJI32t8ZhTg259oJUMJ7jVHNGaQFknybA\n tyIAv5a0ATzPf8UhKzZ4I3R6kcfxpA25ZdS2F2ev+Kr6323Z2IccMG5xX194NzNdZnpXUiCYSLg\n edY8lL9CNM5qq+jqTxblykyaG4EA2rKp1yBK8=",
        "X-Received": [
            "by 2002:a05:620a:2591:b0:8d8:ba4a:596c with SMTP id\n af79cd13be357-8fa8961620dmr625117585a.51.1777569782850;\n Thu, 30 Apr 2026 10:23:02 -0700 (PDT)",
            "by 2002:a05:620a:2591:b0:8d8:ba4a:596c with SMTP id\n af79cd13be357-8fa8961620dmr625107985a.51.1777569781957;\n Thu, 30 Apr 2026 10:23:01 -0700 (PDT)"
        ],
        "From": "Paolo Bonzini <pbonzini@redhat.com>",
        "To": "qemu-devel@nongnu.org",
        "Cc": "=?utf-8?q?Marc-Andr=C3=A9_Lureau?= <marcandre.lureau@redhat.com>",
        "Subject": "[PULL 17/58] minikconf: add mypy annotations",
        "Date": "Thu, 30 Apr 2026 19:21:23 +0200",
        "Message-ID": "<20260430172204.1006673-18-pbonzini@redhat.com>",
        "X-Mailer": "git-send-email 2.54.0",
        "In-Reply-To": "<20260430172204.1006673-1-pbonzini@redhat.com>",
        "References": "<20260430172204.1006673-1-pbonzini@redhat.com>",
        "MIME-Version": "1.0",
        "Content-Type": "text/plain; charset=UTF-8",
        "Content-Transfer-Encoding": "8bit",
        "Received-SPF": "pass client-ip=170.10.129.124;\n envelope-from=pbonzini@redhat.com;\n helo=us-smtp-delivery-124.mimecast.com",
        "X-Spam_score_int": "-20",
        "X-Spam_score": "-2.1",
        "X-Spam_bar": "--",
        "X-Spam_report": "(-2.1 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.001,\n DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1,\n RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H4=0.001, RCVD_IN_MSPIKE_WL=0.001,\n SPF_HELO_PASS=-0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no",
        "X-Spam_action": "no action",
        "X-BeenThere": "qemu-devel@nongnu.org",
        "X-Mailman-Version": "2.1.29",
        "Precedence": "list",
        "List-Id": "qemu development <qemu-devel.nongnu.org>",
        "List-Unsubscribe": "<https://lists.nongnu.org/mailman/options/qemu-devel>,\n <mailto:qemu-devel-request@nongnu.org?subject=unsubscribe>",
        "List-Archive": "<https://lists.nongnu.org/archive/html/qemu-devel>",
        "List-Post": "<mailto:qemu-devel@nongnu.org>",
        "List-Help": "<mailto:qemu-devel-request@nongnu.org?subject=help>",
        "List-Subscribe": "<https://lists.nongnu.org/mailman/listinfo/qemu-devel>,\n <mailto:qemu-devel-request@nongnu.org?subject=subscribe>",
        "Errors-To": "qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org",
        "Sender": "qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org"
    },
    "content": "Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>\nSigned-off-by: Paolo Bonzini <pbonzini@redhat.com>\n---\n python/tests/linters.py |   3 +\n scripts/minikconf.py    | 220 ++++++++++++++++++++++------------------\n 2 files changed, 122 insertions(+), 101 deletions(-)",
    "diff": "diff --git a/python/tests/linters.py b/python/tests/linters.py\nindex 4ce4e8a9805..82b88aebb20 100644\n--- a/python/tests/linters.py\n+++ b/python/tests/linters.py\n@@ -65,6 +65,9 @@ def test_isort_qapi_sphinx(self):\n             ]\n         )\n \n+    def test_mypy_minikconf(self):\n+        check_call([sys.executable, \"-m\", \"mypy\", \"../scripts/minikconf.py\"])\n+\n     def test_mypy_pkg(self):\n         check_call([sys.executable, \"-m\", \"mypy\", \"-p\", \"qemu\"])\n \ndiff --git a/scripts/minikconf.py b/scripts/minikconf.py\nindex 7f042daecbf..837c68a2469 100644\n--- a/scripts/minikconf.py\n+++ b/scripts/minikconf.py\n@@ -17,31 +17,34 @@\n import random\n import re\n import sys\n+import typing as T\n from dataclasses import dataclass\n \n __all__ = [ 'KconfigDataError', 'KconfigParserError',\n             'KconfigData', 'KconfigParser' ,\n             'defconfig', 'allyesconfig', 'allnoconfig', 'randconfig' ]\n \n+Mangler = T.Callable[[bool], bool]\n+\n @dataclass\n class IncludeInfo:\n     file: str\n     line: int\n     parent: IncludeInfo | None\n \n-    def __iter__(self):\n-        inf = self\n+    def __iter__(self) -> T.Iterator[str]:\n+        inf: IncludeInfo | None = self\n         while inf is not None:\n             yield \"%s:%d\" % (inf.file, inf.line)\n             inf = inf.parent\n \n-    def error_path(self):\n+    def error_path(self) -> str:\n         res = \"\"\n         for loc in self:\n             res = \"In file included from %s:\\n\" % loc + res\n         return res\n \n-def debug_print(*args):\n+def debug_print(*args: object) -> None:\n     #print('# ' + (' '.join(str(x) for x in args)))\n     pass\n \n@@ -56,81 +59,81 @@ def debug_print(*args):\n # -------------------------------------------\n \n class KconfigDataError(Exception):\n-    def __init__(self, msg):\n+    def __init__(self, msg: str) -> None:\n         self.msg = msg\n \n-    def __str__(self):\n+    def __str__(self) -> str:\n         return self.msg\n \n-allyesconfig = lambda x: True\n-allnoconfig = lambda x: False\n-defconfig = lambda x: x\n-randconfig = lambda x: random.randint(0, 1) == 1\n+allyesconfig: Mangler = lambda x: True\n+allnoconfig: Mangler = lambda x: False\n+defconfig: Mangler = lambda x: x\n+randconfig: Mangler = lambda x: random.randint(0, 1) == 1\n \n class KconfigData:\n     class Expr:\n-        def __and__(self, rhs):\n+        def __and__(self, rhs: KconfigData.Expr) -> KconfigData.Expr:\n             return KconfigData.AND(self, rhs)\n-        def __or__(self, rhs):\n+        def __or__(self, rhs: KconfigData.Expr) -> KconfigData.Expr:\n             return KconfigData.OR(self, rhs)\n-        def __invert__(self):\n+        def __invert__(self) -> KconfigData.Expr:\n             return KconfigData.NOT(self)\n \n         # Abstract methods\n-        def add_edges_to(self, var):\n+        def add_edges_to(self, var: KconfigData.Var) -> None:\n             pass\n-        def evaluate(self):\n+        def evaluate(self) -> bool:\n             assert False\n \n     class AND(Expr):\n-        def __init__(self, lhs, rhs):\n+        def __init__(self, lhs: KconfigData.Expr, rhs: KconfigData.Expr) -> None:\n             self.lhs = lhs\n             self.rhs = rhs\n-        def __str__(self):\n+        def __str__(self) -> str:\n             return \"(%s && %s)\" % (self.lhs, self.rhs)\n \n-        def add_edges_to(self, var):\n+        def add_edges_to(self, var: KconfigData.Var) -> None:\n             self.lhs.add_edges_to(var)\n             self.rhs.add_edges_to(var)\n-        def evaluate(self):\n+        def evaluate(self) -> bool:\n             return self.lhs.evaluate() and self.rhs.evaluate()\n \n     class OR(Expr):\n-        def __init__(self, lhs, rhs):\n+        def __init__(self, lhs: KconfigData.Expr, rhs: KconfigData.Expr) -> None:\n             self.lhs = lhs\n             self.rhs = rhs\n-        def __str__(self):\n+        def __str__(self) -> str:\n             return \"(%s || %s)\" % (self.lhs, self.rhs)\n \n-        def add_edges_to(self, var):\n+        def add_edges_to(self, var: KconfigData.Var) -> None:\n             self.lhs.add_edges_to(var)\n             self.rhs.add_edges_to(var)\n-        def evaluate(self):\n+        def evaluate(self) -> bool:\n             return self.lhs.evaluate() or self.rhs.evaluate()\n \n     class NOT(Expr):\n-        def __init__(self, lhs):\n+        def __init__(self, lhs: KconfigData.Expr) -> None:\n             self.lhs = lhs\n-        def __str__(self):\n+        def __str__(self) -> str:\n             return \"!%s\" % (self.lhs)\n \n-        def add_edges_to(self, var):\n+        def add_edges_to(self, var: KconfigData.Var) -> None:\n             self.lhs.add_edges_to(var)\n-        def evaluate(self):\n+        def evaluate(self) -> bool:\n             return not self.lhs.evaluate()\n \n     class Var(Expr):\n-        def __init__(self, name):\n+        def __init__(self, name: str) -> None:\n             self.name = name\n-            self.value = None\n-            self.outgoing = set()\n-            self.clauses_for_var = list()\n-        def __str__(self):\n+            self.value: bool | None = None\n+            self.outgoing: set[KconfigData.Var] = set()\n+            self.clauses_for_var: list[KconfigData.Clause] = []\n+        def __str__(self) -> str:\n             return self.name\n \n-        def has_value(self):\n+        def has_value(self) -> bool:\n             return self.value is not None\n-        def set_value(self, val, clause):\n+        def set_value(self, val: bool, clause: KconfigData.Clause) -> None:\n             self.clauses_for_var.append(clause)\n             if self.has_value() and self.value != val:\n                 print(\"The following clauses were found for \" + self.name, file=sys.stderr)\n@@ -141,7 +144,8 @@ def set_value(self, val, clause):\n             self.value = val\n \n         # depth first search of the dependency graph\n-        def dfs(self, visited, f):\n+        def dfs(self, visited: set[KconfigData.Var],\n+                f: T.Callable[[KconfigData.Var], None]) -> None:\n             if self in visited:\n                 return\n             visited.add(self)\n@@ -149,87 +153,89 @@ def dfs(self, visited, f):\n                 v.dfs(visited, f)\n             f(self)\n \n-        def add_edges_to(self, var):\n+        def add_edges_to(self, var: KconfigData.Var) -> None:\n             self.outgoing.add(var)\n-        def evaluate(self):\n+        def evaluate(self) -> bool:\n             if not self.has_value():\n                 raise KconfigDataError('cycle found including %s' % self)\n+            assert self.value is not None\n             return self.value\n \n     class Clause:\n-        def __init__(self, dest):\n+        def __init__(self, dest: KconfigData.Var) -> None:\n             self.dest = dest\n-        def priority(self):\n+        def priority(self) -> int:\n             return 0\n-        def process(self):\n+        def process(self) -> None:\n             pass\n \n     class AssignmentClause(Clause):\n-        def __init__(self, dest, value):\n+        def __init__(self, dest: KconfigData.Var, value: bool) -> None:\n             KconfigData.Clause.__init__(self, dest)\n             self.value = value\n-        def __str__(self):\n+        def __str__(self) -> str:\n             return \"CONFIG_%s=%s\" % (self.dest, 'y' if self.value else 'n')\n \n-        def process(self):\n+        def process(self) -> None:\n             self.dest.set_value(self.value, self)\n \n     class DefaultClause(Clause):\n-        def __init__(self, dest, value, cond=None):\n+        def __init__(self, dest: KconfigData.Var, value: bool,\n+                     cond: KconfigData.Expr | None = None) -> None:\n             KconfigData.Clause.__init__(self, dest)\n             self.value = value\n             self.cond = cond\n             if self.cond is not None:\n                 self.cond.add_edges_to(self.dest)\n-        def __str__(self):\n+        def __str__(self) -> str:\n             value = 'y' if self.value else 'n'\n             if self.cond is None:\n                 return \"config %s default %s\" % (self.dest, value)\n             else:\n                 return \"config %s default %s if %s\" % (self.dest, value, self.cond)\n \n-        def priority(self):\n+        def priority(self) -> int:\n             # Defaults are processed just before leaving the variable\n             return -1\n-        def process(self):\n+        def process(self) -> None:\n             if not self.dest.has_value() and \\\n                     (self.cond is None or self.cond.evaluate()):\n                 self.dest.set_value(self.value, self)\n \n     class DependsOnClause(Clause):\n-        def __init__(self, dest, expr):\n+        def __init__(self, dest: KconfigData.Var, expr: KconfigData.Expr) -> None:\n             KconfigData.Clause.__init__(self, dest)\n             self.expr = expr\n             self.expr.add_edges_to(self.dest)\n-        def __str__(self):\n+        def __str__(self) -> str:\n             return \"config %s depends on %s\" % (self.dest, self.expr)\n \n-        def process(self):\n+        def process(self) -> None:\n             if not self.expr.evaluate():\n                 self.dest.set_value(False, self)\n \n     class SelectClause(Clause):\n-        def __init__(self, dest, cond):\n+        def __init__(self, dest: KconfigData.Var, cond: KconfigData.Expr) -> None:\n             KconfigData.Clause.__init__(self, dest)\n             self.cond = cond\n             self.cond.add_edges_to(self.dest)\n-        def __str__(self):\n+        def __str__(self) -> str:\n             return \"select %s if %s\" % (self.dest, self.cond)\n \n-        def process(self):\n+        def process(self) -> None:\n             if self.cond.evaluate():\n                 self.dest.set_value(True, self)\n \n-    def __init__(self, value_mangler=defconfig):\n+    def __init__(self, value_mangler: Mangler = defconfig) -> None:\n         self.value_mangler = value_mangler\n-        self.previously_included = []\n-        self.defined_vars = set()\n-        self.referenced_vars = dict()\n-        self.clauses = list()\n+        self.previously_included: list[str] = []\n+        self.defined_vars: set[str] = set()\n+        self.referenced_vars: dict[str, KconfigData.Var] = {}\n+        self.clauses: list[KconfigData.Clause] = []\n \n     # semantic analysis -------------\n \n-    def check_undefined(self):\n+    def check_undefined(self) -> bool:\n         undef = False\n         for i in self.referenced_vars:\n             if i not in self.defined_vars:\n@@ -237,7 +243,7 @@ def check_undefined(self):\n                 undef = True\n         return undef\n \n-    def compute_config(self):\n+    def compute_config(self) -> dict[str, bool]:\n         if self.check_undefined():\n             raise KconfigDataError(\"there were undefined symbols\")\n \n@@ -250,10 +256,10 @@ def compute_config(self):\n             debug_print(source, \"->\", [str(x) for x in edges.outgoing])\n \n         # The reverse of the depth-first order is the topological sort\n-        dfo = dict()\n-        visited = set()\n+        dfo: dict[KconfigData.Var, int] = {}\n+        visited: set[KconfigData.Var] = set()\n         debug_print(\"\\n\")\n-        def visit_fn(var):\n+        def visit_fn(var: KconfigData.Var) -> None:\n             debug_print(var, \"has DFS number\", len(dfo))\n             dfo[var] = len(dfo)\n \n@@ -272,7 +278,7 @@ def visit_fn(var):\n             clause.process()\n \n         debug_print(\"\")\n-        values = dict()\n+        values: dict[str, bool] = {}\n         for name, v in self.referenced_vars.items():\n             debug_print(\"Evaluating\", name)\n             values[name] = v.evaluate()\n@@ -281,39 +287,42 @@ def visit_fn(var):\n \n     # semantic actions -------------\n \n-    def do_declaration(self, var):\n+    def do_declaration(self, var: KconfigData.Var) -> None:\n         if var.name in self.defined_vars:\n             raise KconfigDataError('variable \"%s\" defined twice' % var.name)\n-\n         self.defined_vars.add(var.name)\n \n     # var is a string with the variable's name.\n-    def do_var(self, var):\n+    def do_var(self, var: str) -> KconfigData.Var:\n         if var in self.referenced_vars:\n             return self.referenced_vars[var]\n \n         var_obj = self.referenced_vars[var] = KconfigData.Var(var)\n         return var_obj\n \n-    def do_assignment(self, var, val):\n+    def do_assignment(self, var: KconfigData.Var, val: bool) -> None:\n         self.clauses.append(KconfigData.AssignmentClause(var, val))\n \n-    def do_cmdline_assignment(self, var, val):\n+    def do_cmdline_assignment(self, var: str, val: bool) -> None:\n         assert var.startswith(\"CONFIG_\")\n         self.do_assignment(self.do_var(var[7:]), val)\n \n-    def do_default(self, var, val, cond=None):\n+    def do_default(self, var: KconfigData.Var, val: bool,\n+                   cond: KconfigData.Expr | None = None) -> None:\n         val = self.value_mangler(val)\n         self.clauses.append(KconfigData.DefaultClause(var, val, cond))\n \n-    def do_depends_on(self, var, expr):\n+    def do_depends_on(self, var: KconfigData.Var,\n+                      expr: KconfigData.Expr) -> None:\n         self.clauses.append(KconfigData.DependsOnClause(var, expr))\n \n-    def do_select(self, var, symbol, cond=None):\n+    def do_select(self, var: KconfigData.Var, symbol: KconfigData.Var,\n+                  cond: KconfigData.Expr | None = None) -> None:\n         cond = (cond & var) if cond is not None else var\n         self.clauses.append(KconfigData.SelectClause(symbol, cond))\n \n-    def do_imply(self, var, symbol, cond=None):\n+    def do_imply(self, var: KconfigData.Var, symbol: KconfigData.Var,\n+                 cond: KconfigData.Expr | None = None) -> None:\n         # \"config X imply Y [if COND]\" is the same as\n         # \"config Y default y if X [&& COND]\"\n         cond = (cond & var) if cond is not None else var\n@@ -325,7 +334,7 @@ def do_imply(self, var, symbol, cond=None):\n # -------------------------------------------\n \n # tokens table\n-TOKENS = {}\n+TOKENS: dict[int, str] = {}\n TOK_NONE = -1\n TOK_LPAREN = 0;   TOKENS[TOK_LPAREN] = '\"(\"'\n TOK_RPAREN = 1;   TOKENS[TOK_RPAREN] = '\")\"'\n@@ -348,7 +357,8 @@ def do_imply(self, var, symbol, cond=None):\n TOK_EOF = 18;     TOKENS[TOK_EOF] = 'end of file'\n \n class KconfigParserError(Exception):\n-    def __init__(self, parser, msg, tok=None):\n+    def __init__(self, parser: KconfigParser, msg: str,\n+                 tok: int | str | None = None) -> None:\n         self.loc = parser.location()\n         tok = tok if tok is not None else parser.tok\n         if tok != TOK_NONE:\n@@ -356,33 +366,37 @@ def __init__(self, parser, msg, tok=None):\n             msg = '%s before %s' % (msg, location)\n         self.msg = msg\n \n-    def __str__(self):\n+    def __str__(self) -> str:\n         return \"%s: %s\" % (self.loc, self.msg)\n \n class KconfigParser:\n \n     @classmethod\n-    def parse(cls, fp, data, incl_info=None):\n+    def parse(cls, fp: T.TextIO, data: KconfigData, incl_info: IncludeInfo | None = None) -> None:\n         cls(fp, data, incl_info).parse_config()\n \n-    def __init__(self, fp, data, incl_info):\n+    def __init__(self, fp: T.TextIO, data: KconfigData, incl_info: IncludeInfo | None = None):\n         self.data = data\n         self.incl_info = incl_info\n         self.abs_fname = os.path.abspath(fp.name)\n         self.fname = fp.name\n         self.data.previously_included.append(self.abs_fname)\n+\n         src = fp.read()\n         if src == '' or src[-1] != '\\n':\n             src += '\\n'\n         self.src = src\n-        self.cursor = 0\n-        self.line = 1\n-        self.line_pos = 0\n+        self.cursor: int = 0\n+        self.line: int = 1\n+        self.line_pos: int = 0\n+        self.pos: int = 0\n+        self.tok: int = TOK_NONE\n+        self.val: str | None = None\n         self.get_token()\n \n     # file management -----\n \n-    def location(self):\n+    def location(self) -> str:\n         col = 1\n         for ch in self.src[self.line_pos:self.pos]:\n             if ch == '\\t':\n@@ -393,7 +407,7 @@ def location(self):\n         incl_chain = inf.error_path() if inf is not None else \"\"\n         return '%s%s:%d:%d' % (incl_chain, self.fname, self.line, col)\n \n-    def do_include(self, include):\n+    def do_include(self, include: str) -> None:\n         incl_abs_fname = os.path.join(os.path.dirname(self.abs_fname),\n                                       include)\n         # catch inclusion cycle\n@@ -421,7 +435,7 @@ def do_include(self, include):\n     # recursive descent parser -----\n \n     # y_or_n: Y | N\n-    def parse_y_or_n(self):\n+    def parse_y_or_n(self) -> bool:\n         if self.tok == TOK_Y:\n             self.get_token()\n             return True\n@@ -431,7 +445,7 @@ def parse_y_or_n(self):\n         raise KconfigParserError(self, 'Expected \"y\" or \"n\"')\n \n     # var: ID\n-    def parse_var(self):\n+    def parse_var(self) -> KconfigData.Var:\n         if self.tok != TOK_ID:\n             raise KconfigParserError(self, 'Expected identifier')\n         val = self.val\n@@ -440,7 +454,7 @@ def parse_var(self):\n         return self.data.do_var(val)\n \n     # assignment_var: ID (starting with \"CONFIG_\")\n-    def parse_assignment_var(self):\n+    def parse_assignment_var(self) -> KconfigData.Var:\n         if self.tok != TOK_ID:\n             raise KconfigParserError(self, 'Expected identifier')\n         val = self.val\n@@ -452,7 +466,7 @@ def parse_assignment_var(self):\n         return self.data.do_var(val[7:])\n \n     # assignment: var EQUAL y_or_n\n-    def parse_assignment(self):\n+    def parse_assignment(self) -> None:\n         var = self.parse_assignment_var()\n         if self.tok != TOK_EQUAL:\n             raise KconfigParserError(self, 'Expected \"=\"')\n@@ -462,7 +476,7 @@ def parse_assignment(self):\n     # primary: NOT primary\n     #       | LPAREN expr RPAREN\n     #       | var\n-    def parse_primary(self):\n+    def parse_primary(self) -> KconfigData.Expr:\n         if self.tok == TOK_NOT:\n             self.get_token()\n             val = ~self.parse_primary()\n@@ -479,7 +493,7 @@ def parse_primary(self):\n         return val\n \n     # disj: primary (OR primary)*\n-    def parse_disj(self):\n+    def parse_disj(self) -> KconfigData.Expr:\n         lhs = self.parse_primary()\n         while self.tok == TOK_OR:\n             self.get_token()\n@@ -487,7 +501,7 @@ def parse_disj(self):\n         return lhs\n \n     # expr: disj (AND disj)*\n-    def parse_expr(self):\n+    def parse_expr(self) -> KconfigData.Expr:\n         lhs = self.parse_disj()\n         while self.tok == TOK_AND:\n             self.get_token()\n@@ -496,7 +510,7 @@ def parse_expr(self):\n \n     # condition: IF expr\n     #       | empty\n-    def parse_condition(self):\n+    def parse_condition(self) -> KconfigData.Expr | None:\n         if self.tok != TOK_IF:\n             return None\n         self.get_token()\n@@ -506,7 +520,7 @@ def parse_condition(self):\n     #       | DEPENDS ON expr\n     #       | SELECT var condition\n     #       | BOOL\n-    def parse_property(self, var):\n+    def parse_property(self, var: KconfigData.Var) -> None:\n         if self.tok == TOK_DEFAULT:\n             self.get_token()\n             val = self.parse_y_or_n()\n@@ -535,7 +549,7 @@ def parse_property(self, var):\n \n     # properties: properties property\n     #       | /* empty */\n-    def parse_properties(self, var):\n+    def parse_properties(self, var: KconfigData.Var) -> None:\n         while self.tok == TOK_DEFAULT or self.tok == TOK_DEPENDS or \\\n               self.tok == TOK_SELECT or self.tok == TOK_BOOL or \\\n               self.tok == TOK_IMPLY:\n@@ -548,7 +562,7 @@ def parse_properties(self, var):\n                     + '\"default\", \"depends on\", \"imply\" or \"select\"')\n \n     # declaration: config var properties\n-    def parse_declaration(self):\n+    def parse_declaration(self) -> None:\n         if self.tok == TOK_CONFIG:\n             self.get_token()\n             var = self.parse_var()\n@@ -560,9 +574,10 @@ def parse_declaration(self):\n     # clause: SOURCE\n     #       | declaration\n     #       | assignment\n-    def parse_clause(self):\n+    def parse_clause(self) -> None:\n         if self.tok == TOK_SOURCE:\n             val = self.val\n+            assert val is not None\n             self.get_token()\n             self.do_include(val)\n         elif self.tok == TOK_CONFIG:\n@@ -573,14 +588,15 @@ def parse_clause(self):\n             raise KconfigParserError(self, 'expected \"source\", \"config\" or identifier')\n \n     # config: clause+ EOF\n-    def parse_config(self):\n+    def parse_config(self) -> KconfigData:\n         while self.tok != TOK_EOF:\n             self.parse_clause()\n         return self.data\n \n     # scanner -----\n \n-    def get_token(self):\n+    def get_token(self) -> None:\n+        assert self.src is not None\n         while True:\n             ch = self.src[self.cursor]\n             self.pos = self.cursor\n@@ -592,7 +608,8 @@ def get_token(self):\n                 self.tok = tok\n                 return\n \n-    def check_keyword(self, rest):\n+    def check_keyword(self, rest: str) -> bool:\n+        assert self.src is not None\n         if not self.src.startswith(rest, self.cursor):\n             return False\n         length = len(rest)\n@@ -601,7 +618,8 @@ def check_keyword(self, rest):\n         self.cursor += length\n         return True\n \n-    def scan_token(self, ch):\n+    def scan_token(self, ch: str) -> int | None:\n+        assert self.src is not None\n         if ch == '#':\n             self.cursor = self.src.find('\\n', self.cursor)\n             return None\n@@ -669,7 +687,7 @@ def scan_token(self, ch):\n \n def main() -> None:\n     argv = sys.argv\n-    mode = defconfig\n+    mode: Mangler = defconfig\n     if len(sys.argv) > 1:\n         if argv[1] == '--defconfig':\n             del argv[1]\n@@ -693,7 +711,7 @@ def main() -> None:\n         sys.exit(1)\n \n     data = KconfigData(mode)\n-    external_vars = set()\n+    external_vars: set[str] = set()\n     for arg in argv[3:]:\n         m = re.match(r'^(CONFIG_[A-Z0-9_]+)=([yn]?)$', arg)\n         if m is not None:\n",
    "prefixes": [
        "PULL",
        "17/58"
    ]
}