mach: add type annotation in function for 'python/servo' folder (#38592)

This part of function strictly typed in python by adding ANN rule in
ruff, similiar to #38531.

Testing: `./mach test-tidy`
Fixes: Not related to any issues

---------

Signed-off-by: Jerens Lensun <jerensslensun@gmail.com>
This commit is contained in:
Jerens Lensun 2025-08-15 18:37:24 +08:00 committed by GitHub
parent 494493ceb7
commit a4fdbe8be3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
18 changed files with 167 additions and 146 deletions

View file

@ -31,7 +31,6 @@ ignore = [
[tool.ruff.lint.per-file-ignores]
"!python/**/**.py" = ["ANN"]
"python/servo/**.py" = ["ANN"]
"**/test.py" = ["ANN"]
"**/*_tests.py" = ["ANN"]
"**/tests/**/*.py" = ["ANN"]

View file

@ -40,7 +40,7 @@ class MachCommands(CommandBase):
@CommandArgument("--force", "-f", action="store_true", help="Boostrap without confirmation")
@CommandArgument("--skip-platform", action="store_true", help="Skip platform bootstrapping.")
@CommandArgument("--skip-lints", action="store_true", help="Skip tool necessary for linting.")
def bootstrap(self, force=False, skip_platform=False, skip_lints=False) -> int:
def bootstrap(self, force: bool = False, skip_platform: bool = False, skip_lints: bool = False) -> int:
# Note: This entry point isn't actually invoked by ./mach bootstrap.
# ./mach bootstrap calls mach_bootstrap.bootstrap_command_only so that
# it can install dependencies without needing mach's dependencies
@ -57,7 +57,7 @@ class MachCommands(CommandBase):
category="bootstrap",
)
@CommandArgument("--force", "-f", action="store_true", help="Boostrap without confirmation")
def bootstrap_gstreamer(self, force=False) -> int:
def bootstrap_gstreamer(self, force: bool = False) -> int:
try:
servo.platform.get().bootstrap_gstreamer(force)
except NotImplementedError as exception:
@ -66,7 +66,7 @@ class MachCommands(CommandBase):
return 0
@Command("update-hsts-preload", description="Download the HSTS preload list", category="bootstrap")
def bootstrap_hsts_preload(self, force=False) -> None:
def bootstrap_hsts_preload(self, force: bool = False) -> None:
preload_filename = "hsts_preload.fstmap"
preload_path = path.join(self.context.topdir, "resources")
@ -104,7 +104,7 @@ class MachCommands(CommandBase):
description="Download the public domains list and update resources/public_domains.txt",
category="bootstrap",
)
def bootstrap_pub_suffix(self, force=False) -> None:
def bootstrap_pub_suffix(self, force: bool = False) -> None:
list_url = "https://publicsuffix.org/list/public_suffix_list.dat"
dst_filename = path.join(self.context.topdir, "resources", "public_domains.txt")
not_implemented_case = re.compile(r"^[^*]+\*")

View file

