All of lore.kernel.org
 help / color / mirror / Atom feed
From: Tamir Duberstein <tamird@kernel.org>
To: "Kernel.org Tools" <tools@kernel.org>
Cc: Konstantin Ryabitsev <konstantin@linuxfoundation.org>,
	 Tamir Duberstein <tamird@kernel.org>
Subject: [PATCH ezgb 4/6] Add pyright checks
Date: Sun, 19 Apr 2026 21:39:25 -0700	[thread overview]
Message-ID: <20260419-stronger-type-checking-v1-4-222775b987e5@kernel.org> (raw)
In-Reply-To: <20260419-stronger-type-checking-v1-0-222775b987e5@kernel.org>

Add pyright to local CI with strict source checking.

Signed-off-by: Tamir Duberstein <tamird@kernel.org>
---
 ci.sh                |  1 +
 pyproject.toml       |  7 +++++++
 src/ezgb/__init__.py |  8 +++-----
 src/ezgb/_git.py     |  2 +-
 src/ezgb/_models.py  |  4 ++--
 src/ezgb/_reader.py  | 38 +++++++++++++++++++++-----------------
 src/ezgb/_writer.py  | 14 +++++++-------
 7 files changed, 42 insertions(+), 32 deletions(-)

diff --git a/ci.sh b/ci.sh
index db7914f..fbc433d 100755
--- a/ci.sh
+++ b/ci.sh
@@ -5,4 +5,5 @@ set -eu
 uv run ruff check
 uv run ruff format --check
 uv run mypy .
+uv run pyright
 uv run pytest --durations=0
