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] [tool.ruff.lint.per-file-ignores]
"!python/**/**.py" = ["ANN"] "!python/**/**.py" = ["ANN"]
"python/servo/**.py" = ["ANN"]
"**/test.py" = ["ANN"] "**/test.py" = ["ANN"]
"**/*_tests.py" = ["ANN"] "**/*_tests.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("--force", "-f", action="store_true", help="Boostrap without confirmation")
@CommandArgument("--skip-platform", action="store_true", help="Skip platform bootstrapping.") @CommandArgument("--skip-platform", action="store_true", help="Skip platform bootstrapping.")
@CommandArgument("--skip-lints", action="store_true", help="Skip tool necessary for linting.") @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. # Note: This entry point isn't actually invoked by ./mach bootstrap.
# ./mach bootstrap calls mach_bootstrap.bootstrap_command_only so that # ./mach bootstrap calls mach_bootstrap.bootstrap_command_only so that
# it can install dependencies without needing mach's dependencies # it can install dependencies without needing mach's dependencies
@ -57,7 +57,7 @@ class MachCommands(CommandBase):
category="bootstrap", category="bootstrap",
) )
@CommandArgument("--force", "-f", action="store_true", help="Boostrap without confirmation") @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: try:
servo.platform.get().bootstrap_gstreamer(force) servo.platform.get().bootstrap_gstreamer(force)
except NotImplementedError as exception: except NotImplementedError as exception:
@ -66,7 +66,7 @@ class MachCommands(CommandBase):
return 0 return 0
@Command("update-hsts-preload", description="Download the HSTS preload list", category="bootstrap") @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_filename = "hsts_preload.fstmap"
preload_path = path.join(self.context.topdir, "resources") 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", description="Download the public domains list and update resources/public_domains.txt",
category="bootstrap", 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" list_url = "https://publicsuffix.org/list/public_suffix_list.dat"
dst_filename = path.join(self.context.topdir, "resources", "public_domains.txt") dst_filename = path.join(self.context.topdir, "resources", "public_domains.txt")
not_implemented_case = re.compile(r"^[^*]+\*") not_implemented_case = re.compile(r"^[^*]+\*")

View file