@ -18,7 +18,7 @@ import subprocess
import sys
from time import time
from typing import Optional, List, Dict, Union
from typing import Optional, Union, Any
from mach.decorators import (
CommandArgument,
@ -54,7 +54,7 @@ SUPPORTED_TSAN_TARGETS = [
]
def get_rustc_llvm_version() -> Optional[List[int]]:
def get_rustc_llvm_version() -> Optional[list[int]]:
"""Determine the LLVM version of `rustc` and return it as a List[major, minor, patch, ...]
In some cases we want to ensure that the LLVM version of rustc and clang match, e.g.
@ -94,14 +94,14 @@ class MachCommands(CommandBase):
def build(
self,
build_type: BuildType,
jobs=None,
params=None,
no_package=False,
verbose=False,
very_verbose=False,
jobs: str | None = None,
params: list[str] | None = None,
no_package: bool = False,
verbose: bool = False,
very_verbose: bool = False,
sanitizer: SanitizerKind = SanitizerKind.NONE,
flavor=None,
**kwargs,
flavor: str | None = None,
**kwargs: Any,
) -> int:
opts = params or []
@ -200,7 +200,7 @@ class MachCommands(CommandBase):
@CommandArgument("--manifest-path", default=None, help="Path to the manifest to the package to clean")
@CommandArgument("--verbose", "-v", action="store_true", help="Print verbose output")
@CommandArgument("params", nargs="...", help="Command-line arguments to be passed through to Cargo")
def clean(self, manifest_path=None, params=[], verbose=False) -> None:
def clean(self, manifest_path: str | None = None, params: list[str] = [], verbose: bool = False) -> None:
self.ensure_bootstrapped()
virtualenv_path = path.join(self.get_top_dir(), ".venv")
@ -215,7 +215,7 @@ class MachCommands(CommandBase):
return check_call(["cargo", "clean"] + opts, env=self.build_env(), verbose=verbose)
def build_sanitizer_env(
self, env: Dict, opts: List[str], kwargs, target_triple: str, sanitizer: SanitizerKind = SanitizerKind.NONE
self, env: dict, opts: list[str], kwargs: Any, target_triple: str, sanitizer: SanitizerKind = SanitizerKind.NONE
) -> None:
if sanitizer.is_none():
return

View file

@ -19,6 +19,7 @@ import shutil
import subprocess
import sys
import tarfile
from tarfile import TarInfo
import urllib
import zipfile
import urllib.error
@ -28,7 +29,8 @@ from enum import Enum
from glob import glob
from os import path
from subprocess import PIPE, CompletedProcess
from typing import Any, Dict, List, Optional, Union, LiteralString, cast
from typing import Any, Optional, Union, LiteralString, cast
from collections.abc import Generator, Callable
from xml.etree.ElementTree import XML
import toml
@ -93,7 +95,7 @@ class BuildType:
@contextlib.contextmanager
def cd(new_path: str):
def cd(new_path: str) -> Generator:
"""Context manager for changing the current working directory"""
previous_path = os.getcwd()
try:
@ -104,7 +106,7 @@ def cd(new_path: str):
@contextlib.contextmanager
def setlocale(name: str):
def setlocale(name: str) -> Generator:
"""Context manager for changing the current locale"""
saved_locale = locale.setlocale(locale.LC_ALL)
try:
@ -113,7 +115,7 @@ def setlocale(name: str):
locale.setlocale(locale.LC_ALL, saved_locale)
def find_dep_path_newest(package, bin_path):
def find_dep_path_newest(package: str, bin_path: str) -> str | None:
deps_path = path.join(path.split(bin_path)[0], "build")
candidates = []
with cd(deps_path):
@ -126,12 +128,12 @@ def find_dep_path_newest(package, bin_path):
return None
def archive_deterministically(dir_to_archive, dest_archive, prepend_path=None) -> None:
def archive_deterministically(dir_to_archive: str, dest_archive: str, prepend_path: str | None = None) -> None:
"""Create a .tar.gz archive in a deterministic (reproducible) manner.
See https://reproducible-builds.org/docs/archives/ for more details."""
def reset(tarinfo):
def reset(tarinfo: TarInfo) -> TarInfo:
"""Helper to reset owner/group and modification time for tar entries"""
tarinfo.uid = tarinfo.gid = 0
tarinfo.uname = tarinfo.gname = "root"
@ -177,7 +179,7 @@ def archive_deterministically(dir_to_archive, dest_archive, prepend_path=None) -
os.rename(temp_file, dest_archive)
def call(*args, **kwargs) -> int:
def call(*args: Any, **kwargs: Any) -> int:
"""Wrap `subprocess.call`, printing the command if verbose=True."""
verbose = kwargs.pop("verbose", False)
if verbose:
@ -188,7 +190,7 @@ def call(*args, **kwargs) -> int:
return subprocess.call(*args, **kwargs)
def check_output(*args, **kwargs) -> Union[str, bytes]:
def check_output(*args: Any, **kwargs: Any) -> Union[str, bytes]:
"""Wrap `subprocess.call`, printing the command if verbose=True."""
verbose = kwargs.pop("verbose", False)
if verbose:
@ -199,7 +201,7 @@ def check_output(*args, **kwargs) -> Union[str, bytes]:
return subprocess.check_output(*args, **kwargs)
def check_call(*args, **kwargs) -> None:
def check_call(*args: Any, **kwargs: Any) -> None:
"""Wrap `subprocess.check_call`, printing the command if verbose=True.
Also fix any unicode-containing `env`, for subprocess"""
@ -239,10 +241,10 @@ def is_linux() -> bool:
class BuildNotFound(Exception):
def __init__(self, message) -> None:
def __init__(self, message: str) -> None:
self.message = message
def __str__(self):
def __str__(self) -> str:
return self.message
@ -253,7 +255,7 @@ class CommandBase(object):
target: BuildTarget
def __init__(self, context) -> None:
def __init__(self, context: Any) -> None:
self.context = context
self.enable_media = False
self.features = []
@ -321,7 +323,7 @@ class CommandBase(object):
_rust_toolchain = None
def rust_toolchain(self):
def rust_toolchain(self) -> str:
if self._rust_toolchain:
return self._rust_toolchain
@ -329,7 +331,7 @@ class CommandBase(object):
self._rust_toolchain = toml.load(toolchain_file)["toolchain"]["channel"]
return self._rust_toolchain
def get_top_dir(self):
def get_top_dir(self) -> str:
return self.context.topdir
def get_binary_path(self, build_type: BuildType, sanitizer: SanitizerKind = SanitizerKind.NONE) -> str:
@ -356,7 +358,7 @@ class CommandBase(object):
if os.path.exists(mounted_volume):
self.detach_volume(mounted_volume)
def mount_dmg(self, dmg_path) -> None:
def mount_dmg(self, dmg_path: str) -> None:
print("Mounting dmg {}".format(dmg_path))
try:
subprocess.check_call(["hdiutil", "attach", dmg_path])
@ -394,7 +396,7 @@ class CommandBase(object):
return path.join(destination_folder, "servo", "servo")
return path.join(destination_folder, "servo")
def get_nightly_binary_path(self, nightly_date) -> str | None:
def get_nightly_binary_path(self, nightly_date: str | None) -> str | None:
if nightly_date is None:
return
if not nightly_date:
@ -462,7 +464,7 @@ class CommandBase(object):
return self.get_executable(destination_folder)
def msvc_package_dir(self, package) -> str:
def msvc_package_dir(self, package: str) -> str:
return servo.platform.windows.get_dependency_dir(package)
def build_env(self) -> dict[str, str]:
@ -531,8 +533,11 @@ class CommandBase(object):
@staticmethod
def common_command_arguments(
build_configuration=False, build_type=False, binary_selection=False, package_configuration=False
):
build_configuration: bool = False,
build_type: bool = False,
binary_selection: bool = False,
package_configuration: bool = False,
) -> Callable:
decorators = []
if build_type or binary_selection:
decorators += [
@ -654,8 +659,8 @@ class CommandBase(object):
CommandArgument("--nightly", "-n", default=None, help="Specify a YYYY-MM-DD nightly build to run"),
]
def decorator_function(original_function):
def configuration_decorator(self, *args, **kwargs):
def decorator_function(original_function: Callable) -> Callable:
def configuration_decorator(self: CommandBase, *args: Any, **kwargs: Any) -> Callable:
if build_type or binary_selection:
# If `build_type` already exists in kwargs we are doing a recursive dispatch.
if "build_type" not in kwargs:
@ -680,7 +685,10 @@ class CommandBase(object):
kwargs["servo_binary"] = (
kwargs.get("bin")
or self.get_nightly_binary_path(kwargs.get("nightly"))
or self.get_binary_path(kwargs.get("build_type"), sanitizer=kwargs.get("sanitizer"))
or self.get_binary_path(
cast(BuildType, kwargs.get("build_type")),
sanitizer=cast(SanitizerKind, kwargs.get("sanitizer")),
)
)
kwargs.pop("bin")
kwargs.pop("nightly")
@ -699,8 +707,8 @@ class CommandBase(object):
return decorator_function
@staticmethod
def allow_target_configuration(original_function):
def target_configuration_decorator(self, *args, **kwargs):
def allow_target_configuration(original_function: Callable) -> Callable:
def target_configuration_decorator(self: CommandBase, *args: Any, **kwargs: Any) -> Callable:
self.configure_build_target(kwargs, suppress_log=True)
kwargs.pop("target", False)
kwargs.pop("android", False)
@ -741,7 +749,7 @@ class CommandBase(object):
else:
return BuildType.custom(profile)
def configure_build_target(self, kwargs: Dict[str, Any], suppress_log: bool = False) -> None:
def configure_build_target(self, kwargs: dict[str, Any], suppress_log: bool = False) -> None:
if hasattr(self.context, "target"):
# This call is for a dispatched command and we've already configured
# the target, so just use it.
@ -778,7 +786,7 @@ class CommandBase(object):
if self.target.is_cross_build() and not suppress_log:
print(f"Targeting '{self.target.triple()}' for cross-compilation")
def is_media_enabled(self, media_stack: Optional[str]):
def is_media_enabled(self, media_stack: Optional[str]) -> bool:
"""Determine whether media is enabled based on the value of the build target
platform and the value of the '--media-stack' command-line argument.
Returns true if media is enabled."""
@ -803,16 +811,16 @@ class CommandBase(object):
def run_cargo_build_like_command(
self,
command: str,
cargo_args: List[str],
env=None,
verbose=False,
debug_mozjs=False,
with_debug_assertions=False,
with_frame_pointer=False,
use_crown=False,
capture_output=False,
cargo_args: list[str],
env: dict[str, Any] | None = None,
verbose: bool = False,
debug_mozjs: bool = False,
with_debug_assertions: bool = False,
with_frame_pointer: bool = False,
use_crown: bool = False,
capture_output: bool = False,
target_override: Optional[str] = None,
**_kwargs,
**_kwargs: Any,
) -> CompletedProcess[bytes] | int:
env = cast(dict[str, str], env or self.build_env())
@ -888,14 +896,14 @@ class CommandBase(object):
return call(["cargo", command] + args + cargo_args, env=env, verbose=verbose)
def android_adb_path(self, env) -> LiteralString:
def android_adb_path(self, env: dict[str, Any]) -> LiteralString:
if "ANDROID_SDK_ROOT" in env:
sdk_adb = path.join(env["ANDROID_SDK_ROOT"], "platform-tools", "adb")
if path.exists(sdk_adb):
return sdk_adb
return "adb"
def android_emulator_path(self, env) -> LiteralString:
def android_emulator_path(self, env: dict[str, Any]) -> LiteralString:
if "ANDROID_SDK_ROOT" in env:
sdk_adb = path.join(env["ANDROID_SDK_ROOT"], "emulator", "emulator")
if path.exists(sdk_adb):
@ -919,7 +927,7 @@ class CommandBase(object):
if self.target.triple() not in installed_targets:
check_call(["rustup", "target", "add", self.target.triple()], cwd=self.context.topdir)
def ensure_clobbered(self, target_dir=None) -> None:
def ensure_clobbered(self, target_dir: str | None = None) -> None:
if target_dir is None:
target_dir = util.get_target_dir()
auto = True if os.environ.get("AUTOCLOBBER", False) else False

