2424from enum import Enum
2525from typing import Generic , TypeVar
2626
27- from pydantic import HttpUrl , NonNegativeInt , PositiveInt
27+ from pydantic import HttpUrl , NonNegativeInt , PositiveInt , field_validator
2828from pydantic_xml import attr , computed_attr , element , wrapped
2929
3030from perdoo .metadata ._base import PascalModel
3131from perdoo .settings import Naming
3232
33- try :
34- from typing import Self # Python >= 3.11
35- except ImportError :
36- from typing_extensions import Self # Python < 3.11
37-
3833LOGGER = logging .getLogger (__name__ )
3934T = TypeVar ("T" )
4035
@@ -49,7 +44,7 @@ class AgeRating(Enum):
4944 ADULT = "Adult"
5045
5146 @staticmethod
52- def load (value : str ) -> Self :
47+ def load (value : str ) -> "AgeRating" :
5348 for entry in AgeRating :
5449 if entry .value .replace (" " , "" ).casefold () == value .replace (" " , "" ).casefold ():
5550 return entry
@@ -142,7 +137,7 @@ class Role(Enum):
142137 OTHER = "Other"
143138
144139 @staticmethod
145- def load (value : str ) -> Self :
140+ def load (value : str ) -> "Role" :
146141 for entry in Role :
147142 if entry .value .replace (" " , "" ).casefold () == value .replace (" " , "" ).casefold ():
148143 return entry
@@ -191,7 +186,7 @@ class InformationSource(Enum):
191186 LEAGUE_OF_COMIC_GEEKS = "League of Comic Geeks"
192187
193188 @staticmethod
194- def load (value : str ) -> Self :
189+ def load (value : str ) -> "InformationSource" :
195190 for entry in InformationSource :
196191 if entry .value .replace (" " , "" ).casefold () == value .replace (" " , "" ).casefold ():
197192 return entry
@@ -366,7 +361,7 @@ class MetronInfo(PascalModel):
366361 universes : list [Universe ] = wrapped (
367362 path = "Universes" , entity = element (tag = "Universe" , default_factory = list )
368363 )
369- urls : list [Url ] = wrapped (path = "URLS " , entity = element (tag = "URLs " , default_factory = list ))
364+ urls : list [Url ] = wrapped (path = "URLs " , entity = element (tag = "URL " , default_factory = list ))
370365
371366 @computed_attr (ns = "xsi" , name = "noNamespaceSchemaLocation" )
372367 def schema_location (self ) -> str :
@@ -389,6 +384,13 @@ def get_filename(self, settings: Naming) -> str:
389384 seperator = settings .seperator ,
390385 )
391386
387+ @field_validator ("last_modified" , mode = "before" )
388+ def ensure_timezone (cls , v : str | datetime | None ) -> str | datetime | None :
389+ if isinstance (v , datetime ) and v .tzinfo is None :
390+ timezone = datetime .now ().astimezone ().tzinfo
391+ return v .replace (tzinfo = timezone )
392+ return v
393+
392394
393395PATTERN_MAP : dict [str , Callable [[MetronInfo ], str | int | None ]] = {
394396 "cover-date" : lambda x : x .cover_date ,
0 commit comments