[DEV] Refactor entrypoint file (#765)

This commit is contained in:
Jesse Bannon 2023-10-18 23:33:25 -07:00 committed by GitHub
parent a7651b202b
commit 1cb70e4520
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 181 additions and 137 deletions

View file

@ -7,12 +7,14 @@ from typing import List
from typing import Optional
from typing import Tuple
from colorama import Fore
from yt_dlp.utils import sanitize_filename
from ytdl_sub.cli.download_args_parser import DownloadArgsParser
from ytdl_sub.cli.main_args_parser import DEFAULT_CONFIG_FILE_NAME
from ytdl_sub.cli.main_args_parser import parser
from ytdl_sub.cli.output_summary import output_summary
from ytdl_sub.cli.output_transaction_log import _maybe_validate_transaction_log_file
from ytdl_sub.cli.output_transaction_log import output_transaction_log
from ytdl_sub.cli.parsers.dl import DownloadArgsParser
from ytdl_sub.cli.parsers.main import DEFAULT_CONFIG_FILE_NAME
from ytdl_sub.cli.parsers.main import parser
from ytdl_sub.config.config_file import ConfigFile
from ytdl_sub.subscriptions.subscription import Subscription
from ytdl_sub.utils.exceptions import ExperimentalFeatureNotEnabled
@ -181,117 +183,6 @@ def _view_url_from_cli(
return subscription, subscription.download(dry_run=True)
def _maybe_validate_transaction_log_file(transaction_log_file_path: Optional[str]) -> None:
if transaction_log_file_path:
try:
with open(transaction_log_file_path, "w", encoding="utf-8"):
pass
except Exception as exc:
raise ValidationException(
f"Transaction log file '{transaction_log_file_path}' cannot be written to. "
f"Reason: {str(exc)}"
) from exc
def _output_transaction_log(
transaction_logs: List[Tuple[Subscription, FileHandlerTransactionLog]],
transaction_log_file_path: str,
) -> None:
transaction_log_file_contents = ""
for subscription, transaction_log in transaction_logs:
if transaction_log.is_empty:
transaction_log_contents = f"\nNo files changed for {subscription.name}"
else:
transaction_log_contents = (
f"Transaction log for {subscription.name}:\n"
f"{transaction_log.to_output_message(subscription.output_directory)}"
)
if transaction_log_file_path:
transaction_log_file_contents += transaction_log_contents
else:
logger.info(transaction_log_contents)
if transaction_log_file_contents:
with open(transaction_log_file_path, "w", encoding="utf-8") as transaction_log_file:
transaction_log_file.write(transaction_log_file_contents)
def _green(value: str) -> str:
return Fore.GREEN + value + Fore.RESET
def _red(value: str) -> str:
return Fore.RED + value + Fore.RESET
def _no_color(value: str) -> str:
return Fore.RESET + value + Fore.RESET
def _str_int(value: int) -> str:
if value > 0:
return f"+{value}"
return str(value)
def _color_int(value: int) -> str:
str_int = _str_int(value)
if value > 0:
return _green(str_int)
if value < 0:
return _red(str_int)
return _no_color(str_int)
def _output_summary(transaction_logs: List[Tuple[Subscription, FileHandlerTransactionLog]]):
summary: List[str] = []
# Initialize widths to 0
width_sub_name: int = 0
width_num_entries_added: int = 0
width_num_entries_modified: int = 0
width_num_entries_removed: int = 0
width_num_entries: int = 0
# Calculate min width needed
for subscription, _ in transaction_logs:
width_sub_name = max(width_sub_name, len(subscription.name))
width_num_entries_added = max(
width_num_entries_added, len(_color_int(subscription.num_entries_added))
)
width_num_entries_modified = max(
width_num_entries_modified, len(_color_int(subscription.num_entries_modified))
)
width_num_entries_removed = max(
width_num_entries_removed, len(_color_int(subscription.num_entries_removed * -1))
)
width_num_entries = max(width_num_entries, len(str(subscription.num_entries)))
# Add spacing for aesthetics
width_sub_name += 4
width_num_entries += 4
# Build the summary
for subscription, _ in transaction_logs:
num_entries_added = _color_int(subscription.num_entries_added)
num_entries_modified = _color_int(subscription.num_entries_modified)
num_entries_removed = _color_int(subscription.num_entries_removed * -1)
num_entries = str(subscription.num_entries)
status = _green("success")
summary.append(
f"{subscription.name:<{width_sub_name}} "
f"{num_entries_added:>{width_num_entries_added}} "
f"{num_entries_modified:>{width_num_entries_modified}} "
f"{num_entries_removed:>{width_num_entries_removed}} "
f"{num_entries:>{width_num_entries}} "
f"{status}"
)
return "\n".join(summary)
def main() -> List[Tuple[Subscription, FileHandlerTransactionLog]]:
"""
Entrypoint for ytdl-sub, without the error handling
@ -353,12 +244,11 @@ def main() -> List[Tuple[Subscription, FileHandlerTransactionLog]]:
raise ValidationException("Must provide one of the commands: sub, dl, view")
if not args.suppress_transaction_log:
_output_transaction_log(
output_transaction_log(
transaction_logs=transaction_logs,
transaction_log_file_path=args.transaction_log,
)
# Hack to always show download summary, even if logs are set to quiet
logger.warning("Download Summary:\n%s", _output_summary(transaction_logs))
output_summary(transaction_logs)
return transaction_logs

View file

@ -0,0 +1,96 @@
from typing import List
from typing import Tuple
from colorama import Fore
from ytdl_sub.subscriptions.subscription import Subscription
from ytdl_sub.utils.file_handler import FileHandlerTransactionLog
from ytdl_sub.utils.logger import Logger
logger = Logger.get()
def _green(value: str) -> str:
return Fore.GREEN + value + Fore.RESET
def _red(value: str) -> str:
return Fore.RED + value + Fore.RESET
def _no_color(value: str) -> str:
return Fore.RESET + value + Fore.RESET
def _str_int(value: int) -> str:
if value > 0:
return f"+{value}"
return str(value)
def _color_int(value: int) -> str:
str_int = _str_int(value)
if value > 0:
return _green(str_int)
if value < 0:
return _red(str_int)
return _no_color(str_int)
def output_summary(transaction_logs: List[Tuple[Subscription, FileHandlerTransactionLog]]) -> str:
"""
Parameters
----------
transaction_logs
Transaction logs from downloaded subscriptions
Returns
-------
Output summary to print
"""
summary: List[str] = []
# Initialize widths to 0
width_sub_name: int = 0
width_num_entries_added: int = 0
width_num_entries_modified: int = 0
width_num_entries_removed: int = 0
width_num_entries: int = 0
# Calculate min width needed
for subscription, _ in transaction_logs:
width_sub_name = max(width_sub_name, len(subscription.name))
width_num_entries_added = max(
width_num_entries_added, len(_color_int(subscription.num_entries_added))
)
width_num_entries_modified = max(
width_num_entries_modified, len(_color_int(subscription.num_entries_modified))
)
width_num_entries_removed = max(
width_num_entries_removed, len(_color_int(subscription.num_entries_removed * -1))
)
width_num_entries = max(width_num_entries, len(str(subscription.num_entries)))
# Add spacing for aesthetics
width_sub_name += 4
width_num_entries += 4
# Build the summary
for subscription, _ in transaction_logs:
num_entries_added = _color_int(subscription.num_entries_added)
num_entries_modified = _color_int(subscription.num_entries_modified)
num_entries_removed = _color_int(subscription.num_entries_removed * -1)
num_entries = str(subscription.num_entries)
status = _green("success")
summary.append(
f"{subscription.name:<{width_sub_name}} "
f"{num_entries_added:>{width_num_entries_added}} "
f"{num_entries_modified:>{width_num_entries_modified}} "
f"{num_entries_removed:>{width_num_entries_removed}} "
f"{num_entries:>{width_num_entries}} "
f"{status}"
)
# Hack to always show download summary, even if logs are set to quiet
logger.warning("Download Summary:\n%s", "\n".join(summary))

View file

@ -0,0 +1,56 @@
from typing import List
from typing import Optional
from typing import Tuple
from ytdl_sub.subscriptions.subscription import Subscription
from ytdl_sub.utils.exceptions import ValidationException
from ytdl_sub.utils.file_handler import FileHandlerTransactionLog
from ytdl_sub.utils.logger import Logger
logger = Logger.get()
def _maybe_validate_transaction_log_file(transaction_log_file_path: Optional[str]) -> None:
if transaction_log_file_path:
try:
with open(transaction_log_file_path, "w", encoding="utf-8"):
pass
except Exception as exc:
raise ValidationException(
f"Transaction log file '{transaction_log_file_path}' cannot be written to. "
f"Reason: {str(exc)}"
) from exc
def output_transaction_log(
transaction_logs: List[Tuple[Subscription, FileHandlerTransactionLog]],
transaction_log_file_path: Optional[str],
) -> None:
"""
Maybe print and/or write transaction logs to a file
Parameters
----------
transaction_logs
The transaction logs from downloaded subscriptions
transaction_log_file_path
Optional file path to write to
"""
transaction_log_file_contents = ""
for subscription, transaction_log in transaction_logs:
if transaction_log.is_empty:
transaction_log_contents = f"\nNo files changed for {subscription.name}"
else:
transaction_log_contents = (
f"Transaction log for {subscription.name}:\n"
f"{transaction_log.to_output_message(subscription.output_directory)}"
)
if transaction_log_file_path:
transaction_log_file_contents += transaction_log_contents
else:
logger.info(transaction_log_contents)
if transaction_log_file_contents:
with open(transaction_log_file_path, "w", encoding="utf-8") as transaction_log_file:
transaction_log_file.write(transaction_log_file_contents)

View file

View file

@ -7,7 +7,7 @@ from typing import Tuple
from mergedeep import mergedeep
from ytdl_sub.cli.main_args_parser import MainArguments
from ytdl_sub.cli.parsers.main import MainArguments
from ytdl_sub.config.config_validator import ConfigOptions
from ytdl_sub.utils.exceptions import InvalidDlArguments

View file

@ -1,6 +1,6 @@
import sys
from ytdl_sub.cli.main_args_parser import parser
from ytdl_sub.cli.parsers.main import parser
from ytdl_sub.utils.logger import Logger
@ -11,11 +11,11 @@ def _main():
Logger.set_log_level(log_level_name=args.ytdl_sub_log_level)
# pylint: disable=import-outside-toplevel
import ytdl_sub.cli.main
import ytdl_sub.cli.entrypoint
# pylint: enable=import-outside-toplevel
ytdl_sub.cli.main.main()
ytdl_sub.cli.entrypoint.main()
def main():

View file

@ -7,7 +7,7 @@ from unittest.mock import patch
import pytest
from ytdl_sub.cli.main import main
from ytdl_sub.cli.entrypoint import main
from ytdl_sub.subscriptions.subscription import Subscription
from ytdl_sub.utils.file_handler import FileHandler
from ytdl_sub.utils.file_handler import FileHandlerTransactionLog

View file

@ -6,9 +6,9 @@ from typing import Optional
import pytest
from ytdl_sub.cli.download_args_parser import DownloadArgsParser
from ytdl_sub.cli.main_args_parser import MainArguments
from ytdl_sub.cli.main_args_parser import parser
from ytdl_sub.cli.parsers.dl import DownloadArgsParser
from ytdl_sub.cli.parsers.main import MainArguments
from ytdl_sub.cli.parsers.main import parser
from ytdl_sub.config.config_validator import ConfigOptions
from ytdl_sub.utils.exceptions import InvalidDlArguments

View file

@ -17,10 +17,10 @@ import mergedeep
import pytest
from conftest import assert_logs
from ytdl_sub.cli.main import _download_subscriptions_from_yaml_files
from ytdl_sub.cli.main import _output_summary
from ytdl_sub.cli.main import logger as main_logger
from ytdl_sub.cli.main import main
from ytdl_sub.cli.entrypoint import _download_subscriptions_from_yaml_files
from ytdl_sub.cli.entrypoint import main
from ytdl_sub.cli.output_summary import output_summary
from ytdl_sub.cli.output_transaction_log import logger as transaction_logger
from ytdl_sub.config.config_file import ConfigFile
from ytdl_sub.subscriptions.subscription import Subscription
from ytdl_sub.utils.exceptions import ExperimentalFeatureNotEnabled
@ -202,7 +202,7 @@ def test_suppress_transaction_log(
"--suppress-transaction-log",
]
+ (["--transaction-log", file_transaction_log] if file_transaction_log else []),
), patch("ytdl_sub.cli.main._output_transaction_log") as mock_transaction_log:
), patch("ytdl_sub.cli.output_transaction_log.output_transaction_log") as mock_transaction_log:
transaction_logs = main()
assert transaction_logs
@ -251,7 +251,9 @@ def test_transaction_log_to_logger(
str(music_video_subscription_path),
],
), assert_logs(
logger=main_logger, expected_message="Transaction log for john_smith:\n", log_level="info"
logger=transaction_logger,
expected_message="Transaction log for john_smith:\n",
log_level="info",
):
transaction_logs = main()
assert transaction_logs
@ -277,7 +279,7 @@ def test_output_summary():
mock_subscriptions.append((sub, FileHandlerTransactionLog()))
_ = _output_summary(transaction_logs=mock_subscriptions)
_ = output_summary(transaction_logs=mock_subscriptions)
assert True # Test used for manual inspection - too hard to test ansi color codes

View file

@ -8,7 +8,7 @@ import pytest
from src.ytdl_sub import __local_version__
from src.ytdl_sub.main import main
from ytdl_sub.cli.main_args_parser import DEFAULT_CONFIG_FILE_NAME
from ytdl_sub.cli.parsers.main import DEFAULT_CONFIG_FILE_NAME
from ytdl_sub.config.config_file import ConfigFile
from ytdl_sub.utils.exceptions import ValidationException
from ytdl_sub.utils.file_handler import FileHandler
@ -93,7 +93,7 @@ def test_args_after_sub_work(mock_sys_exit):
sys,
"argv",
["ytdl-sub", "-c", "examples/tv_show_config.yaml", "sub", "--log-level", "verbose"],
), patch("ytdl_sub.cli.main._download_subscriptions_from_yaml_files") as mock_sub:
), patch("ytdl_sub.cli.entrypoint._download_subscriptions_from_yaml_files") as mock_sub:
main()
assert mock_sub.call_count == 1
@ -107,7 +107,7 @@ def test_no_config_works(mock_sys_exit):
sys,
"argv",
["ytdl-sub", "sub", "--log-level", "verbose"],
), patch("ytdl_sub.cli.main._download_subscriptions_from_yaml_files") as mock_sub:
), patch("ytdl_sub.cli.entrypoint._download_subscriptions_from_yaml_files") as mock_sub:
main()
assert mock_sub.call_count == 1
@ -128,7 +128,7 @@ def test_uses_default_config_if_present(mock_sys_exit):
"argv",
["ytdl-sub", "sub", "--log-level", "verbose"],
), patch(
"ytdl_sub.cli.main._download_subscriptions_from_yaml_files"
"ytdl_sub.cli.entrypoint._download_subscriptions_from_yaml_files"
) as mock_sub, patch.object(
ConfigFile, "from_file_path", new=lambda _: ConfigFile(name="test default", value={})
):