View file

@ -9,6 +9,7 @@
from subprocess import CompletedProcess
import json
from typing import Any
from mach.decorators import (
Command,
@ -27,7 +28,7 @@ class MachCommands(CommandBase):
"params", default=None, nargs="...", help="Command-line arguments to be passed through to cargo check"
)
@CommandBase.common_command_arguments(build_configuration=True, build_type=False)
def check(self, params, **kwargs) -> int:
def check(self, params: list[str], **kwargs: Any) -> int:
if not params:
params = []
@ -42,7 +43,7 @@ class MachCommands(CommandBase):
@Command("rustc", description="Run the Rust compiler", category="devenv")
@CommandArgument("params", default=None, nargs="...", help="Command-line arguments to be passed through to rustc")
def rustc(self, params) -> int:
def rustc(self, params: list[str]) -> int:
if params is None:
params = []
@ -54,7 +55,7 @@ class MachCommands(CommandBase):
"params", default=None, nargs="...", help="Command-line arguments to be passed through to cargo-fix"
)
@CommandBase.common_command_arguments(build_configuration=True, build_type=False)
def cargo_fix(self, params, **kwargs) -> int:
def cargo_fix(self, params: list[str], **kwargs: Any) -> int:
if not params:
params = []
@ -73,7 +74,7 @@ class MachCommands(CommandBase):
help="Emit the clippy warnings in the Github Actions annotations format",
)
@CommandBase.common_command_arguments(build_configuration=True, build_type=False)
def cargo_clippy(self, params, github_annotations=False, **kwargs) -> int:
def cargo_clippy(self, params: list[str], github_annotations: bool = False, **kwargs: Any) -> int:
if not params:
params = []

View file

@ -11,7 +11,7 @@ import os.path
import shutil
import subprocess
import sys
from typing import Set
from collections.abc import Set
# This file is called as a script from components/servo/build.rs, so
# we need to explicitly modify the search path here.

View file

