Skip to content

Commit 0dec9b7

Browse files
committed
Merge branch 'main' into issue/12
2 parents 696ebf0 + f7592af commit 0dec9b7

6 files changed

Lines changed: 57 additions & 13 deletions

File tree

docs/conf.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@
1313
exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]
1414

1515
html_theme = "furo"
16-
html_static_path = ["_static"]
17-
html_extra_path = ["_extra"]
16+
html_static_path = []
17+
html_extra_path = []
1818

1919
# -- Options for Linkcheck output
2020
linkcheck_ignore = [

oembedpy/adapters/sphinx.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,10 @@ def has_cache(self, key: Tuple[str, Union[int, None], Union[int, None]]) -> bool
7171
if key not in self.caches:
7272
return False
7373
content: Content = self.caches[key]
74+
if hasattr(
75+
content, "_expired"
76+
): # NOTE: For if pickled object does not have _expired.
77+
return now < content._expired
7478
if "cache_age" not in content._extra:
7579
return True
7680
return now < content._extra["cache_age"]

oembedpy/application.py

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,9 @@
44
import logging
55
import pickle
66
import time
7-
from typing import Dict, Optional
7+
from typing import Dict, Optional, Union
88

99
import httpx
10-
1110
from platformdirs import PlatformDirs
1211

1312
from oembedpy import consumer, discovery
@@ -21,7 +20,7 @@ class Oembed:
2120
"""Application of oEmbed."""
2221

2322
_registry: ProviderRegistry
24-
_cache: Dict[consumer.RequestParameters, CachedContent]
23+
_cache: Dict[consumer.RequestParameters, Union[Content, CachedContent]]
2524
_fallback_type: bool
2625

2726
def __init__(self, fallback_type: bool = False): # noqa: D107
@@ -52,11 +51,17 @@ def fetch(
5251
params.max_height = max_height
5352
#
5453
now = time.mktime(time.localtime())
55-
if params in self._cache and now <= self._cache[params].expired:
56-
return self._cache[params].content
54+
if params in self._cache:
55+
# For comptibility CachedContent
56+
val = self._cache[params]
57+
if isinstance(val, CachedContent):
58+
if now <= val.expired:
59+
return val.content
60+
elif now <= val._expired:
61+
return val
5762
content = consumer.fetch_content(api_url, params, self._fallback_type)
5863
if content.cache_age:
59-
self._cache[params] = CachedContent(now + int(content.cache_age), content)
64+
self._cache[params] = content
6065
return content
6166

6267

oembedpy/consumer.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
"""For consumer request."""
22

33
import logging
4+
import time
45
from dataclasses import dataclass
6+
from datetime import datetime
57
from typing import Dict, Optional
68

79
import httpx
@@ -48,6 +50,7 @@ def fetch_content(
4850
* OK: ``text/xml``
4951
* NG: ``text/plain`` (even if body is JSON string)
5052
"""
53+
now = time.mktime(time.localtime())
5154
resp = httpx.get(url, params=params.to_dict(), follow_redirects=True)
5255
resp.raise_for_status()
5356
content_type = resp.headers.get("content-type", "").split(";")[0] # Exclude chaset
@@ -75,4 +78,6 @@ def fetch_content(
7578
if not fallback_type:
7679
raise err
7780
content = types.HtmlOnly.from_dict(data)
81+
if content.cache_age:
82+
content._expired = now + int(content.cache_age)
7883
return content

oembedpy/types.py

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,12 @@ class _Optionals:
6060
thumbnail_height: Optional[int] = None
6161

6262

63+
class _Internals:
64+
"""Fields of internal parameters for any types."""
65+
66+
_expired: int = 0
67+
68+
6369
@dataclass
6470
class _Photo:
6571
"""Required fields for ``photo`` types."""
@@ -95,27 +101,27 @@ class _HtmlOnly(_BaseType):
95101

96102

97103
@dataclass
98-
class Photo(_Optionals, _Photo, _Required):
104+
class Photo(_Internals, _Optionals, _Photo, _Required):
99105
"""oEmbed content for photo object."""
100106

101107

102108
@dataclass
103-
class Video(_Optionals, _Video, _Required):
109+
class Video(_Internals, _Optionals, _Video, _Required):
104110
"""oEmbed content for vhoto object."""
105111

106112

107113
@dataclass
108-
class Link(_Optionals, _Required):
114+
class Link(_Internals, _Optionals, _Required):
109115
"""oEmbed content for generic object."""
110116

111117

112118
@dataclass
113-
class Rich(_Optionals, _Rich, _Required):
119+
class Rich(_Internals, _Optionals, _Rich, _Required):
114120
"""oEmbed content for rich HTML object."""
115121

116122

117123
@dataclass
118-
class HtmlOnly(_Optionals, _HtmlOnly):
124+
class HtmlOnly(_Internals, _Optionals, _HtmlOnly):
119125
"""Fallback type for invalid scheme."""
120126

121127

@@ -124,5 +130,14 @@ class HtmlOnly(_Optionals, _HtmlOnly):
124130

125131

126132
class CachedContent(NamedTuple):
133+
"""Content object with expired timestamp for cache.
134+
135+
.. deprecated:: 0.9.0
136+
137+
This is internal class for cache, so it keeps to avoid breaking cache data.
138+
I will remove for v1.
139+
Use :class:`Content` instead if you use in other projects.
140+
"""
141+
127142
expired: float
128143
content: Content

tests/test_consumer.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ def test_json_content(self, httpx_mock):
5050
)
5151
assert isinstance(content, types.Video)
5252
assert content.author_name == "attakei"
53+
assert content._expired == 0
5354

5455
def test_xml_content(self, httpx_mock):
5556
httpx_mock.add_response(
@@ -127,3 +128,17 @@ def test_invalid_xml(self, httpx_mock):
127128
format="xml", url="https://www.youtube.com/watch&v=Oyh8nuaLASA"
128129
),
129130
)
131+
132+
def test_json_has_cache_age(self, httpx_mock):
133+
resp_data = deepcopy(self.content_json)
134+
resp_data["cache_age"] = "3600"
135+
httpx_mock.add_response(json=resp_data)
136+
content = consumer.fetch_content(
137+
"https://www.youtube.com/oembed",
138+
consumer.RequestParameters(
139+
format="json", url="https://www.youtube.com/watch&v=Oyh8nuaLASA"
140+
),
141+
)
142+
assert isinstance(content, types.Video)
143+
assert content.author_name == "attakei"
144+
assert content._expired != 0

0 commit comments

Comments
 (0)