Skip to content

Instantly share code, notes, and snippets.

@lemon24
Last active October 26, 2021 12:28
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lemon24/047f71abe76c47661634459eada7b50a to your computer and use it in GitHub Desktop.
Save lemon24/047f71abe76c47661634459eada7b50a to your computer and use it in GitHub Desktop.
"""
API proposals for Reader.add_entry().
"""
### 1a: one-shot add entry
reader.add_entry(
feed_url='https://explainxkcd.com/rss.xml',
id='http://www.explainxkcd.com/2433',
updated=now,
content=[Content(html, type='text/html')],
)
### 1b: build entry to add, add_entry() options
# entry is dict[str, Any]
entry_data = {
feed_url='https://explainxkcd.com/rss.xml',
id='http://www.explainxkcd.com/2433',
}
if whatever:
entry_data['updated'] = now
entry_data['summary'] = html
reader.add_entry(**entry_data, update_if_exists=True)
### 1c: add existing entry
entry = reader.get_entry(...)
entry_data = vars(entry)
entry_data['id'] += 'suffix'
# otherwise we get "add_entry() got an unexpected keyword argument 'read'"
for attr in ('read', 'read_modified', 'important', 'important_modified', ...):
entry_data.pop(attr)
reader.add_entry(**entry_data)
### 2a: one-shot add entry
from reader import EntryData
reader.add_entry(
EntryData(
feed_url='https://explainxkcd.com/rss.xml',
id='http://www.explainxkcd.com/2433',
updated=now,
content=[Content(html, type='text/html')],
)
)
### 2b: build entry to add, add_entry() options
from reader import EntryData
# entry is EntryData (with type-annotated attributes),
# but any objects with those attributes works too
entry = EntryData(
feed_url='https://explainxkcd.com/rss.xml',
id='http://www.explainxkcd.com/2433',
)
if whatever:
entry.updated = now
content.summary = html
reader.add_entry(entry, update_if_exists=True)
### 2c: add existing entry
from reader import EntryData
entry = reader.get_entry(...)
entry.id += 'suffix'
# add_entry() just looks at the attributes it cares about
reader.add_entry(entry)
"""
An example of how to model a Union[EntryDataLikeProtocol, EntryDataTypedDict] argument.
"""
from dataclasses import dataclass
from types import SimpleNamespace
from typing import Optional
from typing import Union
from typing_extensions import Protocol
from typing_extensions import TypedDict
class EntryDataLike(Protocol):
"""An object that looks like EntryData."""
@property
def feed_url(self) -> str:
...
@property
def id(self) -> str:
...
@property
def title(self) -> Optional[str]:
# presumably, this can be missing, but I don't know how to model it
...
class EntryDataDictBase(TypedDict):
feed_url: str
id: str
class EntryDataDict(EntryDataDictBase, total=False):
"""A dict that looks like EntryData.
Notably, optional EntryData attributes can be missing entirely.
"""
title: Optional[str]
EntryDataInput = Union[EntryDataLike, EntryDataDict]
def add_entry(entry: EntryDataInput) -> None:
...
@dataclass
class ClassWithoutTitle:
feed_url: str
id: str
@dataclass
class ClassWithTitle(ClassWithoutTitle):
title: Optional[str] = None
# Error! missing protocol member: title
# in reality, this will work, we just can't type it
# (PEP 544 postpones optional protocol members)
add_entry(ClassWithoutTitle('id', 'feed'))
# OK
add_entry(ClassWithTitle('id', 'feed'))
# OK
add_entry({'id': 'id', 'feed_url': 'feed'})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment