Skip to content

Commit

Permalink
[PATCH] cli: list all dependencies in debug output (streamlink#4575)
Browse files Browse the repository at this point in the history
- Require importlib-metadata as fallback on Python < 3.8
- Add importlib_metadata to streamlink_cli.compat
- List all dependencies in `log_current_versions`
- Update tests
  • Loading branch information
Billy2011 committed Jun 7, 2022
1 parent 5c4a8d2 commit 22f6869
Show file tree
Hide file tree
Showing 4 changed files with 69 additions and 36 deletions.
1 change: 1 addition & 0 deletions setup.py
Expand Up @@ -10,6 +10,7 @@
here = os.path.abspath(os.path.dirname(__file__))

deps = [
'importlib-metadata;python_version<"3.8"',
'future;python_version<"3.0"',
# Require backport of concurrent.futures on Python 2
'futures;python_version<"3.0"',
Expand Down
7 changes: 7 additions & 0 deletions src/streamlink_cli/compat.py
Expand Up @@ -4,6 +4,13 @@

is_py2 = (sys.version_info[0] == 2)
is_py3 = (sys.version_info[0] == 3)

try:
import importlib.metadata as importlib_metadata # type: ignore[import] # noqa: F401
except ImportError:
import importlib_metadata # type: ignore[import] # noqa: F401


is_win32 = os.name == "nt"

if is_py2:
Expand Down
23 changes: 16 additions & 7 deletions src/streamlink_cli/main.py
Expand Up @@ -4,6 +4,7 @@
import logging
import os
import platform
import re
import signal
import sys
from collections import OrderedDict
Expand All @@ -14,10 +15,6 @@
from itertools import chain
from time import sleep

import requests
from socks import __version__ as socks_version
from websocket import __version__ as websocket_version

import streamlink.logger as logger
from streamlink import NoPluginError, PluginError, StreamError, Streamlink, __version__ as streamlink_version
from streamlink.cache import Cache
Expand All @@ -27,7 +24,7 @@
from streamlink.utils.encoding import get_filesystem_encoding, maybe_decode
from streamlink.utils.named_pipe import NamedPipe
from streamlink_cli.argparser import build_parser
from streamlink_cli.compat import is_py2, is_win32, stdout
from streamlink_cli.compat import importlib_metadata, is_py2, is_win32, stdout
from streamlink_cli.console import ConsoleOutput, ConsoleUserInputRequester
from streamlink_cli.constants import CONFIG_FILES, DEFAULT_STREAM_METADATA, LOG_DIR, PLUGINS_DIR, STREAM_SYNONYMS
from streamlink_cli.output import FileOutput, PlayerOutput
Expand Down Expand Up @@ -957,8 +954,20 @@ def log_current_versions():
log.debug("OS: {0}".format(os_version))
log.debug("Python: {0}".format(platform.python_version()))
log.debug("Streamlink: {0}".format(streamlink_version))
log.debug("Requests({0}), Socks({1}), Websocket({2})".format(
requests.__version__, socks_version, websocket_version))

# https://peps.python.org/pep-0508/#names
re_name = re.compile(r"[A-Z\d](?:[A-Z\d._-]*[A-Z\d])?", re.IGNORECASE)
log.debug("Dependencies:")
for name in [
match.group(0)
for match in map(re_name.match, importlib_metadata.requires("streamlink"))
if match is not None
]:
try:
version = importlib_metadata.version(name)
except importlib_metadata.PackageNotFoundError:
continue
log.debug(" {0}: {1}".format(name, version))


def log_current_arguments(session, parser):
Expand Down
74 changes: 45 additions & 29 deletions tests/test_cli_main.py
Expand Up @@ -424,19 +424,19 @@ def test_stream_failure_no_output_open(self, mock_console, mock_log):


class _TestCLIMainLogging(unittest.TestCase):
# stop test execution at the setup_signals() call, as we're not interested in what comes afterwards
class StopTest(Exception):
pass

@classmethod
def subject(cls, argv, **kwargs):
session = Streamlink()
session.load_plugins(os.path.join(os.path.dirname(__file__), "plugin"))

# stop test execution at the setup_signals() call, as we're not interested in what comes afterwards
class StopTest(Exception):
pass

with patch("streamlink_cli.main.os.geteuid", create=True, new=Mock(return_value=kwargs.get("euid", 1000))), \
patch("streamlink_cli.main.streamlink", session), \
patch("streamlink_cli.main.setup_signals", side_effect=StopTest), \
patch("streamlink_cli.main.CONFIG_FILES", ["/dev/null"]), \
patch("streamlink_cli.main.setup_signals", side_effect=cls.StopTest), \
patch("streamlink_cli.main.CONFIG_FILES", []), \
patch("streamlink_cli.main.setup_streamlink"), \
patch("streamlink_cli.main.setup_plugins"), \
patch("streamlink_cli.main.setup_http_session"), \
Expand All @@ -445,7 +445,7 @@ class StopTest(Exception):
mock_argv.__getitem__.side_effect = lambda x: argv[x]
try:
streamlink_cli.main.main()
except StopTest:
except cls.StopTest:
pass

def tearDown(self):
Expand Down Expand Up @@ -573,41 +573,57 @@ def test_log_root_warning(self, mock_log):

@patch("streamlink_cli.main.log")
@patch("streamlink_cli.main.streamlink_version", "streamlink")
@patch("streamlink_cli.main.requests.__version__", "requests")
@patch("streamlink_cli.main.socks_version", "socks")
@patch("streamlink_cli.main.websocket_version", "websocket")
@patch("streamlink_cli.main.importlib_metadata")
@patch("streamlink_cli.main.log_current_arguments", Mock(side_effect=_TestCLIMainLogging.StopTest))
@patch("platform.python_version", Mock(return_value="python"))
def test_log_current_versions(self, mock_log):
def test_log_current_versions(self, mock_importlib_metadata, mock_log):
# type: (Mock, Mock)
class FakePackageNotFoundError(Exception):
pass

def version(dist):
if dist == "foo":
return "1.2.3"
if dist == "bar-baz":
return "2.0.0"
raise FakePackageNotFoundError()

mock_importlib_metadata.PackageNotFoundError = FakePackageNotFoundError
mock_importlib_metadata.requires.return_value = ["foo>1", "bar-baz==2", "qux~=3"]
mock_importlib_metadata.version.side_effect = version

self.subject(["streamlink", "--loglevel", "info"])
self.assertEqual(mock_log.debug.mock_calls, [], "Doesn't log anything if not debug logging")

with patch("sys.platform", "linux"), \
patch("platform.platform", Mock(return_value="linux")):
self.subject(["streamlink", "--loglevel", "debug"])
self.assertEqual(
mock_log.debug.mock_calls[:4],
[
call("OS: linux"),
call("Python: python"),
call("Streamlink: streamlink"),
call("Requests(requests), Socks(socks), Websocket(websocket)")
]
)
assert mock_importlib_metadata.requires.mock_calls == [call("streamlink")]
assert mock_log.debug.mock_calls == [
call("OS: linux"),
call("Python: python"),
call("Streamlink: streamlink"),
call("Dependencies:"),
call(" foo: 1.2.3"),
call(" bar-baz: 2.0.0"),
]
mock_importlib_metadata.requires.reset_mock()
mock_log.debug.reset_mock()

with patch("sys.platform", "win32"), \
patch("platform.system", Mock(return_value="Windows")), \
patch("platform.release", Mock(return_value="0.0.0")):
self.subject(["streamlink", "--loglevel", "debug"])
self.assertEqual(
mock_log.debug.mock_calls[:4],
[
call("OS: Windows 0.0.0"),
call("Python: python"),
call("Streamlink: streamlink"),
call("Requests(requests), Socks(socks), Websocket(websocket)")
]
)
assert mock_importlib_metadata.requires.mock_calls == [call("streamlink")]
assert mock_log.debug.mock_calls == [
call("OS: Windows 0.0.0"),
call("Python: python"),
call("Streamlink: streamlink"),
call("Dependencies:"),
call(" foo: 1.2.3"),
call(" bar-baz: 2.0.0"),
]
mock_importlib_metadata.requires.reset_mock()
mock_log.debug.reset_mock()

@patch("streamlink_cli.main.log")
Expand Down

0 comments on commit 22f6869

Please sign in to comment.