diff --git a/.readthedocs.yaml b/.readthedocs.yaml index adf055dd1..1227636b8 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -21,6 +21,6 @@ sphinx: # Optionally declare the Python requirements required to build your docs python: install: + - requirements: docs/requirements.txt - method: setuptools path: . - - requirements: docs/requirements.txt diff --git a/docs/requirements.txt b/docs/requirements.txt index 401075189..febb6488b 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,3 +1,5 @@ sphinx_immaterial[json,clang-format,keys,cpp] sphinxcontrib-details-directive sphinx-jinja +pydantic +importlib_resources diff --git a/requirements.txt b/requirements.txt index d815bf5ee..a7fc9e53d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,3 +4,4 @@ pydantic typing-extensions appdirs requests +importlib-resources diff --git a/setup.py b/setup.py index 44a850d28..50d3fbafc 100644 --- a/setup.py +++ b/setup.py @@ -30,6 +30,12 @@ import setuptools.command.develop import setuptools.command.install import setuptools.command.sdist +from sphinx_immaterial.google_fonts import install_google_fonts + +from importlib_resources import files +from sphinx_immaterial import resources + +from sphinx_immaterial import DEFAULT_THEME_OPTIONS with open("requirements.txt", encoding="utf-8") as reqs: REQUIREMENTS = [reqs.readlines()] @@ -146,6 +152,11 @@ def run(self): target = {"min": "build", "dev": "build:dev"} try: + install_google_fonts( + files(resources), + files(resources), + DEFAULT_THEME_OPTIONS["font"].values(), + ) tgt = target[self.bundle_type] node_modules_path = os.path.join(root_dir, "node_modules") if self.skip_npm_reinstall and os.path.exists(node_modules_path): @@ -187,6 +198,8 @@ def run(self): "*.html", "custom_admonitions.css", "theme.conf", + "resources/*.response", + "resources/*/*.response", ], "sphinx_immaterial.apidoc.cpp.cppreference_data": ["*.xml"], }, diff --git a/sphinx_immaterial/external_resource_cache.py b/sphinx_immaterial/external_resource_cache.py index 0bbce2bf3..b5edbf2ea 100644 --- a/sphinx_immaterial/external_resource_cache.py +++ b/sphinx_immaterial/external_resource_cache.py @@ -3,6 +3,7 @@ import os import tempfile from typing import Dict, Optional +from importlib_resources import files import appdirs import requests @@ -10,6 +11,8 @@ import sphinx.config import sphinx.util.logging +from sphinx_immaterial import resources + logger = sphinx.util.logging.getLogger(__name__) @@ -22,13 +25,23 @@ def get_url( req_json_encoded = json.dumps(req_json).encode("utf-8") req_key = hashlib.sha256(req_json_encoded).hexdigest() - resp_path = os.path.join(cache_dir, f"{req_key}.response") + # First try the in-module resources + mod_res_path = files(resources) / f"{req_key}.response" try: - with open(resp_path, "rb") as f: + with open(str(mod_res_path), "rb") as f: return f.read() except FileNotFoundError: pass + # Secondly, look at the cache + resp_path = os.path.join(cache_dir, f"{req_key}.response") + if cache_dir: + try: + with open(resp_path, "rb") as f: + return f.read() + except FileNotFoundError: + pass + logger.info("Fetching: %s with %r", url, headers) r = requests.get( # pylint: disable=missing-timeout url, headers=headers, stream=True @@ -37,6 +50,9 @@ def get_url( response_content = r.content + if not cache_dir: + return response_content + # Write request. req_path = os.path.join(cache_dir, f"{req_key}.request") os.makedirs(cache_dir, exist_ok=True) diff --git a/sphinx_immaterial/google_fonts.py b/sphinx_immaterial/google_fonts.py index de2261a5d..92731d326 100644 --- a/sphinx_immaterial/google_fonts.py +++ b/sphinx_immaterial/google_fonts.py @@ -60,11 +60,25 @@ def _adjust_css_urls(css_content: bytes, renamed_fonts: Dict[str, str]) -> str: _TTF_FONT_PATHS_KEY = "sphinx_immaterial_ttf_font_paths" -def add_google_fonts(app: sphinx.application.Sphinx, fonts: List[str]): - cache_dir = os.path.join(get_cache_dir(app), "google_fonts") - static_dir = os.path.join(app.outdir, "_static") - # _static path - font_dir = os.path.join(static_dir, "fonts") +def install_google_fonts(cache_dir: str, font_dir: str, fonts: List[str]): + """ + Saves google fonts to given directory. + + Firstly, it tries to load fonts from cache directory. + If it fails, it downloads fonts from remote locations. + + The font files are saved to font_dir. + + Parameters + ---------- + cache_dir : str + Directory with cached downloaded files. + If cache_dir is empty, skip checking cache + font_dir : str + Target directory where fonts are saved + fonts : List[str] + List of fonts to save + """ os.makedirs(font_dir, exist_ok=True) with concurrent.futures.ThreadPoolExecutor(max_workers=32) as executor: @@ -166,7 +180,16 @@ async def do_fetch(): css_content = dict(zip(css_future_keys, await asyncio.gather(*css_futures))) return css_content - css_content = asyncio.run(do_fetch()) + return asyncio.run(do_fetch()) + + +def add_google_fonts(app: sphinx.application.Sphinx, fonts: List[str]): + cache_dir = os.path.join(get_cache_dir(app), "google_fonts") + static_dir = os.path.join(app.outdir, "_static") + # _static path + font_dir = os.path.join(static_dir, "fonts") + + css_content = install_google_fonts(cache_dir, font_dir, fonts) # Write fonts css file ttf_font_paths = {} diff --git a/sphinx_immaterial/resources/__init__.py b/sphinx_immaterial/resources/__init__.py new file mode 100644 index 000000000..1b5292084 --- /dev/null +++ b/sphinx_immaterial/resources/__init__.py @@ -0,0 +1,3 @@ +""" +Module holding additional resources, i.e. built-in fonts +"""