diff --git a/src/reader/_types.py b/src/reader/_types.py index d21f9f18..5ffb05d9 100644 --- a/src/reader/_types.py +++ b/src/reader/_types.py @@ -6,7 +6,6 @@ from collections.abc import Mapping from collections.abc import Sequence from dataclasses import dataclass -from dataclasses import field from datetime import datetime from datetime import timezone from functools import cached_property @@ -32,7 +31,6 @@ from .types import Entry from .types import EntryAddedBy from .types import EntryInput -from .types import EntryUpdateStatus from .types import ExceptionInfo from .types import Feed from .types import FeedInput @@ -569,9 +567,6 @@ def fix_datetime_tzinfo( UpdateHook = Callable[..., None] -AfterEntryUpdateHook = Callable[[_T, EntryData, EntryUpdateStatus], None] -FeedUpdateHook = Callable[[_T, str], None] -FeedsUpdateHook = Callable[[_T], None] UpdateHookType = Literal[ 'before_feeds_update', 'before_feed_update', @@ -581,19 +576,18 @@ def fix_datetime_tzinfo( ] -@dataclass(frozen=True) -class UpdateHooks(Generic[_T]): - target: _T - before_feeds_update: list[FeedsUpdateHook[_T]] = field(default_factory=list) - before_feed_update: list[FeedUpdateHook[_T]] = field(default_factory=list) - after_entry_update: list[AfterEntryUpdateHook[_T]] = field(default_factory=list) - after_feed_update: list[FeedUpdateHook[_T]] = field(default_factory=list) - after_feeds_update: list[FeedsUpdateHook[_T]] = field(default_factory=list) +class UpdateHooks(dict[UpdateHookType, list[UpdateHook]], Generic[_T]): + def __init__(self, target: _T): + super().__init__() + self.target = target + + def __missing__(self, key: UpdateHookType) -> list[UpdateHook]: + return self.setdefault(key, []) def run( self, when: UpdateHookType, resource_id: tuple[str, ...] | None, *args: Any ) -> None: - for hook in getattr(self, when): + for hook in self[when]: try: hook(self.target, *args) except Exception as e: @@ -603,12 +597,12 @@ def group(self, message: str) -> _UpdateHookErrorGrouper: return _UpdateHookErrorGrouper(self, message) -@dataclass(frozen=True) class _UpdateHookErrorGrouper: - hooks: UpdateHooks[Any] - message: str - exceptions: list[UpdateHookError] = field(default_factory=list) - seen_dedupe_keys: set[Any] = field(default_factory=set) + def __init__(self, hooks: UpdateHooks[Any], message: str): + self.hooks = hooks + self.message = message + self.exceptions: list[UpdateHookError] = [] + self.seen_dedupe_keys: set[Any] = set() def run( self, @@ -617,7 +611,7 @@ def run( *args: Any, limit: int = 0, ) -> None: - for hook in getattr(self.hooks, when): + for hook in self.hooks[when]: try: hook(self.hooks.target, *args) except Exception as e: diff --git a/src/reader/core.py b/src/reader/core.py index c2756166..90e021b7 100644 --- a/src/reader/core.py +++ b/src/reader/core.py @@ -4,6 +4,7 @@ import logging import numbers import warnings +from collections.abc import Callable from collections.abc import Iterable from collections.abc import Mapping from collections.abc import MutableSequence @@ -22,14 +23,12 @@ from ._requests_utils import TimeoutType from ._search import Search from ._storage import Storage -from ._types import AfterEntryUpdateHook from ._types import DEFAULT_RESERVED_NAME_SCHEME from ._types import entry_data_from_obj +from ._types import EntryData from ._types import EntryFilterOptions from ._types import EntryUpdateIntent from ._types import FeedFilterOptions -from ._types import FeedsUpdateHook -from ._types import FeedUpdateHook from ._types import fix_datetime_tzinfo from ._types import NameScheme from ._types import UpdateHooks @@ -85,6 +84,10 @@ # mypy doesn't seem to support Self yet. _TReader = TypeVar('_TReader', bound='Reader') +AfterEntryUpdateHook = Callable[['Reader', EntryData, EntryUpdateStatus], None] +FeedUpdateHook = Callable[['Reader', str], None] +FeedsUpdateHook = Callable[['Reader'], None] + def make_reader( url: str, @@ -2199,7 +2202,7 @@ def reserved_name_scheme(self, value: Mapping[str, str]) -> None: raise AttributeError(f"invalid reserved name scheme: {value}") from e @property - def before_feeds_update_hooks(self) -> MutableSequence[FeedsUpdateHook[Reader]]: + def before_feeds_update_hooks(self) -> MutableSequence[FeedsUpdateHook]: """List of functions called *once* before updating any feeds, at the beginning of :meth:`update_feeds` / :meth:`update_feeds_iter`, but not :meth:`update_feed`. @@ -2220,10 +2223,10 @@ def before_feeds_update_hooks(self) -> MutableSequence[FeedsUpdateHook[Reader]]: Wrap unexpected exceptions in :exc:`UpdateHookError`. """ - return self._update_hooks.before_feeds_update + return self._update_hooks['before_feeds_update'] @property - def before_feed_update_hooks(self) -> MutableSequence[FeedUpdateHook[Reader]]: + def before_feed_update_hooks(self) -> MutableSequence[FeedUpdateHook]: """List of functions called for each updated feed before the feed is updated. @@ -2244,10 +2247,10 @@ def before_feed_update_hooks(self) -> MutableSequence[FeedUpdateHook[Reader]]: Wrap unexpected exceptions in :exc:`UpdateHookError`. """ - return self._update_hooks.before_feed_update + return self._update_hooks['before_feed_update'] @property - def after_entry_update_hooks(self) -> MutableSequence[AfterEntryUpdateHook[Reader]]: + def after_entry_update_hooks(self) -> MutableSequence[AfterEntryUpdateHook]: """List of functions called for each updated entry after the feed is updated. @@ -2281,10 +2284,10 @@ def after_entry_update_hooks(self) -> MutableSequence[AfterEntryUpdateHook[Reade Try to run all hooks, don't stop after one fails. """ - return self._update_hooks.after_entry_update + return self._update_hooks['after_entry_update'] @property - def after_feed_update_hooks(self) -> MutableSequence[FeedUpdateHook[Reader]]: + def after_feed_update_hooks(self) -> MutableSequence[FeedUpdateHook]: """List of functions called for each updated feed after the feed is updated. @@ -2307,10 +2310,10 @@ def after_feed_update_hooks(self) -> MutableSequence[FeedUpdateHook[Reader]]: Try to run all hooks, don't stop after one fails. """ - return self._update_hooks.after_feed_update + return self._update_hooks['after_feed_update'] @property - def after_feeds_update_hooks(self) -> MutableSequence[FeedsUpdateHook[Reader]]: + def after_feeds_update_hooks(self) -> MutableSequence[FeedsUpdateHook]: """List of functions called *once* after updating all feeds, at the end of :meth:`update_feeds` / :meth:`update_feeds_iter`, but not :meth:`update_feed`. @@ -2333,4 +2336,4 @@ def after_feeds_update_hooks(self) -> MutableSequence[FeedsUpdateHook[Reader]]: Try to run all hooks, don't stop after one fails. """ - return self._update_hooks.after_feeds_update + return self._update_hooks['after_feeds_update']