@ -18,7 +18,7 @@ import random
test_summary = {test.Status.KILLED: 0, test.Status.SURVIVED: 0, test.Status.SKIPPED: 0, test.Status.UNEXPECTED: 0}
def get_folders_list(path):
def get_folders_list(path: str) -> list[str]:
folder_list = []
for filename in listdir(path):
if isdir(join(path, filename)):

View file

@ -11,14 +11,14 @@ import fileinput
import re
from re import Match
import random
from typing import Iterator
from collections.abc import Iterator
def is_comment(line: str) -> Match[str] | None:
return re.search(r"\/\/.*", line)
def init_variables(if_blocks):
def init_variables(if_blocks: list[int]) -> tuple[int, int, int, int, int]:
random_index = random.randint(0, len(if_blocks) - 1)
start_counter = 0
end_counter = 0
@ -27,7 +27,7 @@ def init_variables(if_blocks):
return random_index, start_counter, end_counter, lines_to_delete, line_to_mutate
def deleteStatements(file_name, line_numbers) -> None:
def deleteStatements(file_name: str, line_numbers: list[int]) -> None:
for line in fileinput.input(file_name, inplace=True):
if fileinput.lineno() not in line_numbers:
print(line.rstrip())
@ -38,7 +38,7 @@ class Strategy:
self._strategy_name = ""
self._replace_strategy = {}
def mutate(self, file_name):
def mutate(self, file_name: str) -> int:
line_numbers = []
for line in fileinput.input(file_name):
if not is_comment(line) and re.search(self._replace_strategy["regex"], line):
@ -140,7 +140,7 @@ class DeleteIfBlock(Strategy):
self.if_block = r"^\s+if\s(.+)\s\{"
self.else_block = r"\selse(.+)\{"
def mutate(self, file_name):
def mutate(self, file_name: str) -> int:
code_lines = []
if_blocks = []
for line in fileinput.input(file_name):
@ -192,8 +192,8 @@ def get_strategies() -> Iterator[Strategy]:
class Mutator:
def __init__(self, strategy) -> None:
def __init__(self, strategy: Strategy) -> None:
self._strategy = strategy
def mutate(self, file_name):
def mutate(self, file_name: str) -> int:
return self._strategy.mutate(file_name)

View file

