From 3f2016e552d4101dab3930f14e9da8558b77cf77 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Sat, 16 Sep 2023 23:23:18 +0200 Subject: [PATCH] python: add type hints --- python/build/autotools.py | 20 +++++++++++--------- python/build/cmake.py | 19 +++++++++++-------- python/build/makeproject.py | 15 ++++++++------- python/build/meson.py | 12 +++++++----- python/build/openssl.py | 9 +++++---- python/build/project.py | 20 +++++++++++--------- python/build/quilt.py | 5 +++-- python/build/verify.py | 13 +++++++------ python/build/zlib.py | 9 +++++---- 9 files changed, 68 insertions(+), 54 deletions(-) diff --git a/python/build/autotools.py b/python/build/autotools.py index 46fbd273b..d121bf6bf 100644 --- a/python/build/autotools.py +++ b/python/build/autotools.py @@ -1,15 +1,17 @@ import os.path, subprocess, sys +from typing import Collection, Iterable, Optional from build.makeproject import MakeProject class AutotoolsProject(MakeProject): - def __init__(self, url, md5, installed, configure_args=[], - autogen=False, - autoreconf=False, - cppflags='', - ldflags='', - libs='', - subdirs=None, + def __init__(self, url: str, md5: str, installed: str, + configure_args: Iterable[str]=[], + autogen: bool=False, + autoreconf: bool=False, + cppflags: str='', + ldflags: str='', + libs: str='', + subdirs: Optional[Collection[str]]=None, **kwargs): MakeProject.__init__(self, url, md5, installed, **kwargs) self.configure_args = configure_args @@ -20,7 +22,7 @@ class AutotoolsProject(MakeProject): self.libs = libs self.subdirs = subdirs - def configure(self, toolchain): + def configure(self, toolchain) -> str: src = self.unpack(toolchain) if self.autogen: if sys.platform == 'darwin': @@ -68,7 +70,7 @@ class AutotoolsProject(MakeProject): return build - def _build(self, toolchain): + def _build(self, toolchain) -> None: build = self.configure(toolchain) if self.subdirs is not None: for subdir in self.subdirs: diff --git a/python/build/cmake.py b/python/build/cmake.py index b41d14698..def58c65d 100644 --- a/python/build/cmake.py +++ b/python/build/cmake.py @@ -1,17 +1,19 @@ import os import re import subprocess +from typing import Optional, TextIO +from collections.abc import Mapping from build.project import Project -def __write_cmake_compiler(f, language, compiler): +def __write_cmake_compiler(f: TextIO, language: str, compiler: str) -> None: s = compiler.split(' ', 1) if len(s) == 2: print(f'set(CMAKE_{language}_COMPILER_LAUNCHER {s[0]})', file=f) compiler = s[1] print(f'set(CMAKE_{language}_COMPILER {compiler})', file=f) -def __write_cmake_toolchain_file(f, toolchain): +def __write_cmake_toolchain_file(f: TextIO, toolchain) -> None: if '-darwin' in toolchain.actual_arch: cmake_system_name = 'Darwin' elif toolchain.is_windows: @@ -52,7 +54,7 @@ set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) """) -def configure(toolchain, src, build, args=(), env=None): +def configure(toolchain, src: str, build: str, args: list[str]=[], env: Optional[Mapping[str, str]]=None) -> None: cross_args = [] if toolchain.is_windows: @@ -91,16 +93,17 @@ def configure(toolchain, src, build, args=(), env=None): subprocess.check_call(configure, env=env, cwd=build) class CmakeProject(Project): - def __init__(self, url, md5, installed, configure_args=[], - windows_configure_args=[], - env=None, + def __init__(self, url: str, md5: str, installed: str, + configure_args: list[str]=[], + windows_configure_args: list[str]=[], + env: Optional[Mapping[str, str]]=None, **kwargs): Project.__init__(self, url, md5, installed, **kwargs) self.configure_args = configure_args self.windows_configure_args = windows_configure_args self.env = env - def configure(self, toolchain): + def configure(self, toolchain) -> str: src = self.unpack(toolchain) build = self.make_build_path(toolchain) configure_args = self.configure_args @@ -109,7 +112,7 @@ class CmakeProject(Project): configure(toolchain, src, build, configure_args, self.env) return build - def _build(self, toolchain): + def _build(self, toolchain) -> None: build = self.configure(toolchain) subprocess.check_call(['ninja', '-v', 'install'], cwd=build, env=toolchain.env) diff --git a/python/build/makeproject.py b/python/build/makeproject.py index 9d498cfbd..c9a2b6eb9 100644 --- a/python/build/makeproject.py +++ b/python/build/makeproject.py @@ -1,15 +1,16 @@ import subprocess, multiprocessing +from typing import Optional from build.project import Project class MakeProject(Project): - def __init__(self, url, md5, installed, - install_target='install', + def __init__(self, url: str, md5: str, installed: str, + install_target: str='install', **kwargs): Project.__init__(self, url, md5, installed, **kwargs) self.install_target = install_target - def get_simultaneous_jobs(self): + def get_simultaneous_jobs(self) -> int: try: # use twice as many simultaneous jobs as we have CPU cores return multiprocessing.cpu_count() * 2 @@ -17,17 +18,17 @@ class MakeProject(Project): # default to 12, if multiprocessing.cpu_count() is not implemented return 12 - def get_make_args(self, toolchain): + def get_make_args(self, toolchain) -> list[str]: return ['--quiet', '-j' + str(self.get_simultaneous_jobs())] - def get_make_install_args(self, toolchain): + def get_make_install_args(self, toolchain) -> list[str]: return ['--quiet', self.install_target] - def make(self, toolchain, wd, args): + def make(self, toolchain, wd: str, args: list[str]) -> None: subprocess.check_call(['make'] + args, cwd=wd, env=toolchain.env) - def build_make(self, toolchain, wd, install=True): + def build_make(self, toolchain, wd: str, install: bool=True) -> None: self.make(toolchain, wd, self.get_make_args(toolchain)) if install: self.make(toolchain, wd, self.get_make_install_args(toolchain)) diff --git a/python/build/meson.py b/python/build/meson.py index 084e36610..e585800f1 100644 --- a/python/build/meson.py +++ b/python/build/meson.py @@ -1,10 +1,11 @@ import os import subprocess import platform +from typing import Optional from build.project import Project -def make_cross_file(toolchain): +def make_cross_file(toolchain) -> str: if toolchain.is_windows: system = 'windows' windres = "windres = '%s'" % toolchain.windres @@ -80,7 +81,7 @@ endian = '{endian}' """) return path -def configure(toolchain, src, build, args=()): +def configure(toolchain, src: str, build: str, args: list[str]=[]) -> None: cross_file = make_cross_file(toolchain) configure = [ 'meson', 'setup', @@ -103,18 +104,19 @@ def configure(toolchain, src, build, args=()): subprocess.check_call(configure, env=env) class MesonProject(Project): - def __init__(self, url, md5, installed, configure_args=[], + def __init__(self, url: str, md5: str, installed: str, + configure_args: list[str]=[], **kwargs): Project.__init__(self, url, md5, installed, **kwargs) self.configure_args = configure_args - def configure(self, toolchain): + def configure(self, toolchain) -> str: src = self.unpack(toolchain) build = self.make_build_path(toolchain) configure(toolchain, src, build, self.configure_args) return build - def _build(self, toolchain): + def _build(self, toolchain) -> None: build = self.configure(toolchain) subprocess.check_call(['ninja', '-v', 'install'], cwd=build, env=toolchain.env) diff --git a/python/build/openssl.py b/python/build/openssl.py index dd02941b5..053fe97b8 100644 --- a/python/build/openssl.py +++ b/python/build/openssl.py @@ -1,13 +1,14 @@ import subprocess +from typing import Optional from build.makeproject import MakeProject class OpenSSLProject(MakeProject): - def __init__(self, url, md5, installed, + def __init__(self, url: str, md5: str, installed: str, **kwargs): MakeProject.__init__(self, url, md5, installed, install_target='install_dev', **kwargs) - def get_make_args(self, toolchain): + def get_make_args(self, toolchain) -> list[str]: return MakeProject.get_make_args(self, toolchain) + [ 'CC=' + toolchain.cc, 'CFLAGS=' + toolchain.cflags, @@ -17,13 +18,13 @@ class OpenSSLProject(MakeProject): 'build_libs', ] - def get_make_install_args(self, toolchain): + def get_make_install_args(self, toolchain) -> list[str]: # OpenSSL's Makefile runs "ranlib" during installation return MakeProject.get_make_install_args(self, toolchain) + [ 'RANLIB=' + toolchain.ranlib, ] - def _build(self, toolchain): + def _build(self, toolchain) -> None: src = self.unpack(toolchain, out_of_tree=False) # OpenSSL has a weird target architecture scheme with lots of diff --git a/python/build/project.py b/python/build/project.py index bf787cf43..3607d4942 100644 --- a/python/build/project.py +++ b/python/build/project.py @@ -1,16 +1,18 @@ import os, shutil import re +from typing import cast, BinaryIO, Optional from build.download import download_and_verify from build.tar import untar from build.quilt import push_all class Project: - def __init__(self, url, md5, installed, name=None, version=None, - base=None, - patches=None, + def __init__(self, url: str, md5: str, installed: str, + name: Optional[str]=None, version: Optional[str]=None, + base: Optional[str]=None, + patches: Optional[str]=None, edits=None, - use_cxx=False): + use_cxx: bool=False): if base is None: basename = os.path.basename(url) m = re.match(r'^(.+)\.(tar(\.(gz|bz2|xz|lzma))?|zip)$', basename) @@ -39,10 +41,10 @@ class Project: self.edits = edits self.use_cxx = use_cxx - def download(self, toolchain): + def download(self, toolchain) -> str: return download_and_verify(self.url, self.md5, toolchain.tarball_path) - def is_installed(self, toolchain): + def is_installed(self, toolchain) -> bool: tarball = self.download(toolchain) installed = os.path.join(toolchain.install_prefix, self.installed) tarball_mtime = os.path.getmtime(tarball) @@ -51,7 +53,7 @@ class Project: except FileNotFoundError: return False - def unpack(self, toolchain, out_of_tree=True): + def unpack(self, toolchain, out_of_tree: bool=True) -> str: if out_of_tree: parent_path = toolchain.src_path else: @@ -72,7 +74,7 @@ class Project: return path - def make_build_path(self, toolchain, lazy=False): + def make_build_path(self, toolchain, lazy: bool=False) -> str: path = os.path.join(toolchain.build_path, self.base) if lazy and os.path.isdir(path): return path @@ -83,5 +85,5 @@ class Project: os.makedirs(path, exist_ok=True) return path - def build(self, toolchain): + def build(self, toolchain) -> None: self._build(toolchain) diff --git a/python/build/quilt.py b/python/build/quilt.py index 876453d2b..8751404db 100644 --- a/python/build/quilt.py +++ b/python/build/quilt.py @@ -1,9 +1,10 @@ import subprocess +from typing import Union -def run_quilt(toolchain, cwd, patches_path, *args): +def run_quilt(toolchain, cwd: str, patches_path: str, *args: str) -> None: env = dict(toolchain.env) env['QUILT_PATCHES'] = patches_path subprocess.check_call(['quilt'] + list(args), cwd=cwd, env=env) -def push_all(toolchain, src_path, patches_path): +def push_all(toolchain, src_path: str, patches_path: str) -> None: run_quilt(toolchain, src_path, patches_path, 'push', '-a') diff --git a/python/build/verify.py b/python/build/verify.py index e22ff274d..763f224ce 100644 --- a/python/build/verify.py +++ b/python/build/verify.py @@ -1,6 +1,7 @@ import hashlib +from typing import cast, Any, BinaryIO -def feed_file(h, f): +def feed_file(h: Any, f: BinaryIO) -> None: """Feed data read from an open file into the hashlib instance.""" while True: @@ -10,20 +11,20 @@ def feed_file(h, f): break h.update(data) -def feed_file_path(h, path): +def feed_file_path(h: Any, path: str) -> None: """Feed data read from a file (to be opened by this function) into the hashlib instance.""" with open(path, 'rb') as f: feed_file(h, f) -def file_digest(algorithm, path): +def file_digest(algorithm: Any, path: str) -> str: """Calculate the digest of a file and return it in hexadecimal notation.""" h = algorithm() feed_file_path(h, path) - return h.hexdigest() + return cast(str, h.hexdigest()) -def guess_digest_algorithm(digest): +def guess_digest_algorithm(digest: str) -> Any: l = len(digest) if l == 32: return hashlib.md5 @@ -36,7 +37,7 @@ def guess_digest_algorithm(digest): else: return None -def verify_file_digest(path, expected_digest): +def verify_file_digest(path: str, expected_digest: str) -> bool: """Verify the digest of a file, and return True if the digest matches with the given expected digest.""" algorithm = guess_digest_algorithm(expected_digest) diff --git a/python/build/zlib.py b/python/build/zlib.py index c5aeb8c30..98eede580 100644 --- a/python/build/zlib.py +++ b/python/build/zlib.py @@ -1,13 +1,14 @@ import subprocess +from typing import Optional from build.makeproject import MakeProject class ZlibProject(MakeProject): - def __init__(self, url, md5, installed, + def __init__(self, url: str, md5: str, installed: str, **kwargs): MakeProject.__init__(self, url, md5, installed, **kwargs) - def get_make_args(self, toolchain): + def get_make_args(self, toolchain) -> list[str]: return MakeProject.get_make_args(self, toolchain) + [ 'CC=' + toolchain.cc + ' ' + toolchain.cppflags + ' ' + toolchain.cflags, 'CPP=' + toolchain.cc + ' -E ' + toolchain.cppflags, @@ -18,13 +19,13 @@ class ZlibProject(MakeProject): 'libz.a' ] - def get_make_install_args(self, toolchain): + def get_make_install_args(self, toolchain) -> list[str]: return [ 'RANLIB=' + toolchain.ranlib, self.install_target ] - def _build(self, toolchain): + def _build(self, toolchain) -> None: src = self.unpack(toolchain, out_of_tree=False) subprocess.check_call(['./configure', '--prefix=' + toolchain.install_prefix, '--static'],