Skip to content

reuse of closed sys.stdout in service log output is to be prevented #15747

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

Closed
wants to merge 1 commit into from

Conversation

itzanway
Copy link

@itzanway itzanway commented May 16, 2025

User description

Reference Fixes #15629
@cgoldberg

In the given pr it improves error handling and robustness in the Service class by adding a pre-check before starting the subprocess:

Checks if the executable_path exists and is executable using os.path.isfile() and os.access().

Raises a clear WebDriverException with helpful messages if the path is invalid or lacks execution permissions.

Prevents cryptic or low-level errors when the executable is missing or misconfigured.

Fixes an IndentationError in _start_process caused by missing indentation.


PR Type

Bug fix, Tests


Description

  • Prevents reuse of closed sys.stdout in Service log output

  • Adds pre-check for executable path existence and permissions

  • Improves error handling with clear WebDriverException messages

  • Adds regression test for closed stdout reuse scenario


Changes walkthrough 📝

Relevant files
Bug fix
service.py
Add executable path validation and error handling in Service

py/selenium/webdriver/common/service.py

  • Adds pre-check for executable path existence and executability
  • Improves error messages for invalid or non-executable paths
  • Refactors and removes redundant comments and docstrings
  • Enhances robustness in process start and termination
  • +9/-50   
    Tests
    test_service.py
    Add regression test for closed stdout reuse in Service     

    py/test/selenium/webdriver/common/test_service.py

  • Adds a test to ensure reusing closed sys.stdout raises ValueError
  • Verifies robustness of Service log output handling
  • +9/-0     

    Need help?
  • Type /help how to ... in the comments thread for any questions about Qodo Merge usage.
  • Check out the documentation for more information.
  • @CLAassistant
    Copy link

    CLAassistant commented May 16, 2025

    CLA assistant check
    All committers have signed the CLA.

    Copy link
    Contributor

    PR Reviewer Guide 🔍

    Here are some key observations to aid the review process:

    ⏱️ Estimated effort to review: 2 🔵🔵⚪⚪⚪
    🧪 PR contains tests
    🔒 No security concerns identified
    ⚡ Recommended focus areas for review

    Missing Import

    The test is missing the pytest import which is required for the pytest.raises assertion. This will cause the test to fail when executed.

    def test_reusing_closed_stdout_fails():
        import sys
        from selenium.webdriver.chrome.service import Service
        from selenium.common.exceptions import WebDriverException
    
        service = Service(log_output=sys.stdout)
        service._log_output.close()
        with pytest.raises(ValueError):
            Service(log_output=sys.stdout)
    Test Logic Issue

    The test creates a service, closes its log_output, but then creates a new service without any connection to the first one. This doesn't properly test reuse of a closed stdout.

    service = Service(log_output=sys.stdout)
    service._log_output.close()
    with pytest.raises(ValueError):
        Service(log_output=sys.stdout)

    Copy link
    Contributor

    PR Code Suggestions ✨

    Explore these optional code suggestions:

    CategorySuggestion                                                                                                                                    Impact
    Possible issue
    Add missing pytest import

    The test is missing the pytest import which is required for the pytest.raises()
    context manager. Without this import, the test will fail with a NameError when
    trying to access the undefined pytest variable.

    py/test/selenium/webdriver/common/test_service.py [1-9]

     def test_reusing_closed_stdout_fails():
         import sys
    +    import pytest
         from selenium.webdriver.chrome.service import Service
         from selenium.common.exceptions import WebDriverException
     
         service = Service(log_output=sys.stdout)
         service._log_output.close()
         with pytest.raises(ValueError):
             Service(log_output=sys.stdout)
    • Apply / Chat
    Suggestion importance[1-10]: 7

    __

    Why: The suggestion correctly identifies that pytest is used but not imported, which would cause a NameError and prevent the test from running. Adding the import is necessary for the test's correctness, but it is a standard fix and not critical to application logic.

    Medium
    Use public attribute

    The test accesses service._log_output which appears to be a private attribute
    (prefixed with underscore), but the Service class shown in the diff doesn't
    expose this attribute. The test should use the public service.log_output
    attribute instead.

    py/test/selenium/webdriver/common/test_service.py [6-7]

     service = Service(log_output=sys.stdout)
    -service._log_output.close()
    +service.log_output.close()
    • Apply / Chat
    Suggestion importance[1-10]: 6

    __

    Why: The suggestion improves code style and encapsulation by recommending the use of the public log_output attribute instead of the private _log_output. This enhances maintainability and aligns with best practices, but does not affect core functionality.

    Low
    Learned
    best practice
    Add error handling for cleanup

    Wrap the log_output closing operations in a try-finally block to ensure
    resources are properly cleaned up even if exceptions occur. This prevents
    resource leaks when closing operations fail.

    py/selenium/webdriver/common/service.py [114-120]

     def stop(self) -> None:
    -    if self.log_output not in {PIPE, subprocess.DEVNULL}:
    -        if isinstance(self.log_output, IOBase):
    -            self.log_output.close()
    -        elif isinstance(self.log_output, int):
    -            os.close(self.log_output)
    +    try:
    +        if self.log_output not in {PIPE, subprocess.DEVNULL}:
    +            if isinstance(self.log_output, IOBase):
    +                self.log_output.close()
    +            elif isinstance(self.log_output, int):
    +                os.close(self.log_output)
    +    except Exception:
    +        logger.error("Error closing log output.", exc_info=True)
         
         if hasattr(self, "process"):
             try:
                 self.send_remote_shutdown_command()
             except TypeError:
                 pass
             finally:
                 self._terminate_process()

    [To ensure code accuracy, apply this suggestion manually]

    Suggestion importance[1-10]: 6

    __

    Why:
    Relevant best practice - Use try-finally blocks to ensure proper resource cleanup, especially for disposable resources, even when exceptions occur during execution.

    Low
    • More

    Sorry, something went wrong.

    @selenium-ci selenium-ci added the C-py Python Bindings label May 16, 2025
    @itzanway itzanway closed this May 16, 2025
    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
    Labels
    Projects
    None yet
    Development

    Successfully merging this pull request may close these issues.

    [🐛 Bug]: [py] Service logging to console not working correctly
    3 participants