diff --git a/py/selenium/webdriver/chrome/service.py b/py/selenium/webdriver/chrome/service.py index 036b35ef99623..a16692664ba60 100644 --- a/py/selenium/webdriver/chrome/service.py +++ b/py/selenium/webdriver/chrome/service.py @@ -17,7 +17,7 @@ from collections.abc import Mapping -from typing import Optional +from typing import Optional, Sequence from selenium.types import SubprocessStdAlias from selenium.webdriver.chromium import service @@ -29,7 +29,7 @@ class Service(service.ChromiumService): :param executable_path: install path of the chromedriver executable, defaults to `chromedriver`. :param port: Port for the service to run on, defaults to 0 where the operating system will decide. - :param service_args: (Optional) List of args to be passed to the subprocess when launching the executable. + :param service_args: (Optional) Sequence of args to be passed to the subprocess when launching the executable. :param log_output: (Optional) int representation of STDOUT/DEVNULL, any IO instance or String path to file. :param env: (Optional) Mapping of environment variables for the new process, defaults to `os.environ`. """ @@ -38,11 +38,13 @@ def __init__( self, executable_path: Optional[str] = None, port: int = 0, - service_args: Optional[list[str]] = None, + service_args: Optional[Sequence[str]] = None, log_output: Optional[SubprocessStdAlias] = None, env: Optional[Mapping[str, str]] = None, **kwargs, ) -> None: + self._service_args = service_args or [] + super().__init__( executable_path=executable_path, port=port, @@ -51,3 +53,13 @@ def __init__( env=env, **kwargs, ) + + @property + def service_args(self) -> Sequence[str]: + return self._service_args + + @service_args.setter + def service_args(self, value: Sequence[str]): + if isinstance(value, str) or not isinstance(value, Sequence): + raise TypeError("service_args must be a sequence") + self._service_args = list(value) diff --git a/py/selenium/webdriver/chromium/service.py b/py/selenium/webdriver/chromium/service.py index 9f50e21e8e36f..a428648f8ef9b 100644 --- a/py/selenium/webdriver/chromium/service.py +++ b/py/selenium/webdriver/chromium/service.py @@ -14,9 +14,10 @@ # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. + from collections.abc import Mapping from io import IOBase -from typing import Optional +from typing import Optional, Sequence from selenium.types import SubprocessStdAlias from selenium.webdriver.common import service @@ -28,7 +29,7 @@ class ChromiumService(service.Service): :param executable_path: install path of the executable. :param port: Port for the service to run on, defaults to 0 where the operating system will decide. - :param service_args: (Optional) List of args to be passed to the subprocess when launching the executable. + :param service_args: (Optional) Sequence of args to be passed to the subprocess when launching the executable. :param log_output: (Optional) int representation of STDOUT/DEVNULL, any IO instance or String path to file. :param env: (Optional) Mapping of environment variables for the new process, defaults to `os.environ`. :param driver_path_env_key: (Optional) Environment variable to use to get the path to the driver executable. @@ -38,17 +39,17 @@ def __init__( self, executable_path: Optional[str] = None, port: int = 0, - service_args: Optional[list[str]] = None, + service_args: Optional[Sequence[str]] = None, log_output: Optional[SubprocessStdAlias] = None, env: Optional[Mapping[str, str]] = None, driver_path_env_key: Optional[str] = None, **kwargs, ) -> None: - self.service_args = service_args or [] + self._service_args = list(service_args or []) driver_path_env_key = driver_path_env_key or "SE_CHROMEDRIVER" if isinstance(log_output, str): - self.service_args.append(f"--log-path={log_output}") + self._service_args.append(f"--log-path={log_output}") self.log_output: Optional[IOBase] = None elif isinstance(log_output, IOBase): self.log_output = log_output @@ -65,4 +66,14 @@ def __init__( ) def command_line_args(self) -> list[str]: - return [f"--port={self.port}"] + self.service_args + return [f"--port={self.port}"] + self._service_args + + @property + def service_args(self) -> Sequence[str]: + return self._service_args + + @service_args.setter + def service_args(self, value: Sequence[str]): + if isinstance(value, str) or not isinstance(value, Sequence): + raise TypeError("service_args must be a sequence") + self._service_args = list(value) diff --git a/py/selenium/webdriver/common/service.py b/py/selenium/webdriver/common/service.py index e03adb6202f84..7dbaaacbf2861 100644 --- a/py/selenium/webdriver/common/service.py +++ b/py/selenium/webdriver/common/service.py @@ -14,6 +14,7 @@ # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. + import errno import logging import os diff --git a/py/selenium/webdriver/edge/service.py b/py/selenium/webdriver/edge/service.py index 7a152e58c6ca8..f9171e0d46ba1 100644 --- a/py/selenium/webdriver/edge/service.py +++ b/py/selenium/webdriver/edge/service.py @@ -16,7 +16,7 @@ # under the License. from collections.abc import Mapping -from typing import Optional +from typing import Optional, Sequence from selenium.types import SubprocessStdAlias from selenium.webdriver.chromium import service @@ -29,7 +29,7 @@ class Service(service.ChromiumService): :param executable_path: install path of the msedgedriver executable, defaults to `msedgedriver`. :param port: Port for the service to run on, defaults to 0 where the operating system will decide. :param log_output: (Optional) int representation of STDOUT/DEVNULL, any IO instance or String path to file. - :param service_args: (Optional) List of args to be passed to the subprocess when launching the executable. + :param service_args: (Optional) Sequence of args to be passed to the subprocess when launching the executable. :param env: (Optional) Mapping of environment variables for the new process, defaults to `os.environ`. :param driver_path_env_key: (Optional) Environment variable to use to get the path to the driver executable. """ @@ -39,12 +39,12 @@ def __init__( executable_path: Optional[str] = None, port: int = 0, log_output: Optional[SubprocessStdAlias] = None, - service_args: Optional[list[str]] = None, + service_args: Optional[Sequence[str]] = None, env: Optional[Mapping[str, str]] = None, driver_path_env_key: Optional[str] = None, **kwargs, ) -> None: - self.service_args = service_args or [] + self._service_args = list(service_args or []) driver_path_env_key = driver_path_env_key or "SE_EDGEDRIVER" super().__init__( @@ -56,3 +56,13 @@ def __init__( driver_path_env_key=driver_path_env_key, **kwargs, ) + + @property + def service_args(self) -> Sequence[str]: + return self._service_args + + @service_args.setter + def service_args(self, value: Sequence[str]): + if isinstance(value, str) or not isinstance(value, Sequence): + raise TypeError("service_args must be a sequence") + self._service_args = list(value) diff --git a/py/selenium/webdriver/firefox/service.py b/py/selenium/webdriver/firefox/service.py index 55ff4c6ec89e9..4e0180d1100c5 100644 --- a/py/selenium/webdriver/firefox/service.py +++ b/py/selenium/webdriver/firefox/service.py @@ -14,8 +14,9 @@ # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. + from collections.abc import Mapping -from typing import Optional +from typing import Optional, Sequence from selenium.types import SubprocessStdAlias from selenium.webdriver.common import service, utils @@ -27,7 +28,7 @@ class Service(service.Service): :param executable_path: install path of the geckodriver executable, defaults to `geckodriver`. :param port: Port for the service to run on, defaults to 0 where the operating system will decide. - :param service_args: (Optional) List of args to be passed to the subprocess when launching the executable. + :param service_args: (Optional) Sequence of args to be passed to the subprocess when launching the executable. :param log_output: (Optional) int representation of STDOUT/DEVNULL, any IO instance or String path to file. :param env: (Optional) Mapping of environment variables for the new process, defaults to `os.environ`. :param driver_path_env_key: (Optional) Environment variable to use to get the path to the driver executable. @@ -37,13 +38,13 @@ def __init__( self, executable_path: Optional[str] = None, port: int = 0, - service_args: Optional[list[str]] = None, + service_args: Optional[Sequence[str]] = None, log_output: Optional[SubprocessStdAlias] = None, env: Optional[Mapping[str, str]] = None, driver_path_env_key: Optional[str] = None, **kwargs, ) -> None: - self.service_args = service_args or [] + self._service_args = list(service_args or []) driver_path_env_key = driver_path_env_key or "SE_GECKODRIVER" super().__init__( @@ -56,9 +57,19 @@ def __init__( ) # Set a port for CDP - if "--connect-existing" not in self.service_args: - self.service_args.append("--websocket-port") - self.service_args.append(f"{utils.free_port()}") + if "--connect-existing" not in self._service_args: + self._service_args.append("--websocket-port") + self._service_args.append(f"{utils.free_port()}") def command_line_args(self) -> list[str]: - return ["--port", f"{self.port}"] + self.service_args + return ["--port", f"{self.port}"] + self._service_args + + @property + def service_args(self) -> Sequence[str]: + return self._service_args + + @service_args.setter + def service_args(self, value: Sequence[str]): + if isinstance(value, str) or not isinstance(value, Sequence): + raise TypeError("service_args must be a sequence") + self._service_args = list(value) diff --git a/py/selenium/webdriver/ie/service.py b/py/selenium/webdriver/ie/service.py index 3b1be6be13307..53b3f4bd1c237 100644 --- a/py/selenium/webdriver/ie/service.py +++ b/py/selenium/webdriver/ie/service.py @@ -14,7 +14,8 @@ # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. -from typing import Optional + +from typing import Optional, Sequence from selenium.types import SubprocessStdAlias from selenium.webdriver.common import service @@ -28,7 +29,7 @@ def __init__( executable_path: Optional[str] = None, port: int = 0, host: Optional[str] = None, - service_args: Optional[list[str]] = None, + service_args: Optional[Sequence[str]] = None, log_level: Optional[str] = None, log_output: Optional[SubprocessStdAlias] = None, driver_path_env_key: Optional[str] = None, @@ -39,19 +40,21 @@ def __init__( :Args: - executable_path : Path to the IEDriver - port : Port the service is running on - - host : IP address the service port is bound - - log_level : Level of logging of service, may be "FATAL", "ERROR", "WARN", "INFO", "DEBUG", "TRACE". - Default is "FATAL". + - host : (Optional) IP address the service port is bound + - service_args: (Optional) Sequence of args to be passed to the subprocess when launching the executable. + - log_level : (Optional) Level of logging of service, may be "FATAL", "ERROR", "WARN", "INFO", "DEBUG", + "TRACE". Default is "FATAL". - log_output: (Optional) int representation of STDOUT/DEVNULL, any IO instance or String path to file. Default is "stdout". + - driver_path_env_key: (Optional) Environment variable to use to get the path to the driver executable. """ - self.service_args = service_args or [] + self._service_args = list(service_args or []) driver_path_env_key = driver_path_env_key or "SE_IEDRIVER" if host: - self.service_args.append(f"--host={host}") + self._service_args.append(f"--host={host}") if log_level: - self.service_args.append(f"--log-level={log_level}") + self._service_args.append(f"--log-level={log_level}") super().__init__( executable_path=executable_path, @@ -62,4 +65,14 @@ def __init__( ) def command_line_args(self) -> list[str]: - return [f"--port={self.port}"] + self.service_args + return [f"--port={self.port}"] + self._service_args + + @property + def service_args(self) -> Sequence[str]: + return self._service_args + + @service_args.setter + def service_args(self, value: Sequence[str]): + if isinstance(value, str) or not isinstance(value, Sequence): + raise TypeError("service_args must be a sequence") + self._service_args = list(value) diff --git a/py/selenium/webdriver/safari/service.py b/py/selenium/webdriver/safari/service.py index 4d9f5a35e9774..89c3afe53d203 100644 --- a/py/selenium/webdriver/safari/service.py +++ b/py/selenium/webdriver/safari/service.py @@ -15,9 +15,8 @@ # specific language governing permissions and limitations # under the License. - from collections.abc import Mapping -from typing import Optional +from typing import Optional, Sequence from selenium.webdriver.common import service @@ -28,7 +27,7 @@ class Service(service.Service): :param executable_path: install path of the safaridriver executable, defaults to `/usr/bin/safaridriver`. :param port: Port for the service to run on, defaults to 0 where the operating system will decide. - :param service_args: (Optional) List of args to be passed to the subprocess when launching the executable. + :param service_args: (Optional) Sequence of args to be passed to the subprocess when launching the executable. :param env: (Optional) Mapping of environment variables for the new process, defaults to `os.environ`. :param enable_logging: (Optional) Enable logging of the service. Logs can be located at `~/Library/Logs/com.apple.WebDriver/` @@ -39,18 +38,18 @@ def __init__( self, executable_path: Optional[str] = None, port: int = 0, - service_args: Optional[list[str]] = None, + service_args: Optional[Sequence[str]] = None, env: Optional[Mapping[str, str]] = None, reuse_service=False, enable_logging: bool = False, driver_path_env_key: Optional[str] = None, **kwargs, ) -> None: - self.service_args = service_args or [] + self._service_args = list(service_args or []) driver_path_env_key = driver_path_env_key or "SE_SAFARIDRIVER" if enable_logging: - self.service_args.append("--diagnose") + self._service_args.append("--diagnose") self.reuse_service = reuse_service super().__init__( @@ -62,7 +61,7 @@ def __init__( ) def command_line_args(self) -> list[str]: - return ["-p", f"{self.port}"] + self.service_args + return ["-p", f"{self.port}"] + self._service_args @property def service_url(self) -> str: @@ -78,3 +77,13 @@ def reuse_service(self, reuse: bool) -> None: if not isinstance(reuse, bool): raise TypeError("reuse must be a boolean") self._reuse_service = reuse + + @property + def service_args(self) -> Sequence[str]: + return self._service_args + + @service_args.setter + def service_args(self, value: Sequence[str]): + if isinstance(value, str) or not isinstance(value, Sequence): + raise TypeError("service_args must be a sequence") + self._service_args = list(value) diff --git a/py/selenium/webdriver/webkitgtk/service.py b/py/selenium/webdriver/webkitgtk/service.py index e7f300019c838..5e7d181aa2dcc 100644 --- a/py/selenium/webdriver/webkitgtk/service.py +++ b/py/selenium/webdriver/webkitgtk/service.py @@ -14,10 +14,11 @@ # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. + import shutil import warnings from collections.abc import Mapping -from typing import Optional +from typing import Optional, Sequence from selenium.webdriver.common import service @@ -31,7 +32,7 @@ class Service(service.Service): :param executable_path: install path of the WebKitWebDriver executable, defaults to the first `WebKitWebDriver` in `$PATH`. :param port: Port for the service to run on, defaults to 0 where the operating system will decide. - :param service_args: (Optional) List of args to be passed to the subprocess when launching the executable. + :param service_args: (Optional) Sequence of args to be passed to the subprocess when launching the executable. :param log_output: (Optional) File path for the file to be opened and passed as the subprocess stdout/stderr handler. :param env: (Optional) Mapping of environment variables for the new process, defaults to `os.environ`. @@ -43,15 +44,16 @@ def __init__( port: int = 0, log_path: Optional[str] = None, log_output: Optional[str] = None, - service_args: Optional[list[str]] = None, + service_args: Optional[Sequence[str]] = None, env: Optional[Mapping[str, str]] = None, **kwargs, ) -> None: - self.service_args = service_args or [] + self._service_args = list(service_args or []) if log_path is not None: warnings.warn("log_path is deprecated, use log_output instead", DeprecationWarning, stacklevel=2) log_path = open(log_path, "wb") log_output = open(log_output, "wb") if log_output else None + super().__init__( executable_path=executable_path, port=port, @@ -61,4 +63,14 @@ def __init__( ) def command_line_args(self) -> list[str]: - return ["-p", f"{self.port}"] + self.service_args + return ["-p", f"{self.port}"] + self._service_args + + @property + def service_args(self) -> Sequence[str]: + return self._service_args + + @service_args.setter + def service_args(self, value: Sequence[str]): + if isinstance(value, str) or not isinstance(value, Sequence): + raise TypeError("service_args must be a sequence") + self._service_args = list(value) diff --git a/py/selenium/webdriver/wpewebkit/service.py b/py/selenium/webdriver/wpewebkit/service.py index 1f2b244807583..2941633aa91bb 100644 --- a/py/selenium/webdriver/wpewebkit/service.py +++ b/py/selenium/webdriver/wpewebkit/service.py @@ -14,9 +14,10 @@ # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. + import shutil from collections.abc import Mapping -from typing import Optional +from typing import Optional, Sequence from selenium.webdriver.common import service @@ -30,7 +31,7 @@ class Service(service.Service): :param executable_path: install path of the WPEWebDriver executable, defaults to the first `WPEWebDriver` in `$PATH`. :param port: Port for the service to run on, defaults to 0 where the operating system will decide. - :param service_args: (Optional) List of args to be passed to the subprocess when launching the executable. + :param service_args: (Optional) Sequence of args to be passed to the subprocess when launching the executable. :param log_output: (Optional) File path for the file to be opened and passed as the subprocess stdout/stderr handler. :param env: (Optional) Mapping of environment variables for the new process, defaults to `os.environ`. @@ -41,11 +42,12 @@ def __init__( executable_path: str = DEFAULT_EXECUTABLE_PATH, port: int = 0, log_output: Optional[str] = None, - service_args: Optional[list[str]] = None, + service_args: Optional[Sequence[str]] = None, env: Optional[Mapping[str, str]] = None, **kwargs, ): - self.service_args = service_args or [] + self._service_args = list(service_args or []) + super().__init__( executable_path=executable_path, port=port, @@ -55,4 +57,14 @@ def __init__( ) def command_line_args(self) -> list[str]: - return ["-p", f"{self.port}"] + self.service_args + return ["-p", f"{self.port}"] + self._service_args + + @property + def service_args(self) -> Sequence[str]: + return self._service_args + + @service_args.setter + def service_args(self, value: Sequence[str]): + if isinstance(value, str) or not isinstance(value, Sequence): + raise TypeError("service_args must be a sequence") + self._service_args = list(value)