From 32db305899f28fad2d5ed10b7c2d63a9a7a9fa84 Mon Sep 17 00:00:00 2001 From: Aaron Carlisle Date: Wed, 27 Mar 2024 04:27:40 +0100 Subject: [PATCH] Docs: Python: Refactor the config file out of 'sphinx_doc_gen.py' This was done originally because we need access to some information that is avalable from `bpy` however, we only need a few pieces of information. Instead, I have refactored the code to a traditional `conf.py` file that uses template strings that get substituted when the file is copied to the build folder. The goal here is to make it simpler to adjust the configuration. This change also moves the config generation from `rna2sphinx` to the main function. Pull Request: https://projects.blender.org/blender/blender/pulls/119904 --- doc/python_api/conf.py | 116 +++++++++++++++++++++ doc/python_api/sphinx_doc_gen.py | 168 +++++++------------------------ 2 files changed, 153 insertions(+), 131 deletions(-) create mode 100644 doc/python_api/conf.py diff --git a/doc/python_api/conf.py b/doc/python_api/conf.py new file mode 100644 index 00000000000..f087d00cbf6 --- /dev/null +++ b/doc/python_api/conf.py @@ -0,0 +1,116 @@ +# SPDX-FileCopyrightText: 2024 Blender Authors +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from string import Template +import time + + +# These are substituted when this file is copied to the build directory. +BLENDER_VERSION_STRING = "${BLENDER_VERSION_STRING}" +BLENDER_VERSION_DOTS = "${BLENDER_VERSION_DOTS}" +BLENDER_REVISION = "${BLENDER_REVISION}" +BLENDER_REVISION_TIMESTAMP = "${BLENDER_REVISION_TIMESTAMP}" + +if BLENDER_REVISION != "Unknown": + # SHA1 Git hash + BLENDER_VERSION_HASH = BLENDER_REVISION + BLENDER_VERSION_HASH_HTML_LINK = "%s" % ( + BLENDER_VERSION_HASH, BLENDER_VERSION_HASH) + BLENDER_VERSION_DATE = time.strftime("%d/%m/%Y", time.localtime(BLENDER_REVISION_TIMESTAMP)) +else: + # Fallback: Should not be used + BLENDER_VERSION_HASH = "Hash Unknown" + BLENDER_VERSION_HASH_HTML_LINK = BLENDER_VERSION_HASH + BLENDER_VERSION_DATE = time.strftime("%Y-%m-%d") + +extensions = ['sphinx.ext.intersphinx'] +intersphinx_mapping = {'blender_manual': ("https://docs.blender.org/manual/en/dev/", None)} +project = "Blender %s Python API" % BLENDER_VERSION_STRING +root_doc = "index" +copyright = "Blender Authors" +version = BLENDER_VERSION_DOTS +release = BLENDER_VERSION_DOTS + +# Set this as the default is a super-set of Python3. +highlight_language = 'python3' +# No need to detect encoding. +highlight_options = {'default': {'encoding': 'utf-8'}} + +# Quiet file not in table-of-contents warnings. +exclude_patterns = [ + "include__bmesh.rst", +] + +html_title = "Blender Python API" + +# The fallback to a built-in theme when furo is not found. +html_theme = 'default' + +try: + import furo + html_theme = "furo" + del furo +except ModuleNotFoundError: + pass + +if html_theme == "furo": + html_theme_options = { + "light_css_variables": { + "color-brand-primary": "#265787", + "color-brand-content": "#265787", + }, + } + + html_sidebars = { + "**": [ + "sidebar/brand.html", + "sidebar/search.html", + "sidebar/scroll-start.html", + "sidebar/navigation.html", + "sidebar/scroll-end.html", + "sidebar/variant-selector.html", + ] + } + +# not helpful since the source is generated, adds to upload size. +html_copy_source = False +html_show_sphinx = False +html_baseurl = "https://docs.blender.org/api/current/" +html_use_opensearch = "https://docs.blender.org/api/current" +html_show_search_summary = True +html_split_index = True +html_static_path = ["static"] +templates_path = ["templates"] +html_context = {"commit": "%s - %s" % (BLENDER_VERSION_HASH_HTML_LINK, BLENDER_VERSION_DATE)} +html_extra_path = ["static"] +html_favicon = "static/favicon.ico" +html_logo = "static/blender_logo.svg" +# Disable default `last_updated` value, since this is the date of doc generation, not the one of the source commit. +html_last_updated_fmt = None +if html_theme == 'furo': + html_css_files = ["css/theme_overrides.css", "css/version_switch.css"] + html_js_files = ["js/version_switch.js"] + +# needed for latex, pdf gen +latex_elements = { + 'papersize': 'a4paper', +} + +latex_documents = [("contents", "contents.tex", "Blender Index", "Blender Foundation", "manual"), ] + +# Workaround for useless links leading to compile errors +# See https://github.com/sphinx-doc/sphinx/issues/3866 +from sphinx.domains.python import PythonDomain + + +class PatchedPythonDomain(PythonDomain): + def resolve_xref(self, env, fromdocname, builder, typ, target, node, contnode): + if 'refspecific' in node: + del node['refspecific'] + return super(PatchedPythonDomain, self).resolve_xref( + env, fromdocname, builder, typ, target, node, contnode) + + +def setup(app): + app.add_domain(PatchedPythonDomain, override=True) diff --git a/doc/python_api/sphinx_doc_gen.py b/doc/python_api/sphinx_doc_gen.py index bc0fbc15e41..39b80d5f43d 100644 --- a/doc/python_api/sphinx_doc_gen.py +++ b/doc/python_api/sphinx_doc_gen.py @@ -486,19 +486,6 @@ BLENDER_REVISION_TIMESTAMP = bpy.app.build_commit_timestamp BLENDER_VERSION_STRING = bpy.app.version_string BLENDER_VERSION_DOTS = "%d.%d" % (bpy.app.version[0], bpy.app.version[1]) -if BLENDER_REVISION != "Unknown": - # SHA1 Git hash - BLENDER_VERSION_HASH = BLENDER_REVISION - BLENDER_VERSION_HASH_HTML_LINK = "%s" % ( - BLENDER_VERSION_HASH, BLENDER_VERSION_HASH, - ) - BLENDER_VERSION_DATE = time.strftime("%d/%m/%Y", time.localtime(BLENDER_REVISION_TIMESTAMP)) -else: - # Fallback: Should not be used - BLENDER_VERSION_HASH = "Hash Unknown" - BLENDER_VERSION_HASH_HTML_LINK = BLENDER_VERSION_HASH - BLENDER_VERSION_DATE = time.strftime("%Y-%m-%d") - # Example: `2_83`. BLENDER_VERSION_PATH = "%d_%d" % (bpy.app.version[0], bpy.app.version[1]) @@ -1895,110 +1882,6 @@ def pyrna2sphinx(basepath): write_ops() -def write_sphinx_conf_py(basepath): - """ - Write sphinx's ``conf.py``. - """ - filepath = os.path.join(basepath, "conf.py") - file = open(filepath, "w", encoding="utf-8") - fw = file.write - - fw("import sys, os\n\n") - fw("extensions = ['sphinx.ext.intersphinx']\n\n") - fw("intersphinx_mapping = {'blender_manual': ('https://docs.blender.org/manual/en/dev/', None)}\n\n") - fw("project = 'Blender %s Python API'\n" % BLENDER_VERSION_STRING) - fw("root_doc = 'index'\n") - fw("copyright = 'Blender Authors'\n") - fw("version = '%s'\n" % BLENDER_VERSION_DOTS) - fw("release = '%s'\n" % BLENDER_VERSION_DOTS) - - # Set this as the default is a super-set of Python3. - fw("highlight_language = 'python3'\n") - # No need to detect encoding. - fw("highlight_options = {'default': {'encoding': 'utf-8'}}\n\n") - - # Quiet file not in table-of-contents warnings. - fw("exclude_patterns = [\n") - fw(" 'include__bmesh.rst',\n") - fw("]\n\n") - - fw("html_title = 'Blender Python API'\n") - - fw("html_theme = 'default'\n") - # The theme 'sphinx_rtd_theme' is no longer distributed with sphinx by default, only use when available. - fw(r""" -try: - import furo - html_theme = "furo" - del furo -except ModuleNotFoundError: - pass -if html_theme == "furo": - html_theme_options = { - "light_css_variables": { - "color-brand-primary": "#265787", - "color-brand-content": "#265787", - }, - } - - html_sidebars = { - "**": [ - "sidebar/brand.html", - "sidebar/search.html", - "sidebar/scroll-start.html", - "sidebar/navigation.html", - "sidebar/scroll-end.html", - "sidebar/variant-selector.html", - ] - } -""") - - # not helpful since the source is generated, adds to upload size. - fw("html_copy_source = False\n") - fw("html_show_sphinx = False\n") - fw("html_baseurl = 'https://docs.blender.org/api/current/'\n") - fw("html_use_opensearch = 'https://docs.blender.org/api/current'\n") - fw("html_show_search_summary = True\n") - fw("html_split_index = True\n") - fw("html_static_path = ['static']\n") - fw("templates_path = ['templates']\n") - fw("html_context = {'commit': '%s - %s'}\n" % (BLENDER_VERSION_HASH_HTML_LINK, BLENDER_VERSION_DATE)) - fw("html_extra_path = ['static']\n") - fw("html_favicon = 'static/favicon.ico'\n") - fw("html_logo = 'static/blender_logo.svg'\n") - # Disable default `last_updated` value, since this is the date of doc generation, not the one of the source commit. - fw("html_last_updated_fmt = None\n\n") - fw("if html_theme == 'furo':\n") - fw(" html_css_files = ['css/theme_overrides.css', 'css/version_switch.css']\n") - fw(" html_js_files = ['js/version_switch.js']\n") - - # needed for latex, pdf gen - fw("latex_elements = {\n") - fw(" 'papersize': 'a4paper',\n") - fw("}\n\n") - - fw("latex_documents = [ ('contents', 'contents.tex', 'Blender Index', 'Blender Foundation', 'manual'), ]\n") - - # Workaround for useless links leading to compile errors - # See https://github.com/sphinx-doc/sphinx/issues/3866 - fw(r""" -from sphinx.domains.python import PythonDomain - -class PatchedPythonDomain(PythonDomain): - def resolve_xref(self, env, fromdocname, builder, typ, target, node, contnode): - if 'refspecific' in node: - del node['refspecific'] - return super(PatchedPythonDomain, self).resolve_xref( - env, fromdocname, builder, typ, target, node, contnode) - -def setup(app): - app.add_domain(PatchedPythonDomain, override=True) -""") - # end workaround - - file.close() - - def write_rst_index(basepath): """ Write the RST file of the main page, needed for sphinx: ``index.html``. @@ -2411,7 +2294,7 @@ def copy_handwritten_extra(basepath): shutil.copy2(f_src, f_dst) -def copy_theme_assets(basepath): +def copy_sphinx_files(basepath): shutil.copytree( os.path.join(SCRIPT_DIR, "static"), os.path.join(basepath, "static"), @@ -2423,17 +2306,31 @@ def copy_theme_assets(basepath): copy_function=shutil.copy, ) + shutil.copy2(os.path.join(SCRIPT_DIR, "conf.py"), basepath, ) + + +def format_config(basepath): + """ + Updates conf.py with context infromation from Blender. + """ + from string import Template + + substitutions = { + 'BLENDER_VERSION_STRING': BLENDER_VERSION_STRING, + 'BLENDER_VERSION_DOTS': BLENDER_VERSION_DOTS, + 'BLENDER_REVISION_TIMESTAMP': BLENDER_REVISION_TIMESTAMP, + 'BLENDER_REVISION': BLENDER_REVISION, + } + + # Read the template string from the template file + with open(os.path.join(basepath, "conf.py"), 'r') as file: + template_file = file.read() + + with open(os.path.join(basepath, "conf.py"), 'w') as file: + file.write(Template(template_file).substitute(substitutions)) + def rna2sphinx(basepath): - - try: - os.mkdir(basepath) - except: - pass - - # sphinx setup - write_sphinx_conf_py(basepath) - # main page write_rst_index(basepath) @@ -2460,9 +2357,6 @@ def rna2sphinx(basepath): # copy source files referenced copy_handwritten_extra(basepath) - # copy extra files needed for theme - copy_theme_assets(basepath) - def align_sphinx_in_to_sphinx_in_tmp(dir_src, dir_dst): """ @@ -2583,10 +2477,22 @@ def main(): copy_function=shutil.copy, ) - # Dump the API in RST files. + # start from a clean directory everytime if os.path.exists(SPHINX_IN_TMP): shutil.rmtree(SPHINX_IN_TMP, True) + try: + os.mkdir(SPHINX_IN_TMP) + except: + pass + + # copy extra files needed for theme + copy_sphinx_files(SPHINX_IN_TMP) + + # write infromation needed for 'conf.py' + format_config(SPHINX_IN_TMP) + + # Dump the API in RST files. rna2sphinx(SPHINX_IN_TMP) if ARGS.changelog: