From 327a4f40fc2a1742e0643bcdd348609e5663171b Mon Sep 17 00:00:00 2001 From: lemon24 Date: Sat, 15 Jan 2022 22:40:24 +0200 Subject: [PATCH] Add new generic tag methods. For #266. --- src/reader/core.py | 107 +++++++++++++++++++++++++++++++++--------- src/reader/types.py | 1 + tests/test_storage.py | 4 -- 3 files changed, 86 insertions(+), 26 deletions(-) diff --git a/src/reader/core.py b/src/reader/core.py index f3b01b4a..47cd3e40 100644 --- a/src/reader/core.py +++ b/src/reader/core.py @@ -1556,10 +1556,7 @@ def get_feed_metadata( :meth:`get_feed_metadata_item` alias was removed. """ - - # get_feed_metadata(feed, *, key=None) -> (key, value), ... - feed_url = _feed_argument(feed) - return self._storage.get_tags((feed_url,), key) + return self.get_tags(feed, key) @overload def get_feed_metadata_item( @@ -1598,11 +1595,10 @@ def get_feed_metadata_item( Renamed from :meth:`get_feed_metadata`. """ - return zero_or_one( - (v for _, v in self.get_feed_metadata(feed, key=key)), - lambda: FeedMetadataNotFoundError(_feed_argument(feed), key), - default, - ) + if isinstance(default, MissingType): + return self.get_tag(feed, key) + else: + return self.get_tag(feed, key, default) def set_feed_metadata_item( self, feed: FeedInput, key: str, value: JSONType @@ -1623,8 +1619,7 @@ def set_feed_metadata_item( Renamed from :meth:`set_feed_metadata`. """ - feed_url = _feed_argument(feed) - self._storage.set_tag((feed_url,), key, value) + self.set_tag(feed, key, value) def delete_feed_metadata_item(self, feed: FeedInput, key: str) -> None: """Delete metadata for a feed. @@ -1641,8 +1636,7 @@ def delete_feed_metadata_item(self, feed: FeedInput, key: str) -> None: Renamed from :meth:`delete_feed_metadata`. """ - feed_url = _feed_argument(feed) - self._storage.delete_tag((feed_url,), key) + self.delete_tag(feed, key) def enable_search(self) -> None: """Enable full-text search. @@ -1892,8 +1886,7 @@ def add_feed_tag(self, feed: FeedInput, tag: str) -> None: .. versionadded:: 1.7 """ - feed_url = _feed_argument(feed) - self._storage.set_tag((feed_url,), tag) + self.set_tag(feed, tag) def remove_feed_tag(self, feed: FeedInput, tag: str) -> None: """Remove a tag from a feed. @@ -1910,11 +1903,7 @@ def remove_feed_tag(self, feed: FeedInput, tag: str) -> None: .. versionadded:: 1.7 """ - feed_url = _feed_argument(feed) - try: - self.delete_feed_metadata_item(feed_url, tag) - except FeedMetadataNotFoundError: - pass + self.delete_tag(feed, tag, True) def get_feed_tags(self, feed: Optional[FeedInput] = None) -> Iterable[str]: """Get all or some of the feed tags. @@ -1931,8 +1920,82 @@ def get_feed_tags(self, feed: Optional[FeedInput] = None) -> Iterable[str]: .. versionadded:: 1.7 """ - feed_url = _feed_argument(feed) if feed is not None else None - return (k for k, _ in self._storage.get_tags((feed_url,))) + return (k for k, _ in self.get_tags(feed)) + + def get_tags( + self, + resource: Optional[FeedInput], + key: Optional[str] = None, + ) -> Iterable[Tuple[str, JSONType]]: + # FIXME: docstring (copy from get_feed_metadata) + feed_url = _feed_argument(resource) if resource is not None else None + return self._storage.get_tags((feed_url,), key) + + def get_tag_keys( + self, + resource: Optional[FeedInput] = None, + ) -> Iterable[str]: # pragma: no cover + # FIXME: cover + # FIXME: docstring (copy from get_feed_tags) + # TODO: (later) efficient implementation + return (k for k, _ in self.get_tags(resource)) + + @overload + def get_tag(self, resource: FeedInput, key: str) -> JSONType: # pragma: no cover + ... + + @overload + def get_tag( + self, resource: FeedInput, key: str, default: _T + ) -> Union[JSONType, _T]: # pragma: no cover + ... + + def get_tag( + self, resource: FeedInput, key: str, default: Union[MissingType, _T] = MISSING + ) -> Union[JSONType, _T]: + # FIXME: docstring (copy from get_feed_metadata_item) + # FIXME: raise TagNotFoundError + return zero_or_one( + (v for _, v in self.get_tags(resource, key=key)), + lambda: FeedMetadataNotFoundError(_feed_argument(resource), key), + default, + ) + + @overload + def set_tag(self, resource: FeedInput, key: str) -> None: # pragma: no cover + ... + + @overload + def set_tag( + self, resource: FeedInput, key: str, value: JSONType + ) -> None: # pragma: no cover + ... + + def set_tag( + self, + resource: FeedInput, + key: str, + value: Union[JSONType, MissingType] = MISSING, + ) -> None: + # FIXME: docstring (copy from set_feed_metadata_item) + # TODO: this would not need to be overloaded if JSONType could be None + feed_url = _feed_argument(resource) + if not isinstance(value, MissingType): + self._storage.set_tag((feed_url,), key, value) + else: + self._storage.set_tag((feed_url,), key) + + def delete_tag( + self, resource: FeedInput, key: str, missing_ok: bool = False + ) -> None: + # FIXME: docstring (copy from delete_feed_metadata_item/remove_feed_tag) + # FIXME: raise TagNotFoundError + feed_url = _feed_argument(resource) + try: + self._storage.delete_tag((feed_url,), key) + except FeedMetadataNotFoundError: + if not missing_ok: + raise def make_reader_reserved_name(self, key: str) -> str: """Create a *reader*-reserved tag or metadata name. diff --git a/src/reader/types.py b/src/reader/types.py index 00653bb7..69d11ee7 100644 --- a/src/reader/types.py +++ b/src/reader/types.py @@ -608,6 +608,7 @@ class EntryUpdateStatus(enum.Enum): # https://github.com/python/typing/issues/182 +# TODO: allow JSONType to be str, int, ... JSONValue = Union[str, int, float, bool, None, Dict[str, Any], List[Any]] JSONType = Union[Dict[str, JSONValue], List[JSONValue]] diff --git a/tests/test_storage.py b/tests/test_storage.py index 58de44cb..962fcfaa 100644 --- a/tests/test_storage.py +++ b/tests/test_storage.py @@ -254,10 +254,6 @@ def get_entries_chunk_size_1(storage, _, __): list(storage.get_entries_page(chunk_size=1, now=datetime(2010, 1, 1))) -def iter_metadata(storage, feed, __): - list(storage.iter_metadata((feed.url,))) - - def get_tags(storage, feed, __): list(storage.get_tags((feed.url,)))