Skip to content

Commit

Permalink
Switch to a custom type for cache length validation
Browse files Browse the repository at this point in the history
Switched to using a class for cache duration instead.
  • Loading branch information
jbsparrow committed Nov 28, 2024
1 parent ecd35df commit 6a0662d
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 17 deletions.
33 changes: 33 additions & 0 deletions cyberdrop_dl/config_definitions/custom_types.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
from __future__ import annotations

from typing import Annotated
from datetime import timedelta
import humanfriendly

from pydantic import (
AfterValidator,
Expand Down Expand Up @@ -108,3 +110,34 @@ class HttpAppriseURLModel(AppriseURLModel):

class HttpAppriseURL(AppriseURL):
_validator = HttpAppriseURLModel


class CacheDuration(BaseModel):
duration: timedelta = timedelta(days=7)

@staticmethod
def parse_cache_duration(value: str) -> timedelta:
"""
Parses a human-readable duration string (e.g., '7 days', '2 hours').
"""
try:
seconds = humanfriendly.parse_timespan(value)
return timedelta(seconds=seconds)
except humanfriendly.InvalidTimespan:
raise ValueError(f"Invalid cache duration format: {value}")

Check failure on line 127 in cyberdrop_dl/config_definitions/custom_types.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (B904)

cyberdrop_dl/config_definitions/custom_types.py:127:13: B904 Within an `except` clause, raise exceptions with `raise ... from err` or `raise ... from None` to distinguish them from errors in exception handling

@model_validator(mode="before")
@classmethod
def validate_cache_duration(cls, values):
if isinstance(values, dict) and isinstance(values.get("duration"), str):
values["duration"] = cls.parse_cache_duration(values["duration"])
elif isinstance(values, str): # For direct string initialization
return {"duration": cls.parse_cache_duration(values)}
return values

@model_serializer()
def serialize_cache_duration(self):
"""
Serializes the duration into a human-readable string.
"""
return humanfriendly.format_timespan(self.total_seconds(), detailed=False)
20 changes: 3 additions & 17 deletions cyberdrop_dl/config_definitions/global_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from datetime import timedelta
import humanfriendly

Check failure on line 4 in cyberdrop_dl/config_definitions/global_settings.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (F401)

cyberdrop_dl/config_definitions/global_settings.py:4:8: F401 `humanfriendly` imported but unused

from .custom_types import AliasModel, HttpURL, NonEmptyStr
from .custom_types import AliasModel, HttpURL, NonEmptyStr, CacheDuration


class General(BaseModel):

Check failure on line 9 in cyberdrop_dl/config_definitions/global_settings.py

View workflow job for this annotation

GitHub Actions / ruff

Ruff (I001)

cyberdrop_dl/config_definitions/global_settings.py:1:1: I001 Import block is un-sorted or un-formatted
Expand Down Expand Up @@ -37,29 +37,15 @@ class RateLimitingOptions(BaseModel):
max_simultaneous_downloads: PositiveInt = 15
max_simultaneous_downloads_per_domain: PositiveInt = 3
download_speed_limit: ByteSize = ByteSize(0)
file_host_cache_length: timedelta = Field(default=timedelta(days=7))
forum_cache_length: timedelta = Field(default=timedelta(days=28, hours=12, minutes=30, seconds=15, milliseconds=500, microseconds=250))
file_host_cache_length: CacheDuration = timedelta(days=7)
forum_cache_length: CacheDuration = timedelta(weeks=4)

@field_serializer("download_speed_limit")
def human_readable(self, value: ByteSize | int) -> str:
if not isinstance(value, ByteSize):
value = ByteSize(value)
return value.human_readable(decimal=True)

@field_serializer("file_host_cache_length", "forum_cache_length")
def serialize_timedelta(self, value: timedelta) -> str:
return humanfriendly.format_timespan(value.total_seconds())

@field_validator("file_host_cache_length", "forum_cache_length", mode="before")
def parse_timedelta(cls, value):
if isinstance(value, str):
try:
seconds = humanfriendly.parse_timespan(value)
return timedelta(seconds=seconds)
except humanfriendly.InvalidTimespan:
raise ValueError(f"Invalid time span format: {value}")
return value


class UIOptions(BaseModel):
vi_mode: bool = False
Expand Down

0 comments on commit 6a0662d

Please sign in to comment.