diff --git a/oembedpy/adapters/sphinx.py b/oembedpy/adapters/sphinx.py index 9fe8538..60fcd6e 100644 --- a/oembedpy/adapters/sphinx.py +++ b/oembedpy/adapters/sphinx.py @@ -92,6 +92,22 @@ def resolve_any_xref( # because this does not have roles and directives that are refered by outside. return [] + def merge_domaindata(self, docnames: list[str], otherdata) -> None: + """Merge domain data from parallel builds. + + This method merges cached oEmbed content from parallel build environments. + The merge strategy prioritizes entries with longer expiration times to ensure + the most up-to-date and longest-lived cached content is retained. + """ + other_caches = otherdata.get("caches", {}) + for cache_key, content in other_caches.items(): + if cache_key not in self.caches: + self.caches[cache_key] = content + continue + exist = self.caches[cache_key] + if content._expired > exist._expired: + self.caches[cache_key] = content + class oembed(nodes.General, nodes.Element): # noqa: D101,E501 pass @@ -138,6 +154,6 @@ def setup(app: Sphinx): # noqa: D103 app.add_domain(OembedDomain) return { "version": __version__, - "parallel_read_safe": False, + "parallel_read_safe": True, "parallel_write_safe": True, } diff --git a/tests/roots/test-parallel/conf.py b/tests/roots/test-parallel/conf.py new file mode 100644 index 0000000..fcc6a9a --- /dev/null +++ b/tests/roots/test-parallel/conf.py @@ -0,0 +1,10 @@ +"""Configuration is cases for default behavior.""" + +extensions = [ + "oembedpy.adapters.sphinx", +] + +# To skip toctree +rst_prolog = """ +:orphan: +""" diff --git a/tests/roots/test-parallel/index.rst b/tests/roots/test-parallel/index.rst new file mode 100644 index 0000000..5aafd10 --- /dev/null +++ b/tests/roots/test-parallel/index.rst @@ -0,0 +1,8 @@ +============= +Test document +============= + +.. toctree:: + + sub1 + sub2 diff --git a/tests/roots/test-parallel/sub1.rst b/tests/roots/test-parallel/sub1.rst new file mode 100644 index 0000000..dcc6c16 --- /dev/null +++ b/tests/roots/test-parallel/sub1.rst @@ -0,0 +1,5 @@ +============== +Sub document 1 +============== + +.. oembed:: https://mastodon.cloud/@attakei/109368512525772407 diff --git a/tests/roots/test-parallel/sub2.rst b/tests/roots/test-parallel/sub2.rst new file mode 100644 index 0000000..306e481 --- /dev/null +++ b/tests/roots/test-parallel/sub2.rst @@ -0,0 +1,7 @@ +============== +Sub document 2 +============== + +.. oembed:: https://www.youtube.com/watch?v=Oyh8nuaLASA + :maxwidth: 1200 + :maxheight: 1200 diff --git a/tests/roots/test-parallel/sub3.rst b/tests/roots/test-parallel/sub3.rst new file mode 100644 index 0000000..7c3679c --- /dev/null +++ b/tests/roots/test-parallel/sub3.rst @@ -0,0 +1,3 @@ +============== +Sub document 3 +============== diff --git a/tests/roots/test-parallel/sub4.rst b/tests/roots/test-parallel/sub4.rst new file mode 100644 index 0000000..5ca896c --- /dev/null +++ b/tests/roots/test-parallel/sub4.rst @@ -0,0 +1,3 @@ +============== +Sub document 4 +============== diff --git a/tests/roots/test-parallel/sub5.rst b/tests/roots/test-parallel/sub5.rst new file mode 100644 index 0000000..d8b835d --- /dev/null +++ b/tests/roots/test-parallel/sub5.rst @@ -0,0 +1,3 @@ +============== +Sub document 5 +============== diff --git a/tests/test_adapters/test_sphinx.py b/tests/test_adapters/test_sphinx.py index 71be6f9..fb9ede8 100644 --- a/tests/test_adapters/test_sphinx.py +++ b/tests/test_adapters/test_sphinx.py @@ -4,8 +4,12 @@ import pytest from bs4 import BeautifulSoup +from sphinx.environment import BuildEnvironment from sphinx.testing.util import SphinxTestApp +from oembedpy.adapters import sphinx as T +from oembedpy.types import Link + @pytest.fixture(scope="module") def rootdir(): @@ -55,3 +59,54 @@ def test_caches(app: SphinxTestApp): # noqa def test_use_caches(app: SphinxTestApp): # noqa app.build() assert len(app.env.get_domain("oembedpy").caches) == 1 # type: ignore[attr-defined] + + +@pytest.mark.webtest +@pytest.mark.sphinx("html", testroot="parallel", parallel=2) +def test_build_parallel(app: SphinxTestApp, status): # noqa + app.build() + + +class TestFor_OembedDomain__merge_domaindata: + CACHE_KEY = ("http://example.com", 1, 1) + + @pytest.mark.sphinx("html", testroot="default") + def test_difference_items(self, app: SphinxTestApp): + domain1 = T.OembedDomain(BuildEnvironment(app)) + domain1.caches[self.CACHE_KEY] = Link(type="link", version="1.0", _extra={}) + domain2 = T.OembedDomain(BuildEnvironment(app)) + domain2.caches[("http://example.com", 1, 2)] = Link( + type="link", version="1.0", _extra={} + ) + domain1.merge_domaindata([], domain2.data) + assert len(domain1.caches) == 2 + + @pytest.mark.sphinx("html", testroot="default") + def test_keep_main_domain(self, app: SphinxTestApp): + domain1 = T.OembedDomain(BuildEnvironment(app)) + domain1.caches[self.CACHE_KEY] = Link( + type="link", version="1.0", title="Hello", _extra={} + ) + domain2 = T.OembedDomain(BuildEnvironment(app)) + domain2.caches[self.CACHE_KEY] = Link( + type="link", version="1.0", title="World", _extra={} + ) + print(domain1.caches) + domain1.merge_domaindata([], domain2.data) + assert len(domain1.caches) == 1 + print(domain1.caches) + assert domain1.caches[self.CACHE_KEY].title == "Hello" + + @pytest.mark.sphinx("html", testroot="default") + def test_keep_overrides(self, app: SphinxTestApp): + domain1 = T.OembedDomain(BuildEnvironment(app)) + link1 = Link(type="link", version="1.0", title="Hello", _extra={}) + link1._expired = 3600 + domain1.caches[self.CACHE_KEY] = link1 + domain2 = T.OembedDomain(BuildEnvironment(app)) + link2 = Link(type="link", version="1.0", title="World", _extra={}) + link2._expired = 3601 + domain2.caches[self.CACHE_KEY] = link2 + domain1.merge_domaindata([], domain2.data) + assert len(domain1.caches) == 1 + assert domain1.caches[("http://example.com", 1, 1)].title == "World"