pmb: Add more type hints (MR 2513)

And fix some consequential type errors.

[ci:skip-build]: already built successfully in CI
This commit is contained in:
Newbyte 2024-12-19 18:49:49 +01:00
parent c8194302fc
commit 0925b3e425
No known key found for this signature in database
GPG key ID: ACD854892B38D898
12 changed files with 69 additions and 19 deletions

View file

@ -8,7 +8,7 @@ from pmb.core.arch import Arch
from pmb.core.context import Context from pmb.core.context import Context
from pmb.core.pkgrepo import pkgrepo_relative_path from pmb.core.pkgrepo import pkgrepo_relative_path
from pmb.helpers import logging from pmb.helpers import logging
from pmb.types import CrossCompileType from pmb.types import Apkbuild, CrossCompileType
from pathlib import Path from pathlib import Path
import pmb.build import pmb.build
@ -187,7 +187,7 @@ def is_cached_or_cache(arch: Arch, pkgname: str) -> bool:
return visited return visited
def get_apkbuild(pkgname): def get_apkbuild(pkgname: str) -> tuple[Path | None, Apkbuild | None]:
"""Parse the APKBUILD path for pkgname. """Parse the APKBUILD path for pkgname.
When there is none, try to find it in the binary package APKINDEX files or raise an exception. When there is none, try to find it in the binary package APKINDEX files or raise an exception.
@ -580,7 +580,7 @@ def packages(
for pkgname in pmb.config.build_packages: for pkgname in pmb.config.build_packages:
if pkgname not in pkgnames: if pkgname not in pkgnames:
aport, apkbuild = get_apkbuild(pkgname) aport, apkbuild = get_apkbuild(pkgname)
if not aport: if not aport or not apkbuild:
continue continue
bstatus = pmb.build.get_status(arch, apkbuild) bstatus = pmb.build.get_status(arch, apkbuild)
if bstatus.necessary(): if bstatus.necessary():

View file

@ -4,7 +4,6 @@ import enum
from pmb.helpers import logging from pmb.helpers import logging
import os import os
from pathlib import Path from pathlib import Path
from typing import Any
import shlex import shlex
import datetime import datetime
@ -20,6 +19,7 @@ import pmb.parse.version
from pmb.core import Chroot from pmb.core import Chroot
from pmb.core.arch import Arch from pmb.core.arch import Arch
from pmb.core.context import get_context from pmb.core.context import get_context
from pmb.types import Apkbuild
def copy_to_buildpath( def copy_to_buildpath(
@ -81,7 +81,7 @@ class BuildStatus(enum.Enum):
return self in [BuildStatus.OUTDATED, BuildStatus.NEW] return self in [BuildStatus.OUTDATED, BuildStatus.NEW]
def get_status(arch: Arch | None, apkbuild: dict[str, Any]) -> BuildStatus: def get_status(arch: Arch | None, apkbuild: Apkbuild) -> BuildStatus:
"""Check if the package has already been built. """Check if the package has already been built.
Compared to abuild's check, this check also works for different architectures. Compared to abuild's check, this check also works for different architectures.

View file

@ -83,7 +83,7 @@ def init_usr_merge(chroot: Chroot) -> None:
@Cache() @Cache()
def warn_if_chroots_outdated(): def warn_if_chroots_outdated() -> None:
outdated = pmb.config.workdir.chroots_outdated() outdated = pmb.config.workdir.chroots_outdated()
if outdated: if outdated:
days_warn = int(pmb.config.chroot_outdated / 3600 / 24) days_warn = int(pmb.config.chroot_outdated / 3600 / 24)

View file

@ -13,11 +13,11 @@ class Log(commands.Command):
clear_log: bool clear_log: bool
lines: int lines: int
def __init__(self, clear_log: bool, lines: int): def __init__(self, clear_log: bool, lines: int) -> None:
self.clear_log = clear_log self.clear_log = clear_log
self.lines = lines self.lines = lines
def run(self): def run(self) -> None:
context = get_context() context = get_context()
log_testsuite = pmb.config.pmb_src / ".pytest_tmp/log_testsuite.txt" log_testsuite = pmb.config.pmb_src / ".pytest_tmp/log_testsuite.txt"

View file

@ -58,7 +58,9 @@ def replace_apkbuild(
) )
def is_up_to_date(path_sources, path_target=None, lastmod_target=None): def is_up_to_date(
path_sources: list[Path], path_target: Path | None = None, lastmod_target: float | None = None
) -> bool:
"""Check if a file is up-to-date by comparing the last modified timestamps. """Check if a file is up-to-date by comparing the last modified timestamps.
(just like make does it). (just like make does it).
@ -81,6 +83,9 @@ def is_up_to_date(path_sources, path_target=None, lastmod_target=None):
if path_target: if path_target:
lastmod_target = os.path.getmtime(path_target) lastmod_target = os.path.getmtime(path_target)
if lastmod_target is None or lastmod_source is None:
raise AssertionError
return lastmod_target >= lastmod_source return lastmod_target >= lastmod_source

View file

@ -285,6 +285,8 @@ def config(args: PmbArgs) -> None:
def repo_missing(args: PmbArgs) -> None: def repo_missing(args: PmbArgs) -> None:
if args.arch is None or isinstance(args.package, list):
raise AssertionError
missing = pmb.helpers.repo_missing.generate(args.arch, args.overview, args.package, args.built) missing = pmb.helpers.repo_missing.generate(args.arch, args.overview, args.package, args.built)
print(json.dumps(missing, indent=4)) print(json.dumps(missing, indent=4))

View file

@ -34,7 +34,7 @@ class log_handler(logging.StreamHandler):
self.styles = pmb.config.styles self.styles = pmb.config.styles
def emit(self, record): def emit(self, record: logging.LogRecord) -> None:
try: try:
msg = self.format(record) msg = self.format(record)

View file

@ -12,8 +12,8 @@ from pmb.core.arch import Arch
from pmb.core.pkgrepo import pkgrepo_iter_package_dirs from pmb.core.pkgrepo import pkgrepo_iter_package_dirs
from pmb.helpers import logging from pmb.helpers import logging
from pathlib import Path from pathlib import Path
from typing import Any from typing import overload, Any, Literal
from pmb.types import WithExtraRepos from pmb.types import Apkbuild, WithExtraRepos
from pmb.meta import Cache from pmb.meta import Cache
import pmb.parse import pmb.parse
@ -260,7 +260,7 @@ def get_with_path(
must_exist: bool = True, must_exist: bool = True,
subpackages: bool = True, subpackages: bool = True,
with_extra_repos: WithExtraRepos = "default", with_extra_repos: WithExtraRepos = "default",
) -> tuple[Path | None, dict[str, Any] | None]: ) -> tuple[Path | None, Apkbuild | None]:
"""Find and parse an APKBUILD file. """Find and parse an APKBUILD file.
Run 'pmbootstrap apkbuild_parse hello-world' for a full output example. Run 'pmbootstrap apkbuild_parse hello-world' for a full output example.
@ -288,12 +288,30 @@ def get_with_path(
return None, None return None, None
@overload
def get(
pkgname: str,
must_exist: Literal[True] = ...,
subpackages: bool = ...,
with_extra_repos: WithExtraRepos = ...,
) -> Apkbuild: ...
@overload
def get(
pkgname: str,
must_exist: bool = ...,
subpackages: bool = ...,
with_extra_repos: WithExtraRepos = ...,
) -> Apkbuild | None: ...
def get( def get(
pkgname: str, pkgname: str,
must_exist: bool = True, must_exist: bool = True,
subpackages: bool = True, subpackages: bool = True,
with_extra_repos: WithExtraRepos = "default", with_extra_repos: WithExtraRepos = "default",
) -> dict[str, Any]: ) -> Apkbuild | None:
return get_with_path(pkgname, must_exist, subpackages, with_extra_repos)[1] return get_with_path(pkgname, must_exist, subpackages, with_extra_repos)[1]

View file

@ -1,6 +1,8 @@
# Copyright 2023 Oliver Smith # Copyright 2023 Oliver Smith
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
from typing import overload, Literal
from pmb.core.arch import Arch from pmb.core.arch import Arch
from pmb.helpers import logging from pmb.helpers import logging
from pmb.types import Apkbuild from pmb.types import Apkbuild
@ -126,7 +128,27 @@ def generate_output_format(arch: Arch, pkgnames: list[str]) -> list[Apkbuild]:
return ret return ret
def generate(arch, overview, pkgname=None, built=False): @overload
def generate(
arch: Arch, overview: Literal[False], pkgname: str | None = ..., built: bool = ...
) -> list[Apkbuild]: ...
@overload
def generate(
arch: Arch, overview: Literal[True], pkgname: str | None = ..., built: bool = ...
) -> list[str]: ...
@overload
def generate(
arch: Arch, overview: bool, pkgname: str | None = ..., built: bool = ...
) -> list[Apkbuild] | list[str]: ...
def generate(
arch: Arch, overview: bool, pkgname: str | None = None, built: bool = False
) -> list[Apkbuild] | list[str]:
"""Get packages that need to be built, with all their dependencies. """Get packages that need to be built, with all their dependencies.
:param arch: architecture (e.g. "armhf") :param arch: architecture (e.g. "armhf")

View file

@ -4,6 +4,7 @@ import argparse
import os import os
from pathlib import Path from pathlib import Path
import sys import sys
from typing import cast
from pmb.core.arch import Arch from pmb.core.arch import Arch
from pmb.core import Config from pmb.core import Config
@ -827,7 +828,7 @@ def add_kernel_arg(subparser, name="package", nargs="?", *args, **kwargs):
arg.completer = kernel_completer arg.completer = kernel_completer
def get_parser(): def get_parser() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser(prog="pmbootstrap") parser = argparse.ArgumentParser(prog="pmbootstrap")
arch_native = Arch.native() arch_native = Arch.native()
arch_choices = Arch.supported() arch_choices = Arch.supported()
@ -1297,7 +1298,8 @@ def get_parser():
def arguments() -> PmbArgs: def arguments() -> PmbArgs:
args: PmbArgs = get_parser().parse_args() # FIXME: It would be nice to not use cast here, but I don't know what else we could do.
args = cast(PmbArgs, get_parser().parse_args())
if getattr(args, "fork_alpine_retain_branch", False): if getattr(args, "fork_alpine_retain_branch", False):
# fork_alpine_retain_branch largely matches the behaviour of fork_alpine, so # fork_alpine_retain_branch largely matches the behaviour of fork_alpine, so

View file

@ -18,7 +18,7 @@ from pmb.meta import Cache
# to specify which one they're using. # to specify which one they're using.
# Basically: treat Deviceinfo as a standalone type that # Basically: treat Deviceinfo as a standalone type that
# doesn't need to traverse pmaports. # doesn't need to traverse pmaports.
def _parse_kernel_suffix(info, device, kernel): def _parse_kernel_suffix(info: dict[str, str], device: str, kernel: str | None) -> dict[str, str]:
""" """
Remove the kernel suffix (as selected in 'pmbootstrap init') from Remove the kernel suffix (as selected in 'pmbootstrap init') from
deviceinfo variables. Related: deviceinfo variables. Related:

View file

@ -12,6 +12,7 @@ import re
import signal import signal
import shlex import shlex
import shutil import shutil
from types import FrameType
import pmb.build import pmb.build
import pmb.chroot import pmb.chroot
@ -327,7 +328,7 @@ def resize_image(img_size_new: str, img_path: Path) -> None:
raise RuntimeError(f"IMAGE_SIZE must be {img_size_str} or greater") raise RuntimeError(f"IMAGE_SIZE must be {img_size_str} or greater")
def sigterm_handler(number, frame): def sigterm_handler(number: int, stack_frame: FrameType | None) -> None:
raise RuntimeError( raise RuntimeError(
"pmbootstrap was terminated by another process, and killed the QEMU VM it was running." "pmbootstrap was terminated by another process, and killed the QEMU VM it was running."
) )