Skip to content

[py] Create module for importing latest devtools#17133

Open
cgoldberg wants to merge 8 commits intoSeleniumHQ:trunkfrom
cgoldberg:py-devtools-latest
Open

[py] Create module for importing latest devtools#17133
cgoldberg wants to merge 8 commits intoSeleniumHQ:trunkfrom
cgoldberg:py-devtools-latest

Conversation

@cgoldberg
Copy link
Member

💥 What does this PR do?

This PR creates a selenium.webdriver.common.devtools.latest module that points to the latest devtools module.

It is generated by bazel during build and simply consists of a an __init.__.py that does a wildcard import of the latest devtools.

i.e.:

from ..v145 import *

This allows users to import the latest devtools modules with:

from selenium.webdriver.common.devtools.latest import network

instead of always specifying the version number in imports:

from selenium.webdriver.common.devtools.v145 import network

🔄 Types of changes

  • New feature (non-breaking change which adds functionality and tests!)

@qodo-code-review
Copy link
Contributor

PR Type

Enhancement


Description

  • Creates selenium.webdriver.common.devtools.latest module for simplified imports

  • Generates latest devtools __init__.py dynamically during build via Bazel

  • Allows importing latest devtools without specifying version numbers

  • Adds new Bazel rule generate_devtools_latest to identify and import latest version


File Walkthrough

Relevant files
Build
defs.bzl
Export new devtools latest generation rule                             

py/defs.bzl

  • Imports new generate_devtools_latest rule from private Bazel module
  • Exposes generate_devtools_latest as public Bazel rule
+2/-0     
generate_devtools.bzl
Implement devtools latest generation rule                               

py/private/generate_devtools.bzl

  • Implements _generate_latest_impl function to determine latest devtools
    version
  • Generates __init__.py file with wildcard import from latest version
  • Defines generate_devtools_latest Bazel rule with browser_versions
    attribute
+17/-0   
BUILD.bazel
Integrate latest devtools module into build                           

py/BUILD.bazel

  • Imports generate_devtools_latest rule from defs
  • Adds :latest target to common library srcs
  • Creates new latest py_library target with generated devtools module
  • Adds py.selenium.webdriver.common.devtools.latest to py_package
  • Invokes generate_devtools_latest rule with BROWSER_VERSIONS
+16/-2   

@selenium-ci selenium-ci added C-py Python Bindings B-build Includes scripting, bazel and CI integrations labels Feb 23, 2026
@qodo-code-review
Copy link
Contributor

qodo-code-review bot commented Feb 23, 2026

PR Compliance Guide 🔍

Below is a summary of compliance checks for this PR:

Security Compliance
🟢
No security concerns identified No security vulnerabilities detected by AI analysis. Human verification advised for critical code.
Ticket Compliance
🎫 No ticket provided
  • Create ticket/issue
Codebase Duplication Compliance
Codebase context is not defined

Follow the guide to enable codebase context checks.

Custom Compliance
🟢
Generic: Comprehensive Audit Trails

Objective: To create a detailed and reliable record of critical system actions for security analysis
and compliance.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Meaningful Naming and Self-Documenting Code

Objective: Ensure all identifiers clearly express their purpose and intent, making code
self-documenting

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Error Handling

Objective: To prevent the leakage of sensitive system information through error messages while
providing sufficient detail for internal debugging.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Logging Practices

Objective: To ensure logs are useful for debugging and auditing without exposing sensitive
information like PII, PHI, or cardholder data.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

🔴
Generic: Robust Error Handling and Edge Case Management

Objective: Ensure comprehensive error handling that provides meaningful context and graceful
degradation

Status:
Missing edge checks: The computation of latest can raise runtime errors (e.g., empty browser_versions or
non-vNNN entries) because it blindly calls max() and int(v[1:]) without validation or a
clear failure message.

Referred Code
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")

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Security-First Input Validation and Data Handling

Objective: Ensure all data inputs are validated, sanitized, and handled securely to prevent
vulnerabilities

Status:
Unvalidated version input: The rule trusts browser_versions formatting (vNNN) and contents without validation, which
may allow malformed inputs to break builds unless upstream guarantees are enforced.

Referred Code
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,
)

Learn more about managing compliance generic rules or creating your own custom rules

  • Update
Compliance status legend 🟢 - Fully Compliant
🟡 - Partial Compliant
🔴 - Not Compliant
⚪ - Requires Further Human Verification
🏷️ - Compliance label

@qodo-code-review
Copy link
Contributor

qodo-code-review bot commented Feb 23, 2026

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
General
Validate version inputs

In _generate_latest_impl, validate that browser_versions is not empty and that
its entries follow the expected "v" format to prevent runtime errors.

py/private/generate_devtools.bzl [53-54]

 versions = ctx.attr.browser_versions
