diff --git a/src/ytdl_sub/cli/download_args_parser.py b/src/ytdl_sub/cli/download_args_parser.py index 903dbee1..4a1afb95 100644 --- a/src/ytdl_sub/cli/download_args_parser.py +++ b/src/ytdl_sub/cli/download_args_parser.py @@ -7,7 +7,7 @@ from typing import Tuple from mergedeep import mergedeep -from ytdl_sub.cli.main_args_parser import MainArgs +from ytdl_sub.cli.main_args_parser import MainArguments from ytdl_sub.config.config_validator import ConfigOptions from ytdl_sub.utils.exceptions import InvalidDlArguments @@ -41,7 +41,7 @@ class DownloadArgsParser: self._config_options = config_options for arg in extra_arguments: - if arg in MainArgs.all(): + if arg in MainArguments.all(): raise InvalidDlArguments( f"'{arg}' is a ytdl-sub argument and must placed behind 'dl'" ) diff --git a/src/ytdl_sub/cli/main.py b/src/ytdl_sub/cli/main.py index b6b93548..e09485dd 100644 --- a/src/ytdl_sub/cli/main.py +++ b/src/ytdl_sub/cli/main.py @@ -5,9 +5,11 @@ from typing import List from typing import Tuple 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.config.config_file import ConfigFile 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.file_lock import working_directory_lock from ytdl_sub.utils.logger import Logger @@ -35,11 +37,19 @@ def _download_subscriptions_from_yaml_files( Returns ------- List of (subscription, transaction_log) + + Raises + ------ + Validation exception if main arg is specified as a subscription path """ subscription_paths: List[str] = args.subscription_paths subscriptions: List[Subscription] = [] output: List[Tuple[Subscription, FileHandlerTransactionLog]] = [] + # Make sure no main args are passed as a subscription path + if main_argument := MainArguments.get_argument_if_exists(subscription_paths) is not None: + raise ValidationException(f"The argument '{main_argument}' must be passed before 'sub'") + # Load all the subscriptions first to perform all validation before downloading for path in subscription_paths: subscriptions += Subscription.from_file_path(config=config, subscription_path=path) diff --git a/src/ytdl_sub/cli/main_args_parser.py b/src/ytdl_sub/cli/main_args_parser.py index 0c9eb295..aa22c249 100644 --- a/src/ytdl_sub/cli/main_args_parser.py +++ b/src/ytdl_sub/cli/main_args_parser.py @@ -1,23 +1,73 @@ import argparse +import dataclasses from enum import Enum from typing import List +from typing import Optional from ytdl_sub.utils.logger import LoggerLevels -class MainArgs(Enum): - CONFIG = "--config" - DRY_RUN = "--dry-run" - LOG_LEVEL = "--log-level" +@dataclasses.dataclass +class MainArgument: + short: str + long: str + is_positional: bool = False + + +class MainArguments: + CONFIG = MainArgument( + short="-c", + long="--config", + ) + DRY_RUN = MainArgument( + short="-d", + long="--dry-run", + is_positional=True, + ) + LOG_LEVEL = MainArgument( + short="-l", + long="--log-level", + is_positional=True, + ) @classmethod - def all(cls) -> List[str]: + def all(cls) -> List[MainArgument]: """ Returns ------- - List of all args used in main CLI + List of MainArgument classes """ - return list(map(lambda arg: arg.value, cls)) + return [cls.CONFIG, cls.DRY_RUN, cls.LOG_LEVEL] + + @classmethod + def all_arguments(cls) -> List[str]: + """ + Returns + ------- + List of all string args that can be used in the CLI + """ + all_args = [] + for arg in cls.all(): + all_args.extend([arg.short, arg.long]) + return all_args + + @classmethod + def get_argument_if_exists(cls, input_args: List[str]) -> Optional[str]: + """ + Parameters + ---------- + input_args + List of arguments + + Returns + ------- + True if a Main argument is in the input arguments. False otherwise. + """ + for input_arg in input_args: + for main_arg in cls.all_arguments(): + if input_arg == main_arg: + return input_arg + return None ################################################################################################### @@ -26,21 +76,23 @@ parser = argparse.ArgumentParser( description="ytdl-sub: Automate download and adding metadata with YoutubeDL" ) parser.add_argument( - "-c", - MainArgs.CONFIG.value, + MainArguments.CONFIG.short, + MainArguments.CONFIG.long, metavar="CONFIGPATH", type=str, help="path to the config yaml, uses config.yaml if not provided", default="config.yaml", ) parser.add_argument( - MainArgs.DRY_RUN.value, + MainArguments.DRY_RUN.short, + MainArguments.DRY_RUN.long, action="store_true", help="preview what a download would output, " "does not perform any video downloads or writes to output directories", ) parser.add_argument( - MainArgs.LOG_LEVEL.value, + MainArguments.LOG_LEVEL.short, + MainArguments.LOG_LEVEL.long, metavar="|".join(LoggerLevels.names()), type=str, help="level of logs to print to console, defaults to info", diff --git a/tests/unit/cli/test_download_args_parser.py b/tests/unit/cli/test_download_args_parser.py index bc73384f..149397b4 100644 --- a/tests/unit/cli/test_download_args_parser.py +++ b/tests/unit/cli/test_download_args_parser.py @@ -7,7 +7,7 @@ from typing import Optional import pytest from ytdl_sub.cli.download_args_parser import DownloadArgsParser -from ytdl_sub.cli.main_args_parser import MainArgs +from ytdl_sub.cli.main_args_parser import MainArguments from ytdl_sub.cli.main_args_parser import parser from ytdl_sub.config.config_validator import ConfigOptions from ytdl_sub.utils.exceptions import InvalidDlArguments @@ -151,7 +151,7 @@ class TestDownloadArgsParser: extra_arguments=extra_args, config_options=config_options ).to_subscription_dict() - @pytest.mark.parametrize("main_argument", MainArgs.all()) + @pytest.mark.parametrize("main_argument", MainArguments.all()) def test_error_uses_main_args(self, main_argument, config_options_generator): config_options = config_options_generator() extra_args = _get_extra_arguments(cmd_string=f"dl {main_argument}")