From 291c2fe130bcab24ac59a451e67c127d5c2667d5 Mon Sep 17 00:00:00 2001 From: bbhtt Date: Wed, 7 Aug 2024 09:50:44 +0530 Subject: [PATCH] Add complete type hints --- flatpak_builder_lint/appstream.py | 38 ++++++++++++++++------------- flatpak_builder_lint/builddir.py | 12 ++++----- flatpak_builder_lint/cli.py | 4 +-- flatpak_builder_lint/domainutils.py | 4 +-- flatpak_builder_lint/manifest.py | 6 ++--- poetry.lock | 24 +++++++++++++----- pyproject.toml | 2 +- 7 files changed, 52 insertions(+), 38 deletions(-) diff --git a/flatpak_builder_lint/appstream.py b/flatpak_builder_lint/appstream.py index 4a256719..0c149d77 100644 --- a/flatpak_builder_lint/appstream.py +++ b/flatpak_builder_lint/appstream.py @@ -1,15 +1,17 @@ import os import subprocess -from typing import Optional +from typing import List, Optional, TypedDict, cast from lxml import etree -# for mypy -Element = etree._Element -ElementTree = etree._ElementTree +class SubprocessResult(TypedDict): + stdout: str + stderr: str + returncode: int -def validate(path: str, *args: str) -> dict: + +def validate(path: str, *args: str) -> SubprocessResult: if not os.path.isfile(path): raise FileNotFoundError("AppStream file not found") @@ -45,7 +47,7 @@ def validate(path: str, *args: str) -> dict: capture_output=True, ) - ret = { + ret: SubprocessResult = { "stdout": cmd.stdout.decode("utf-8"), "stderr": cmd.stderr.decode("utf-8"), "returncode": cmd.returncode, @@ -54,18 +56,16 @@ def validate(path: str, *args: str) -> dict: return ret -def parse_xml(path: str) -> ElementTree: +def parse_xml(path: str) -> etree._ElementTree: return etree.parse(path) -def components(path: str) -> list: - components = parse_xml(path).xpath("/components/component") - return list(components) +def components(path: str) -> List[etree._Element]: + return cast(List[etree._Element], parse_xml(path).xpath("/components/component")) -def metainfo_components(path: str) -> list: - components = parse_xml(path).xpath("/component") - return list(components) +def metainfo_components(path: str) -> List[etree._Element]: + return cast(List[etree._Element], parse_xml(path).xpath("/component")) def appstream_id(path: str) -> Optional[str]: @@ -73,7 +73,7 @@ def appstream_id(path: str) -> Optional[str]: return str(aps_cid) -def get_launchable(path: str) -> list: +def get_launchable(path: str) -> List[str]: launchable = components(path)[0].xpath("launchable[@type='desktop-id']/text()") return list(launchable) @@ -133,9 +133,13 @@ def has_icon_key(path: str) -> bool: def icon_no_type(path: str) -> bool: - for icon in parse_xml(path).findall("component/icon"): - if icon.attrib.get("type") is None: - return True + icon_types = set( + [icon.attrib.get("type") for icon in components(path)[0].xpath("icon")] + ) + + if None in icon_types: + return True + return False diff --git a/flatpak_builder_lint/builddir.py b/flatpak_builder_lint/builddir.py index 4878a8aa..98e6a7db 100644 --- a/flatpak_builder_lint/builddir.py +++ b/flatpak_builder_lint/builddir.py @@ -2,7 +2,7 @@ import json import os from collections import defaultdict -from typing import Optional +from typing import List, Optional from gi.repository import GLib @@ -83,12 +83,12 @@ def infer_appid(path: str) -> Optional[str]: return None -def get_flathub_json(path: str) -> Optional[dict]: +def get_flathub_json(path: str) -> dict[str, str | bool | List[str]]: flathub_json_path = f"{path}/files/flathub.json" - if not os.path.exists(flathub_json_path): - return None + flathub_json: dict = {} - with open(flathub_json_path, "r") as f: - flathub_json: dict = json.load(f) + if os.path.exists(flathub_json_path): + with open(flathub_json_path, "r") as f: + flathub_json = json.load(f) return flathub_json diff --git a/flatpak_builder_lint/cli.py b/flatpak_builder_lint/cli.py index 8f9dec92..6de4e866 100644 --- a/flatpak_builder_lint/cli.py +++ b/flatpak_builder_lint/cli.py @@ -40,7 +40,7 @@ def _filter(info: set, excepts: set) -> list: return list(final) -def get_local_exceptions(appid: str) -> set: +def get_local_exceptions(appid: str) -> set[str]: with importlib.resources.open_text(staticfiles, "exceptions.json") as f: exceptions = json.load(f) ret = exceptions.get(appid) @@ -53,7 +53,7 @@ def get_local_exceptions(appid: str) -> set: def run_checks( kind: str, path: str, enable_exceptions: bool = False, appid: Optional[str] = None -) -> dict: +) -> Dict[str, Union[str, List[Optional[str]]]]: match kind: case "manifest": check_method_name = "check_manifest" diff --git a/flatpak_builder_lint/domainutils.py b/flatpak_builder_lint/domainutils.py index 547c6114..7f076ceb 100644 --- a/flatpak_builder_lint/domainutils.py +++ b/flatpak_builder_lint/domainutils.py @@ -60,7 +60,7 @@ def fetch_summary_bytes(url: str) -> bytes: @cache -def get_appids_from_summary(url: str) -> set: +def get_appids_from_summary(url: str) -> set[str]: appids = set() summary = GLib.Bytes.new(fetch_summary_bytes(url)) @@ -82,7 +82,7 @@ def get_appids_from_summary(url: str) -> set: @cache -def get_all_apps_on_flathub() -> set: +def get_all_apps_on_flathub() -> set[str]: return get_appids_from_summary( f"{FLATHUB_STABLE_REPO_URL}/summary" ) | get_appids_from_summary(f"{FLATHUB_BETA_REPO_URL}/summary") diff --git a/flatpak_builder_lint/manifest.py b/flatpak_builder_lint/manifest.py index 6eb2b33b..f138d624 100644 --- a/flatpak_builder_lint/manifest.py +++ b/flatpak_builder_lint/manifest.py @@ -21,7 +21,7 @@ def show_manifest(filename: str) -> dict: raise Exception(ret.stderr.decode("utf-8")) manifest = ret.stdout.decode("utf-8") - manifest_json = json.loads(manifest) + manifest_json: dict = json.loads(manifest) manifest_json["x-manifest-filename"] = filename manifest_basedir = os.path.dirname(filename) @@ -31,9 +31,7 @@ def show_manifest(filename: str) -> dict: flathub_json = json.load(f) manifest_json["x-flathub"] = flathub_json - # mypy does not support circular types - # https://github.com/python/typing/issues/182 - return manifest_json # type: ignore + return manifest_json def infer_appid(path: str) -> Optional[str]: diff --git a/poetry.lock b/poetry.lock index ed1676c8..abd28033 100644 --- a/poetry.lock +++ b/poetry.lock @@ -208,6 +208,17 @@ files = [ {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] +[[package]] +name = "cssselect" +version = "1.2.0" +description = "cssselect parses CSS3 Selectors and translates them to XPath 1.0" +optional = false +python-versions = ">=3.7" +files = [ + {file = "cssselect-1.2.0-py2.py3-none-any.whl", hash = "sha256:da1885f0c10b60c03ed5eccbb6b68d6eff248d91976fcde348f395d54c9fd35e"}, + {file = "cssselect-1.2.0.tar.gz", hash = "sha256:666b19839cfaddb9ce9d36bfe4c969132c647b92fc9088c4e23f786b30f1b3dc"}, +] + [[package]] name = "decorator" version = "5.1.1" @@ -1499,21 +1510,22 @@ referencing = "*" [[package]] name = "types-lxml" -version = "2023.10.21" +version = "2024.4.14" description = "Complete lxml external type annotation" optional = false python-versions = ">=3.8" files = [ - {file = "types-lxml-2023.10.21.tar.gz", hash = "sha256:daf1458b7d9b2fb421354137b97c6029c4fe75cef2cbab0d8e144d8c548d9b98"}, - {file = "types_lxml-2023.10.21-py3-none-any.whl", hash = "sha256:545097ca5f69d568827416d671285cae203a5b3d99937294e155c4cc0e46e712"}, + {file = "types_lxml-2024.4.14-py3-none-any.whl", hash = "sha256:7e5f836067cde4fddce3cdbf2bac7192c764bf5ee6d3eb86c732ad1b84f265c5"}, + {file = "types_lxml-2024.4.14.tar.gz", hash = "sha256:dd8105b579925af1b6ae77469f4fc835be3872b15e86cb46ad4fcc33b20c781d"}, ] [package.dependencies] -types-beautifulsoup4 = "*" +cssselect = ">=1.2,<2.0" +types-beautifulsoup4 = ">=4.12,<5.0" typing-extensions = ">=4.5,<5.0" [package.extras] -dev = ["black", "isort (>=5)", "lxml (==4.9.*)", "mypy (>=1.1,<1.4)", "pyright (>=1.1.289,<1.1.332)", "pytest (>=7)", "pytest-mypy-plugins (>=1.10.1,<2.0)", "typeguard (==3.0.*)"] +test = ["beautifulsoup4 (>=4.8,<5.0)", "html5lib (==1.1)", "lxml (>=4.9)", "mypy (==1.9.*)", "pyright (>=1.1.289)", "pytest (>=7.0,<9)", "pytest-mypy-plugins (==1.11.1)", "tox (>=4.0,<5.0)", "typeguard (>=3.0,<5)"] [[package]] name = "types-requests" @@ -1689,4 +1701,4 @@ files = [ [metadata] lock-version = "2.0" python-versions = "^3.10" -content-hash = "29afc546ed83e0f1b1c19bd7248d58d07406fa1bf8c174fb1e50544d014df505" +content-hash = "e922ef126470132ee506e50824f2d374e67f9c5ce75d15a777ff206d3d440a82" diff --git a/pyproject.toml b/pyproject.toml index 23148d45..5e0071af 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -23,7 +23,7 @@ black = "^24.3.0" Flake8-pyproject = "^1.1.0" types-requests = "^2.28.11" types-jsonschema = "^4.16.0" -types-lxml = "^2023.10.21" +types-lxml = "^2024.4.14" PyGObject-stubs = "^2.10.0" [tool.poetry.scripts]