mirror of
https://github.com/Second-Hand-Friends/kleinanzeigen-bot.git
synced 2026-03-12 10:31:50 +01:00
fix: replace usage of legacy pydantic validators
This commit is contained in:
@@ -5,9 +5,9 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import hashlib, json # isort: skip
|
import hashlib, json # isort: skip
|
||||||
from datetime import datetime # noqa: TC003 Move import into a type-checking block
|
from datetime import datetime # noqa: TC003 Move import into a type-checking block
|
||||||
from typing import Any, Dict, Final, List, Literal, Mapping, Sequence
|
from typing import Annotated, Any, Dict, Final, List, Literal, Mapping, Sequence
|
||||||
|
|
||||||
from pydantic import Field, model_validator, validator
|
from pydantic import AfterValidator, Field, field_validator, model_validator
|
||||||
from typing_extensions import Self
|
from typing_extensions import Self
|
||||||
|
|
||||||
from kleinanzeigen_bot.model.config_model import AdDefaults # noqa: TC001 Move application import into a type-checking block
|
from kleinanzeigen_bot.model.config_model import AdDefaults # noqa: TC001 Move application import into a type-checking block
|
||||||
@@ -52,6 +52,15 @@ class ContactPartial(ContextualModel):
|
|||||||
phone:str | None = _OPTIONAL()
|
phone:str | None = _OPTIONAL()
|
||||||
|
|
||||||
|
|
||||||
|
def _validate_shipping_option_item(v:str) -> str:
|
||||||
|
if not v.strip():
|
||||||
|
raise ValueError("must be non-empty and non-blank")
|
||||||
|
return v
|
||||||
|
|
||||||
|
|
||||||
|
ShippingOption = Annotated[str, AfterValidator(_validate_shipping_option_item)]
|
||||||
|
|
||||||
|
|
||||||
class AdPartial(ContextualModel):
|
class AdPartial(ContextualModel):
|
||||||
active:bool | None = _OPTIONAL()
|
active:bool | None = _OPTIONAL()
|
||||||
type:Literal["OFFER", "WANTED"] | None = _OPTIONAL()
|
type:Literal["OFFER", "WANTED"] | None = _OPTIONAL()
|
||||||
@@ -65,7 +74,7 @@ class AdPartial(ContextualModel):
|
|||||||
price_type:Literal["FIXED", "NEGOTIABLE", "GIVE_AWAY", "NOT_APPLICABLE"] | None = _OPTIONAL()
|
price_type:Literal["FIXED", "NEGOTIABLE", "GIVE_AWAY", "NOT_APPLICABLE"] | None = _OPTIONAL()
|
||||||
shipping_type:Literal["PICKUP", "SHIPPING", "NOT_APPLICABLE"] | None = _OPTIONAL()
|
shipping_type:Literal["PICKUP", "SHIPPING", "NOT_APPLICABLE"] | None = _OPTIONAL()
|
||||||
shipping_costs:float | None = _OPTIONAL()
|
shipping_costs:float | None = _OPTIONAL()
|
||||||
shipping_options:List[str] | None = _OPTIONAL()
|
shipping_options:List[ShippingOption] | None = _OPTIONAL()
|
||||||
sell_directly:bool | None = _OPTIONAL()
|
sell_directly:bool | None = _OPTIONAL()
|
||||||
images:List[str] | None = _OPTIONAL()
|
images:List[str] | None = _OPTIONAL()
|
||||||
contact:ContactPartial | None = _OPTIONAL()
|
contact:ContactPartial | None = _OPTIONAL()
|
||||||
@@ -76,19 +85,19 @@ class AdPartial(ContextualModel):
|
|||||||
updated_on:datetime | None = _ISO_DATETIME()
|
updated_on:datetime | None = _ISO_DATETIME()
|
||||||
content_hash:str | None = _OPTIONAL()
|
content_hash:str | None = _OPTIONAL()
|
||||||
|
|
||||||
@validator("created_on", "updated_on", pre = True)
|
@field_validator("created_on", "updated_on", mode = "before")
|
||||||
@classmethod
|
@classmethod
|
||||||
def _parse_dates(cls, v:Any) -> Any:
|
def _parse_dates(cls, v:Any) -> Any:
|
||||||
return parse_datetime(v)
|
return parse_datetime(v)
|
||||||
|
|
||||||
@validator("shipping_costs", pre = True)
|
@field_validator("shipping_costs", mode = "before")
|
||||||
@classmethod
|
@classmethod
|
||||||
def _parse_shipping_costs(cls, v:float | int | str) -> Any:
|
def _parse_shipping_costs(cls, v:float | int | str) -> Any:
|
||||||
if v:
|
if v:
|
||||||
return round(parse_decimal(v), 2)
|
return round(parse_decimal(v), 2)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@validator("description")
|
@field_validator("description")
|
||||||
@classmethod
|
@classmethod
|
||||||
def _validate_description_length(cls, v:str) -> str:
|
def _validate_description_length(cls, v:str) -> str:
|
||||||
if len(v) > MAX_DESCRIPTION_LENGTH:
|
if len(v) > MAX_DESCRIPTION_LENGTH:
|
||||||
@@ -106,13 +115,6 @@ class AdPartial(ContextualModel):
|
|||||||
raise ValueError("price is required when price_type is FIXED")
|
raise ValueError("price is required when price_type is FIXED")
|
||||||
return values
|
return values
|
||||||
|
|
||||||
@validator("shipping_options", each_item = True)
|
|
||||||
@classmethod
|
|
||||||
def _validate_shipping_option(cls, v:str) -> str:
|
|
||||||
if not v.strip():
|
|
||||||
raise ValueError("shipping_options entries must be non-empty")
|
|
||||||
return v
|
|
||||||
|
|
||||||
def update_content_hash(self) -> Self:
|
def update_content_hash(self) -> Self:
|
||||||
"""Calculate and updates the content_hash value for user-modifiable fields of the ad."""
|
"""Calculate and updates the content_hash value for user-modifiable fields of the ad."""
|
||||||
|
|
||||||
|
|||||||
@@ -4,9 +4,9 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import copy
|
import copy
|
||||||
from typing import Any, List, Literal
|
from typing import Annotated, Any, List, Literal
|
||||||
|
|
||||||
from pydantic import Field, model_validator, validator
|
from pydantic import AfterValidator, Field, model_validator
|
||||||
from typing_extensions import deprecated
|
from typing_extensions import deprecated
|
||||||
|
|
||||||
from kleinanzeigen_bot.utils import dicts
|
from kleinanzeigen_bot.utils import dicts
|
||||||
@@ -102,8 +102,17 @@ class CaptchaConfig(ContextualModel):
|
|||||||
restart_delay:str = "6h"
|
restart_delay:str = "6h"
|
||||||
|
|
||||||
|
|
||||||
|
def _validate_glob_pattern(v:str) -> str:
|
||||||
|
if not v.strip():
|
||||||
|
raise ValueError("must be a non-empty, non-blank glob pattern")
|
||||||
|
return v
|
||||||
|
|
||||||
|
|
||||||
|
GlobPattern = Annotated[str, AfterValidator(_validate_glob_pattern)]
|
||||||
|
|
||||||
|
|
||||||
class Config(ContextualModel):
|
class Config(ContextualModel):
|
||||||
ad_files:List[str] = Field(
|
ad_files:List[GlobPattern] = Field(
|
||||||
default_factory = lambda: ["./**/ad_*.{json,yml,yaml}"],
|
default_factory = lambda: ["./**/ad_*.{json,yml,yaml}"],
|
||||||
min_items = 1,
|
min_items = 1,
|
||||||
description = """
|
description = """
|
||||||
@@ -137,10 +146,3 @@ Example:
|
|||||||
return Config.model_validate(
|
return Config.model_validate(
|
||||||
dicts.apply_defaults(copy.deepcopy(values), defaults = self.model_dump())
|
dicts.apply_defaults(copy.deepcopy(values), defaults = self.model_dump())
|
||||||
)
|
)
|
||||||
|
|
||||||
@validator("ad_files", each_item = True)
|
|
||||||
@classmethod
|
|
||||||
def _non_empty_glob_pattern(cls, v:str) -> str:
|
|
||||||
if not v.strip():
|
|
||||||
raise ValueError("ad_files entries must be non-empty glob patterns")
|
|
||||||
return v
|
|
||||||
|
|||||||
Reference in New Issue
Block a user