Skip to content
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

plugin.api.http_session: remove parse_* methods #4803

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
33 changes: 0 additions & 33 deletions src/streamlink/plugin/api/http_session.py
Expand Up @@ -80,15 +80,6 @@ def __len__(self) -> int:
urllib3.util.url.PERCENT_RE = Urllib3UtilUrlPercentReOverride # type: ignore[attr-defined]


def _parse_keyvalue_list(val):
for keyvalue in val.split(";"):
try:
key, value = keyvalue.split("=", 1)
yield key.strip(), value.strip()
except ValueError:
continue


# requests.Request.__init__ keywords, except for "hooks"
_VALID_REQUEST_ARGS = "method", "url", "headers", "files", "data", "params", "auth", "cookies", "json"

Expand Down Expand Up @@ -139,30 +130,6 @@ def xml(cls, res, *args, **kwargs):
"""Parses XML from a response."""
return parse_xml(res.text, *args, **kwargs)

def parse_cookies(self, cookies, **kwargs):
"""Parses a semi-colon delimited list of cookies.

Example: foo=bar;baz=qux
"""
for name, value in _parse_keyvalue_list(cookies):
self.cookies.set(name, value, **kwargs)

def parse_headers(self, headers):
"""Parses a semi-colon delimited list of headers.

Example: foo=bar;baz=qux
"""
for name, value in _parse_keyvalue_list(headers):
self.headers[name] = value

def parse_query_params(self, cookies, **kwargs):
"""Parses a semi-colon delimited list of query parameters.

Example: foo=bar;baz=qux
"""
for name, value in _parse_keyvalue_list(cookies):
self.params[name] = value

def resolve_url(self, url):
"""Resolves any redirects and returns the final URL."""
return self.get(url, stream=True).url
Expand Down
35 changes: 17 additions & 18 deletions src/streamlink/session.py
Expand Up @@ -2,7 +2,7 @@
import pkgutil
from functools import lru_cache
from socket import AF_INET, AF_INET6
from typing import Any, Dict, Optional, Tuple, Type
from typing import Any, Dict, Iterator, Optional, Tuple, Type

# noinspection PyPackageRequirements
import urllib3.util.connection as urllib3_util_connection
Expand All @@ -27,6 +27,18 @@
# noinspection PyUnresolvedReferences
_original_allowed_gai_family = urllib3_util_connection.allowed_gai_family # type: ignore[attr-defined]

# options which support `key1=value1;key2=value2;...` strings as value
_OPTIONS_HTTP_KEYEQUALSVALUE = {"http-cookies": "cookies", "http-headers": "headers", "http-query-params": "params"}


def _parse_keyvalue_string(value: str) -> Iterator[Tuple[str, str]]:
for keyval in value.split(";"):
try:
key, val = keyval.split("=", 1)
yield key.strip(), val.strip()
except ValueError:
continue


class PythonDeprecatedWarning(UserWarning):
pass
Expand Down Expand Up @@ -235,23 +247,10 @@ def set_option(self, key: str, value: Any):
if key == "https-proxy":
log.warning("The https-proxy option has been deprecated in favor of a single http-proxy option")

elif key == "http-cookies":
if isinstance(value, dict):
self.http.cookies.update(value)
else:
self.http.parse_cookies(value)

elif key == "http-headers":
if isinstance(value, dict):
self.http.headers.update(value)
else:
self.http.parse_headers(value)

elif key == "http-query-params":
if isinstance(value, dict):
self.http.params.update(value)
else:
self.http.parse_query_params(value)
elif key in _OPTIONS_HTTP_KEYEQUALSVALUE:
getattr(self.http, _OPTIONS_HTTP_KEYEQUALSVALUE[key]).update(
value if isinstance(value, dict) else dict(_parse_keyvalue_string(value))
)

elif key == "http-trust-env":
self.http.trust_env = value
Expand Down
35 changes: 30 additions & 5 deletions tests/test_session.py
Expand Up @@ -23,6 +23,12 @@
_original_allowed_gai_family = urllib3.util.connection.allowed_gai_family # type: ignore[attr-defined]


@pytest.fixture
def session():
with patch("streamlink.session.Streamlink.load_builtin_plugins"):
yield Streamlink()


class EmptyPlugin(Plugin):
def _get_streams(self):
pass # pragma: no cover
Expand Down Expand Up @@ -409,11 +415,6 @@ def test_http_disable_dh(self, mock_urllib3_util_ssl):


class TestSessionOptionHttpProxy:
@pytest.fixture
def session(self):
with patch("streamlink.session.Streamlink.load_builtin_plugins"):
yield Streamlink()

@pytest.fixture
def no_deprecation(self, caplog: pytest.LogCaptureFixture):
yield
Expand Down Expand Up @@ -463,3 +464,27 @@ def test_https_proxy_socks(self, session: Streamlink, logs_deprecation):

assert session.http.proxies["http"] == "socks5://localhost:1234"
assert session.http.proxies["https"] == "socks5://localhost:1234"


@pytest.mark.parametrize("option", [
pytest.param(("http-cookies", "cookies"), id="http-cookies"),
pytest.param(("http-headers", "headers"), id="http-headers"),
pytest.param(("http-query-params", "params"), id="http-query-params"),
], indirect=True)
class TestOptionsKeyEqualsValue:
@pytest.fixture
def option(self, request, session: Streamlink):
option, attr = request.param
httpsessionattr = getattr(session.http, attr)
assert session.get_option(option) is httpsessionattr
assert "foo" not in httpsessionattr
assert "bar" not in httpsessionattr
yield option
assert httpsessionattr.get("foo") == "foo=bar"
assert httpsessionattr.get("bar") == "123"

def test_dict(self, session: Streamlink, option: str):
session.set_option(option, {"foo": "foo=bar", "bar": "123"})

def test_string(self, session: Streamlink, option: str):
session.set_option(option, "foo=foo=bar;bar=123;baz")