Coverage for documents/plugins/date_parsing/base.py: 62%
30 statements
« prev ^ index » next coverage.py v7.15.2, created at 2026-10-10 09:07 +0000
« prev ^ index » next coverage.py v7.15.2, created at 2026-10-10 09:07 +0000
1import datetime
2import logging
3from abc import ABC
4from abc import abstractmethod
5from collections.abc import Iterator
6from dataclasses import dataclass
7from types import TracebackType
8from typing import Self
10import dateparser
12logger = logging.getLogger(__name__)
15@dataclass(frozen=True, slots=True)
16class DateParserConfig:
17 """
18 Configuration for a DateParser instance.
20 This object is created by the factory and passed to the
21 parser's constructor, decoupling the parser from settings.
22 """
24 languages: list[str]
25 timezone_str: str
26 ignore_dates: set[datetime.date]
28 # A "now" timestamp for filtering future dates.
29 # Passed in by the factory.
30 reference_time: datetime.datetime
32 # Settings for the default RegexDateParser
33 # Other plugins should use or consider these, but it is not required
34 filename_date_order: str | None
35 content_date_order: str
38class DateParserPluginBase(ABC):
39 """
40 Abstract base class for date parsing strategies.
42 Instances are configured via a DateParserConfig object.
43 """
45 def __init__(self, config: DateParserConfig):
46 """
47 Initializes the parser with its configuration.
48 """
49 self.config = config
51 def __enter__(self) -> Self:
52 """
53 Enter the runtime context related to this object.
55 Subclasses can override this to acquire resources (connections, handles).
56 """
57 return self
59 def __exit__(
60 self,
61 exc_type: type[BaseException] | None,
62 exc_val: BaseException | None,
63 exc_tb: TracebackType | None,
64 ) -> None:
65 """
66 Exit the runtime context related to this object.
68 Subclasses can override this to release resources.
69 """
70 # Default implementation does nothing.
71 # Returning None implies exceptions are propagated.
73 def _parse_string(
74 self,
75 date_string: str,
76 date_order: str,
77 ) -> datetime.datetime | None:
78 """
79 Helper method to parse a single date string using dateparser.
81 Uses configuration from `self.config`.
82 """
83 try:
84 return dateparser.parse(
85 date_string,
86 settings={
87 "DATE_ORDER": date_order,
88 "PREFER_DAY_OF_MONTH": "first",
89 "RETURN_AS_TIMEZONE_AWARE": True,
90 "TIMEZONE": self.config.timezone_str,
91 },
92 locales=self.config.languages,
93 )
94 except Exception as e:
95 logger.exception(f"Error while parsing date string '{date_string}': {e}")
96 return None
98 def _filter_date(
99 self,
100 date: datetime.datetime | None,
101 ) -> datetime.datetime | None:
102 """
103 Helper method to validate a parsed datetime object.
105 Uses configuration from `self.config`.
106 """
107 if (
108 date is not None
109 and date.year > 1900
110 and date <= self.config.reference_time
111 and date.date() not in self.config.ignore_dates
112 ):
113 return date
114 return None
116 @abstractmethod
117 def parse(self, filename: str, content: str) -> Iterator[datetime.datetime]:
118 """
119 Parses a document's filename and content, yielding valid datetime objects.
120 """