Skip to content

[py] integrate mypy type checking with Bazel#16958

Merged
titusfortner merged 3 commits intotrunkfrom
py-mypy-bazel
Jan 20, 2026
Merged

[py] integrate mypy type checking with Bazel#16958
titusfortner merged 3 commits intotrunkfrom
py-mypy-bazel

Conversation

@titusfortner
Copy link
Member

@titusfortner titusfortner commented Jan 20, 2026

User description

💥 What does this PR do?

Replaces tox-based mypy type checking with a Bazel target, integrating Python type checking into the standard build system.

  • Added mypy==1.19.1 to py/requirements.txt and updated the lockfile
  • Created //py:mypy Bazel target using py_binary that imports mypy's API directly
  • Updated CI workflow to use bazel run //py:mypy instead of tox
  • Added to all:lint Rakefile task

🔧 Implementation Notes

The run_mypy.py script uses mypy's Python API rather than subprocess to ensure it works correctly within Bazel's sandbox environment.
Configuration is read from the existing [tool.mypy] section in py/pyproject.toml.

💡 Additional Considerations

Is there anything else tox related to bazelfy?

🔄 Types of changes

  • Cleanup (formatting, renaming)

PR Type

Enhancement, Tests


Description

  • Integrate mypy type checking into Bazel build system

  • Replace tox-based type checking with bazel run //py:mypy

  • Add mypy==1.19.1 dependency and update lockfile

  • Update CI workflow and Rakefile lint task


Diagram Walkthrough

flowchart LR
  A["tox-based mypy"] -->|"Replace with"| B["Bazel py_binary target"]
  B -->|"Uses"| C["run_mypy.py script"]
  C -->|"Calls"| D["mypy Python API"]
  E["CI workflow"] -->|"Updated to"| F["bazel run //py:mypy"]
  G["Rakefile lint"] -->|"Includes"| F
Loading

File Walkthrough

Relevant files
Enhancement
run_mypy.py
New mypy wrapper script for Bazel                                               

py/run_mypy.py

  • New script that wraps mypy's Python API for Bazel execution
  • Handles workspace root detection via BUILD_WORKSPACE_DIRECTORY
    environment variable
  • Reads configuration from pyproject.toml [tool.mypy] section
  • Runs mypy on the selenium package with proper stdout/stderr handling
+55/-0   
Configuration changes
BUILD.bazel
Add mypy Bazel target                                                                       

py/BUILD.bazel

  • Added new py_binary target named "mypy"
  • Includes run_mypy.py as main entry point
  • Depends on mypy requirement and selenium package
  • Includes pyproject.toml as data dependency for configuration
+14/-0   
ci-python.yml
Update CI workflow for Bazel mypy                                               

.github/workflows/ci-python.yml

  • Replaced tox-based type checking job with Bazel workflow
  • Removed Python setup, pip install, and tox execution steps
  • Now uses reusable bazel.yml workflow with bazel run //py:mypy command
  • Simplified CI configuration by delegating to Bazel
+4/-17   
Rakefile
Add mypy to Rakefile lint task                                                     

Rakefile

  • Added Bazel.execute('run', [], '//py:mypy') to all:lint task
  • Integrated mypy type checking into standard lint workflow
  • Executes before steep linter for Ruby code
+1/-0     
Dependencies
requirements.txt
Add mypy dependency                                                                           

py/requirements.txt

  • Added mypy==1.19.1 as direct dependency
  • Placed in alphabetical order between more-itertools and
    mypy_extensions
+1/-0     
requirements_lock.txt
Update lockfile with mypy dependencies                                     

py/requirements_lock.txt

  • Added mypy==1.19.1 with full hash verification
  • Added librt==0.7.8 as transitive dependency of mypy
  • Added pathspec==1.0.3 as transitive dependency of mypy
  • Updated dependency comments to reflect mypy as source
  • Updated tomli and typing-extensions comments to include mypy
+126/-4 

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

qodo-code-review bot commented Jan 20, 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: Robust Error Handling and Edge Case Management

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

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: Security-First Input Validation and Data Handling

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

Status: Passed

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 Jan 20, 2026

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
High-level
Use a standard Bazel mypy ruleset

Replace the custom run_mypy.py script and py_binary target with a standard,
community-maintained Bazel ruleset for mypy (e.g., from rules_python). This
change would enable more idiomatic and efficient per-target type checking,
leveraging Bazel's caching and parallelism.

Examples:

py/run_mypy.py [1-55]
# 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
#

 ... (clipped 45 lines)
