Patch Detail
get:
Show a patch.
patch:
Update a patch.
put:
Update a patch.
GET /api/1.1/patches/2230966/?format=api
{ "id": 2230966, "url": "http://patchwork.ozlabs.org/api/1.1/patches/2230966/?format=api", "web_url": "http://patchwork.ozlabs.org/project/qemu-devel/patch/20260430090534.841894-14-pbonzini@redhat.com/", "project": { "id": 14, "url": "http://patchwork.ozlabs.org/api/1.1/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": "" }, "msgid": "<20260430090534.841894-14-pbonzini@redhat.com>", "date": "2026-04-30T09:05:34", "name": "[13/13] minikconf: add mypy annotations", "commit_ref": null, "pull_url": null, "state": "new", "archived": false, "hash": "5fdd82147ee995cb9a98a02720f9bd1c67c52f88", "submitter": { "id": 2701, "url": "http://patchwork.ozlabs.org/api/1.1/people/2701/?format=api", "name": "Paolo Bonzini", "email": "pbonzini@redhat.com" }, "delegate": null, "mbox": "http://patchwork.ozlabs.org/project/qemu-devel/patch/20260430090534.841894-14-pbonzini@redhat.com/mbox/", "series": [ { "id": 502245, "url": "http://patchwork.ozlabs.org/api/1.1/series/502245/?format=api", "web_url": "http://patchwork.ozlabs.org/project/qemu-devel/list/?series=502245", "date": "2026-04-30T09:05:25", "name": "kconfig cleanups and mypy annotations", "version": 1, "mbox": "http://patchwork.ozlabs.org/series/502245/mbox/" } ], "comments": "http://patchwork.ozlabs.org/api/patches/2230966/comments/", "check": "pending", "checks": "http://patchwork.ozlabs.org/api/patches/2230966/checks/", "tags": {}, "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=ivY3ZPGm;\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=i8YQhOYp;\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 4g5pGG0C7Tz1yGq\n\tfor <incoming@patchwork.ozlabs.org>; Thu, 30 Apr 2026 19:07:18 +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 1wINLe-0008Ku-J0; Thu, 30 Apr 2026 05:06:18 -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 1wINLT-00082M-Mg\n for qemu-devel@nongnu.org; Thu, 30 Apr 2026 05:06:10 -0400", "from us-smtp-delivery-124.mimecast.com ([170.10.133.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 1wINLQ-0003AB-V6\n for qemu-devel@nongnu.org; Thu, 30 Apr 2026 05:06:07 -0400", "from mail-wr1-f72.google.com (mail-wr1-f72.google.com\n [209.85.221.72]) by relay.mimecast.com with ESMTP with STARTTLS\n (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id\n us-mta-460-lk1blbmePDOfUZa2tdNuHw-1; Thu, 30 Apr 2026 05:06:02 -0400", "by mail-wr1-f72.google.com with SMTP id\n ffacd0b85a97d-43d121c4271so464108f8f.3\n for <qemu-devel@nongnu.org>; Thu, 30 Apr 2026 02:06:01 -0700 (PDT)", "from [192.168.10.48] ([151.49.85.67])\n by smtp.gmail.com with ESMTPSA id\n ffacd0b85a97d-447b7217c56sm11675508f8f.20.2026.04.30.02.05.57\n for <qemu-devel@nongnu.org>\n (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n Thu, 30 Apr 2026 02:05:58 -0700 (PDT)" ], "DKIM-Signature": [ "v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com;\n s=mimecast20190719; t=1777539964;\n h=from:from:reply-to:subject:subject:date:date:message-id:message-id:\n to:to:cc:mime-version:mime-version:\n content-transfer-encoding:content-transfer-encoding:\n in-reply-to:in-reply-to:references:references;\n bh=LVORDiAtsyF67SvxbxKKHAquX3JZKNCa8iS/ZxVnKeU=;\n b=ivY3ZPGmpIqK+V8H46xTcMz2jOrUDf7gd8QM+MFpdNhJrz2XSdAnyAxTD4qMgOfqKXXHC0\n SOVAJZx++sqPrfRgRidDosw+FsYKGU8vCt7jiO1YH40FE4oW/3byEV4o0a3B1X/qQbnDZ4\n M9z0gxOtGAXV1tRq5wrEHi4Baq3TK04=", "v=1; a=rsa-sha256; c=relaxed/relaxed;\n d=redhat.com; s=google; t=1777539960; x=1778144760; darn=nongnu.org;\n h=content-transfer-encoding:mime-version:references:in-reply-to\n :message-id:date:subject:to:from:from:to:cc:subject:date:message-id\n :reply-to; bh=LVORDiAtsyF67SvxbxKKHAquX3JZKNCa8iS/ZxVnKeU=;\n b=i8YQhOYpMXMKA1lpkkv8W8uQTs1wJtoUmvLQGJGPKK35p4YfDEvxQ4sgdlBEn0ys3C\n X2vnk71lPuavk3LuKmhbCjF3oek/C5e5z45AMxz7Dufm33Jqm8afzA2SW1SVQutCAzVR\n arWJG3jsBADtpJ3AcVyt/+rEqOId20onsq2neVG3RNJcui1mCxtjb3Zv0t+7CCBzvkxq\n d7+CkSsu/wbQ+Nf+xxmuDz0jewqmgViwvvrCmXnJjFq0X3uIID96EPV17QONsovdtnNc\n uvGruK31vjfm2TonV+G1qCR0GB/v2nNYKdExzXxkKEAtrAxeNydHGEdzBa1NHFI9k3Xe\n B6JA==" ], "X-MC-Unique": "lk1blbmePDOfUZa2tdNuHw-1", "X-Mimecast-MFC-AGG-ID": "lk1blbmePDOfUZa2tdNuHw_1777539961", "X-Google-DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n d=1e100.net; s=20251104; t=1777539960; x=1778144760;\n h=content-transfer-encoding:mime-version:references:in-reply-to\n :message-id:date:subject:to:from:x-gm-gg:x-gm-message-state:from:to\n :cc:subject:date:message-id:reply-to;\n bh=LVORDiAtsyF67SvxbxKKHAquX3JZKNCa8iS/ZxVnKeU=;\n b=hDOtdlrl2Nw6SO3ou/H/hWuj9AHURMTesmvncV1hGt9O41wO2fO+oidyEOpPCRCuE8\n Zj9vopdZWRF/HVBs44PSoPfStISV+BMqfcSGljewEY7tKJD1yNFrd9wkM40F9wVDGJPA\n 8CkuTW/EWUYn7vV489RGrnOru7p7U0nzX/s2mZddjbhNZ5jFU+XyvuzFuchTw0B56Xvp\n lFROSV6lneAfzx2fr/fQ5S9UvnPnOQ7AXLf/SlVCZoEUiLmlYEzG1ICOz4vJXGITekwE\n BowWNSNua38vtMbb5l5koRyEn/THK60MXl0ziadziXgyu1KvzsZql32MgPEcB1mazTFv\n IuAA==", "X-Gm-Message-State": "AOJu0Yx3TNzC0JdI8qKqrY2/af+YuEPGFh8F3bABpYAlyhl1jH7H8Giw\n Rl25wZ5d223guiA9/p6+lB+JJ9Mu3QKlI2QKUJI1HiQObp90yzulxZ8BywRJYa+Ran1EW6f0QG4\n kbzQM/++JLeepQUPMV+mk3cGFRZVdOTzbme8Mf2hv8REJ3h+3ZxMIP8wYyaoPw2ztEvbmhpiaEW\n gHBi1Qu2q7gUdw+2s1yTw06ZSRynLhvMfES3yESl4H", "X-Gm-Gg": "AeBDieuJ7zHHoV3rEp89Y6REW1hnz5/dVSmULzp8uGWej+1oiq5+2UDdcO3aa3rvIhK\n YH8VPTlh2IjZuZ/mzRVx7AfVq3JnwJiCOTCPHf5XfK+mvOqONtz0+hXQgahIFXIfX88C6NG1Mkh\n ZE3LAHQ+WpfaQEkbhWG3MEi9ZqxiBXqwtXrjGWwgatPsiALmtciz8vhnjQBihK9TAF4ljJKOO08\n EP0AcGltr8TADNYbqtFxHNtkrTy2oWkcFTwIxCtmVqk2shIk27G+FjJwNMi2Slqzg9Lzy4+yyIT\n KsR7rfh8IyBm0VtPNJ4xdIThwo9GUu2lSgmLyppY5sbcz39G2O7dkbjfzDeH4kw/Ug9WcVp1+PH\n fvvth0gBGymxHKJ7wluj7H3NkAwAegnX8TyBaLJb78K7xek4N8T7JqMEU+nBxh3udHOfjM5SETH\n IF8oiWebTjuridMiGIv5Urhy87RrJWztNVfMM=", "X-Received": [ "by 2002:a5d:588b:0:b0:43f:e7c9:2402 with SMTP id\n ffacd0b85a97d-4493dbdbc78mr3258713f8f.3.1777539959858;\n Thu, 30 Apr 2026 02:05:59 -0700 (PDT)", "by 2002:a5d:588b:0:b0:43f:e7c9:2402 with SMTP id\n ffacd0b85a97d-4493dbdbc78mr3258584f8f.3.1777539958650;\n Thu, 30 Apr 2026 02:05:58 -0700 (PDT)" ], "From": "Paolo Bonzini <pbonzini@redhat.com>", "To": "qemu-devel@nongnu.org", "Subject": "[PATCH 13/13] minikconf: add mypy annotations", "Date": "Thu, 30 Apr 2026 11:05:34 +0200", "Message-ID": "<20260430090534.841894-14-pbonzini@redhat.com>", "X-Mailer": "git-send-email 2.54.0", "In-Reply-To": "<20260430090534.841894-1-pbonzini@redhat.com>", "References": "<20260430090534.841894-1-pbonzini@redhat.com>", "MIME-Version": "1.0", "Content-Transfer-Encoding": "8bit", "Received-SPF": "pass client-ip=170.10.133.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_H5=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": "Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>\n---\n python/tests/linters.py | 3 +\n scripts/minikconf.py | 222 ++++++++++++++++++++++------------------\n 2 files changed, 124 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 7a2fe865484..6d7dbbf2ccd 100644\n--- a/scripts/minikconf.py\n+++ b/scripts/minikconf.py\n@@ -11,35 +11,40 @@\n # or, at your option, any later version. See the COPYING file in\n # the top-level directory.\n \n+from __future__ import annotations\n+\n import os\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@@ -54,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@@ -139,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@@ -147,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@@ -235,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@@ -248,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@@ -270,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@@ -279,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@@ -323,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@@ -346,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@@ -354,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@@ -391,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@@ -419,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@@ -429,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@@ -438,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@@ -450,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@@ -460,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@@ -477,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@@ -485,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@@ -494,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@@ -504,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@@ -533,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@@ -546,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@@ -558,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@@ -571,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@@ -590,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@@ -599,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@@ -667,7 +687,7 @@ def scan_token(self, ch):\n \n def main():\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@@ -691,7 +711,7 @@ def main():\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": [ "13/13" ] }