@ -10,7 +10,7 @@
from datetime import datetime
import random
import time
from typing import List
from collections.abc import Generator
from github import Github
import hashlib
@ -68,14 +68,14 @@ PACKAGES = {
}
def packages_for_platform(platform):
def packages_for_platform(platform: str) -> Generator[str]:
target_dir = get_target_dir()
for package in PACKAGES[platform]:
yield path.join(target_dir, package)
def listfiles(directory) -> list[str]:
def listfiles(directory: str) -> list[str]:
return [f for f in os.listdir(directory) if path.isfile(path.join(directory, f))]
@ -85,7 +85,7 @@ def copy_windows_dependencies(binary_path: str, destination: str) -> None:
shutil.copy(path.join(binary_path, f), destination)
def check_call_with_randomized_backoff(args: List[str], retries: int) -> int:
def check_call_with_randomized_backoff(args: list[str], retries: int) -> int:
"""
Run the given command-line arguments via `subprocess.check_call()`. If the command
fails sleep for a random number of seconds between 2 and 5 and then try to the command
@ -111,7 +111,9 @@ class PackageCommands(CommandBase):
@CommandArgument("--target", "-t", default=None, help="Package for given target platform")
@CommandBase.common_command_arguments(build_configuration=False, build_type=True, package_configuration=True)
@CommandBase.allow_target_configuration
def package(self, build_type: BuildType, flavor=None, sanitizer: SanitizerKind = SanitizerKind.NONE) -> int | None:
def package(
self, build_type: BuildType, flavor: str | None = None, sanitizer: SanitizerKind = SanitizerKind.NONE
) -> int | None:
env = self.build_env()
binary_path = self.get_binary_path(build_type, sanitizer=sanitizer)
dir_to_root = self.get_top_dir()
@ -398,10 +400,10 @@ class PackageCommands(CommandBase):
def install(
self,
build_type: BuildType,
emulator=False,
usb=False,
emulator: bool = False,
usb: bool = False,
sanitizer: SanitizerKind = SanitizerKind.NONE,
flavor=None,
flavor: str | None = None,
) -> int:
env = self.build_env()
try:
@ -453,10 +455,10 @@ class PackageCommands(CommandBase):
@CommandArgument(
"--github-release-id", default=None, type=int, help="The github release to upload the nightly builds."
)
def upload_nightly(self, platform, secret_from_environment, github_release_id) -> int:
def upload_nightly(self, platform: str, secret_from_environment: bool, github_release_id: int | None) -> int:
import boto3
def get_s3_secret():
def get_s3_secret() -> tuple:
aws_access_key = None
aws_secret_access_key = None
if secret_from_environment:
@ -465,13 +467,13 @@ class PackageCommands(CommandBase):
aws_secret_access_key = secret["aws_secret_access_key"]
return (aws_access_key, aws_secret_access_key)
def nightly_filename(package, timestamp) -> str:
def nightly_filename(package: str, timestamp: datetime) -> str:
return "{}-{}".format(
timestamp.isoformat() + "Z", # The `Z` denotes UTC
path.basename(package),
)
def upload_to_github_release(platform, package: str, package_hash: str) -> None:
def upload_to_github_release(platform: str, package: str, package_hash: str) -> None:
if not github_release_id:
return
@ -488,7 +490,7 @@ class PackageCommands(CommandBase):
package_hash_fileobj, package_hash_fileobj.getbuffer().nbytes, name=f"{asset_name}.sha256"
)
def upload_to_s3(platform, package: str, package_hash: str, timestamp: datetime) -> None:
def upload_to_s3(platform: str, package: str, package_hash: str, timestamp: datetime) -> None:
(aws_access_key, aws_secret_access_key) = get_s3_secret()
s3 = boto3.client("s3", aws_access_key_id=aws_access_key, aws_secret_access_key=aws_secret_access_key)

View file

@ -47,7 +47,7 @@ def host_triple() -> str:
return f"{cpu_type}-{os_type}"
def get():
def get(): # noqa
# pylint: disable=global-statement
global __platform__
if __platform__:

View file

@ -20,7 +20,7 @@ from enum import Enum
from os import path
from packaging.version import parse as parse_version
from typing import Any, Dict, Optional
from typing import Any, Optional
import servo.platform
import servo.util as util
@ -70,7 +70,7 @@ class BuildTarget(object):
def binary_name(self) -> str:
return f"servo{servo.platform.get().executable_suffix()}"
def configure_build_environment(self, env: Dict[str, str], config: Dict[str, Any], topdir: pathlib.Path) -> None:
def configure_build_environment(self, env: dict[str, str], config: dict[str, Any], topdir: pathlib.Path) -> None:
pass
def is_cross_build(self) -> bool:
@ -88,7 +88,7 @@ class CrossBuildTarget(BuildTarget):
class AndroidTarget(CrossBuildTarget):
DEFAULT_TRIPLE = "aarch64-linux-android"
def ndk_configuration(self) -> Dict[str, str]:
def ndk_configuration(self) -> dict[str, str]:
target = self.triple()
config = {}
if target == "armv7-linux-androideabi":
@ -125,7 +125,7 @@ class AndroidTarget(CrossBuildTarget):
return config
def configure_build_environment(self, env: Dict[str, str], config: Dict[str, Any], topdir: pathlib.Path) -> None:
def configure_build_environment(self, env: dict[str, str], config: dict[str, Any], topdir: pathlib.Path) -> None:
# Paths to Android build tools:
if config["android"]["sdk"]:
env["ANDROID_SDK_ROOT"] = config["android"]["sdk"]
@ -296,7 +296,7 @@ class AndroidTarget(CrossBuildTarget):
class OpenHarmonyTarget(CrossBuildTarget):
DEFAULT_TRIPLE = "aarch64-unknown-linux-ohos"
def configure_build_environment(self, env: Dict[str, str], config: Dict[str, Any], topdir: pathlib.Path) -> None:
def configure_build_environment(self, env: dict[str, str], config: dict[str, Any], topdir: pathlib.Path) -> None:
# Paths to OpenHarmony SDK and build tools:
# Note: `OHOS_SDK_NATIVE` is the CMake variable name the `hvigor` build-system
# uses for the native directory of the SDK, so we use the same name to be consistent.

View file

@ -11,7 +11,7 @@ import distro
import os
import subprocess
import shutil
from typing import Optional
from typing import Optional, Any
from .base import Base
from .build_target import BuildTarget
@ -164,7 +164,7 @@ GSTREAMER_URL = (
class Linux(Base):
def __init__(self, *args, **kwargs) -> None:
def __init__(self, *args: str, **kwargs: Any) -> None:
super().__init__(*args, **kwargs)
self.is_linux = True
self.distro = distro.name()

View file

@ -10,7 +10,7 @@
import os
import subprocess
import tempfile
from typing import Optional
from typing import Optional, Any
from .. import util
from .base import Base
@ -24,7 +24,7 @@ GSTREAMER_ROOT = "/Library/Frameworks/GStreamer.framework/Versions/1.0"
class MacOS(Base):
def __init__(self, *args, **kwargs) -> None:
def __init__(self, *args: str, **kwargs: Any) -> None:
super().__init__(*args, **kwargs)
self.is_macos = True

View file

@ -13,7 +13,7 @@ import os.path as path
import subprocess
from subprocess import CompletedProcess
from shutil import copy2
from typing import List
from typing import Any
import mozdebug
@ -36,7 +36,7 @@ from servo.platform.build_target import is_android
ANDROID_APP_NAME = "org.servo.servoshell"
def read_file(filename, if_exists=False) -> str | None:
def read_file(filename: str, if_exists: bool = False) -> str | None:
if if_exists and not path.exists(filename):
return None
with open(filename) as f:
@ -44,7 +44,7 @@ def read_file(filename, if_exists=False) -> str | None:
# Copied from Python 3.3+'s shlex.quote()
def shell_quote(arg: str):
def shell_quote(arg: str) -> str:
# use single quotes, and put single quotes into double quotes
# the string $'b is then quoted as '$'"'"'b'
return "'" + arg.replace("'", "'\"'\"'") + "'"
@ -75,26 +75,26 @@ class PostBuildCommands(CommandBase):
def run(
self,
servo_binary: str,
params,
debugger=False,
debugger_cmd=None,
headless=False,
software=False,
emulator=False,
usb=False,
params: list[str],
debugger: bool = False,
debugger_cmd: str | None = None,
headless: bool = False,
software: bool = False,
emulator: bool = False,
usb: bool = False,
) -> int | None:
return self._run(servo_binary, params, debugger, debugger_cmd, headless, software, emulator, usb)
def _run(
self,
servo_binary: str,
params,
debugger=False,
debugger_cmd=None,
headless=False,
software=False,
emulator=False,
usb=False,
params: list[str],
debugger: bool = False,
debugger_cmd: str | None = None,
headless: bool = False,
software: bool = False,
emulator: bool = False,
usb: bool = False,
) -> int | None:
env = self.build_env()
env["RUST_BACKTRACE"] = "1"
@ -194,7 +194,7 @@ class PostBuildCommands(CommandBase):
@Command("android-emulator", description="Run the Android emulator", category="post-build")
@CommandArgument("args", nargs="...", help="Command-line arguments to be passed through to the emulator")
def android_emulator(self, args=None) -> int:
def android_emulator(self, args: list[str] | None = None) -> int:
if not args:
args = []
print("AVDs created by `./mach bootstrap-android` are servo-arm and servo-x86.")
@ -204,7 +204,7 @@ class PostBuildCommands(CommandBase):
@Command("rr-record", description="Run Servo whilst recording execution with rr", category="post-build")
@CommandArgument("params", nargs="...", help="Command-line arguments to be passed through to Servo")
@CommandBase.common_command_arguments(binary_selection=True)
def rr_record(self, servo_binary: str, params=[]) -> None:
def rr_record(self, servo_binary: str, params: list[str] = []) -> None:
env = self.build_env()
env["RUST_BACKTRACE"] = "1"
@ -235,7 +235,7 @@ class PostBuildCommands(CommandBase):
@Command("doc", description="Generate documentation", category="post-build")
@CommandArgument("params", nargs="...", help="Command-line arguments to be passed through to cargo doc")
@CommandBase.common_command_arguments(build_configuration=True, build_type=False)
def doc(self, params: List[str], **kwargs) -> CompletedProcess[bytes] | int | None:
def doc(self, params: list[str], **kwargs: Any) -> CompletedProcess[bytes] | int | None:
self.ensure_bootstrapped()
docs = path.join(servo.util.get_target_dir(), "doc")

View file

@ -118,7 +118,7 @@ class MachCommands(CommandBase):
DEFAULT_RENDER_MODE = "cpu"
HELP_RENDER_MODE = "Value can be 'cpu', 'gpu' or 'both' (default " + DEFAULT_RENDER_MODE + ")"
def __init__(self, context) -> None:
def __init__(self, context: Any) -> None:
CommandBase.__init__(self, context)
if not hasattr(self.context, "built_tests"):
self.context.built_tests = False
@ -127,7 +127,7 @@ class MachCommands(CommandBase):
@CommandArgument("--base", default=None, help="the base URL for testcases")
@CommandArgument("--date", default=None, help="the datestamp for the data")
@CommandArgument("--submit", "-a", default=False, action="store_true", help="submit the data to perfherder")
def test_perf(self, base=None, date=None, submit=False) -> int:
def test_perf(self, base: str | None = None, date: str | None = None, submit: bool = False) -> int:
env = self.build_env()
cmd = ["bash", "test_perf.sh"]
if base:
@ -147,7 +147,13 @@ class MachCommands(CommandBase):
)
@CommandBase.common_command_arguments(build_configuration=True, build_type=True)
def test_unit(
self, build_type: BuildType, test_name=None, package=None, bench=False, nocapture=False, **kwargs
self,
build_type: BuildType,
test_name: list[str] | None = None,
package: str | None = None,
bench: bool = False,
nocapture: bool = False,
**kwargs: Any,
) -> int:
if test_name is None:
test_name = []
@ -265,7 +271,7 @@ class MachCommands(CommandBase):
action="store_true",
help="Emit tidy warnings in the Github Actions annotations format",
)
def test_tidy(self, all_files, no_progress, github_annotations) -> int:
def test_tidy(self, all_files: bool, no_progress: bool, github_annotations: bool) -> int:
tidy_failed = tidy.scan(not all_files, not no_progress, github_annotations)
print("\r ➤ Checking formatting of Rust files...")
@ -299,7 +305,7 @@ class MachCommands(CommandBase):
@CommandArgument(
"tests", default=None, nargs="...", help="Specific WebIDL tests to run, relative to the tests directory"
)
def test_scripts(self, verbose, very_verbose, all, tests) -> int:
def test_scripts(self, verbose: bool, very_verbose: bool, all: bool, tests: list[str]) -> int:
if very_verbose:
logging.getLogger().level = logging.DEBUG
elif verbose:
@ -357,7 +363,7 @@ class MachCommands(CommandBase):
@Command("test-devtools", description="Run tests for devtools.", category="testing")
@CommandArgument("test_names", nargs=argparse.REMAINDER, help="Only run tests that match these patterns")
@CommandBase.common_command_arguments(build_type=True)
def test_devtools(self, build_type: BuildType, test_names: list[str], **kwargs) -> int:
def test_devtools(self, build_type: BuildType, test_names: list[str], **kwargs: Any) -> int:
print("Running devtools tests...")
passed = servo.devtools_tests.run_tests(SCRIPT_PATH, build_type, test_names)
return 0 if passed else 1
@ -369,7 +375,7 @@ class MachCommands(CommandBase):
parser=wpt.create_parser,
)
@CommandBase.common_command_arguments(build_configuration=False, build_type=True)
def test_wpt_failure(self, build_type: BuildType, **kwargs) -> bool:
def test_wpt_failure(self, build_type: BuildType, **kwargs: Any) -> bool:
kwargs["pause_after_test"] = False
kwargs["include"] = ["infrastructure/failing-test.html"]
return not self._test_wpt(build_type=build_type, **kwargs)
@ -378,11 +384,11 @@ class MachCommands(CommandBase):
"test-wpt", description="Run the regular web platform test suite", category="testing", parser=wpt.create_parser
)
@CommandBase.common_command_arguments(binary_selection=True)
def test_wpt(self, servo_binary: str, **kwargs):
def test_wpt(self, servo_binary: str, **kwargs: Any) -> int:
return self._test_wpt(servo_binary, **kwargs)
@CommandBase.allow_target_configuration
def _test_wpt(self, servo_binary: str, **kwargs) -> int:
def _test_wpt(self, servo_binary: str, **kwargs: Any) -> int:
# TODO(mrobinson): Why do we pass the wrong binary path in when running WPT on Android?
return_value = wpt.run.run_tests(servo_binary, **kwargs)
return return_value if not kwargs["always_succeed"] else 0
@ -393,7 +399,7 @@ class MachCommands(CommandBase):
category="testing",
parser=wpt.manifestupdate.create_parser,
)
def update_manifest(self, **kwargs) -> int:
def update_manifest(self, **kwargs: Any) -> int:
return wpt.manifestupdate.update(check_clean=False)
@Command("fmt", description="Format Rust, Python, and TOML files", category="testing")
@ -411,7 +417,7 @@ class MachCommands(CommandBase):
@Command(
"update-wpt", description="Update the web platform tests", category="testing", parser=wpt.update.create_parser
)
def update_wpt(self, **kwargs) -> int:
def update_wpt(self, **kwargs: Any) -> int:
patch = kwargs.get("patch", False)
if not patch and kwargs["sync"]:
print("Are you sure you don't want a patch?")
@ -427,7 +433,7 @@ class MachCommands(CommandBase):
@CommandArgument("tests", default=["recommended"], nargs="...", help="Specific tests to run")
@CommandArgument("--bmf-output", default=None, help="Specify BMF JSON output file")
@CommandBase.common_command_arguments(binary_selection=True)
def test_dromaeo(self, tests, servo_binary: str, bmf_output: str | None = None) -> None:
def test_dromaeo(self, tests: list[str], servo_binary: str, bmf_output: str | None = None) -> None:
return self.dromaeo_test_runner(tests, servo_binary, bmf_output)
@Command("test-speedometer", description="Run servo's speedometer", category="testing")
@ -455,7 +461,7 @@ class MachCommands(CommandBase):
@CommandArgument(
"params", default=None, nargs="...", help=" filepaths of output files of two runs of dromaeo test "
)
def compare_dromaeo(self, params) -> None:
def compare_dromaeo(self, params: list[str]) -> None:
prev_op_filename = params[0]
cur_op_filename = params[1]
result = {"Test": [], "Prev_Time": [], "Cur_Time": [], "Difference(%)": []}
@ -552,7 +558,7 @@ class MachCommands(CommandBase):
return call([run_file, cmd, bin_path, base_dir])
def dromaeo_test_runner(self, tests, binary: str, bmf_output: str | None) -> None:
def dromaeo_test_runner(self, tests: list[str], binary: str, bmf_output: str | None) -> None:
base_dir = path.abspath(path.join("tests", "dromaeo"))
dromaeo_dir = path.join(base_dir, "dromaeo")
run_file = path.join(base_dir, "run_dromaeo.py")
@ -582,7 +588,7 @@ class MachCommands(CommandBase):
output = dict()
profile = "" if profile is None else profile + "/"
def parse_speedometer_result(result) -> None:
def parse_speedometer_result(result: dict[str, Any]) -> None:
if result["unit"] == "ms":
output[profile + f"Speedometer/{result['name']}"] = {
"latency": { # speedometer has ms we need to convert to ns
@ -700,7 +706,7 @@ class MachCommands(CommandBase):
description="Update the net unit tests with cookie tests from http-state",
category="testing",
)
def update_net_cookies(self):
def update_net_cookies(self) -> int:
cache_dir = path.join(self.config["tools"]["cache-dir"], "tests")
run_file = path.abspath(
path.join(PROJECT_TOPLEVEL_PATH, "components", "net", "tests", "cookie_http_state_utils.py")
@ -713,7 +719,7 @@ class MachCommands(CommandBase):
"update-webgl", description="Update the WebGL conformance suite tests from Khronos repo", category="testing"
)
@CommandArgument("--version", default="2.0.0", help="WebGL conformance suite version")
def update_webgl(self, version=None):
def update_webgl(self, version: str | None = None) -> None:
base_dir = path.abspath(path.join(PROJECT_TOPLEVEL_PATH, "tests", "wpt", "mozilla", "tests", "webgl"))
run_file = path.join(base_dir, "tools", "import-conformance-tests.py")
dest_folder = path.join(base_dir, "conformance-%s" % version)
@ -729,7 +735,7 @@ class MachCommands(CommandBase):
@Command("update-webgpu", description="Update the WebGPU conformance test suite", category="testing")
@CommandArgument("--repo", "-r", default="https://github.com/gpuweb/cts", help="Repo to vendor cts from")
@CommandArgument("--checkout", "-c", default="main", help="Branch or commit of repo")
def cts(self, repo="https://github.com/gpuweb/cts", checkout="main"):
def cts(self, repo: str = "https://github.com/gpuweb/cts", checkout: str = "main") -> int:
tdir = path.join(self.context.topdir, "tests/wpt/webgpu/tests")
clone_dir = path.join(tdir, "cts_clone")
# clone
@ -786,7 +792,7 @@ class MachCommands(CommandBase):
)
@CommandArgument("params", nargs="...", help="Command-line arguments to be passed through to Servo")
@CommandBase.common_command_arguments(binary_selection=True)
def smoketest(self, servo_binary: str, params, **kwargs) -> int | None:
def smoketest(self, servo_binary: str, params: list[str], **kwargs: Any) -> int | None:
# We pass `-f` here so that any thread panic will cause Servo to exit,
# preventing a panic from hanging execution. This means that these kind
# of panics won't cause timeouts on CI.

View file

@ -13,7 +13,7 @@ from __future__ import annotations
import json
import sys
from typing import ClassVar, List, Optional
from typing import ClassVar, Optional, Any
import unittest
import logging
@ -44,7 +44,7 @@ class JobConfig(object):
number_of_wpt_chunks: int = 20
# These are the fields that must match in between two JobConfigs for them to be able to be
# merged. If you modify any of the fields above, make sure to update this line as well.
merge_compatibility_fields: ClassVar[List[str]] = ["workflow", "profile", "wpt_args", "build_args"]
merge_compatibility_fields: ClassVar[list[str]] = ["workflow", "profile", "wpt_args", "build_args"]
def merge(self, other: JobConfig) -> bool:
"""Try to merge another job with this job. Returns True if merging is successful
@ -177,7 +177,7 @@ def handle_modifier(config: Optional[JobConfig], s: str) -> Optional[JobConfig]:
class Encoder(json.JSONEncoder):
def default(self, o):
def default(self, o: Any) -> Any:
if isinstance(o, (Config, JobConfig)):
return o.__dict__
return json.JSONEncoder.default(self, o)
@ -236,7 +236,7 @@ class Config(object):
return
self.matrix.append(job)
def to_json(self, **kwargs) -> str:
def to_json(self, **kwargs: Any) -> str:
return json.dumps(self, cls=Encoder, **kwargs)

