diff --git a/py/selenium/deprecated.py b/py/selenium/deprecated.py new file mode 100644 index 0000000000000..8fdfdd9ca8201 --- /dev/null +++ b/py/selenium/deprecated.py @@ -0,0 +1,57 @@ +# 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 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +from functools import wraps +from warnings import warn + +version = "4.12" + + +def deprecated_function(message): + """decorator to log deprecation warning messgaes for deprecated methods.""" + + @wraps + def _deprecated_function(func): + def wrapper(*args, **kwargs): + warn(f"{message}: will be removed from {version}", DeprecationWarning, stacklevel=2) + return func(*args, **kwargs) + + return wrapper + + return _deprecated_function + + +def deprecated_attribute(message, **dep_attr): + @wraps + def _deprecated_attributes(func): + def wrapper(*args, **kwargs): + func(*args, **kwargs) + dep_attr_name = list(dep_attr.keys())[0] # getting the name of deprecated attr + dep_attr_type = list(dep_attr.values())[0] # should the value be truthy or falsy? + # get the value of the deprecated attributes + dep_attr_value = getattr(args[0], dep_attr_name) # getting actual value passed to depattr + # check the truthiness of the deprecated attribute + if bool(dep_attr_value) is dep_attr_type: + warn( + f"'{dep_attr_name}': will be removed from {version}: {message}", + DeprecationWarning, + stacklevel=2, + ) + + return wrapper + + return _deprecated_attributes diff --git a/py/selenium/webdriver/chromium/options.py b/py/selenium/webdriver/chromium/options.py index 06c936a6eeec5..7988dd7b38fc5 100644 --- a/py/selenium/webdriver/chromium/options.py +++ b/py/selenium/webdriver/chromium/options.py @@ -17,11 +17,11 @@ import base64 import os -import warnings from typing import BinaryIO from typing import List from typing import Union +from selenium.deprecated import deprecated_function from selenium.webdriver.common.desired_capabilities import DesiredCapabilities from selenium.webdriver.common.options import ArgOptions @@ -139,18 +139,17 @@ def add_experimental_option(self, name: str, value: Union[str, int, dict, List[s self._experimental_options[name] = value @property + @deprecated_function("headless property is deprecated, instead check for '--headless' in arguments") def headless(self) -> bool: """ :Returns: True if the headless argument is set, else False """ - warnings.warn( - "headless property is deprecated, instead check for '--headless' in arguments", - DeprecationWarning, - stacklevel=2, - ) return "--headless" in self._arguments @headless.setter + @deprecated_function( + "headless property is deprecated, instead use add_argument('--headless') or add_argument('--headless=new')" + ) def headless(self, value: bool) -> None: """Sets the headless argument Old headless uses a non-production browser and is set with `--headless` @@ -160,11 +159,6 @@ def headless(self, value: bool) -> None: :Args: value: boolean value indicating to set the headless option """ - warnings.warn( - "headless property is deprecated, instead use add_argument('--headless') or add_argument('--headless=new')", - DeprecationWarning, - stacklevel=2, - ) args = {"--headless"} if not isinstance(value, bool): diff --git a/py/selenium/webdriver/chromium/service.py b/py/selenium/webdriver/chromium/service.py index 3e8d43c9ecd20..3e892655a12d0 100644 --- a/py/selenium/webdriver/chromium/service.py +++ b/py/selenium/webdriver/chromium/service.py @@ -15,9 +15,9 @@ # specific language governing permissions and limitations # under the License. import typing -import warnings from selenium.common import InvalidArgumentException +from selenium.deprecated import deprecated_attribute from selenium.types import SubprocessStdAlias from selenium.webdriver.common import service @@ -33,6 +33,7 @@ class ChromiumService(service.Service): :param env: (Optional) Mapping of environment variables for the new process, defaults to `os.environ`. """ + @deprecated_attribute("log_path has been deprecated, please use log_output", log_path=True) def __init__( self, executable_path: str = None, @@ -45,9 +46,7 @@ def __init__( ) -> None: self.service_args = service_args or [] self.log_output = log_output - if log_path is not None: - warnings.warn("log_path has been deprecated, please use log_output", DeprecationWarning, stacklevel=2) - self.log_output = log_path + self.log_output = log_path if "--append-log" in self.service_args or "--readable-timestamp" in self.service_args: if isinstance(self.log_output, str): diff --git a/py/selenium/webdriver/edge/service.py b/py/selenium/webdriver/edge/service.py index 1d07fad8caf32..11a386c25f81c 100644 --- a/py/selenium/webdriver/edge/service.py +++ b/py/selenium/webdriver/edge/service.py @@ -15,8 +15,8 @@ # specific language governing permissions and limitations # under the License. import typing -import warnings +from selenium.deprecated import deprecated_attribute from selenium.types import SubprocessStdAlias from selenium.webdriver.chromium import service @@ -34,6 +34,7 @@ class Service(service.ChromiumService): :param env: (Optional) Mapping of environment variables for the new process, defaults to `os.environ`. """ + @deprecated_attribute("verbose=True is deprecated. Use `service_args=['--verbose', ...]` instead.", verbose=True) def __init__( self, executable_path: str = None, @@ -46,13 +47,7 @@ def __init__( **kwargs, ) -> None: self.service_args = service_args or [] - if verbose: - warnings.warn( - "verbose=True is deprecated. Use `service_args=['--verbose', ...]` instead.", - DeprecationWarning, - stacklevel=2, - ) self.service_args.append("--verbose") super().__init__( diff --git a/py/selenium/webdriver/firefox/firefox_profile.py b/py/selenium/webdriver/firefox/firefox_profile.py index 570b03a06d116..40e12ca9a7d12 100644 --- a/py/selenium/webdriver/firefox/firefox_profile.py +++ b/py/selenium/webdriver/firefox/firefox_profile.py @@ -29,6 +29,7 @@ from xml.dom import minidom from selenium.common.exceptions import WebDriverException +from selenium.deprecated import deprecated_function WEBDRIVER_EXT = "webdriver.xpi" WEBDRIVER_PREFERENCES = "webdriver_prefs.json" @@ -43,6 +44,7 @@ class FirefoxProfile: ANONYMOUS_PROFILE_NAME = "WEBDRIVER_ANONYMOUS_PROFILE" DEFAULT_PREFERENCES = None + @deprecated_function("firefox_profile has been deprecated, please use an Options object") def __init__(self, profile_directory=None): """Initialises a new instance of a Firefox Profile. @@ -53,9 +55,6 @@ def __init__(self, profile_directory=None): This defaults to None and will create a new directory when object is created. """ - warnings.warn( - "firefox_profile has been deprecated, please use an Options object", DeprecationWarning, stacklevel=2 - ) if not FirefoxProfile.DEFAULT_PREFERENCES: with open( os.path.join(os.path.dirname(__file__), WEBDRIVER_PREFERENCES), encoding="utf-8" diff --git a/py/selenium/webdriver/firefox/options.py b/py/selenium/webdriver/firefox/options.py index 5d312d0d0c73c..d3fcb0c35a78e 100644 --- a/py/selenium/webdriver/firefox/options.py +++ b/py/selenium/webdriver/firefox/options.py @@ -15,9 +15,9 @@ # specific language governing permissions and limitations # under the License. import typing -import warnings from typing import Union +from selenium.deprecated import deprecated_function from selenium.webdriver.common.desired_capabilities import DesiredCapabilities from selenium.webdriver.common.options import ArgOptions from selenium.webdriver.firefox.firefox_binary import FirefoxBinary @@ -81,49 +81,40 @@ def set_preference(self, name: str, value: Union[str, int, bool]): self._preferences[name] = value @property + @deprecated_function("Getting a profile has been deprecated.") def profile(self) -> FirefoxProfile: """ :Returns: The Firefox profile to use. """ - if self._profile: - warnings.warn("Getting a profile has been deprecated.", DeprecationWarning, stacklevel=2) return self._profile @profile.setter + @deprecated_function( + "Setting a profile has been deprecated. Please use the set_preference and install_addons methods" + ) def profile(self, new_profile: Union[str, FirefoxProfile]) -> None: """Sets location of the browser profile to use, either by string or ``FirefoxProfile``.""" - warnings.warn( - "Setting a profile has been deprecated. Please use the set_preference and install_addons methods", - DeprecationWarning, - stacklevel=2, - ) if not isinstance(new_profile, FirefoxProfile): new_profile = FirefoxProfile(new_profile) self._profile = new_profile @property + @deprecated_function("headless property is deprecated, instead check for '-headless' in arguments") def headless(self) -> bool: """ :Returns: True if the headless argument is set, else False """ - warnings.warn( - "headless property is deprecated, instead check for '-headless' in arguments", - DeprecationWarning, - stacklevel=2, - ) return "-headless" in self._arguments @headless.setter + @deprecated_function("headless property is deprecated, instead use add_argument('-headless')") def headless(self, value: bool) -> None: """Sets the headless argument. Args: value: boolean value indicating to set the headless option """ - warnings.warn( - "headless property is deprecated, instead use add_argument('-headless')", DeprecationWarning, stacklevel=2 - ) if not isinstance(value, bool): raise TypeError("value must be a boolean") if value: diff --git a/py/selenium/webdriver/firefox/service.py b/py/selenium/webdriver/firefox/service.py index a6fd13e254d63..2e823d81b9da7 100644 --- a/py/selenium/webdriver/firefox/service.py +++ b/py/selenium/webdriver/firefox/service.py @@ -15,9 +15,9 @@ # specific language governing permissions and limitations # under the License. import typing -import warnings from typing import List +from selenium.deprecated import deprecated_attribute from selenium.types import SubprocessStdAlias from selenium.webdriver.common import service from selenium.webdriver.common import utils @@ -35,6 +35,11 @@ class Service(service.Service): :param env: (Optional) Mapping of environment variables for the new process, defaults to `os.environ`. """ + @deprecated_attribute("log_path has been deprecated, please use log_output", log_path=True) + @deprecated_attribute( + "Firefox will soon stop logging to geckodriver.log by default; Specify desired logs with log_output", + log_output=False, + ) def __init__( self, executable_path: str = None, @@ -46,16 +51,10 @@ def __init__( **kwargs, ) -> None: self.service_args = service_args or [] - if log_path is not None: - warnings.warn("log_path has been deprecated, please use log_output", DeprecationWarning, stacklevel=2) + if log_path: log_output = open(log_path, "a+", encoding="utf-8") if log_path is None and log_output is None: - warnings.warn( - "Firefox will soon stop logging to geckodriver.log by default; Specify desired logs with log_output", - DeprecationWarning, - stacklevel=2, - ) log_output = open("geckodriver.log", "a+", encoding="utf-8") super().__init__( diff --git a/py/selenium/webdriver/ie/service.py b/py/selenium/webdriver/ie/service.py index 06e58c2948410..2f253987b91f5 100644 --- a/py/selenium/webdriver/ie/service.py +++ b/py/selenium/webdriver/ie/service.py @@ -15,9 +15,9 @@ # specific language governing permissions and limitations # under the License. import typing -import warnings from typing import List +from selenium.deprecated import deprecated_attribute from selenium.types import SubprocessStdAlias from selenium.webdriver.common import service @@ -25,6 +25,7 @@ class Service(service.Service): """Object that manages the starting and stopping of the IEDriver.""" + @deprecated_attribute("log_file has been deprecated, please use log_output", log_file=True) def __init__( self, executable_path: str = None, @@ -53,7 +54,6 @@ def __init__( if log_level: self.service_args.append(f"--log-level={log_level}") if log_file: - warnings.warn("log_file has been deprecated, please use log_output", DeprecationWarning, stacklevel=2) self.service_args.append(f"--log-file={log_file}") super().__init__( diff --git a/py/selenium/webdriver/remote/webdriver.py b/py/selenium/webdriver/remote/webdriver.py index dbf40a91868a3..d9a8185439090 100644 --- a/py/selenium/webdriver/remote/webdriver.py +++ b/py/selenium/webdriver/remote/webdriver.py @@ -38,6 +38,7 @@ from selenium.common.exceptions import NoSuchCookieException from selenium.common.exceptions import NoSuchElementException from selenium.common.exceptions import WebDriverException +from selenium.deprecated import deprecated_function from selenium.webdriver.common.by import By from selenium.webdriver.common.html5.application_cache import ApplicationCache from selenium.webdriver.common.options import BaseOptions @@ -769,9 +770,9 @@ def find_elements(self, by=By.ID, value: Optional[str] = None) -> List[WebElemen return self.execute(Command.FIND_ELEMENTS, {"using": by, "value": value})["value"] or [] @property + @deprecated_function("desired_capabilities is deprecated. Please call capabilities.") def desired_capabilities(self) -> dict: """returns the drivers current desired capabilities being used.""" - warnings.warn("desired_capabilities is deprecated. Please call capabilities.", DeprecationWarning, stacklevel=2) return self.caps @property diff --git a/py/selenium/webdriver/safari/service.py b/py/selenium/webdriver/safari/service.py index be6427960f37e..31945d9fd04f2 100644 --- a/py/selenium/webdriver/safari/service.py +++ b/py/selenium/webdriver/safari/service.py @@ -16,8 +16,8 @@ # under the License. import typing -import warnings +from selenium.deprecated import deprecated_attribute from selenium.webdriver.common import service @@ -32,6 +32,7 @@ class Service(service.Service): :param env: (Optional) Mapping of environment variables for the new process, defaults to `os.environ`. """ + @deprecated_attribute(message="quiet is no longer needed to supress output", quiet=True) def __init__( self, executable_path: str = None, @@ -43,9 +44,6 @@ def __init__( **kwargs, ) -> None: self.service_args = service_args or [] - if quiet is not None: - warnings.warn("quiet is no longer needed to supress output", DeprecationWarning, stacklevel=2) - self._reuse_service = reuse_service super().__init__( executable=executable_path, diff --git a/py/selenium/webdriver/safari/webdriver.py b/py/selenium/webdriver/safari/webdriver.py index 90fd755ba47ec..ab1eeb11ee4fe 100644 --- a/py/selenium/webdriver/safari/webdriver.py +++ b/py/selenium/webdriver/safari/webdriver.py @@ -16,9 +16,9 @@ # under the License. import http.client as http_client -import warnings from selenium.common.exceptions import WebDriverException +from selenium.deprecated import deprecated_attribute from selenium.webdriver.remote.webdriver import WebDriver as RemoteWebDriver from ..common.driver_finder import DriverFinder @@ -30,6 +30,10 @@ class WebDriver(RemoteWebDriver): """Controls the SafariDriver and allows you to drive the browser.""" + @deprecated_attribute( + "reuse_service has been deprecated, please use the Service class to set it", + reuse_service=True, + ) def __init__( self, reuse_service=False, @@ -47,13 +51,6 @@ def __init__( - options - Instance of ``options.Options``. - service - Service object for handling the browser driver if you need to pass extra details """ - if reuse_service: - warnings.warn( - "reuse_service has been deprecated, please use the Service class to set it", - DeprecationWarning, - stacklevel=2, - ) - self.service = service if service else Service() options = options if options else Options()