diff --git a/.github/workflows/ci-python.yml b/.github/workflows/ci-python.yml index e0f9442d15224..e46c56d42135d 100644 --- a/.github/workflows/ci-python.yml +++ b/.github/workflows/ci-python.yml @@ -43,6 +43,13 @@ jobs: name: Type Checker run: bazel run //py:mypy + lint: + name: Lint + uses: ./.github/workflows/bazel.yml + with: + name: Lint + run: bazel run //py:ruff -- --check + unit-tests: name: Unit Tests needs: build diff --git a/Rakefile b/Rakefile index 8de6c5bcb080d..51d9ad652a8c2 100644 --- a/Rakefile +++ b/Rakefile @@ -1495,6 +1495,7 @@ namespace :all do end Bazel.execute('run', [], '//py:mypy') + Bazel.execute('run', [], '//py:ruff') Bazel.execute('run', [], '//rb:steep') shellcheck = Bazel.execute('build', [], '@multitool//tools/shellcheck') Bazel.execute('run', ['--', '-shellcheck', shellcheck], '@multitool//tools/actionlint:cwd') diff --git a/common/devtools/convert_protocol_to_json.py b/common/devtools/convert_protocol_to_json.py index de17844be0a93..7c6c0ed0c2a43 100644 --- a/common/devtools/convert_protocol_to_json.py +++ b/common/devtools/convert_protocol_to_json.py @@ -12,9 +12,7 @@ def main(argv): - parser = argparse.ArgumentParser( - description=("Converts from .pdl to .json by invoking the pdl Python module.") - ) + parser = argparse.ArgumentParser(description=("Converts from .pdl to .json by invoking the pdl Python module.")) parser.add_argument( "--map_binary_to_string", type=bool, @@ -28,14 +26,13 @@ def main(argv): parser.add_argument("json_file", help="The .json output file write.") args = parser.parse_args(argv) file_name = os.path.normpath(args.pdl_file) - input_file = open(file_name, "r") - pdl_string = input_file.read() + with open(file_name) as input_file: + pdl_string = input_file.read() + protocol = pdl.loads(pdl_string, file_name, args.map_binary_to_string) - input_file.close() - output_file = open(os.path.normpath(args.json_file), "w") - json.dump(protocol, output_file, indent=4, separators=(",", ": ")) - output_file.close() + with open(os.path.normpath(args.json_file), "w") as output_file: + json.dump(protocol, output_file, indent=4, separators=(",", ": ")) if __name__ == "__main__": diff --git a/common/devtools/pdl.py b/common/devtools/pdl.py index de0dd516702ab..d7f94a182f43b 100644 --- a/common/devtools/pdl.py +++ b/common/devtools/pdl.py @@ -2,11 +2,10 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -from __future__ import print_function -from collections import OrderedDict import json import re import sys +from collections import OrderedDict from typing import Any description = "" @@ -24,9 +23,7 @@ ] -def assignType( - item: dict, type: str, is_array: bool = False, map_binary_to_string: bool = False -) -> None: +def assignType(item: dict, type: str, is_array: bool = False, map_binary_to_string: bool = False) -> None: if is_array: item["type"] = "array" item["items"] = OrderedDict() @@ -59,9 +56,7 @@ def createItem( return result -def parse( - data: str, file_name: str, map_binary_to_string: bool = False -) -> OrderedDict[str, Any]: +def parse(data: str, file_name: str, map_binary_to_string: bool = False) -> OrderedDict[str, Any]: protocol = OrderedDict() protocol["version"] = OrderedDict() protocol["domains"] = [] @@ -91,9 +86,7 @@ def parse( match = re.compile(r"^(experimental )?(deprecated )?domain (.*)").match(line) if match: - domain = createItem( - {"domain": match.group(3)}, match.group(1), match.group(2) - ) + domain = createItem({"domain": match.group(3)}, match.group(1), match.group(2)) protocol["domains"].append(domain) continue @@ -116,9 +109,7 @@ def parse( domain["types"].append(item) continue - match = re.compile( - r"^ (experimental )?(deprecated )?(command|event) (.*)" - ).match(line) + match = re.compile(r"^ (experimental )?(deprecated )?(command|event) (.*)").match(line) if match: list = [] if match.group(3) == "command": @@ -185,14 +176,12 @@ def parse( enumliterals.append(trimLine) continue - print("Error in %s:%s, illegal token: \t%s" % (file_name, i, line)) + print(f"Error in {file_name}:{i}, illegal token: \t{line}") sys.exit(1) return protocol -def loads( - data: str, file_name: str, map_binary_to_string: bool = False -) -> OrderedDict[str, Any] | Any: +def loads(data: str, file_name: str, map_binary_to_string: bool = False) -> OrderedDict[str, Any] | Any: if file_name.endswith(".pdl"): return parse(data, file_name, map_binary_to_string) return json.loads(data) diff --git a/dotnet/private/generate_resources_tool.py b/dotnet/private/generate_resources_tool.py index b60ee6f02db92..b47f0e0197400 100644 --- a/dotnet/private/generate_resources_tool.py +++ b/dotnet/private/generate_resources_tool.py @@ -9,7 +9,7 @@ Each identifier becomes a const string in ResourceUtilities class. The content is emitted as a C# raw string literal using 5-quotes. -TODO: +Todo: It would be nice to convert this small single-file utility to .NET10/C#, so it would work like `dotnet run generate_resources.cs -- `. Meaning .NET developers can easily support it. @@ -18,17 +18,16 @@ import argparse import os import sys -from typing import List, Tuple -def parse_args(argv: List[str]) -> argparse.Namespace: +def parse_args(argv: list[str]) -> argparse.Namespace: parser = argparse.ArgumentParser() parser.add_argument("--output", required=True) parser.add_argument("--input", action="append", default=[], help="IDENT=path") return parser.parse_args(argv) -def parse_input_spec(spec: str) -> Tuple[str, str]: +def parse_input_spec(spec: str) -> tuple[str, str]: if "=" not in spec: raise ValueError(f"Invalid --input value, expected IDENT=path, got: {spec}") ident, path = spec.split("=", 1) @@ -41,10 +40,10 @@ def parse_input_spec(spec: str) -> Tuple[str, str]: return ident, path -def generate(output: str, inputs: List[Tuple[str, str]]) -> None: - props: List[str] = [] +def generate(output: str, inputs: list[tuple[str, str]]) -> None: + props: list[str] = [] for prop_name, path in inputs: - with open(path, "r", encoding="utf-8") as f: + with open(path, encoding="utf-8") as f: content = f.read() # Use a C# raw string literal with five quotes. For a valid raw # literal, the content must start on a new line and the closing @@ -58,7 +57,7 @@ def generate(output: str, inputs: List[Tuple[str, str]]) -> None: literal = '"""""\n' + content + '\n"""""' props.append(f" internal const string {prop_name} = {literal};") - lines: List[str] = [] + lines: list[str] = [] lines.append("// ") lines.append("namespace OpenQA.Selenium.Internal;") lines.append("") @@ -74,9 +73,9 @@ def generate(output: str, inputs: List[Tuple[str, str]]) -> None: f.write("\n".join(lines)) -def main(argv: List[str]) -> int: +def main(argv: list[str]) -> int: args = parse_args(argv) - inputs: List[Tuple[str, str]] = [] + inputs: list[tuple[str, str]] = [] for spec in args.input: ident, path = parse_input_spec(spec) inputs.append((ident, path)) diff --git a/javascript/private/gen_file.py b/javascript/private/gen_file.py index 21c5bc2f29ed4..58c52fb75fe75 100644 --- a/javascript/private/gen_file.py +++ b/javascript/private/gen_file.py @@ -43,17 +43,17 @@ def write_atom_literal(out, name, contents, lang, utf8): elif "java" == lang: line_format = ' .append\("{}")\n' else: - raise RuntimeError("Unknown language: %s " % lang) + raise RuntimeError(f"Unknown language: {lang} ") name = get_atom_name(name) if "cc" == lang or "hh" == lang: char_type = "char" if utf8 else "wchar_t" - out.write("const %s* const %s[] = {\n" % (char_type, name)) + out.write(f"const {char_type}* const {name}[] = {{\n") elif "java" == lang: - out.write(" %s(new StringBuilder()\n" % name) + out.write(f" {name}(new StringBuilder()\n") else: - raise RuntimeError("Unknown language: %s " % lang) + raise RuntimeError(f"Unknown language: {lang} ") # Make the header file play nicely in a terminal: limit lines to 80 # characters, but make sure we don't cut off a line in the middle @@ -76,24 +76,21 @@ def write_atom_literal(out, name, contents, lang, utf8): def generate_header(file_name, out, js_map, just_declare, utf8): - define_guard = "WEBDRIVER_%s" % os.path.basename(file_name.upper()).replace( - ".", "_" - ) + define_guard = "WEBDRIVER_{}".format(os.path.basename(file_name.upper()).replace(".", "_")) include_stddef = "" if utf8 else "\n#include // For wchar_t." out.write( - """%s + f"""{_copyright} /* AUTO GENERATED - DO NOT EDIT BY HAND */ -#ifndef %s -#define %s -%s +#ifndef {define_guard} +#define {define_guard} +{include_stddef} #include // For std::(w)string. -namespace webdriver { -namespace atoms { +namespace webdriver {{ +namespace atoms {{ """ - % (_copyright, define_guard, define_guard, include_stddef) ) string_type = "std::string" if utf8 else "std::wstring" @@ -101,48 +98,46 @@ def generate_header(file_name, out, js_map, just_declare, utf8): for name, file in js_map.items(): if just_declare: - out.write("extern const %s* const %s[];\n" % (char_type, name.upper())) + out.write(f"extern const {char_type}* const {name.upper()}[];\n") else: - contents = open(file, "r").read() + contents = open(file).read() write_atom_literal(out, name, contents, "hh", utf8) out.write( - """ -static inline %s asString(const %s* const atom[]) { - %s source; - for (int i = 0; atom[i] != NULL; i++) { + f""" +static inline {string_type} asString(const {char_type}* const atom[]) {{ + {string_type} source; + for (int i = 0; atom[i] != NULL; i++) {{ source += atom[i]; - } + }} return source; -} +}} -} // namespace atoms -} // namespace webdriver +}} // namespace atoms +}} // namespace webdriver -#endif // %s +#endif // {define_guard} """ - % (string_type, char_type, string_type, define_guard) ) def generate_cc_source(out, js_map, utf8): out.write( - """%s + f"""{_copyright} /* AUTO GENERATED - DO NOT EDIT BY HAND */ #include // For NULL. #include "atoms.h" -namespace webdriver { -namespace atoms { +namespace webdriver {{ +namespace atoms {{ """ - % _copyright ) for name, file in js_map.items(): - contents = open(file, "r").read() + contents = open(file).read() write_atom_literal(out, name, contents, "cc", utf8) out.write(""" @@ -162,37 +157,35 @@ def generate_java_source(file_name, out, preamble, js_map): out.write(preamble) out.write("") out.write( - """ -public enum %s { + f""" +public enum {class_name} {{ // AUTO GENERATED - DO NOT EDIT BY HAND """ - % class_name ) for name, file in js_map.items(): - contents = open(file, "r").read() + contents = open(file).read() write_atom_literal(out, name, contents, "java", True) out.write( - """ + f""" ; private final String value; - public String getValue() { + public String getValue() {{ return value; - } + }} - public String toString() { + public String toString() {{ return getValue(); - } + }} - %s(String value) { + {class_name}(String value) {{ this.value = value; - } -} + }} +}} """ - % class_name ) @@ -216,7 +209,7 @@ def main(argv=[]): elif "java" == lang: generate_java_source(file_name, out, preamble, js_map) else: - raise RuntimeError("Unknown lang: %s" % lang) + raise RuntimeError(f"Unknown lang: {lang}") if __name__ == "__main__": diff --git a/py/BUILD.bazel b/py/BUILD.bazel index 9e91f7f0797aa..f141a0c75f481 100644 --- a/py/BUILD.bazel +++ b/py/BUILD.bazel @@ -12,6 +12,11 @@ load("//py:defs.bzl", "generate_devtools", "py_test_suite") load("//py/private:browsers.bzl", "BROWSERS") load("//py/private:import.bzl", "py_import") +exports_files( + ["pyproject.toml"], + visibility = ["//py:__subpackages__"], +) + py_binary( name = "selenium-release", srcs = [ @@ -661,3 +666,8 @@ py_binary( requirement("mypy"), ], ) + +alias( + name = "ruff", + actual = "//py/private:ruff", +) diff --git a/py/private/BUILD.bazel b/py/private/BUILD.bazel index 43611ccd9eb74..52b484db3b808 100644 --- a/py/private/BUILD.bazel +++ b/py/private/BUILD.bazel @@ -8,3 +8,14 @@ py_binary( legacy_create_init = False, visibility = ["//visibility:public"], ) + +py_binary( + name = "ruff", + srcs = ["ruff.py"], + data = [ + "//py:pyproject.toml", + "@multitool//tools/ruff", + ], + visibility = ["//visibility:public"], + deps = ["@rules_python//python/runfiles"], +) diff --git a/py/private/ruff.py b/py/private/ruff.py new file mode 100644 index 0000000000000..415f0f7cd30ab --- /dev/null +++ b/py/private/ruff.py @@ -0,0 +1,54 @@ +# Licensed to the Software Freedom Conservancy (SFC) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The SFC licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +"""Run ruff linter on Python files outside py/ directory.""" + +import os +import subprocess +import sys + +from python.runfiles import Runfiles + +LINT_DIRS = ["scripts", "common", "dotnet", "java", "javascript", "rb"] +EXCLUDES = ["**/node_modules/**", "**/.bundle/**"] + + +if __name__ == "__main__": + r = Runfiles.Create() + ruff = r.Rlocation("rules_multitool++multitool+multitool/tools/ruff/ruff") + + os.chdir(os.environ["BUILD_WORKSPACE_DIRECTORY"]) + + # Check if --check flag is passed (for CI - verify without fixing) + check_only = "--check" in sys.argv + extra_args = [arg for arg in sys.argv[1:] if arg != "--check"] + + exclude_args = [] + for pattern in EXCLUDES: + exclude_args.extend(["--exclude", pattern]) + + check_cmd = [ruff, "check", "--config=py/pyproject.toml"] + if not check_only: + check_cmd.extend(["--fix", "--show-fixes"]) + check_result = subprocess.run(check_cmd + exclude_args + LINT_DIRS + extra_args) + + format_cmd = [ruff, "format", "--config=py/pyproject.toml"] + if check_only: + format_cmd.append("--check") + format_result = subprocess.run(format_cmd + exclude_args + LINT_DIRS) + + sys.exit(check_result.returncode or format_result.returncode) diff --git a/scripts/format.ps1 b/scripts/format.ps1 index 715956ed1dcea..fe32e2af417e6 100644 --- a/scripts/format.ps1 +++ b/scripts/format.ps1 @@ -35,8 +35,7 @@ bazel run @rules_rust//:rustfmt section "Python" Write-Host " python - ruff" -ForegroundColor Green -bazel run @multitool//tools/ruff:cwd -- check --fix --show-fixes -bazel run @multitool//tools/ruff:cwd -- format +bazel run //py:ruff section "Copyright" bazel run //scripts:update_copyright diff --git a/scripts/format.sh b/scripts/format.sh index cc97d853b8e5b..2d80782b1f1f5 100755 --- a/scripts/format.sh +++ b/scripts/format.sh @@ -33,8 +33,7 @@ bazel run @rules_rust//:rustfmt section "Python" echo " python - ruff" >&2 -bazel run @multitool//tools/ruff:cwd -- check --fix --show-fixes -bazel run @multitool//tools/ruff:cwd -- format +bazel run //py:ruff section "Copyright" bazel run //scripts:update_copyright diff --git a/scripts/pinned_browsers.py b/scripts/pinned_browsers.py index 4fdc017ad3f74..5b844cd289da7 100755 --- a/scripts/pinned_browsers.py +++ b/scripts/pinned_browsers.py @@ -17,7 +17,7 @@ def calculate_hash(url): - print("Calculate hash for %s" % url, file=sys.stderr) + print(f"Calculate hash for {url}", file=sys.stderr) h = hashlib.sha256() r = http.request("GET", url, preload_content=False) for b in iter(lambda: r.read(4096), b""): @@ -32,9 +32,10 @@ def get_chrome_info_for_channel(channel): ) all_versions = json.loads(r.data) # use the same milestone for all chrome releases, so pick the lowest - milestone = min( - [version["milestone"] for version in all_versions if version["milestone"]] - ) + milestones = [version["milestone"] for version in all_versions if version["milestone"]] + if not milestones: + raise ValueError(f"No Chrome versions with milestones found for channel '{channel}'") + milestone = min(milestones) r = http.request( "GET", "https://googlechromelabs.github.io/chrome-for-testing/known-good-versions-with-downloads.json", @@ -52,7 +53,9 @@ def chromedriver(selected_version, workspace_prefix=""): drivers = selected_version["downloads"]["chromedriver"] - url = [d["url"] for d in drivers if d["platform"] == "linux64"][0] + url = next((d["url"] for d in drivers if d["platform"] == "linux64"), None) + if url is None: + raise ValueError("No chromedriver download found for platform 'linux64'") sha = calculate_hash(url) content += f""" http_archive( @@ -74,7 +77,9 @@ def chromedriver(selected_version, workspace_prefix=""): ) """ - url = [d["url"] for d in drivers if d["platform"] == "mac-arm64"][0] + url = next((d["url"] for d in drivers if d["platform"] == "mac-arm64"), None) + if url is None: + raise ValueError("No chromedriver download found for platform 'mac-arm64'") sha = calculate_hash(url) content += f""" @@ -104,7 +109,9 @@ def chrome(selected_version, workspace_prefix=""): content = "" chrome_downloads = selected_version["downloads"]["chrome"] - url = [d["url"] for d in chrome_downloads if d["platform"] == "linux64"][0] + url = next((d["url"] for d in chrome_downloads if d["platform"] == "linux64"), None) + if url is None: + raise ValueError("No Chrome download found for platform 'linux64'") sha = calculate_hash(url) content += f""" @@ -131,7 +138,9 @@ def chrome(selected_version, workspace_prefix=""): ) """ - url = [d["url"] for d in chrome_downloads if d["platform"] == "mac-arm64"][0] + url = next((d["url"] for d in chrome_downloads if d["platform"] == "mac-arm64"), None) + if url is None: + raise ValueError("No Chrome download found for platform 'mac-arm64'") sha = calculate_hash(url) # Calculate SHA for Mac chrome content += f""" http_archive( @@ -176,9 +185,7 @@ def case_insensitive_json_loads(json_str): def get_edge_versions(platform): """Fetch all available Edge browser versions for a platform from enterprise API.""" - r = http.request( - "GET", "https://edgeupdates.microsoft.com/api/products?view=enterprise" - ) + r = http.request("GET", "https://edgeupdates.microsoft.com/api/products?view=enterprise") all_data = case_insensitive_json_loads(r.data) platform_name = "MacOS" if platform == "mac" else "Linux" @@ -262,14 +269,14 @@ def find_matching_edge_version(platform): def mac_edge_browser_content(browser_url, browser_hash, browser_version): """Generate Bazel content for Mac Edge browser.""" - return """ + return f""" pkg_archive( name = "mac_edge", - url = "%s", - sha256 = "%s", - move = { - "MicrosoftEdge-%s.pkg/Payload/Microsoft Edge.app": "Edge.app", - }, + url = "{browser_url}", + sha256 = "{browser_hash.lower()}", + move = {{ + "MicrosoftEdge-{browser_version}.pkg/Payload/Microsoft Edge.app": "Edge.app", + }}, build_file_content = \"\"\" load("@aspect_rules_js//js:defs.bzl", "js_library") package(default_visibility = ["//visibility:public"]) @@ -282,16 +289,16 @@ def mac_edge_browser_content(browser_url, browser_hash, browser_version): ) \"\"\", ) -""" % (browser_url, browser_hash.lower(), browser_version) +""" def linux_edge_browser_content(browser_url, browser_hash): """Generate Bazel content for Linux Edge browser.""" - return """ + return f""" deb_archive( name = "linux_edge", - url = "%s", - sha256 = "%s", + url = "{browser_url}", + sha256 = "{browser_hash.lower()}", build_file_content = \"\"\" load("@aspect_rules_js//js:defs.bzl", "js_library") package(default_visibility = ["//visibility:public"]) @@ -309,7 +316,7 @@ def linux_edge_browser_content(browser_url, browser_hash): ) \"\"\", ) -""" % (browser_url, browser_hash.lower()) +""" def edge_and_edgedriver(): @@ -332,9 +339,7 @@ def edge_and_edgedriver(): # Output browsers first: mac, then linux if "mac" in matches: browser = matches["mac"]["browser"] - content += mac_edge_browser_content( - browser["url"], browser["hash"], browser["version"] - ) + content += mac_edge_browser_content(browser["url"], browser["hash"], browser["version"]) if "linux" in matches: browser = matches["linux"]["browser"] @@ -342,9 +347,7 @@ def edge_and_edgedriver(): # Output drivers: linux, then mac if "linux" in matches: - content += edgedriver_content( - "linux_edgedriver", matches["linux"]["driver_url"] - ) + content += edgedriver_content("linux_edgedriver", matches["linux"]["driver_url"]) if "mac" in matches: content += edgedriver_content("mac_edgedriver", matches["mac"]["driver_url"]) @@ -355,11 +358,11 @@ def edge_and_edgedriver(): def edgedriver_content(name, driver_url): """Generate Bazel content for EdgeDriver.""" driver_sha = calculate_hash(driver_url) - return """ + return f""" http_archive( - name = "%s", - url = "%s", - sha256 = "%s", + name = "{name}", + url = "{driver_url}", + sha256 = "{driver_sha}", build_file_content = \"\"\" load("@aspect_rules_js//js:defs.bzl", "js_library") package(default_visibility = ["//visibility:public"]) @@ -372,25 +375,23 @@ def edgedriver_content(name, driver_url): ) \"\"\", ) -""" % (name, driver_url, driver_sha) +""" def geckodriver(): content = "" - r = http.request( - "GET", "https://api.github.com/repos/mozilla/geckodriver/releases/latest" - ) + r = http.request("GET", "https://api.github.com/repos/mozilla/geckodriver/releases/latest") for a in json.loads(r.data)["assets"]: if a["name"].endswith("-linux64.tar.gz"): url = a["browser_download_url"] sha = calculate_hash(url) content = ( content - + """ http_archive( + + f""" http_archive( name = "linux_geckodriver", - url = "%s", - sha256 = "%s", + url = "{url}", + sha256 = "{sha}", build_file_content = \"\"\" load("@aspect_rules_js//js:defs.bzl", "js_library") package(default_visibility = ["//visibility:public"]) @@ -404,7 +405,6 @@ def geckodriver(): \"\"\", ) """ - % (url, sha) ) if a["name"].endswith("-macos-aarch64.tar.gz"): @@ -412,11 +412,11 @@ def geckodriver(): sha = calculate_hash(url) content = ( content - + """ + + f""" http_archive( name = "mac_geckodriver", - url = "%s", - sha256 = "%s", + url = "{url}", + sha256 = "{sha}", build_file_content = \"\"\" load("@aspect_rules_js//js:defs.bzl", "js_library") package(default_visibility = ["//visibility:public"]) @@ -430,7 +430,6 @@ def geckodriver(): \"\"\", ) """ - % (url, sha) ) return content @@ -451,30 +450,19 @@ def firefox(): def firefox_version_data(): - versions = http.request( - "GET", "https://product-details.mozilla.org/1.0/firefox_versions.json" - ) + versions = http.request("GET", "https://product-details.mozilla.org/1.0/firefox_versions.json") return versions.data def firefox_linux(version): if int(version.split(".")[0]) < 135: - return ( - "https://ftp.mozilla.org/pub/firefox/releases/%s/linux-x86_64/en-US/firefox-%s.tar.bz2" - % (version, version) - ) + return f"https://ftp.mozilla.org/pub/firefox/releases/{version}/linux-x86_64/en-US/firefox-{version}.tar.bz2" else: - return ( - "https://ftp.mozilla.org/pub/firefox/releases/%s/linux-x86_64/en-US/firefox-%s.tar.xz" - % (version, version) - ) + return f"https://ftp.mozilla.org/pub/firefox/releases/{version}/linux-x86_64/en-US/firefox-{version}.tar.xz" def firefox_mac(version): - return ( - "https://ftp.mozilla.org/pub/firefox/releases/%s/mac/en-US/Firefox%%20%s.dmg" - % (version, version) - ) + return f"https://ftp.mozilla.org/pub/firefox/releases/{version}/mac/en-US/Firefox%20{version}.dmg" def print_firefox(version, workspace_name, sha_linux, sha_mac): diff --git a/scripts/selenium_manager.py b/scripts/selenium_manager.py index 4e4336e62be8d..cab544840bea8 100755 --- a/scripts/selenium_manager.py +++ b/scripts/selenium_manager.py @@ -32,32 +32,32 @@ def print_linux(base_url, sha): return """ http_file( name = "download_sm_linux", executable = True, - sha256 = "%s", - url = "%s", + sha256 = "{}", + url = "{}", ) -""" % (sha, base_url + "/selenium-manager-linux") +""".format(sha, base_url + "/selenium-manager-linux") def print_macos(base_url, sha): return """ http_file( name = "download_sm_macos", executable = True, - sha256 = "%s", - url = "%s", + sha256 = "{}", + url = "{}", ) -""" % (sha, base_url + "/selenium-manager-macos") +""".format(sha, base_url + "/selenium-manager-macos") def print_windows(base_url, sha): return """ http_file( name = "download_sm_windows", executable = True, - sha256 = "%s", - url = "%s", + sha256 = "{}", + url = "{}", ) -""" % (sha, base_url + "/selenium-manager-windows.exe") +""".format(sha, base_url + "/selenium-manager-windows.exe") if __name__ == "__main__": diff --git a/scripts/update_cdp.py b/scripts/update_cdp.py index 2b15df543798d..cb084af50e126 100755 --- a/scripts/update_cdp.py +++ b/scripts/update_cdp.py @@ -16,12 +16,13 @@ def get_chrome_milestone(): - """This is the same method from pinned_browser. Use --chrome_channel=Beta if - using early stable release.""" + """Get the Chrome milestone from the channel. + + This is the same method from pinned_browser. Use --chrome_channel=Beta if + using early stable release. + """ parser = argparse.ArgumentParser() - parser.add_argument( - "--chrome_channel", default="Stable", help="Set the Chrome channel" - ) + parser.add_argument("--chrome_channel", default="Stable", help="Set the Chrome channel") args = parser.parse_args() channel = args.chrome_channel @@ -31,9 +32,10 @@ def get_chrome_milestone(): ) all_versions = json.loads(r.data) # use the same milestone for all Chrome releases, so pick the lowest - milestone = min( - [version["milestone"] for version in all_versions if version["milestone"]] - ) + milestones = [version["milestone"] for version in all_versions if version["milestone"]] + if not milestones: + raise ValueError(f"No Chrome versions with milestones found for channel '{channel}'") + milestone = min(milestones) r = http.request( "GET", "https://googlechromelabs.github.io/chrome-for-testing/known-good-versions-with-downloads.json", @@ -66,7 +68,7 @@ def old_chrome(chrome_milestone): def flatten_browser_pdl(file_path, chrome_version): """Preserves the version block and concatenates all included domain .pdl files.""" - with open(file_path, "r") as file: + with open(file_path) as file: content = file.read() # Extract version block version_match = re.search(r"(version\s+major\s+\d+\s+minor\s+\d+)", content) @@ -85,9 +87,7 @@ def flatten_browser_pdl(file_path, chrome_version): def add_pdls(chrome_milestone): - source_dir = ( - root_dir / f"common/devtools/chromium/v{previous_chrome(chrome_milestone)}" - ) + source_dir = root_dir / f"common/devtools/chromium/v{previous_chrome(chrome_milestone)}" target_dir = root_dir / f"common/devtools/chromium/v{new_chrome(chrome_milestone)}" old_dir = root_dir / f"common/devtools/chromium/v{old_chrome(chrome_milestone)}" @@ -104,19 +104,16 @@ def add_pdls(chrome_milestone): f"{target_dir}/browser_protocol.pdl", ) - flatten_browser_pdl( - f"{target_dir}/browser_protocol.pdl", chrome_milestone["version"] - ) + flatten_browser_pdl(f"{target_dir}/browser_protocol.pdl", chrome_milestone["version"]) deps_content = http.request( "GET", f"https://raw.githubusercontent.com/chromium/chromium/{chrome_milestone['version']}/DEPS", ).data.decode("utf-8") - v8_revision = ( - [line for line in deps_content.split("\n") if "v8_revision" in line][0] - .split(": ")[1] - .strip("',") - ) + v8_revision_line = next((line for line in deps_content.split("\n") if "v8_revision" in line), None) + if v8_revision_line is None: + raise ValueError(f"No v8_revision found in DEPS for Chrome {chrome_milestone['version']}") + v8_revision = v8_revision_line.split(": ")[1].strip("',") fetch_and_save( f"https://raw.githubusercontent.com/v8/v8/{v8_revision}/include/js_protocol.pdl", f"{target_dir}/js_protocol.pdl", @@ -133,8 +130,11 @@ def add_pdls(chrome_milestone): def create_new_chrome_files(src_base, chrome_milestone): - """Java and .NET need to copy previous version directory into new version - directory.""" + """Create new Chrome devtools files for a language binding. + + Java and .NET need to copy previous version directory into new version + directory. + """ source_dir = root_dir / f"{src_base}/v{previous_chrome(chrome_milestone)}" target_dir = root_dir / f"{src_base}/v{new_chrome(chrome_milestone)}" old_dir = root_dir / f"{src_base}/v{old_chrome(chrome_milestone)}" @@ -148,12 +148,8 @@ def create_new_chrome_files(src_base, chrome_milestone): shutil.copy(item, target_dir) for file in target_dir.iterdir(): - replace_in_file( - file, previous_chrome(chrome_milestone), new_chrome(chrome_milestone) - ) - new_filename = file.name.replace( - previous_chrome(chrome_milestone), new_chrome(chrome_milestone) - ) + replace_in_file(file, previous_chrome(chrome_milestone), new_chrome(chrome_milestone)) + new_filename = file.name.replace(previous_chrome(chrome_milestone), new_chrome(chrome_milestone)) file.rename(target_dir / new_filename) subprocess.run(["git", "add", str(target_dir / "*")], cwd=root_dir) @@ -177,9 +173,7 @@ def update_java(chrome_milestone): root_dir / "Rakefile", ] for file in files: - replace_in_file( - file, old_chrome(chrome_milestone), new_chrome(chrome_milestone) - ) + replace_in_file(file, old_chrome(chrome_milestone), new_chrome(chrome_milestone)) def update_dotnet(chrome_milestone): @@ -191,19 +185,13 @@ def update_dotnet(chrome_milestone): root_dir / "dotnet/src/webdriver/DevTools/DevToolsDomains.cs", ] for file in files: - replace_in_file( - file, old_chrome(chrome_milestone), new_chrome(chrome_milestone) - ) + replace_in_file(file, old_chrome(chrome_milestone), new_chrome(chrome_milestone)) - files = [ - root_dir / "dotnet/test/common/CustomDriverConfigs/StableChannelChromeDriver.cs" - ] + files = [root_dir / "dotnet/test/common/CustomDriverConfigs/StableChannelChromeDriver.cs"] dir_path = root_dir / "dotnet/test/common/DevTools" files.extend(str(file) for file in dir_path.glob("*") if file.is_file()) for file in files: - replace_in_file( - file, previous_chrome(chrome_milestone), new_chrome(chrome_milestone) - ) + replace_in_file(file, previous_chrome(chrome_milestone), new_chrome(chrome_milestone)) def update_ruby(chrome_milestone): @@ -240,6 +228,4 @@ def update_js(chrome_milestone): update_python(chrome_milestone) update_js(chrome_milestone) - print( - f"adding CDP {new_chrome(chrome_milestone)} and removing {old_chrome(chrome_milestone)}" - ) + print(f"adding CDP {new_chrome(chrome_milestone)} and removing {old_chrome(chrome_milestone)}") diff --git a/scripts/update_copyright.py b/scripts/update_copyright.py index 035b0626b977b..4829dae8e07c5 100755 --- a/scripts/update_copyright.py +++ b/scripts/update_copyright.py @@ -29,14 +29,12 @@ def __init__(self, comment_characters="//", prefix=None): def update(self, files): for file in files: - with open(file, "r", encoding="utf-8-sig") as f: + with open(file, encoding="utf-8-sig") as f: lines = f.readlines() index = -1 for i, line in enumerate(lines): - if line.startswith( - self._comment_characters - ) or self.valid_copyright_notice_line(line, index, file): + if line.startswith(self._comment_characters) or self.valid_copyright_notice_line(line, index, file): index += 1 else: break @@ -57,11 +55,7 @@ def copyright_notice(self, file): return "".join(self.copyright_notice_lines(file)) def copyright_notice_lines(self, file): - return ( - self.dotnet(file) - if file.endswith("cs") - else self._prefix + self.commented_notice_lines - ) + return self.dotnet(file) if file.endswith("cs") else self._prefix + self.commented_notice_lines def dotnet(self, file): file_name = os.path.basename(file) @@ -71,10 +65,7 @@ def dotnet(self, file): @property def commented_notice_lines(self): - return [ - f"{self._comment_characters} {line}".rstrip() + "\n" - for line in self.NOTICE.split("\n") - ] + return [f"{self._comment_characters} {line}".rstrip() + "\n" for line in self.NOTICE.split("\n")] def write_update_notice(self, file, lines): # Build new content @@ -84,7 +75,7 @@ def write_update_notice(self, file, lines): new_content += "".join(line.rstrip() + "\n" for line in lines) # Only write if different - with open(file, "r", encoding="utf-8-sig") as f: + with open(file, encoding="utf-8-sig") as f: old_content = f.read() if new_content == old_content: return diff --git a/scripts/update_multitool_binaries.py b/scripts/update_multitool_binaries.py index 78b18a1bef559..697581226dc60 100755 --- a/scripts/update_multitool_binaries.py +++ b/scripts/update_multitool_binaries.py @@ -1,6 +1,6 @@ #!/usr/bin/env python +"""Update tool binaries in a Bazel rules_multitool lockfile. -""" This script updates the version of tool binaries defined in a Bazel rules_multitool lockfile. If the tool has binaries hosted in a public GitHub repo's Release assets, it will update the lockfile's URL and hash to the latest versions, otherwise it will skip it.