commit:     e40b7253f505d3d6c48743ab539e89d308e725d6
Author:     Brian Harring <ferringb <AT> gmail <DOT> com>
AuthorDate: Sat Nov 22 10:28:39 2025 +0000
Commit:     Brian Harring <ferringb <AT> gmail <DOT> com>
CommitDate: Sat Nov 22 18:19:28 2025 +0000
URL:        
https://gitweb.gentoo.org/proj/pkgcore/pkgcheck.git/commit/?id=e40b7253

chore: minor code cleanup

Roughly:
* replace `raise NotImplemented` with abc.ABC.  The latter should
  be used- it just didn't exist at the time.
* if a generator is a single `yield from`, just return the thing
  being consumed.  Drop the intermediate frame and yield traversing
  the frames.
* Add some annotations as I'm going for things that caught my eye.

Signed-off-by: Brian Harring <ferringb <AT> gmail.com>

 src/pkgcheck/addons/git.py     | 18 +++++++++++------
 src/pkgcheck/checks/network.py | 13 ++++++++----
 src/pkgcheck/sources.py        | 45 ++++++++++++++++++------------------------
 3 files changed, 40 insertions(+), 36 deletions(-)

diff --git a/src/pkgcheck/addons/git.py b/src/pkgcheck/addons/git.py
index 302c5bc1..2c8f1094 100644
--- a/src/pkgcheck/addons/git.py
+++ b/src/pkgcheck/addons/git.py
@@ -1,16 +1,18 @@
 """Git specific support and addon."""
 
+import abc
 import argparse
 import os
 import re
 import shlex
 import subprocess
+import tempfile
+import typing
 from collections import defaultdict, deque
 from dataclasses import dataclass
 from datetime import datetime
 from functools import partial
 from itertools import takewhile
-import tempfile
 
 from pathspec import PathSpec
 from pkgcore.ebuild import cpv
@@ -142,7 +144,10 @@ class GitLog:
         return line.rstrip()
 
 
-class _ParseGitRepo:
+T = typing.TypeVar("T")
+
+
+class _ParseGitRepo(typing.Generic[T], abc.ABC):
     """Generic iterator for custom git log output parsing support."""
 
     # git command to run on the targeted repo
@@ -169,8 +174,9 @@ class _ParseGitRepo:
     def __iter__(self):
         return self
 
-    def __next__(self):
-        raise NotImplementedError(self.__next__)
+    @abc.abstractmethod
+    def __next__(self) -> T:
+        pass
 
     @property
     def changes(self):
@@ -201,7 +207,7 @@ class _ParseGitRepo:
                         continue
 
 