py/BUILD.bazel [651-663]
py_binary(
    name = "mypy",
    srcs = ["run_mypy.py"],
    data = [
        "pyproject.toml",
        ":selenium",
    ],
    main = "run_mypy.py",
    deps = [
        ":selenium",

 ... (clipped 3 lines)

Solution Walkthrough:

Before:

# py/BUILD.bazel
py_binary(
    name = "mypy",
    srcs = ["run_mypy.py"],
    main = "run_mypy.py",
    deps = [":selenium", requirement("mypy")],
)

# py/run_mypy.py
def main():
    # ... setup logic ...
    # Run mypy on the entire 'selenium' package at once
    args = ["selenium", *sys.argv[1:]]
    stdout, stderr, exit_code = api.run(args)
    # ... handle output ...
    sys.exit(exit_code)

After:

# py/BUILD.bazel
# (The custom py_binary and run_mypy.py are removed)
# A standard mypy rule is used instead.
# This enables per-target caching and analysis.

load("@rules_python//python/integrations:mypy.bzl", "mypy_test")

mypy_test(
    name = "mypy_test",
    targets = [
        ":some_py_library",
        ":another_py_library",
        # ... granular targets instead of the whole package
    ],
)
Suggestion importance[1-10]: 9

__

Why: This is a high-impact architectural suggestion that correctly identifies that the PR's custom script approach misses key Bazel benefits like caching and parallelism, proposing a more idiomatic and efficient solution.

High
General
Ensure build before typechecking

In the GitHub Actions workflow, add needs: build to the typing job to ensure it
runs after the build job, consistent with other jobs.

.github/workflows/ci-python.yml [39-44]

 typing:
+  needs: build
   uses: ./.github/workflows/bazel.yml
   with:
     name: Type Checker
     run: bazel run //py:mypy
  • Apply / Chat
Suggestion importance[1-10]: 7

__

Why: This change aligns the typing job with the unit-tests job, which already depends on build, likely improving CI efficiency by reusing build artifacts and cache.

Medium
Lock Python version

In py/BUILD.bazel, add python_version = "PY3" to the mypy py_binary target to
lock the Python interpreter version.

py/BUILD.bazel [651-653]

 py_binary(
     name = "mypy",
+    python_version = "PY3",
     srcs = ["run_mypy.py"],
  • Apply / Chat
Suggestion importance[1-10]: 6

__

Why: Explicitly setting python_version = "PY3" in the py_binary target improves build hermeticity and ensures the script runs with a consistent Python 3 interpreter.

Low
Possible issue
Add validation for directory existence

In py/run_mypy.py, add a check to verify that the target directory py_dir exists
before calling os.chdir() to prevent potential FileNotFoundError.

py/run_mypy.py [30-51]

 def main():
     # Find the workspace root - Bazel sets BUILD_WORKSPACE_DIRECTORY when using 'bazel run'
     workspace = os.environ.get("BUILD_WORKSPACE_DIRECTORY")
     if workspace:
         py_dir = os.path.join(workspace, "py")
     else:
         # Fallback for direct execution
         py_dir = os.path.dirname(os.path.abspath(__file__))
+
+    if not os.path.isdir(py_dir):
+        print(f"Error: Directory '{py_dir}' not found.", file=sys.stderr)
+        sys.exit(1)
 
     os.chdir(py_dir)
 
     # Run mypy on the selenium package
     # Configuration is read from pyproject.toml [tool.mypy] section
     args = ["selenium", *sys.argv[1:]]
     stdout, stderr, exit_code = api.run(args)
 
     if stdout:
         print(stdout, end="")
     if stderr:
         print(stderr, end="", file=sys.stderr)
 
     sys.exit(exit_code)
  • Apply / Chat
Suggestion importance[1-10]: 5

__

Why: The suggestion improves the script's robustness by adding a check for the existence of py_dir before changing to it, which is good practice for error handling.

Low
  • Update

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR migrates mypy type checking from tox to Bazel, consolidating Python tooling into the standard build system. The change adds mypy as a direct dependency, creates a Bazel-compatible wrapper script, and updates CI workflows.

Changes:

  • Added mypy==1.19.1 dependency with lockfile updates including new transitive dependencies (librt, pathspec)
  • Created run_mypy.py script that uses mypy's Python API for Bazel sandbox compatibility
  • Updated CI workflow to use bazel run //py:mypy instead of tox-based type checking
  • Integrated mypy into the all:lint Rakefile task

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
py/run_mypy.py New script using mypy API for Bazel integration with workspace directory detection
py/BUILD.bazel Added py_binary target for mypy with selenium package and pyproject.toml as dependencies
py/requirements.txt Added mypy==1.19.1 dependency
py/requirements_lock.txt Updated lockfile with mypy and transitive dependencies (librt, pathspec), modified dependency comments
.github/workflows/ci-python.yml Replaced tox-based type checking with Bazel workflow integration
Rakefile Added mypy execution to the all:lint task

Copy link
Member

@cgoldberg cgoldberg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM 👍

I just pushed a small update to your branch that removes the typecheck dependency group. We no longer need it if mypy is part of requirements.txt.

@cgoldberg
Copy link
Member

I also set the executable permission on the new run_* scripts while I'm here.

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.

4 participants