From 2212715435a1b7692ec804959f1c419debbbeb17 Mon Sep 17 00:00:00 2001 From: Dalton Smith Date: Fri, 29 Jan 2021 21:49:01 -0500 Subject: [PATCH] Implement RTD Support (#30) --- README.md | 2 ++ docs/source/conf.py | 15 ++++++----- output.txt | Bin 0 -> 62412 bytes sphinxext/opengraph/__init__.py | 33 ++++++++++++++++++++--- tests/conftest.py | 9 +++++++ tests/roots/test-rtd-default/conf.py | 6 +++++ tests/roots/test-rtd-default/index.rst | 8 ++++++ tests/roots/test-rtd-invalid/conf.py | 6 +++++ tests/roots/test-rtd-invalid/index.rst | 8 ++++++ tests/test_options.py | 36 +++++++++++++++++++++++++ 10 files changed, 112 insertions(+), 11 deletions(-) create mode 100644 output.txt create mode 100644 tests/roots/test-rtd-default/conf.py create mode 100644 tests/roots/test-rtd-default/index.rst create mode 100644 tests/roots/test-rtd-invalid/conf.py create mode 100644 tests/roots/test-rtd-invalid/index.rst diff --git a/README.md b/README.md index 679ed66..1ab7535 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,8 @@ extensions = [ ## Options These values are placed in the conf.py of your sphinx project. +Users hosting documentation on Read The Docs *do not* need to set any of the following unless custom configuration is wanted. The extension will automatically retrieve your site url. + * `ogp_site_url` * This config option is very important, set it to the URL the site is being hosted on. * `ogp_description_length` diff --git a/docs/source/conf.py b/docs/source/conf.py index 46f901f..ee638f3 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -12,17 +12,18 @@ # import os import sys -sys.path.insert(0, os.path.abspath('../..')) + +sys.path.insert(0, os.path.abspath("../..")) # -- Project information ----------------------------------------------------- -project = 'sphinxext-opengraph' -copyright = '2020, FIRST' -author = 'WPILib' +project = "sphinxext-opengraph" +copyright = "2020, FIRST" +author = "WPILib" # The full version, including alpha/beta/rc tags -release = '1.0' +release = "1.0" # -- General configuration --------------------------------------------------- @@ -36,7 +37,7 @@ ] # Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] +templates_path = ["_templates"] # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. @@ -49,4 +50,4 @@ # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # -html_theme = 'furo' +html_theme = "furo" diff --git a/output.txt b/output.txt new file mode 100644 index 0000000000000000000000000000000000000000..9fe0e6e315b5a5a759750fd2e39b4bc28fdaf8cc GIT binary patch literal 62412 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 G_4j}NMdg|R literal 0 HcmV?d00001 diff --git a/sphinxext/opengraph/__init__.py b/sphinxext/opengraph/__init__.py index 03c57a8..74cb70c 100644 --- a/sphinxext/opengraph/__init__.py +++ b/sphinxext/opengraph/__init__.py @@ -1,13 +1,17 @@ from typing import Any, Dict -from urllib.parse import urljoin -from pathlib import Path +from urllib.parse import urljoin, urlparse, urlunparse +from pathlib import Path, PosixPath import docutils.nodes as nodes from sphinx.application import Sphinx +from sphinx.util import logger from .descriptionparser import get_description from .titleparser import get_title +import os + + DEFAULT_DESCRIPTION_LENGTH = 200 # A selection from https://www.iana.org/assignments/media-types/media-types.xhtml#image @@ -30,7 +34,10 @@ def make_tag(property: str, content: str) -> str: def get_tags( - context: Dict[str, Any], doctree: nodes.document, config: Dict[str, Any] + app: Sphinx, + context: Dict[str, Any], + doctree: nodes.document, + config: Dict[str, Any], ) -> str: # Set length of description @@ -53,6 +60,24 @@ def get_tags( # type tag tags += make_tag("og:type", config["ogp_type"]) + if os.getenv("READTHEDOCS") and config["ogp_site_url"] is None: + # readthedocs uses html_baseurl for sphinx > 1.8 + parse_result = urlparse(config["html_baseurl"]) + + if config["html_baseurl"] is None: + raise EnvironmentError("ReadTheDocs did not provide a valid canonical URL!") + + # Grab root url from canonical url + config["ogp_site_url"] = urlunparse( + ( + parse_result.scheme, + parse_result.netloc, + parse_result.path, + "", + "", + "", + ) + ) # url tag # Get the URL of the specific page @@ -109,7 +134,7 @@ def html_page_context( doctree: nodes.document, ) -> None: if doctree: - context["metatags"] += get_tags(context, doctree, app.config) + context["metatags"] += get_tags(app, context, doctree, app.config) def setup(app: Sphinx) -> Dict[str, Any]: diff --git a/tests/conftest.py b/tests/conftest.py index e0d15b3..913e5bc 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -2,6 +2,9 @@ from bs4 import BeautifulSoup from sphinx.testing.path import path +from sphinx.application import Sphinx + + pytest_plugins = "sphinx.testing.fixtures" @@ -21,6 +24,12 @@ def _meta_tags(content): return BeautifulSoup(c, "html.parser").find_all("meta") +def _og_meta_tags(content): + return [ + tag for tag in _meta_tags(content) if tag.get("property", "").startswith("og:") + ] + + @pytest.fixture() def meta_tags(content): return _meta_tags(content) diff --git a/tests/roots/test-rtd-default/conf.py b/tests/roots/test-rtd-default/conf.py new file mode 100644 index 0000000..7f99bb3 --- /dev/null +++ b/tests/roots/test-rtd-default/conf.py @@ -0,0 +1,6 @@ +extensions = ["sphinxext.opengraph"] + +master_doc = "index" +exclude_patterns = ["_build"] + +html_theme = "basic" diff --git a/tests/roots/test-rtd-default/index.rst b/tests/roots/test-rtd-default/index.rst new file mode 100644 index 0000000..634fe79 --- /dev/null +++ b/tests/roots/test-rtd-default/index.rst @@ -0,0 +1,8 @@ +* Item 1 +* Item 2 + + * Nested Item 1 + * Nested Item 2 + +* Item 3 +* Item 4 \ No newline at end of file diff --git a/tests/roots/test-rtd-invalid/conf.py b/tests/roots/test-rtd-invalid/conf.py new file mode 100644 index 0000000..7f99bb3 --- /dev/null +++ b/tests/roots/test-rtd-invalid/conf.py @@ -0,0 +1,6 @@ +extensions = ["sphinxext.opengraph"] + +master_doc = "index" +exclude_patterns = ["_build"] + +html_theme = "basic" diff --git a/tests/roots/test-rtd-invalid/index.rst b/tests/roots/test-rtd-invalid/index.rst new file mode 100644 index 0000000..634fe79 --- /dev/null +++ b/tests/roots/test-rtd-invalid/index.rst @@ -0,0 +1,8 @@ +* Item 1 +* Item 2 + + * Nested Item 1 + * Nested Item 2 + +* Item 3 +* Item 4 \ No newline at end of file diff --git a/tests/test_options.py b/tests/test_options.py index 81b0995..f6439ae 100644 --- a/tests/test_options.py +++ b/tests/test_options.py @@ -1,4 +1,7 @@ import pytest +from sphinx.application import Sphinx +import conftest +import os def get_tag(tags, tag_type): @@ -122,3 +125,36 @@ def test_skip_raw(og_meta_tags): description == "This text should be included. This text should also be included." ) + + +# use same as simple, as configuration is identical to overriden +@pytest.mark.sphinx("html", testroot="simple") +def test_rtd_override(app: Sphinx, monkeypatch): + monkeypatch.setenv("READTHEDOCS", "True") + app.config.html_baseurl = "https://failure.com" + + app.build() + tags = conftest._og_meta_tags(app) + + assert get_tag_content(tags, "url") == "http://example.org/index.html" + + +@pytest.mark.sphinx("html", testroot="rtd-default") +def test_rtd_valid(app: Sphinx, monkeypatch): + monkeypatch.setenv("READTHEDOCS", "True") + app.config.html_baseurl = "https://failure.com" + + app.build() + tags = conftest._og_meta_tags(app) + + assert get_tag_content(tags, "url") == "https://failure.com/index.html" + + +# use rtd-default, as we are not changing configuration, but RTD variables +@pytest.mark.sphinx("html", testroot="rtd-invalid") +def test_rtd_invalid(app: Sphinx, monkeypatch): + monkeypatch.setenv("READTHEDOCS", "True") + app.config.html_baseurl = None + + with pytest.raises(Exception): + app.build()