View file

@ -7,6 +7,7 @@
# option. This file may not be copied, modified, or distributed
# except according to those terms.
from os import PathLike
import hashlib
import os
import os.path
@ -18,7 +19,8 @@ import urllib.error
import urllib.request
import zipfile
from zipfile import ZipInfo
from typing import Dict, List, Union
from typing import Union, Any
from collections.abc import Callable
from io import BufferedIOBase, BytesIO
from socket import error as socket_error
@ -27,13 +29,13 @@ SCRIPT_PATH = os.path.abspath(os.path.dirname(__file__))
SERVO_ROOT = os.path.abspath(os.path.join(SCRIPT_PATH, "..", ".."))
def remove_readonly(func, path, _) -> None:
def remove_readonly(func: Callable[[str], None], path: str, _: Any) -> None:
"Clear the readonly bit and reattempt the removal"
os.chmod(path, stat.S_IWRITE)
func(path)
def delete(path) -> None:
def delete(path: str) -> None:
if os.path.isdir(path) and not os.path.islink(path):
shutil.rmtree(path, onerror=remove_readonly)
else:
@ -123,7 +125,7 @@ def download_file(description: str, url: str, destination_path: str) -> None:
# https://stackoverflow.com/questions/39296101/python-zipfile-removes-execute-permissions-from-binaries
# In particular, we want the executable bit for executable files.
class ZipFileWithUnixPermissions(zipfile.ZipFile):
def extract(self, member, path=None, pwd=None) -> str:
def extract(self, member: ZipInfo | str, path: PathLike[str] | str | None = None, pwd: bytes | None = None) -> str:
if not isinstance(member, zipfile.ZipInfo):
member = self.getinfo(member)
@ -137,7 +139,7 @@ class ZipFileWithUnixPermissions(zipfile.ZipFile):
return extracted
# For Python 3.x
def _extract_member(self, member: ZipInfo, targetpath, pwd) -> str:
def _extract_member(self, member: ZipInfo, targetpath: PathLike[str] | str, pwd: bytes | None) -> str:
if int(sys.version_info[0]) >= 3:
if not isinstance(member, zipfile.ZipInfo):
member = self.getinfo(member)
@ -154,7 +156,7 @@ class ZipFileWithUnixPermissions(zipfile.ZipFile):
return super(ZipFileWithUnixPermissions, self)._extract_member(member, targetpath, pwd)
def extract(src, dst, movedir=None, remove=True) -> None:
def extract(src: str, dst: str, movedir: PathLike[str] | str | None = None, remove: bool = True) -> None:
assert src.endswith(".zip")
ZipFileWithUnixPermissions(src).extractall(dst)
@ -169,7 +171,7 @@ def extract(src, dst, movedir=None, remove=True) -> None:
os.remove(src)
def check_hash(filename, expected, algorithm) -> None:
def check_hash(filename: str, expected: str, algorithm: str) -> None:
hasher = hashlib.new(algorithm)
with open(filename, "rb") as f:
while True:
@ -182,11 +184,11 @@ def check_hash(filename, expected, algorithm) -> None:
sys.exit(1)
def get_default_cache_dir(topdir) -> str:
def get_default_cache_dir(topdir: str) -> str:
return os.environ.get("SERVO_CACHE_DIR", os.path.join(topdir, ".servo"))
def append_paths_to_env(env: Dict[str, str], key: str, paths: Union[str, List[str]]) -> None:
def append_paths_to_env(env: dict[str, str], key: str, paths: Union[str, list[str]]) -> None:
if isinstance(paths, list):
paths = os.pathsep.join(paths)
@ -198,7 +200,7 @@ def append_paths_to_env(env: Dict[str, str], key: str, paths: Union[str, List[st
env[key] = new_value
def prepend_paths_to_env(env: Dict[str, str], key: str, paths: Union[str, List[str]]) -> None:
def prepend_paths_to_env(env: dict[str, str], key: str, paths: Union[str, list[str]]) -> None:
if isinstance(paths, list):
paths = os.pathsep.join(paths)

View file

@ -7,12 +7,15 @@
# option. This file may not be copied, modified, or distributed
# except according to those terms.
from __future__ import annotations
import dataclasses
import json
import os
import subprocess
import sys
from typing import Generator, List, Optional
from typing import Optional
from collections.abc import Generator
COMPATIBLE_MSVC_VERSIONS = {
"2019": "16.0",
@ -30,7 +33,7 @@ class VisualStudioInstallation:
installation_path: str
vc_install_path: str
def __lt__(self, other) -> bool:
def __lt__(self, other: VisualStudioInstallation) -> bool:
return self.version_number < other.version_number
@ -108,7 +111,7 @@ def find_compatible_msvc_with_environment_variables() -> Optional[VisualStudioIn
)
def find_msvc_installations() -> List[VisualStudioInstallation]:
def find_msvc_installations() -> list[VisualStudioInstallation]:
# First try to find Visual Studio via `vswhere.exe` and in well-known paths.
installations = list(find_compatible_msvc_with_vswhere())
installations.extend(find_compatible_msvc_with_path())