[DEV] Refactor entrypoint file (#765)
This commit is contained in:
parent
a7651b202b
commit
1cb70e4520
11 changed files with 181 additions and 137 deletions
|
|
@ -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
|
||||
96
src/ytdl_sub/cli/output_summary.py
Normal file
96
src/ytdl_sub/cli/output_summary.py
Normal 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))
|
||||
56
src/ytdl_sub/cli/output_transaction_log.py
Normal file
56
src/ytdl_sub/cli/output_transaction_log.py
Normal 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)
|
||||
0
src/ytdl_sub/cli/parsers/__init__.py
Normal file
0
src/ytdl_sub/cli/parsers/__init__.py
Normal 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
|
||||
|
||||
|
|
@ -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():
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
||||
|
|
@ -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={})
|
||||
):
|
||||
|
|
|
|||
Loading…
Reference in a new issue