@ -18,7 +18,7 @@ import subprocess
import sys import sys
from time import time from time import time
from typing import Optional, List, Dict, Union from typing import Optional, Union, Any
from mach.decorators import ( from mach.decorators import (
CommandArgument, 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, ...] """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. 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( def build(
self, self,
build_type: BuildType, build_type: BuildType,
jobs=None, jobs: str | None = None,
params=None, params: list[str] | None = None,
no_package=False, no_package: bool = False,
verbose=False, verbose: bool = False,
very_verbose=False, very_verbose: bool = False,
sanitizer: SanitizerKind = SanitizerKind.NONE, sanitizer: SanitizerKind = SanitizerKind.NONE,
flavor=None, flavor: str | None = None,
**kwargs, **kwargs: Any,
) -> int: ) -> int:
opts = params or [] 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("--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("--verbose", "-v", action="store_true", help="Print verbose output")
@CommandArgument("params", nargs="...", help="Command-line arguments to be passed through to Cargo") @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() self.ensure_bootstrapped()
virtualenv_path = path.join(self.get_top_dir(), ".venv") 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) return check_call(["cargo", "clean"] + opts, env=self.build_env(), verbose=verbose)
def build_sanitizer_env( 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: ) -> None:
if sanitizer.is_none(): if sanitizer.is_none():
return return

View file

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

View file

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

View file

@ -11,7 +11,7 @@ import os.path
import shutil import shutil
import subprocess import subprocess
import sys import sys
from typing import Set from collections.abc import Set
# This file is called as a script from components/servo/build.rs, so # This file is called as a script from components/servo/build.rs, so
# we need to explicitly modify the search path here. # 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} 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 = [] folder_list = []
for filename in listdir(path): for filename in listdir(path):
if isdir(join(path, filename)): if isdir(join(path, filename)):

View file

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

View file

@ -10,7 +10,7 @@
from datetime import datetime from datetime import datetime
import random import random
import time import time
from typing import List from collections.abc import Generator
from github import Github from github import Github
import hashlib 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() target_dir = get_target_dir()
for package in PACKAGES[platform]: for package in PACKAGES[platform]:
yield path.join(target_dir, package) 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))] 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) 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 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 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") @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.common_command_arguments(build_configuration=False, build_type=True, package_configuration=True)
@CommandBase.allow_target_configuration @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() env = self.build_env()
binary_path = self.get_binary_path(build_type, sanitizer=sanitizer) binary_path = self.get_binary_path(build_type, sanitizer=sanitizer)
dir_to_root = self.get_top_dir() dir_to_root = self.get_top_dir()
@ -398,10 +400,10 @@ class PackageCommands(CommandBase):
def install( def install(
self, self,
build_type: BuildType, build_type: BuildType,
emulator=False, emulator: bool = False,
usb=False, usb: bool = False,
sanitizer: SanitizerKind = SanitizerKind.NONE, sanitizer: SanitizerKind = SanitizerKind.NONE,
flavor=None, flavor: str | None = None,
) -> int: ) -> int:
env = self.build_env() env = self.build_env()
try: try:
@ -453,10 +455,10 @@ class PackageCommands(CommandBase):
@CommandArgument( @CommandArgument(
"--github-release-id", default=None, type=int, help="The github release to upload the nightly builds." "--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 import boto3
def get_s3_secret(): def get_s3_secret() -> tuple:
aws_access_key = None aws_access_key = None
aws_secret_access_key = None aws_secret_access_key = None
if secret_from_environment: if secret_from_environment:
@ -465,13 +467,13 @@ class PackageCommands(CommandBase):
aws_secret_access_key = secret["aws_secret_access_key"] aws_secret_access_key = secret["aws_secret_access_key"]
return (aws_access_key, 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( return "{}-{}".format(
timestamp.isoformat() + "Z", # The `Z` denotes UTC timestamp.isoformat() + "Z", # The `Z` denotes UTC
path.basename(package), 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: if not github_release_id:
return return
@ -488,7 +490,7 @@ class PackageCommands(CommandBase):
package_hash_fileobj, package_hash_fileobj.getbuffer().nbytes, name=f"{asset_name}.sha256" 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() (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) 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}" return f"{cpu_type}-{os_type}"
def get(): def get(): # noqa
# pylint: disable=global-statement # pylint: disable=global-statement
global __platform__ global __platform__
if __platform__: if __platform__:

View file

@ -20,7 +20,7 @@ from enum import Enum
from os import path from os import path
from packaging.version import parse as parse_version from packaging.version import parse as parse_version
from typing import Any, Dict, Optional from typing import Any, Optional
import servo.platform import servo.platform
import servo.util as util import servo.util as util
@ -70,7 +70,7 @@ class BuildTarget(object):
def binary_name(self) -> str: def binary_name(self) -> str:
return f"servo{servo.platform.get().executable_suffix()}" 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 pass
def is_cross_build(self) -> bool: def is_cross_build(self) -> bool:
@ -88,7 +88,7 @@ class CrossBuildTarget(BuildTarget):
class AndroidTarget(CrossBuildTarget): class AndroidTarget(CrossBuildTarget):
DEFAULT_TRIPLE = "aarch64-linux-android" DEFAULT_TRIPLE = "aarch64-linux-android"
def ndk_configuration(self) -> Dict[str, str]: def ndk_configuration(self) -> dict[str, str]:
target = self.triple() target = self.triple()
config = {} config = {}
if target == "armv7-linux-androideabi": if target == "armv7-linux-androideabi":
@ -125,7 +125,7 @@ class AndroidTarget(CrossBuildTarget):
return config 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: # Paths to Android build tools:
if config["android"]["sdk"]: if config["android"]["sdk"]:
env["ANDROID_SDK_ROOT"] = config["android"]["sdk"] env["ANDROID_SDK_ROOT"] = config["android"]["sdk"]
@ -296,7 +296,7 @@ class AndroidTarget(CrossBuildTarget):
class OpenHarmonyTarget(CrossBuildTarget): class OpenHarmonyTarget(CrossBuildTarget):
DEFAULT_TRIPLE = "aarch64-unknown-linux-ohos" 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: # Paths to OpenHarmony SDK and build tools:
# Note: `OHOS_SDK_NATIVE` is the CMake variable name the `hvigor` build-system # 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. # 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 os
import subprocess import subprocess
import shutil import shutil
from typing import Optional from typing import Optional, Any
from .base import Base from .base import Base
from .build_target import BuildTarget from .build_target import BuildTarget
@ -164,7 +164,7 @@ GSTREAMER_URL = (
class Linux(Base): class Linux(Base):
def __init__(self, *args, **kwargs) -> None: def __init__(self, *args: str, **kwargs: Any) -> None:
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self.is_linux = True self.is_linux = True
self.distro = distro.name() self.distro = distro.name()

View file

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

View file

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

View file

@ -118,7 +118,7 @@ class MachCommands(CommandBase):
DEFAULT_RENDER_MODE = "cpu" DEFAULT_RENDER_MODE = "cpu"
HELP_RENDER_MODE = "Value can be 'cpu', 'gpu' or 'both' (default " + DEFAULT_RENDER_MODE + ")" 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) CommandBase.__init__(self, context)
if not hasattr(self.context, "built_tests"): if not hasattr(self.context, "built_tests"):
self.context.built_tests = False self.context.built_tests = False
@ -127,7 +127,7 @@ class MachCommands(CommandBase):
@CommandArgument("--base", default=None, help="the base URL for testcases") @CommandArgument("--base", default=None, help="the base URL for testcases")
@CommandArgument("--date", default=None, help="the datestamp for the data") @CommandArgument("--date", default=None, help="the datestamp for the data")
@CommandArgument("--submit", "-a", default=False, action="store_true", help="submit the data to perfherder") @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() env = self.build_env()
cmd = ["bash", "test_perf.sh"] cmd = ["bash", "test_perf.sh"]
if base: if base:
@ -147,7 +147,13 @@ class MachCommands(CommandBase):
) )
@CommandBase.common_command_arguments(build_configuration=True, build_type=True) @CommandBase.common_command_arguments(build_configuration=True, build_type=True)
def test_unit( 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: ) -> int:
if test_name is None: if test_name is None:
test_name = [] test_name = []
@ -265,7 +271,7 @@ class MachCommands(CommandBase):
action="store_true", action="store_true",
help="Emit tidy warnings in the Github Actions annotations format", 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) tidy_failed = tidy.scan(not all_files, not no_progress, github_annotations)
print("\r ➤ Checking formatting of Rust files...") print("\r ➤ Checking formatting of Rust files...")
@ -299,7 +305,7 @@ class MachCommands(CommandBase):
@CommandArgument( @CommandArgument(
"tests", default=None, nargs="...", help="Specific WebIDL tests to run, relative to the tests directory" "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: if very_verbose:
logging.getLogger().level = logging.DEBUG logging.getLogger().level = logging.DEBUG
elif verbose: elif verbose:
@ -357,7 +363,7 @@ class MachCommands(CommandBase):
@Command("test-devtools", description="Run tests for devtools.", category="testing") @Command("test-devtools", description="Run tests for devtools.", category="testing")
@CommandArgument("test_names", nargs=argparse.REMAINDER, help="Only run tests that match these patterns") @CommandArgument("test_names", nargs=argparse.REMAINDER, help="Only run tests that match these patterns")
@CommandBase.common_command_arguments(build_type=True) @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...") print("Running devtools tests...")
passed = servo.devtools_tests.run_tests(SCRIPT_PATH, build_type, test_names) passed = servo.devtools_tests.run_tests(SCRIPT_PATH, build_type, test_names)
return 0 if passed else 1 return 0 if passed else 1
@ -369,7 +375,7 @@ class MachCommands(CommandBase):
parser=wpt.create_parser, parser=wpt.create_parser,
) )
@CommandBase.common_command_arguments(build_configuration=False, build_type=True) @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["pause_after_test"] = False
kwargs["include"] = ["infrastructure/failing-test.html"] kwargs["include"] = ["infrastructure/failing-test.html"]
return not self._test_wpt(build_type=build_type, **kwargs) 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 "test-wpt", description="Run the regular web platform test suite", category="testing", parser=wpt.create_parser
) )
@CommandBase.common_command_arguments(binary_selection=True) @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) return self._test_wpt(servo_binary, **kwargs)
@CommandBase.allow_target_configuration @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? # 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_value = wpt.run.run_tests(servo_binary, **kwargs)
return return_value if not kwargs["always_succeed"] else 0 return return_value if not kwargs["always_succeed"] else 0
@ -393,7 +399,7 @@ class MachCommands(CommandBase):
category="testing", category="testing",
parser=wpt.manifestupdate.create_parser, 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) return wpt.manifestupdate.update(check_clean=False)
@Command("fmt", description="Format Rust, Python, and TOML files", category="testing") @Command("fmt", description="Format Rust, Python, and TOML files", category="testing")
@ -411,7 +417,7 @@ class MachCommands(CommandBase):
@Command( @Command(
"update-wpt", description="Update the web platform tests", category="testing", parser=wpt.update.create_parser "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) patch = kwargs.get("patch", False)
if not patch and kwargs["sync"]: if not patch and kwargs["sync"]:
print("Are you sure you don't want a patch?") 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("tests", default=["recommended"], nargs="...", help="Specific tests to run")
@CommandArgument("--bmf-output", default=None, help="Specify BMF JSON output file") @CommandArgument("--bmf-output", default=None, help="Specify BMF JSON output file")
@CommandBase.common_command_arguments(binary_selection=True) @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) return self.dromaeo_test_runner(tests, servo_binary, bmf_output)
@Command("test-speedometer", description="Run servo's speedometer", category="testing") @Command("test-speedometer", description="Run servo's speedometer", category="testing")
@ -455,7 +461,7 @@ class MachCommands(CommandBase):
@CommandArgument( @CommandArgument(
"params", default=None, nargs="...", help=" filepaths of output files of two runs of dromaeo test " "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] prev_op_filename = params[0]
cur_op_filename = params[1] cur_op_filename = params[1]
result = {"Test": [], "Prev_Time": [], "Cur_Time": [], "Difference(%)": []} result = {"Test": [], "Prev_Time": [], "Cur_Time": [], "Difference(%)": []}
@ -552,7 +558,7 @@ class MachCommands(CommandBase):
return call([run_file, cmd, bin_path, base_dir]) 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")) base_dir = path.abspath(path.join("tests", "dromaeo"))
dromaeo_dir = path.join(base_dir, "dromaeo") dromaeo_dir = path.join(base_dir, "dromaeo")
run_file = path.join(base_dir, "run_dromaeo.py") run_file = path.join(base_dir, "run_dromaeo.py")
@ -582,7 +588,7 @@ class MachCommands(CommandBase):
output = dict() output = dict()
profile = "" if profile is None else profile + "/" 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": if result["unit"] == "ms":
output[profile + f"Speedometer/{result['name']}"] = { output[profile + f"Speedometer/{result['name']}"] = {
"latency": { # speedometer has ms we need to convert to ns "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", description="Update the net unit tests with cookie tests from http-state",
category="testing", category="testing",
) )
def update_net_cookies(self): def update_net_cookies(self) -> int:
cache_dir = path.join(self.config["tools"]["cache-dir"], "tests") cache_dir = path.join(self.config["tools"]["cache-dir"], "tests")
run_file = path.abspath( run_file = path.abspath(
path.join(PROJECT_TOPLEVEL_PATH, "components", "net", "tests", "cookie_http_state_utils.py") 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" "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") @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")) 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") run_file = path.join(base_dir, "tools", "import-conformance-tests.py")
dest_folder = path.join(base_dir, "conformance-%s" % version) 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") @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("--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") @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") tdir = path.join(self.context.topdir, "tests/wpt/webgpu/tests")
clone_dir = path.join(tdir, "cts_clone") clone_dir = path.join(tdir, "cts_clone")
# clone # clone
@ -786,7 +792,7 @@ class MachCommands(CommandBase):
) )
@CommandArgument("params", nargs="...", help="Command-line arguments to be passed through to Servo") @CommandArgument("params", nargs="...", help="Command-line arguments to be passed through to Servo")
@CommandBase.common_command_arguments(binary_selection=True) @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, # 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 # preventing a panic from hanging execution. This means that these kind
# of panics won't cause timeouts on CI. # of panics won't cause timeouts on CI.

View file

@ -13,7 +13,7 @@ from __future__ import annotations
import json import json
import sys import sys
from typing import ClassVar, List, Optional from typing import ClassVar, Optional, Any
import unittest import unittest
import logging import logging
@ -44,7 +44,7 @@ class JobConfig(object):
number_of_wpt_chunks: int = 20 number_of_wpt_chunks: int = 20
# These are the fields that must match in between two JobConfigs for them to be able to be # 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. # 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: def merge(self, other: JobConfig) -> bool:
"""Try to merge another job with this job. Returns True if merging is successful """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): class Encoder(json.JSONEncoder):
def default(self, o): def default(self, o: Any) -> Any:
if isinstance(o, (Config, JobConfig)): if isinstance(o, (Config, JobConfig)):
return o.__dict__ return o.__dict__
return json.JSONEncoder.default(self, o) return json.JSONEncoder.default(self, o)
@ -236,7 +236,7 @@ class Config(object):
return return
self.matrix.append(job) self.matrix.append(job)
def to_json(self, **kwargs) -> str: def to_json(self, **kwargs: Any) -> str:
return json.dumps(self, cls=Encoder, **kwargs) return json.dumps(self, cls=Encoder, **kwargs)

View file

@ -7,6 +7,7 @@
# option. This file may not be copied, modified, or distributed # option. This file may not be copied, modified, or distributed
# except according to those terms. # except according to those terms.
from os import PathLike
import hashlib import hashlib
import os import os
import os.path import os.path
@ -18,7 +19,8 @@ import urllib.error
import urllib.request import urllib.request
import zipfile import zipfile
from zipfile import ZipInfo 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 io import BufferedIOBase, BytesIO
from socket import error as socket_error 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, "..", "..")) 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" "Clear the readonly bit and reattempt the removal"
os.chmod(path, stat.S_IWRITE) os.chmod(path, stat.S_IWRITE)
func(path) func(path)
def delete(path) -> None: def delete(path: str) -> None:
if os.path.isdir(path) and not os.path.islink(path): if os.path.isdir(path) and not os.path.islink(path):
shutil.rmtree(path, onerror=remove_readonly) shutil.rmtree(path, onerror=remove_readonly)
else: 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 # https://stackoverflow.com/questions/39296101/python-zipfile-removes-execute-permissions-from-binaries
# In particular, we want the executable bit for executable files. # In particular, we want the executable bit for executable files.
class ZipFileWithUnixPermissions(zipfile.ZipFile): 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): if not isinstance(member, zipfile.ZipInfo):
member = self.getinfo(member) member = self.getinfo(member)
@ -137,7 +139,7 @@ class ZipFileWithUnixPermissions(zipfile.ZipFile):
return extracted return extracted
# For Python 3.x # 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 int(sys.version_info[0]) >= 3:
if not isinstance(member, zipfile.ZipInfo): if not isinstance(member, zipfile.ZipInfo):
member = self.getinfo(member) member = self.getinfo(member)
@ -154,7 +156,7 @@ class ZipFileWithUnixPermissions(zipfile.ZipFile):
return super(ZipFileWithUnixPermissions, self)._extract_member(member, targetpath, pwd) 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") assert src.endswith(".zip")
ZipFileWithUnixPermissions(src).extractall(dst) ZipFileWithUnixPermissions(src).extractall(dst)
@ -169,7 +171,7 @@ def extract(src, dst, movedir=None, remove=True) -> None:
os.remove(src) os.remove(src)
def check_hash(filename, expected, algorithm) -> None: def check_hash(filename: str, expected: str, algorithm: str) -> None:
hasher = hashlib.new(algorithm) hasher = hashlib.new(algorithm)
with open(filename, "rb") as f: with open(filename, "rb") as f:
while True: while True:
@ -182,11 +184,11 @@ def check_hash(filename, expected, algorithm) -> None:
sys.exit(1) 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")) 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): if isinstance(paths, list):
paths = os.pathsep.join(paths) 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 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): if isinstance(paths, list):
paths = os.pathsep.join(paths) paths = os.pathsep.join(paths)

View file

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