Skip to content

Fix mypy type errors across service-related files in Selenium WebDriver #15834

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: trunk
Choose a base branch
from

Conversation

Bradltr95
Copy link
Contributor

@Bradltr95 Bradltr95 commented May 30, 2025

User description

🔗 Related Issues

#15697

💥 What does this PR do?

This PR resolves type annotation issues flagged by mypy in various service.py files within the Selenium WebDriver codebase. It ensures compatibility between assigned values and their expected types.

🔧 Implementation Notes

The changes involve explicit type casting and validation to align with the expected types defined in the base classes. This approach was chosen to maintain backward compatibility while resolving type errors.

🔄 Types of changes

  • Bug fix (backwards compatible)

PR Type

Bug fix


Description

  • Fixes mypy type annotation errors across multiple files

  • Adds explicit type casting and validation for constructor arguments

  • Ensures correct types in data class factory methods and dictionary conversions

  • Improves runtime type safety and error handling in service and connection classes


Changes walkthrough 📝

Relevant files
Bug fix
15 files
service.py
Fix type annotation for log_output assignment                       
+1/-1     
webdriver.py
Enforce type safety for options, service, and executor     
+10/-4   
browser.py
Add explicit type conversions in from_dict                             
+7/-7     
browsing_context.py
Add type conversions and Callable typing for event handlers
+34/-34 
cdp.py
Add runtime check for devtools.target in connect_session 
+4/-0     
storage.py
Add type conversions in Cookie and CookieFilter methods   
+10/-10 
options.py
Add explicit type for mobile_options and binary_location 
+2/-1     
service.py
Refactor log_output handling for type safety                         
+5/-5     
utils.py
Add type conversions for socket and key processing             
+4/-4     
virtual_authenticator.py
Remove redundant type annotations in Enum subclasses         
+6/-6     
remote_connection.py
Add explicit type for browser_name                                             
+1/-1     
remote_connection.py
Add explicit type for extra_commands                                         
+2/-1     
webdriver.py
Add type checks, casting, and error handling for attributes and
methods
+29/-15 
service.py
Make DEFAULT_EXECUTABLE_PATH Optional for type safety       
+1/-1     
service.py
Make executable_path Optional in Service constructor         
+1/-1     

Need help?
  • Type /help how to ... in the comments thread for any questions about Qodo Merge usage.
  • Check out the documentation for more information.
  • @selenium-ci selenium-ci added C-py Python Bindings B-devtools Includes everything BiDi or Chrome DevTools related labels May 30, 2025
    Copy link
    Contributor

    PR Reviewer Guide 🔍

    Here are some key observations to aid the review process:

    🎫 Ticket compliance analysis 🔶

    15697 - Partially compliant

    Compliant requirements:

    • Fix type annotation errors flagged by mypy in the Python bindings
    • Go through the codebase and fix all errors that Mypy finds

    Non-compliant requirements:

    • Update CI job to fail when Mypy encounters errors to enforce no future regressions
    ⏱️ Estimated effort to review: 3 🔵🔵🔵⚪⚪
    🧪 No relevant tests
    🔒 No security concerns identified
    ⚡ Recommended focus areas for review

    Potential Bug

    The execute method now checks if command_executor is an instance of RemoteConnection before calling its execute method, but the response variable is used before being defined if this check fails.

    if response:
        self.error_handler.check_response(response)
        response["value"] = self._unwrap_value(response.get("value", None))
    Redundant Code

    The session_id variable is assigned twice - once conditionally and once unconditionally, which could lead to unexpected behavior.

    if devtools and devtools.target:
        session_id = await self.execute(devtools.target.attach_to_target(target_id, True))
    else:
        raise RuntimeError("devtools.target is not available.")
    session_id = await self.execute(devtools.target.attach_to_target(target_id, True))
    Potential Error

    The service validation check is added after the service is already used in the DriverFinder constructor, which could lead to NoneType errors.

    if self.service is None:
        raise ValueError("Service must be provided and cannot be None")
    
    finder = DriverFinder(self.service, options)

    Copy link
    Contributor

    PR Code Suggestions ✨

    Explore these optional code suggestions:

    CategorySuggestion                                                                                                                                    Impact
    Possible issue
    Remove duplicate function call

    There's a duplicate call to
    self.execute(devtools.target.attach_to_target(target_id, True)) after the
    if-else block. This will cause the function to execute twice, potentially
    creating multiple sessions.

    py/selenium/webdriver/common/bidi/cdp.py [430-434]

     if devtools and devtools.target:
         session_id = await self.execute(devtools.target.attach_to_target(target_id, True))
     else:
         raise RuntimeError("devtools.target is not available.")
    -session_id = await self.execute(devtools.target.attach_to_target(target_id, True))
    • Apply / Chat
    Suggestion importance[1-10]: 9

    __

    Why: This correctly identifies a serious bug where session_id is assigned twice, causing the function to execute unnecessarily and potentially creating multiple sessions.

    High
    Fix incorrect type casting

    The current code attempts to cast None to IOBase which is incorrect. Instead,
    use Optional[IOBase] to properly type-hint that log_output can be None or an
    IOBase object.

    py/selenium/webdriver/chromium/service.py [50-52]

     if isinstance(log_output, str):
         self.service_args.append(f"--log-path={log_output}")
    -    self.log_output: cast(IOBase, None)
    +    self.log_output: Optional[IOBase] = None
    • Apply / Chat
    Suggestion importance[1-10]: 7

    __

    Why: The suggestion correctly identifies that cast(IOBase, None) is semantically incorrect since None is not an IOBase object. Using Optional[IOBase] = None is more accurate typing.

    Medium
    • More

    @cgoldberg
    Copy link
    Contributor

    I think type hinting can be useful, but I don't like the idea of forcing type conversions and type checking everywhere just to keep Mypy happy. Python is very much a dynamically typed language that makes heavy use of duck-typing, and most of these changes don't feel "pythonic".

    I'd be curious to hear what other people think and ideas for how we should better handle type hinting across the codebase.

    @Bradltr95
    Copy link
    Contributor Author

    I think type hinting can be useful, but I don't like the idea of forcing type conversions and type checking everywhere just to keep Mypy happy. Python is very much a dynamically typed language that makes heavy use of duck-typing, and most of these changes don't feel "pythonic".

    I'd be curious to hear what other people think and ideas for how we should better handle type hinting across the codebase.

    Thanks for the input.. So, just to understand, its okay to have type hinting such as: cf9fda4#diff-2f176dbccc5f64167acc6345e2351cab42ccebdefcc77374c69cb9e23b130806R24
    and cf9fda4#diff-5ff2f2a84a79fce9e04511b572dcaab921c08e956345f4c41b7e701d4d018589R425.

    However, we don't want to cast every type just to resolve errors like: cf9fda4#diff-b1516e150256fb4a5ef5144d5d8314fc45e92872415ba8900d4cd8d2cc9b9841R70

    Or do you think both the type hinting and casting pull away from the dynamic typing of python?

    In this case I figured at the very least the type hinting would help with better code readability as it explicitly states the types on top of resolving the mypy errors. However, I agree it gets away from the dynamically typed nature of python.

    If you feel both solutions (casting AND type hinting) aren't useful, we can go ahead and close this PR :)

    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
    Labels
    B-devtools Includes everything BiDi or Chrome DevTools related C-py Python Bindings Review effort 3/5
    Projects
    None yet
    Development

    Successfully merging this pull request may close these issues.

    3 participants