session/plugin: fix DeprecationWarning stacklevel

This commit is contained in:
bastimeyer 2023-03-24 15:33:48 +01:00 committed by Forrest
parent 5e6f03c3cd
commit 45e515eb5a
10 changed files with 62 additions and 18 deletions

View File

@ -102,7 +102,6 @@ select = [
]
extend-ignore = [
"A003", # builtin-attribute-shadowing
"B028", # no-explicit-stacklevel
"C408", # unnecessary-collection-call
"ISC003", # explicit-string-concatenation
"PLC1901", # compare-to-empty-string

View File

@ -136,6 +136,9 @@ class Argument:
warnings.warn(
"Defining global plugin arguments is deprecated. Use the session options instead.",
StreamlinkDeprecationWarning,
# set stacklevel to 3 because of the @pluginargument decorator
# which is the public interface for defining plugin arguments
stacklevel=3,
)
@staticmethod

View File

@ -64,7 +64,7 @@ def _deprecations():
from streamlink.exceptions import StreamlinkDeprecationWarning
val, msg = deprecations[_attr]
warnings.warn(msg, StreamlinkDeprecationWarning)
warnings.warn(msg, StreamlinkDeprecationWarning, stacklevel=2)
return val

View File

@ -306,6 +306,7 @@ class Plugin:
warnings.warn(
f"Initialized {self.module} plugin with deprecated constructor",
FutureWarning,
stacklevel=2,
)
# Wrapper class which comes after the deprecated plugin in the MRO

View File

@ -27,6 +27,21 @@ log = logging.getLogger(__name__)
_original_allowed_gai_family = urllib3_util_connection.allowed_gai_family # type: ignore[attr-defined]
def _get_deprecation_stacklevel_offset():
"""Deal with stacklevels of both session.{g,s}et_option() and session.options.{g,s}et() calls"""
from inspect import currentframe
frame = currentframe().f_back.f_back
offset = 0
while frame:
if frame.f_code.co_filename == __file__ and frame.f_code.co_name in ("set_option", "get_option"):
offset += 1
break
frame = frame.f_back
return offset
class PythonDeprecatedWarning(UserWarning):
pass
@ -47,14 +62,19 @@ class StreamlinkOptions(Options):
except ValueError:
continue
# ---- getters
def _get_http_proxy(self, key):
@staticmethod
def _deprecate_https_proxy(key: str) -> None:
if key == "https-proxy":
warnings.warn(
"The `https-proxy` option has been deprecated in favor of a single `http-proxy` option",
StreamlinkDeprecationWarning,
stacklevel=4 + _get_deprecation_stacklevel_offset(),
)
# ---- getters
def _get_http_proxy(self, key):
self._deprecate_https_proxy(key)
return self.session.http.proxies.get("https" if key == "https-proxy" else "http")
def _get_http_attr(self, key):
@ -88,11 +108,7 @@ class StreamlinkOptions(Options):
self.session.http.proxies["http"] \
= self.session.http.proxies["https"] \
= update_scheme("https://", value, force=False)
if key == "https-proxy":
warnings.warn(
"The `https-proxy` option has been deprecated in favor of a single `http-proxy` option",
StreamlinkDeprecationWarning,
)
self._deprecate_https_proxy(key)
def _set_http_attr(self, key, value):
setattr(self.session.http, self._OPTIONS_HTTP_ATTRS[key], value)
@ -124,6 +140,7 @@ class StreamlinkOptions(Options):
warnings.warn(
f"`{key}` has been deprecated in favor of the `{name}` option",
StreamlinkDeprecationWarning,
stacklevel=3 + _get_deprecation_stacklevel_offset(),
)
return inner
@ -547,6 +564,7 @@ class Streamlink:
warnings.warn(
f"Resolved plugin {name} with deprecated can_handle_url API",
StreamlinkDeprecationWarning,
stacklevel=1,
)
candidate = name, plugin
priority = prio

View File

@ -610,6 +610,7 @@ def load_plugins(dirs: List[Path], showwarning: bool = True):
warnings.warn(
f"Loaded plugins from deprecated path, see CLI docs for how to migrate: {directory}",
StreamlinkDeprecationWarning,
stacklevel=1,
)
elif showwarning:
log.warning(f"Plugin path {directory} does not exist or is not a directory!")
@ -658,6 +659,7 @@ def setup_config_args(parser, ignore_unknown=False):
warnings.warn(
f"Loaded config from deprecated path, see CLI docs for how to migrate: {config_file}",
StreamlinkDeprecationWarning,
stacklevel=1,
)
config_files.append(config_file)
break
@ -674,6 +676,7 @@ def setup_config_args(parser, ignore_unknown=False):
warnings.warn(
f"Loaded plugin config from deprecated path, see CLI docs for how to migrate: {config_file}",
StreamlinkDeprecationWarning,
stacklevel=1,
)
config_files.append(config_file)
break

View File

@ -19,10 +19,11 @@ def test_text_is_str(recwarn: pytest.WarningsRecorder):
assert "text" not in getattr(validate, "__dict__", {})
assert "text" in getattr(validate, "__all__", [])
assert validate.text is str, "Exports text as str alias for backwards compatiblity"
assert [(record.category, str(record.message)) for record in recwarn.list] == [
assert [(record.category, str(record.message), record.filename) for record in recwarn.list] == [
(
StreamlinkDeprecationWarning,
"`streamlink.plugin.api.validate.text` is deprecated. Use `str` instead.",
__file__,
),
]

View File

@ -242,8 +242,12 @@ class TestSetupOptions:
def _get_streams(self): # pragma: no cover
pass
assert [(record.category, str(record.message)) for record in recwarn.list] == [
(StreamlinkDeprecationWarning, "Defining global plugin arguments is deprecated. Use the session options instead."),
assert [(record.category, str(record.message), record.filename) for record in recwarn.list] == [
(
StreamlinkDeprecationWarning,
"Defining global plugin arguments is deprecated. Use the session options instead.",
__file__,
),
]
session = Mock()

View File

@ -85,8 +85,12 @@ class TestPlugin:
assert isinstance(plugin, DeprecatedPlugin)
assert plugin.custom_attribute == "HTTP://LOCALHOST"
assert [(record.category, str(record.message)) for record in recwarn.list] == [
(FutureWarning, "Initialized test_plugin plugin with deprecated constructor"),
assert [(record.category, str(record.message), record.filename) for record in recwarn.list] == [
(
FutureWarning,
"Initialized test_plugin plugin with deprecated constructor",
__file__,
),
]
assert plugin.session is session

View File

@ -10,9 +10,9 @@ import requests_mock
import urllib3
import tests.plugin
from streamlink import NoPluginError, Streamlink
from streamlink.exceptions import StreamlinkDeprecationWarning
from streamlink.exceptions import NoPluginError, StreamlinkDeprecationWarning
from streamlink.plugin import HIGH_PRIORITY, LOW_PRIORITY, NO_PRIORITY, NORMAL_PRIORITY, Plugin, pluginmatcher
from streamlink.session import Streamlink
from streamlink.stream.hls import HLSStream
from streamlink.stream.http import HTTPStream
@ -418,10 +418,11 @@ class TestSessionOptionHttpProxy:
@pytest.fixture()
def _logs_deprecation(self, recwarn: pytest.WarningsRecorder):
yield
assert [(record.category, str(record.message)) for record in recwarn.list] == [
assert [(record.category, str(record.message), record.filename) for record in recwarn.list] == [
(
StreamlinkDeprecationWarning,
"The `https-proxy` option has been deprecated in favor of a single `http-proxy` option",
__file__,
),
]
@ -481,6 +482,16 @@ class TestSessionOptionHttpProxy:
session.http.proxies["https"] = "http://testproxy2.com"
assert session.get_option("https-proxy") == "http://testproxy2.com"
@pytest.mark.usefixtures("_logs_deprecation")
def test_https_proxy_get_directly(self, session: Streamlink):
# The DeprecationWarning's origin must point to this call, even without the set_option() wrapper
session.options.get("https-proxy")
@pytest.mark.usefixtures("_logs_deprecation")
def test_https_proxy_set_directly(self, session: Streamlink):
# The DeprecationWarning's origin must point to this call, even without the set_option() wrapper
session.options.set("https-proxy", "https://foo")
class TestOptionsKeyEqualsValue:
@pytest.fixture()