Patch Detail
get:
Show a patch.
patch:
Update a patch.
put:
Update a patch.
GET /api/patches/2231423/?format=api
{ "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" ] }