diff --git a/pyproject.toml b/pyproject.toml
index c043817..452bd6a 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -26,6 +26,7 @@ classifiers = [
 [dependency-groups]
 dev = [
     "mypy",
+    "pyright",
     "pytest",
     "ruff",
 ]
@@ -50,3 +51,9 @@ select = ["E", "F", "W", "I"]
 
 [tool.ruff.format]
 quote-style = "single"
+
+[tool.pyright]
+typeCheckingMode = "strict"
+executionEnvironments = [
+    { root = "tests", reportPrivateUsage = false },
+]
diff --git a/src/ezgb/__init__.py b/src/ezgb/__init__.py
index 5cb23ae..f2f5d3d 100644
--- a/src/ezgb/__init__.py
+++ b/src/ezgb/__init__.py
@@ -23,7 +23,7 @@ from ezgb._models import (
     Status,
     UnsupportedFormatError,
 )
-from ezgb._reader import BugReader
+from ezgb._reader import BugReader, parse_since
 from ezgb._types import JsonValue
 from ezgb._writer import BugWriter
 
@@ -77,9 +77,7 @@ class GitBugRepo:
                 since = since.replace(tzinfo=timezone.utc)
             return int(since.timestamp())
         # str -- delegate to the reader's parser
-        from ezgb._reader import BugReader
-
-        return BugReader._parse_since(since)
+        return parse_since(since)
 
     def list_bugs(
         self,
@@ -206,7 +204,7 @@ class GitBugRepo:
             author_id = author.get('id', '') if isinstance(author, dict) else ''
             raw_labels = raw.get('labels') or []
             if isinstance(raw_labels, list):
-                labels = frozenset(str(lb) for lb in raw_labels)
+                labels: frozenset[str] = frozenset(str(lb) for lb in raw_labels)
             else:
                 labels = frozenset()
             comment_count = raw.get('comments', 0)
diff --git a/src/ezgb/_git.py b/src/ezgb/_git.py
index 4e29646..c1c326b 100644
--- a/src/ezgb/_git.py
+++ b/src/ezgb/_git.py
@@ -45,7 +45,7 @@ def git_run(
 
 def git_lines(repo_path: str, args: list[str]) -> list[str]:
     """Run a git command and return non-empty output lines."""
-    ecode, out = git_run(repo_path, args)
+    _ecode, out = git_run(repo_path, args)
     if not isinstance(out, str):
         out = out.decode(errors='replace')
     return [line for line in out.split('\n') if line]
diff --git a/src/ezgb/_models.py b/src/ezgb/_models.py
index b8716f0..22e45a7 100644
--- a/src/ezgb/_models.py
+++ b/src/ezgb/_models.py
@@ -96,7 +96,7 @@ class Comment:
     text: str
     created_at: datetime
     count: int
-    attachment_ids: list[str] = field(default_factory=list)
+    attachment_ids: list[str] = field(default_factory=list[str])
 
 
 @dataclass
@@ -110,7 +110,7 @@ class Bug:
     created_at: datetime
     labels: set[str]
     comments: list[Comment]
-    metadata: dict[str, str] = field(default_factory=dict)
+    metadata: dict[str, str] = field(default_factory=dict[str, str])
 
 
 @dataclass(frozen=True)
diff --git a/src/ezgb/_reader.py b/src/ezgb/_reader.py
index 735d41d..2d44478 100644
--- a/src/ezgb/_reader.py
+++ b/src/ezgb/_reader.py
@@ -57,6 +57,26 @@ def _is_dict_values(value: dict[_K, _T], value_ty: type[_U]) -> TypeGuard[dict[_
     return all(isinstance(value, value_ty) for value in value.values())
 
 
+def parse_since(since: str) -> int:
+    """Parse a datetime string to a unix timestamp.
+
+    Accepts ISO-8601 variants and ``YYYYMMDDHHMMSS``.
+    """
+    since = since.strip()
+    for fmt in (
+        '%Y-%m-%d %H:%M:%S',
+        '%Y%m%d%H%M%S',
+        '%Y-%m-%dT%H:%M:%SZ',
+        '%Y-%m-%dT%H:%M:%S',
+    ):
+        try:
+            dt = datetime.strptime(since, fmt).replace(tzinfo=timezone.utc)
+            return int(dt.timestamp())
+        except ValueError:
+            continue
+    raise ValueError('cannot parse since=%s' % since)
+
+
 def _combine_ids(primary: str, secondary: str) -> str:
     """Interleave primary and secondary IDs into a CombinedId.
 
@@ -415,23 +435,7 @@ class BugReader:
 
     @staticmethod
     def _parse_since(since: str) -> int:
-        """Parse a datetime string to a unix timestamp.
-
-        Accepts ISO-8601 variants and ``YYYYMMDDHHMMSS``.
-        """
-        since = since.strip()
-        for fmt in (
-            '%Y-%m-%d %H:%M:%S',
-            '%Y%m%d%H%M%S',
-            '%Y-%m-%dT%H:%M:%SZ',
-            '%Y-%m-%dT%H:%M:%S',
-        ):
-            try:
-                dt = datetime.strptime(since, fmt).replace(tzinfo=timezone.utc)
-                return int(dt.timestamp())
-            except ValueError:
-                continue
-        raise ValueError('cannot parse since=%s' % since)
+        return parse_since(since)
 
     def build_bug(
         self,
diff --git a/src/ezgb/_writer.py b/src/ezgb/_writer.py
index ae1d45f..adacfa1 100644
--- a/src/ezgb/_writer.py
+++ b/src/ezgb/_writer.py
@@ -77,7 +77,7 @@ class BugWriter:
             '-',
             '--non-interactive',
         ]
-        ecode, out, err = self._cli(args, stdin=text)
+        ecode, _out, err = self._cli(args, stdin=text)
         if ecode != 0:
             raise CliError('git bug comment new failed: %s' % err)
         self._reader.invalidate(bid)
@@ -100,7 +100,7 @@ class BugWriter:
             '-',
             '--non-interactive',
         ]
-        ecode, out, err = self._cli(args, stdin=text)
+        ecode, _out, err = self._cli(args, stdin=text)
         if ecode != 0:
             raise CliError('git bug comment edit failed: %s' % err)
         self._reader.invalidate(bid)
@@ -112,7 +112,7 @@ class BugWriter:
             subcmd = 'close'
         else:
             subcmd = 'open'
-        ecode, out, err = self._cli(['bug', 'status', subcmd, bid])
+        ecode, _out, err = self._cli(['bug', 'status', subcmd, bid])
         if ecode != 0:
             raise CliError('git bug status %s failed: %s' % (subcmd, err))
         self._reader.invalidate(bid)
@@ -121,7 +121,7 @@ class BugWriter:
         """Edit a bug's title."""
         bid = self._reader.resolve_bug_id(bid)
         args = ['bug', 'title', 'edit', bid, '-t', title]
-        ecode, out, err = self._cli(args)
+        ecode, _out, err = self._cli(args)
         if ecode != 0:
             raise CliError('git bug title edit failed: %s' % err)
         self._reader.invalidate(bid)
@@ -129,7 +129,7 @@ class BugWriter:
     def add_label(self, bid: str, label: str) -> None:
         """Add a label to a bug."""
         bid = self._reader.resolve_bug_id(bid)
-        ecode, out, err = self._cli(
+        ecode, _out, err = self._cli(
             ['bug', 'label', 'new', bid, label],
         )
         if ecode != 0:
@@ -139,7 +139,7 @@ class BugWriter:
     def remove_label(self, bid: str, label: str) -> None:
         """Remove a label from a bug."""
         bid = self._reader.resolve_bug_id(bid)
-        ecode, out, err = self._cli(
+        ecode, _out, err = self._cli(
             ['bug', 'label', 'rm', bid, label],
         )
         if ecode != 0:
@@ -153,7 +153,7 @@ class BugWriter:
         remote copies. This is irreversible.
         """
         bid = self._reader.resolve_bug_id(bid)
-        ecode, out, err = self._cli(['bug', 'rm', bid])
+        ecode, _out, err = self._cli(['bug', 'rm', bid])
         if ecode != 0:
             raise CliError('git bug rm failed: %s' % err)
         # Invalidate everything — the refs list has changed

-- 
2.53.0


  parent reply	other threads:[~2026-04-20  4:39 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-04-20  4:39 [PATCH ezgb 0/6] Harden local CI checks Tamir Duberstein
2026-04-20  4:39 ` [PATCH ezgb 1/6] Add local CI script Tamir Duberstein
2026-04-20  4:39 ` [PATCH ezgb 2/6] Add mypy checks Tamir Duberstein
2026-04-20  4:39 ` [PATCH ezgb 3/6] Add Ruff format check Tamir Duberstein
2026-04-20  4:39 ` Tamir Duberstein [this message]
2026-04-20  4:39 ` [PATCH ezgb 5/6] Add ty checks Tamir Duberstein
2026-04-20  4:39 ` [PATCH ezgb 6/6] Document local CI checks Tamir Duberstein
2026-04-27 20:20 ` [PATCH ezgb 0/6] Harden " Tamir Duberstein

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260419-stronger-type-checking-v1-4-222775b987e5@kernel.org \
    --to=tamird@kernel.org \
    --cc=konstantin@linuxfoundation.org \
    --cc=tools@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.