-latest = "v%s" % max([int(v[1:]) for v in versions])
+if not versions:
+    fail("`browser_versions` must not be empty")
+filtered = [v for v in versions if v.startswith("v") and v[1:].isdigit()]
+if not filtered:
+    fail("`browser_versions` entries must be in format 'v<digits>'")
+latest = "v%s" % max(int(v[1:]) for v in filtered)
  • Apply / Chat
Suggestion importance[1-10]: 8

__

Why: This suggestion significantly improves the robustness of the new rule by adding comprehensive validation for the browser_versions input, preventing potential build failures with clear error messages.

Medium
Add runfiles to generated rule
Suggestion Impact:Updated the rule to include a runfiles field in DefaultInfo, exposing the generated output_file in runfiles.

code diff:

-    return [DefaultInfo(files = depset([output_file]))]
+    return [DefaultInfo(
+        files   = depset([output_file]),
+        runfiles = ctx.runfiles(files = [output_file]),
+    )]

In the generate_devtools_latest rule, add the generated init.py file to the
runfiles of the DefaultInfo provider to make it available at runtime.

py/private/generate_devtools.bzl [60]

-return [DefaultInfo(files = depset([output_file]))]
+return [DefaultInfo(
+    files   = depset([output_file]),
+    runfiles = ctx.runfiles(files = [output_file]),
+)]

[Suggestion processed]

Suggestion importance[1-10]: 7

__

Why: This suggestion correctly points out that generated files needed at runtime should be exposed via runfiles to ensure they are available, which is a good practice for Bazel rule implementation.

Medium
High-level
Avoid wildcard import for module aliasing

*Instead of using a wildcard import (from ..vXXX import ) to create the latest
module alias, generate an init.py that uses sys.modules to directly alias
the latest module to the specific versioned module. This avoids issues with
static analysis tools and IDEs.

Examples:

py/private/generate_devtools.bzl [56-59]
    ctx.actions.write(
        output = output_file,
        content = "from ..%s import *\n" % latest,
    )

Solution Walkthrough:

Before:

# In py/private/generate_devtools.bzl
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(".../latest/__init__.py")
    ctx.actions.write(
        output = output_file,
        content = "from ..%s import *\n" % latest,
    )
    ...

After:

# In py/private/generate_devtools.bzl
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(".../latest/__init__.py")
    content = f"""
import sys
from .. import {latest}
sys.modules[__name__] = {latest}
"""
    ctx.actions.write(
        output = output_file,
        content = content,
    )
    ...
Suggestion importance[1-10]: 7

__

Why: The suggestion correctly identifies that using a wildcard import is an anti-pattern and proposes a more robust sys.modules aliasing technique, which improves static analysis and IDE support.

Medium
Learned
best practice
Fix incorrect Bazel srcs usage
Suggestion Impact:Removed ":latest" from the srcs list and added it to deps so Bazel treats it as a library dependency.

code diff:

@@ -280,7 +280,7 @@
         exclude = [
             "selenium/webdriver/common/bidi/**",
         ],
-    ) + [":latest"],
+    ),
     data = [
         ":manager-linux",
         ":manager-macos",
@@ -292,6 +292,7 @@
         ":bidi",
         ":exceptions",
         ":remote",
+        ":latest",
         requirement("typing_extensions"),
     ],

:latest is a py_library target, not a source file; remove it from srcs and add
it to deps so Bazel treats it as a library dependency.

py/BUILD.bazel [276-297]

 py_library(
     name = "common",
     srcs = glob(
         ["selenium/webdriver/common/**/*.py"],
         exclude = [
             "selenium/webdriver/common/bidi/**",
         ],
-    ) + [":latest"],
+    ),
     data = [
         ":manager-linux",
         ":manager-macos",
         ":manager-windows",
     ] + [":create-cdp-srcs-" + n for n in BROWSER_VERSIONS],
     imports = ["."],
     visibility = ["//visibility:public"],
     deps = [
         ":bidi",
         ":exceptions",
         ":remote",
+        ":latest",
         requirement("typing_extensions"),
     ],
 )

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 6

__

Why:
Relevant best practice - Do not put non-file targets in srcs; depend on libraries via deps to keep build graphs correct and avoid accidental build breakages.

Low
  • Update

@AutomatedTester
Copy link
Member

We need to be aware that using latest may lead to breaking changes as CDP is not stable

@cgoldberg
Copy link
Member Author

We need to be aware that using latest may lead to breaking changes as CDP is not stable

yea.. using "latest" could lead to code breakage if there are changes in CDP.

However, if you pin your imports to a specific version of devtools and upgrade selenium, you could also have issues with having an incompatible CDP version.

Users would have to be aware of the tradeoffs either way... I think adding this just gives some flexibility. Specifying a version in imports still works as it did previously.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

B-build Includes scripting, bazel and CI integrations C-py Python Bindings Review effort 2/5

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants