From d307ae8db7a2d05240b19c285bf11b8bab816dde Mon Sep 17 00:00:00 2001 From: Ryszard Knop Date: Fri, 14 Feb 2025 15:41:14 +0100 Subject: [PATCH] Remove Pydantic and update remaining dependencies Pydantic was used only for validating settings. Since we don't have any really complex types there, we can easily do it manually. We would have to upgrade to Pydantic V2 soon, and that means +6.5MB of dependencies, so let's drop it and enjoy a smaller install venv. --- itch_dl/cli.py | 2 +- itch_dl/config.py | 56 ++++++++++++++++++++++++++++++----------------- pyproject.toml | 7 +++--- 3 files changed, 40 insertions(+), 25 deletions(-) diff --git a/itch_dl/cli.py b/itch_dl/cli.py index 3df36c0..273d0d8 100644 --- a/itch_dl/cli.py +++ b/itch_dl/cli.py @@ -22,7 +22,7 @@ def parse_args() -> argparse.Namespace: help="configuration profile to load") # These args must match config.py -> Settings class. Make sure all defaults here - # evaluate to False, or apply_args_on_settings will override profile settings. + # evaluate to False, or load_config will override profile settings. parser.add_argument("--api-key", metavar="key", default=None, help="itch.io API key - https://itch.io/user/settings/api-keys") parser.add_argument("--user-agent", metavar="agent", default=None, diff --git a/itch_dl/config.py b/itch_dl/config.py index d8ec1af..1f0df61 100644 --- a/itch_dl/config.py +++ b/itch_dl/config.py @@ -3,27 +3,16 @@ import json import logging import platform import argparse -from typing import Optional +from dataclasses import dataclass, fields +from typing import Optional, Any, get_type_hints import requests -from pydantic import BaseModel from . import __version__ -OVERRIDABLE_SETTINGS = ( - "api_key", - "user_agent", - "download_to", - "mirror_web", - "urls_only", - "parallel", - "filter_files_glob", - "filter_files_regex", - "verbose", -) - -class Settings(BaseModel): +@dataclass +class Settings: """Available settings for itch-dl. Make sure all of them have default values, as the config file may not exist.""" @@ -57,6 +46,33 @@ def create_and_get_config_path() -> str: return os.path.join(base_path, "itch-dl") +def clean_config(config_data: dict[str, Any]) -> dict[str, Any]: + cleaned_config = {} + settings_invalid = False + type_hints = get_type_hints(Settings) + + # Complain about invalid types, if any: + for key, value in config_data.items(): + if not (expected_type := type_hints.get(key)): + logging.warning("Settings contain an unknown item, ignoring: '%s'", key) + continue + + if not isinstance(value, expected_type): + logging.fatal("Settings.%s has invalid type '%s', expected '%s'", key, type(value), expected_type) + + # Keep iterating to look up all the bad keys: + settings_invalid = True + continue + + cleaned_config[key] = value + + if settings_invalid: + logging.fatal("Settings invalid, bailing out!") + exit(1) + + return cleaned_config + + def load_config(args: argparse.Namespace, profile: Optional[str] = None) -> Settings: """Loads the configuration from the file system if it exists, the returns a Settings object.""" @@ -79,12 +95,12 @@ def load_config(args: argparse.Namespace, profile: Optional[str] = None) -> Sett config_data.update(profile_data) # All settings from the base file: - settings = Settings(**config_data) + settings = Settings(**clean_config(config_data)) - # Apply overrides from CLI args: - for key in OVERRIDABLE_SETTINGS: - value = getattr(args, key) - if value: + # Apply overrides from CLI args on each field in Settings: + for field in fields(Settings): + key = field.name + if value := getattr(args, key): setattr(settings, key, value) return settings diff --git a/pyproject.toml b/pyproject.toml index d5b4a30..ab01414 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -26,12 +26,11 @@ classifiers = [ [tool.poetry.dependencies] python = "^3.8" -tqdm = "^4.67.0" +tqdm = "^4.67.1" urllib3 = "^1.26.20" requests = "^2.32.3" -beautifulsoup4 = "^4.12.3" -lxml = "^5.3.0" -pydantic = "^1.10.19" +beautifulsoup4 = "^4.13.3" +lxml = "^5.3.1" [tool.poetry.scripts] itch-dl = "itch_dl.cli:run"