From 2c9c08d520e6131f0314caf6e1d541ec34d91fc4 Mon Sep 17 00:00:00 2001 From: Corey Goldberg <1113081+cgoldberg@users.noreply.github.com> Date: Mon, 23 Feb 2026 12:18:47 -0500 Subject: [PATCH 1/7] [py] Create module for importing latest devtools --- py/BUILD.bazel | 18 ++++++++++++++++-- py/defs.bzl | 2 ++ py/private/generate_devtools.bzl | 17 +++++++++++++++++ 3 files changed, 35 insertions(+), 2 deletions(-) diff --git a/py/BUILD.bazel b/py/BUILD.bazel index 0a4d5228d518d..994cb714e7ab8 100644 --- a/py/BUILD.bazel +++ b/py/BUILD.bazel @@ -8,7 +8,7 @@ load("@rules_python//python:packaging.bzl", "py_package", "py_wheel") load("@rules_python//python:pip.bzl", "compile_pip_requirements") load("@rules_python//sphinxdocs:sphinx.bzl", "sphinx_build_binary", "sphinx_docs") load("//common:defs.bzl", "copy_file") -load("//py:defs.bzl", "generate_devtools", "py_test_suite") +load("//py:defs.bzl", "generate_devtools", "generate_devtools_latest", "py_test_suite") load("//py/private:browsers.bzl", "BROWSERS") load("//py/private:import.bzl", "py_import") @@ -280,7 +280,7 @@ py_library( exclude = [ "selenium/webdriver/common/bidi/**", ], - ), + ) + [":latest"], data = [ ":manager-linux", ":manager-macos", @@ -296,6 +296,14 @@ py_library( ], ) +# Module that points to the latest devtools module +py_library( + name = "latest", + srcs = [":create-latest-devtools"], + imports = ["selenium.webdriver.common.devtools"], + visibility = ["//visibility:public"], +) + # Support utilities (wait conditions, event listeners, etc.) py_library( name = "support", @@ -438,6 +446,7 @@ py_package( "py.selenium.webdriver.common", "py.selenium.webdriver.common.bidi", "py.selenium.webdriver.common.devtools", + "py.selenium.webdriver.common.devtools.latest", "py.selenium.webdriver.edge", "py.selenium.webdriver.firefox", "py.selenium.webdriver.remote", @@ -583,6 +592,11 @@ py_binary( protocol_version = devtools_version, ) for devtools_version in BROWSER_VERSIONS] +generate_devtools_latest( + name = "create-latest-devtools", + browser_versions = BROWSER_VERSIONS, +) + py_test_suite( name = "unit", size = "small", diff --git a/py/defs.bzl b/py/defs.bzl index 8f8b4ff94b5ac..09eb7c89fc1e6 100644 --- a/py/defs.bzl +++ b/py/defs.bzl @@ -1,9 +1,11 @@ load("//py/private:generate_devtools.bzl", _generate_devtools = "generate_devtools") +load("//py/private:generate_devtools.bzl", _generate_devtools_latest = "generate_devtools_latest") load("//py/private:import.bzl", _py_import = "py_import") load("//py/private:pytest.bzl", _pytest_test = "pytest_test") load("//py/private:suite.bzl", _py_test_suite = "py_test_suite") generate_devtools = _generate_devtools +generate_devtools_latest = _generate_devtools_latest pytest_test = _pytest_test py_import = _py_import py_test_suite = _py_test_suite diff --git a/py/private/generate_devtools.bzl b/py/private/generate_devtools.bzl index 5fea436ec40f8..81c90dc375862 100644 --- a/py/private/generate_devtools.bzl +++ b/py/private/generate_devtools.bzl @@ -48,3 +48,20 @@ generate_devtools = rule( "deps": attr.label_list(), }, ) + +def _generate_latest_impl(ctx): + versions = ctx.attr.browser_versions + latest = "v%s" % max([int(v[1:]) for v in versions]) + output_file = ctx.actions.declare_file("selenium/webdriver/common/devtools/latest/__init__.py") + ctx.actions.write( + output = output_file, + content = "from ..%s import *\n" % latest, + ) + return [DefaultInfo(files = depset([output_file]))] + +generate_devtools_latest = rule( + implementation = _generate_latest_impl, + attrs = { + "browser_versions": attr.string_list(mandatory = True), + }, +) From 8028eca7e5b1ef8ba90d752b80810b7d6db32adc Mon Sep 17 00:00:00 2001 From: Corey Goldberg <1113081+cgoldberg@users.noreply.github.com> Date: Mon, 23 Feb 2026 12:42:09 -0500 Subject: [PATCH 2/7] Fix build files --- py/BUILD.bazel | 3 ++- py/private/generate_devtools.bzl | 5 ++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/py/BUILD.bazel b/py/BUILD.bazel index 994cb714e7ab8..0af2675190b2c 100644 --- a/py/BUILD.bazel +++ b/py/BUILD.bazel @@ -280,7 +280,7 @@ py_library( exclude = [ "selenium/webdriver/common/bidi/**", ], - ) + [":latest"], + ), data = [ ":manager-linux", ":manager-macos", @@ -292,6 +292,7 @@ py_library( ":bidi", ":exceptions", ":remote", + ":latest", requirement("typing_extensions"), ], ) diff --git a/py/private/generate_devtools.bzl b/py/private/generate_devtools.bzl index 81c90dc375862..07da2243d16e4 100644 --- a/py/private/generate_devtools.bzl +++ b/py/private/generate_devtools.bzl @@ -57,7 +57,10 @@ def _generate_latest_impl(ctx): output = output_file, content = "from ..%s import *\n" % latest, ) - return [DefaultInfo(files = depset([output_file]))] + return [DefaultInfo( + files = depset([output_file]), + runfiles = ctx.runfiles(files = [output_file]), + )] generate_devtools_latest = rule( implementation = _generate_latest_impl, From cfb84ab06a16088e9a1f7b3fc832f765d0fab4c8 Mon Sep 17 00:00:00 2001 From: Corey Goldberg <1113081+cgoldberg@users.noreply.github.com> Date: Mon, 23 Feb 2026 12:54:27 -0500 Subject: [PATCH 3/7] Fix formatting --- py/BUILD.bazel | 2 +- py/defs.bzl | 3 +-- py/private/generate_devtools.bzl | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/py/BUILD.bazel b/py/BUILD.bazel index 0af2675190b2c..763b9eb78ad32 100644 --- a/py/BUILD.bazel +++ b/py/BUILD.bazel @@ -291,8 +291,8 @@ py_library( deps = [ ":bidi", ":exceptions", - ":remote", ":latest", + ":remote", requirement("typing_extensions"), ], ) diff --git a/py/defs.bzl b/py/defs.bzl index 09eb7c89fc1e6..7979bfe4c1d52 100644 --- a/py/defs.bzl +++ b/py/defs.bzl @@ -1,5 +1,4 @@ -load("//py/private:generate_devtools.bzl", _generate_devtools = "generate_devtools") -load("//py/private:generate_devtools.bzl", _generate_devtools_latest = "generate_devtools_latest") +load("//py/private:generate_devtools.bzl", _generate_devtools = "generate_devtools", _generate_devtools_latest = "generate_devtools_latest") load("//py/private:import.bzl", _py_import = "py_import") load("//py/private:pytest.bzl", _pytest_test = "pytest_test") load("//py/private:suite.bzl", _py_test_suite = "py_test_suite") diff --git a/py/private/generate_devtools.bzl b/py/private/generate_devtools.bzl index 07da2243d16e4..ab1664e7fd1d3 100644 --- a/py/private/generate_devtools.bzl +++ b/py/private/generate_devtools.bzl @@ -58,7 +58,7 @@ def _generate_latest_impl(ctx): content = "from ..%s import *\n" % latest, ) return [DefaultInfo( - files = depset([output_file]), + files = depset([output_file]), runfiles = ctx.runfiles(files = [output_file]), )] From bc6af213720d5e0dbbcd6edbb94f3767650439e8 Mon Sep 17 00:00:00 2001 From: Corey Goldberg <1113081+cgoldberg@users.noreply.github.com> Date: Mon, 23 Feb 2026 13:20:33 -0500 Subject: [PATCH 4/7] Skip explicit 'latest' dir during fallback --- py/selenium/webdriver/common/bidi/cdp.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/py/selenium/webdriver/common/bidi/cdp.py b/py/selenium/webdriver/common/bidi/cdp.py index b097762fe50cd..38dcf8d803ea3 100644 --- a/py/selenium/webdriver/common/bidi/cdp.py +++ b/py/selenium/webdriver/common/bidi/cdp.py @@ -59,7 +59,7 @@ def import_devtools(ver): # Attempt to parse and load the 'most recent' devtools module. This is likely # because cdp has been updated but selenium python has not been released yet. devtools_path = pathlib.Path(__file__).parents[1].joinpath("devtools") - versions = tuple(f.name for f in devtools_path.iterdir() if f.is_dir()) + versions = tuple(f.name for f in devtools_path.iterdir() if f.is_dir() and f.name != "latest") latest = max(int(x[1:]) for x in versions) selenium_logger = logging.getLogger(__name__) selenium_logger.debug("Falling back to loading `devtools`: v%s", latest) From 9623390a12700745a9ddaf82d397e39f7027623d Mon Sep 17 00:00:00 2001 From: Corey Goldberg <1113081+cgoldberg@users.noreply.github.com> Date: Wed, 25 Feb 2026 11:32:11 -0500 Subject: [PATCH 5/7] Add test for importing latest --- ...dule_fallback_tests.py => devtools_import_tests.py} | 10 ++++++++++ 1 file changed, 10 insertions(+) rename py/test/unit/selenium/webdriver/common/{cdp_module_fallback_tests.py => devtools_import_tests.py} (70%) diff --git a/py/test/unit/selenium/webdriver/common/cdp_module_fallback_tests.py b/py/test/unit/selenium/webdriver/common/devtools_import_tests.py similarity index 70% rename from py/test/unit/selenium/webdriver/common/cdp_module_fallback_tests.py rename to py/test/unit/selenium/webdriver/common/devtools_import_tests.py index 5becf19a97875..8156c60b9b79f 100644 --- a/py/test/unit/selenium/webdriver/common/cdp_module_fallback_tests.py +++ b/py/test/unit/selenium/webdriver/common/devtools_import_tests.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. +import importlib import logging import re import types @@ -23,7 +24,16 @@ def test_missing_cdp_devtools_version_falls_back(caplog): + """This test verifies the most recent devtools module is imported if an unknown devtools version is requested.""" with caplog.at_level(logging.DEBUG, logger="selenium"): assert isinstance(import_devtools("will_never_exist"), types.ModuleType) # assert the fallback occurred successfully offered up a v{n} option. assert re.match(r"Falling back to loading `devtools`: v\d+", caplog.records[-1].getMessage()) is not None + + +def test_devtools_import_latest(): + """This test verifies the `latest` devtools module can be imported and it contains submodules.""" + latest_module = importlib.import_module("selenium.webdriver.common.devtools.latest") + assert isinstance(latest_module, types.ModuleType) + submodules = [attr for attr in dir(latest_module) if isinstance(attr, types.ModuleType)] + assert len(submodules) > 1 From 9a559b96af015efd876152fb2dbec4c280156e44 Mon Sep 17 00:00:00 2001 From: Corey Goldberg <1113081+cgoldberg@users.noreply.github.com> Date: Wed, 25 Feb 2026 11:52:14 -0500 Subject: [PATCH 6/7] Fix test --- .../selenium/webdriver/common/devtools_import_tests.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/py/test/unit/selenium/webdriver/common/devtools_import_tests.py b/py/test/unit/selenium/webdriver/common/devtools_import_tests.py index 8156c60b9b79f..f48df3f4ff922 100644 --- a/py/test/unit/selenium/webdriver/common/devtools_import_tests.py +++ b/py/test/unit/selenium/webdriver/common/devtools_import_tests.py @@ -35,5 +35,9 @@ def test_devtools_import_latest(): """This test verifies the `latest` devtools module can be imported and it contains submodules.""" latest_module = importlib.import_module("selenium.webdriver.common.devtools.latest") assert isinstance(latest_module, types.ModuleType) - submodules = [attr for attr in dir(latest_module) if isinstance(attr, types.ModuleType)] - assert len(submodules) > 1 + devtools_submodules = [ + getattr(latest_module, name) + for name in dir(latest_module) + if isinstance(getattr(latest_module, name), types.ModuleType) + ] + assert len(devtools_submodules) > 1 From 726dc71190fe786d6eac0104eb97cf547b6ba069 Mon Sep 17 00:00:00 2001 From: Corey Goldberg <1113081+cgoldberg@users.noreply.github.com> Date: Tue, 3 Mar 2026 17:24:49 -0500 Subject: [PATCH 7/7] Update tests --- .../webdriver/common/devtools_import_tests.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/py/test/unit/selenium/webdriver/common/devtools_import_tests.py b/py/test/unit/selenium/webdriver/common/devtools_import_tests.py index f48df3f4ff922..f6a72b209fbdb 100644 --- a/py/test/unit/selenium/webdriver/common/devtools_import_tests.py +++ b/py/test/unit/selenium/webdriver/common/devtools_import_tests.py @@ -18,7 +18,7 @@ import importlib import logging import re -import types +from types import ModuleType from selenium.webdriver.common.bidi.cdp import import_devtools @@ -26,18 +26,19 @@ def test_missing_cdp_devtools_version_falls_back(caplog): """This test verifies the most recent devtools module is imported if an unknown devtools version is requested.""" with caplog.at_level(logging.DEBUG, logger="selenium"): - assert isinstance(import_devtools("will_never_exist"), types.ModuleType) + devtools_module = import_devtools("will_never_exist") + assert isinstance(devtools_module, ModuleType) # assert the fallback occurred successfully offered up a v{n} option. assert re.match(r"Falling back to loading `devtools`: v\d+", caplog.records[-1].getMessage()) is not None -def test_devtools_import_latest(): +def test_import_latest_cdp_devtools(): """This test verifies the `latest` devtools module can be imported and it contains submodules.""" latest_module = importlib.import_module("selenium.webdriver.common.devtools.latest") - assert isinstance(latest_module, types.ModuleType) + assert isinstance(latest_module, ModuleType) devtools_submodules = [ getattr(latest_module, name) for name in dir(latest_module) - if isinstance(getattr(latest_module, name), types.ModuleType) + if isinstance(getattr(latest_module, name), ModuleType) ] assert len(devtools_submodules) > 1