Skip to content

Commit 788d377

Browse files
committed
feat(sphinx): Content cache in build environment of sphinx
1 parent 7b32184 commit 788d377

File tree

4 files changed

+64
-5
lines changed

4 files changed

+64
-5
lines changed

oembedpy/ext/sphinx.py

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,26 @@
11
"""Sphinx extension module."""
22

33
import logging
4+
from datetime import datetime
5+
from typing import Union, Tuple
46

57
try:
68
from docutils import nodes
79
from docutils.parsers.rst import Directive, directives
810
from sphinx.application import Sphinx
911
from sphinx.domains import Domain
1012
from sphinx.environment import BuildEnvironment
11-
12-
logger = logging.getLogger(__name__)
1313
except ModuleNotFoundError as err:
14-
logger = logging.getLogger(__name__)
15-
1614
msg = "To use it, install with Sphinx."
1715
logging.error(msg)
1816
raise err
1917

2018
from oembedpy import __version__
2119
from oembedpy.application import Oembed, Workspace
20+
from oembedpy.types import Content
21+
from sphinx.util.logging import getLogger
22+
23+
logger = getLogger(__name__)
2224

2325

2426
class OembedDomain(Domain):
@@ -34,12 +36,34 @@ def __init__(self, env: BuildEnvironment):
3436
)
3537
self._client.init()
3638

39+
@property
40+
def caches(self) -> dict[Tuple[str, Union[int, None], Union[int, None]], Content]:
41+
return self.data.setdefault("caches", {})
42+
3743
def process_doc(
3844
self, env: BuildEnvironment, docname: str, document: nodes.document
3945
):
4046
for node in document.findall(oembed):
4147
params = node["params"]
42-
node["content"] = self._client.fetch(**params)
48+
cache_key = (params["url"], params["max_width"], params["max_height"])
49+
logger.debug(f"Target content for {cache_key}")
50+
if self.has_cache(cache_key):
51+
logger.debug("Cache is found. Use this.")
52+
content = self.caches[cache_key]
53+
else:
54+
logger.debug("Cache is not exists. Fetching content from service.")
55+
content = self._client.fetch(**node["params"])
56+
self.caches[cache_key] = content
57+
node["content"] = content
58+
59+
def has_cache(self, key: Tuple[str, Union[int, None], Union[int, None]]) -> bool:
60+
now = int(datetime.now().timestamp())
61+
if key not in self.caches:
62+
return False
63+
content: Content = self.caches[key]
64+
if "cache_age" not in content._extra:
65+
return True
66+
return now < content._extra["cache_age"]
4367

4468

4569
class oembed(nodes.General, nodes.Element): # noqa: D101,E501
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
"""Configuration is cases for default behavior."""
2+
3+
extensions = [
4+
"oembedpy.ext.sphinx",
5+
]
6+
7+
# To skip toctree
8+
rst_prolog = """
9+
:orphan:
10+
"""
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
=============
2+
Test document
3+
=============
4+
5+
Discover from registry 1
6+
========================
7+
8+
.. oembed:: https://twitter.com/attakei
9+
10+
Discover from registry 2
11+
========================
12+
13+
.. oembed:: https://twitter.com/attakei

tests/test_ext_sphinx.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,15 @@ def test_build_with_fallback(app: SphinxTestApp): # noqa
3939
href="https://www.reddit.com/r/Python/comments/vdopqj/sphinxrevealjs_html_presentation_builder_for/",
4040
)
4141
assert link is not None
42+
43+
44+
@pytest.mark.sphinx("html", testroot="default")
45+
def test_caches(app: SphinxTestApp): # noqa
46+
app.build()
47+
assert len(app.env.get_domain("oembedpy.ext.sphinx").caches) == 3 # type: ignore[attr-defined]
48+
49+
50+
@pytest.mark.sphinx("html", testroot="for-sphinx-cached")
51+
def test_use_caches(app: SphinxTestApp): # noqa
52+
app.build()
53+
assert len(app.env.get_domain("oembedpy.ext.sphinx").caches) == 1 # type: ignore[attr-defined]

0 commit comments

Comments
 (0)