-class GitRepoCommits(_ParseGitRepo):
+class GitRepoCommits(_ParseGitRepo["GitCommit"]):
     """Parse git log output into an iterator of commit objects."""
 
     _format = (
@@ -229,7 +235,7 @@ class GitRepoCommits(_ParseGitRepo):
         return GitCommit(commit_hash, commit_time, author, committer, message, 
ImmutableDict(pkgs))
 
 
-class GitRepoPkgs(_ParseGitRepo):
+class GitRepoPkgs(_ParseGitRepo["GitPkgChange"]):
     """Parse git log output into an iterator of package change objects."""
 
     _format = (

diff --git a/src/pkgcheck/checks/network.py b/src/pkgcheck/checks/network.py
index b26603b1..fb5e2fd1 100644
--- a/src/pkgcheck/checks/network.py
+++ b/src/pkgcheck/checks/network.py
@@ -1,8 +1,10 @@
 """Various checks that require network support."""
 
+import abc
 import re
 import socket
 import traceback
+import typing
 import urllib.request
 from functools import partial
 
@@ -96,7 +98,7 @@ class RequestError(_RequestException):
     """Wrapper for generic requests exception."""
 
 
-class _UrlCheck(NetworkCheck):
+class _UrlCheck(abc.ABC, NetworkCheck):
     """Generic URL verification check requiring network support."""
 
     _source = sources.LatestVersionRepoSource
@@ -194,9 +196,12 @@ class _UrlCheck(NetworkCheck):
                 result = result._create(**attrs, pkg=pkg)
             self.results_q.put([result])
 
-    def _get_urls(self, pkg):
-        """Get URLs to verify for a given package."""
-        raise NotImplementedError
+    @abc.abstractmethod
+    def _get_urls(self, pkg) -> typing.Iterable[tuple[str, str]]:
+        """Get URLs to verify for a given package.
+
+        yields pairs of (key, url)
+        """
 
     def _schedule_check(self, func, attr, url, executor, futures, **kwargs):
         """Schedule verification method to run in a separate thread against a 
given URL.

diff --git a/src/pkgcheck/sources.py b/src/pkgcheck/sources.py
index 2d0832cd..b94f9758 100644
--- a/src/pkgcheck/sources.py
+++ b/src/pkgcheck/sources.py
@@ -1,6 +1,9 @@
 """Custom package sources used for feeding checks."""
 
+import abc
+import itertools
 import os
+import typing
 from collections import defaultdict, deque
 from collections.abc import Set
 from dataclasses import dataclass
@@ -14,9 +17,9 @@ from snakeoil import klass
 from snakeoil.osutils import listdir_files, pjoin
 
 from . import addons, base
-from .bash import ParseTree
 from .addons.eclass import Eclass, EclassAddon
 from .addons.profiles import ProfileAddon, ProfileNode
+from .bash import ParseTree
 from .packages import FilteredPkg, RawCPV, WrappedPkg
 
 
@@ -31,10 +34,10 @@ class Source:
         self.source = source
 
     def __iter__(self):
-        yield from self.source
+        return iter(self.source)
 
     def itermatch(self, restrict, **kwargs):
-        yield from self.source
+        return iter(self.source)
 
 
 class EmptySource(Source):
@@ -150,7 +153,7 @@ class FilteredRepoSource(RepoSource):
         self._pkg_filter = pkg_filter
 
     def itermatch(self, restrict, **kwargs):
-        yield from self._pkg_filter(super().itermatch(restrict, **kwargs))
+        return self._pkg_filter(super().itermatch(restrict, **kwargs))
 
 
 class FilteredPackageRepoSource(FilteredRepoSource):
@@ -255,7 +258,7 @@ class RawRepoSource(RepoSource):
         super().__init__(options, source)
 
     def itermatch(self, restrict, **kwargs):
-        yield from super().itermatch(restrict, raw_pkg_cls=RawCPV, **kwargs)
+        return super().itermatch(restrict, raw_pkg_cls=RawCPV, **kwargs)
 
 
 class RestrictionRepoSource(RepoSource):
@@ -267,7 +270,7 @@ class RestrictionRepoSource(RepoSource):
 
     def itermatch(self, restrict, **kwargs):
         restrict = packages.AndRestriction(*(restrict, self.restriction))
-        yield from super().itermatch(restrict, **kwargs)
+        return super().itermatch(restrict, **kwargs)
 
 
 class UnmaskedRepoSource(RepoSource):
@@ -286,7 +289,7 @@ class UnmaskedRepoSource(RepoSource):
         )
 
     def itermatch(self, restrict, **kwargs):
-        yield from self._filtered_repo.itermatch(restrict, **kwargs)
+        return self._filtered_repo.itermatch(restrict, **kwargs)
 
 
 class _SourcePkg(WrappedPkg):
@@ -343,27 +346,16 @@ class EclassParseRepoSource(EclassRepoSource):
             yield _ParsedEclass(data, eclass=eclass)
 
 
-class _CombinedSource(RepoSource):
+class _CombinedSource(abc.ABC, RepoSource):
     """Generic source combining packages into similar chunks."""
 
-    def keyfunc(self, pkg):
+    @abc.abstractmethod
+    def keyfunc(self, pkg) -> typing.Hashable:
         """Function targeting attribute used to group packages."""
-        raise NotImplementedError(self.keyfunc)
 
     def itermatch(self, restrict, **kwargs):
-        key = None
-        chunk = None
-        for pkg in super().itermatch(restrict, **kwargs):
-            new = self.keyfunc(pkg)
-            if new == key:
-                chunk.append(pkg)
-            else:
-                if chunk is not None:
-                    yield chunk
-                chunk = [pkg]
-                key = new
-        if chunk is not None:
-            yield chunk
+        for _key, pkgs in itertools.groupby(super().itermatch(restrict, 
**kwargs), self.keyfunc):
+            yield list(pkgs)
 
 
 class PackageRepoSource(_CombinedSource):
@@ -386,11 +378,12 @@ class RepositoryRepoSource(RepoSource):
     scope = base.repo_scope
 
 
-class _FilteredSource(RawRepoSource):
+class _FilteredSource(abc.ABC, RawRepoSource):
     """Generic source yielding selected attribute from matching packages."""
 
-    def keyfunc(self, pkg):
-        raise NotImplementedError(self.keyfunc)
+    @abc.abstractmethod
+    def keyfunc(self, pkg) -> typing.Hashable:
+        pass
 
     def itermatch(self, restrict, **kwargs):
         key = None

Reply via email to