From ae32cc8fbb0fc4a3b7e1dc16104da10b6e96e156 Mon Sep 17 00:00:00 2001 From: Evgenii Alekseev Date: Tue, 15 Jul 2025 21:20:49 +0300 Subject: [PATCH] type: use custom comparable for comparable functions --- src/ahriman/application/handlers/search.py | 3 ++- src/ahriman/application/handlers/status.py | 3 ++- src/ahriman/core/report/jinja_template.py | 3 ++- src/ahriman/core/report/rss.py | 3 ++- src/ahriman/core/types.py | 10 +++++++++- src/ahriman/web/views/v1/distributed/workers.py | 3 ++- src/ahriman/web/views/v1/packages/packages.py | 3 ++- src/ahriman/web/views/v1/service/search.py | 10 ++++++---- 8 files changed, 27 insertions(+), 11 deletions(-) diff --git a/src/ahriman/application/handlers/search.py b/src/ahriman/application/handlers/search.py index c981791a..a20afd0a 100644 --- a/src/ahriman/application/handlers/search.py +++ b/src/ahriman/application/handlers/search.py @@ -28,6 +28,7 @@ from ahriman.core.alpm.remote import AUR, Official from ahriman.core.configuration import Configuration from ahriman.core.exceptions import OptionError from ahriman.core.formatters import AurPrinter +from ahriman.core.types import Comparable from ahriman.models.aur_package import AURPackage from ahriman.models.repository_id import RepositoryId @@ -115,7 +116,7 @@ class Search(Handler): raise OptionError(sort_by) # always sort by package name at the last # well technically it is not a string, but we can deal with it - comparator: Callable[[AURPackage], tuple[str, str]] =\ + comparator: Callable[[AURPackage], Comparable] = \ lambda package: (getattr(package, sort_by), package.name) return sorted(packages, key=comparator) diff --git a/src/ahriman/application/handlers/status.py b/src/ahriman/application/handlers/status.py index b4e1503d..235cca54 100644 --- a/src/ahriman/application/handlers/status.py +++ b/src/ahriman/application/handlers/status.py @@ -25,6 +25,7 @@ from ahriman.application.application import Application from ahriman.application.handlers.handler import Handler, SubParserAction from ahriman.core.configuration import Configuration from ahriman.core.formatters import PackagePrinter, StatusPrinter +from ahriman.core.types import Comparable from ahriman.core.utils import enum_values from ahriman.models.build_status import BuildStatus, BuildStatusEnum from ahriman.models.package import Package @@ -64,7 +65,7 @@ class Status(Handler): Status.check_status(args.exit_code, packages) - comparator: Callable[[tuple[Package, BuildStatus]], str] = lambda item: item[0].base + comparator: Callable[[tuple[Package, BuildStatus]], Comparable] = lambda item: item[0].base filter_fn: Callable[[tuple[Package, BuildStatus]], bool] =\ lambda item: args.status is None or item[1].status == args.status for package, package_status in sorted(filter(filter_fn, packages), key=comparator): diff --git a/src/ahriman/core/report/jinja_template.py b/src/ahriman/core/report/jinja_template.py index 6f6291f8..a33f6b61 100644 --- a/src/ahriman/core/report/jinja_template.py +++ b/src/ahriman/core/report/jinja_template.py @@ -26,6 +26,7 @@ from typing import Any from ahriman.core.configuration import Configuration from ahriman.core.sign.gpg import GPG +from ahriman.core.types import Comparable from ahriman.core.utils import pretty_datetime, pretty_size, utcnow from ahriman.models.repository_id import RepositoryId from ahriman.models.result import Result @@ -111,7 +112,7 @@ class JinjaTemplate: Returns: list[dict[str, str]]: sorted content according to comparator defined """ - comparator: Callable[[dict[str, str]], str] = lambda item: item["filename"] + comparator: Callable[[dict[str, str]], Comparable] = lambda item: item["filename"] return sorted(content, key=comparator) def make_html(self, result: Result, template_name: Path | str) -> str: diff --git a/src/ahriman/core/report/rss.py b/src/ahriman/core/report/rss.py index 47007d9b..8e09c230 100644 --- a/src/ahriman/core/report/rss.py +++ b/src/ahriman/core/report/rss.py @@ -28,6 +28,7 @@ from ahriman.core.configuration import Configuration from ahriman.core.report.jinja_template import JinjaTemplate from ahriman.core.report.report import Report from ahriman.core.status import Client +from ahriman.core.types import Comparable from ahriman.models.event import EventType from ahriman.models.package import Package from ahriman.models.repository_id import RepositoryId @@ -86,7 +87,7 @@ class RSS(Report, JinjaTemplate): Returns: list[dict[str, str]]: sorted content according to comparator defined """ - comparator: Callable[[dict[str, str]], datetime.datetime] = \ + comparator: Callable[[dict[str, str]], Comparable] = \ lambda item: parsedate_to_datetime(item["build_date"]) return sorted(content, key=comparator, reverse=True) diff --git a/src/ahriman/core/types.py b/src/ahriman/core/types.py index ba4ad29a..9454f8d1 100644 --- a/src/ahriman/core/types.py +++ b/src/ahriman/core/types.py @@ -17,7 +17,15 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # -from typing import Protocol +from typing import Any, Protocol + + +class Comparable(Protocol): + """ + class which supports :func:`__lt__` operation` + """ + + def __lt__(self, other: Any) -> bool: ... class HasBool(Protocol): diff --git a/src/ahriman/web/views/v1/distributed/workers.py b/src/ahriman/web/views/v1/distributed/workers.py index f2b54f82..ddd6c452 100644 --- a/src/ahriman/web/views/v1/distributed/workers.py +++ b/src/ahriman/web/views/v1/distributed/workers.py @@ -21,6 +21,7 @@ from aiohttp.web import HTTPBadRequest, HTTPNoContent, Response, json_response from collections.abc import Callable from typing import ClassVar +from ahriman.core.types import Comparable from ahriman.models.user_access import UserAccess from ahriman.models.worker import Worker from ahriman.web.apispec.decorators import apidocs @@ -74,7 +75,7 @@ class WorkersView(BaseView): """ workers = self.workers.workers - comparator: Callable[[Worker], str] = lambda item: item.identifier + comparator: Callable[[Worker], Comparable] = lambda item: item.identifier response = [worker.view() for worker in sorted(workers, key=comparator)] return json_response(response) diff --git a/src/ahriman/web/views/v1/packages/packages.py b/src/ahriman/web/views/v1/packages/packages.py index 818addec..fd215a5d 100644 --- a/src/ahriman/web/views/v1/packages/packages.py +++ b/src/ahriman/web/views/v1/packages/packages.py @@ -23,6 +23,7 @@ from aiohttp.web import HTTPNoContent, Response, json_response from collections.abc import Callable from typing import ClassVar +from ahriman.core.types import Comparable from ahriman.models.build_status import BuildStatus from ahriman.models.package import Package from ahriman.models.user_access import UserAccess @@ -68,7 +69,7 @@ class PackagesView(StatusViewGuard, BaseView): repository_id = self.repository_id() packages = self.service(repository_id).packages - comparator: Callable[[tuple[Package, BuildStatus]], str] = lambda items: items[0].base + comparator: Callable[[tuple[Package, BuildStatus]], Comparable] = lambda items: items[0].base response = [ { "package": package.view(), diff --git a/src/ahriman/web/views/v1/service/search.py b/src/ahriman/web/views/v1/service/search.py index 5d0bbe34..c2966d8a 100644 --- a/src/ahriman/web/views/v1/service/search.py +++ b/src/ahriman/web/views/v1/service/search.py @@ -22,6 +22,7 @@ from collections.abc import Callable from typing import ClassVar from ahriman.core.alpm.remote import AUR +from ahriman.core.types import Comparable from ahriman.models.aur_package import AURPackage from ahriman.models.user_access import UserAccess from ahriman.web.apispec.decorators import apidocs @@ -70,10 +71,11 @@ class SearchView(BaseView): if not packages: raise HTTPNotFound(reason=f"No packages found for terms: {search}") - comparator: Callable[[AURPackage], tuple[bool, bool, str]] = lambda item: ( - item.package_base not in search, # inverted because False < True - not any(item.package_base.startswith(term) for term in search), # same as above - item.package_base, + comparator: Callable[[AURPackage], Comparable] = \ + lambda item: ( + item.package_base not in search, # inverted because False < True + not any(item.package_base.startswith(term) for term in search), # same as above + item.package_base, ) response = [ {