Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'doc/python_api/sphinx_doc_gen.py')
-rw-r--r--doc/python_api/sphinx_doc_gen.py254
1 files changed, 219 insertions, 35 deletions
diff --git a/doc/python_api/sphinx_doc_gen.py b/doc/python_api/sphinx_doc_gen.py
index b29251b67cc..f5e0369cede 100644
--- a/doc/python_api/sphinx_doc_gen.py
+++ b/doc/python_api/sphinx_doc_gen.py
@@ -1,7 +1,5 @@
# SPDX-License-Identifier: GPL-2.0-or-later
-# <pep8 compliant>
-
"""
API dump in RST files
---------------------
@@ -78,6 +76,27 @@ SCRIPT_DIR = os.path.abspath(os.path.dirname(__file__))
# See: D6261 for reference.
USE_ONLY_BUILTIN_RNA_TYPES = True
+# Write a page for each static enum defined in:
+# `source/blender/makesrna/RNA_enum_items.h` so the enums can be linked to instead of being expanded everywhere.
+USE_SHARED_RNA_ENUM_ITEMS_STATIC = True
+
+if USE_SHARED_RNA_ENUM_ITEMS_STATIC:
+ from _bpy import rna_enum_items_static
+ rna_enum_dict = rna_enum_items_static()
+ for key in ("DummyRNA_DEFAULT_items", "DummyRNA_NULL_items"):
+ del rna_enum_dict[key]
+ del key, rna_enum_items_static
+
+ # Build enum `{pointer: identifier}` map, so any enum property pointer can
+ # lookup an identifier using `InfoPropertyRNA.enum_pointer` as the key.
+ rna_enum_pointer_to_id_map = {
+ enum_prop.as_pointer(): key
+ for key, enum_items in rna_enum_dict.items()
+ # It's possible the first item is a heading (which has no identifier).
+ # skip these as the `EnumProperty.enum_items` does not expose them.
+ if (enum_prop := next(iter(enum_prop for enum_prop in enum_items if enum_prop.identifier), None))
+ }
+
def handle_args():
"""
@@ -123,6 +142,26 @@ def handle_args():
)
parser.add_argument(
+ "--api-changelog-generate",
+ dest="changelog",
+ default=False,
+ action='store_true',
+ help="Generate the API changelog RST file "
+ "(default=False, requires `--api-dump-index-path` parameter)",
+ required=False,
+ )
+
+ parser.add_argument(
+ "--api-dump-index-path",
+ dest="api_dump_index_path",
+ metavar='FILE',
+ default=None,
+ help="Path to the API dump index JSON file "
+ "(required when `--api-changelog-generate` is True)",
+ required=False,
+ )
+
+ parser.add_argument(
"-o", "--output",
dest="output_dir",
type=str,
@@ -495,6 +534,42 @@ if ARGS.sphinx_build_pdf:
sphinx_make_pdf_log = os.path.join(ARGS.output_dir, ".latex_make.log")
SPHINX_MAKE_PDF_STDOUT = open(sphinx_make_pdf_log, "w", encoding="utf-8")
+
+# --------------------------------CHANGELOG GENERATION--------------------------------------
+
+def generate_changelog():
+ import importlib.util
+ spec = importlib.util.spec_from_file_location(
+ "sphinx_changelog_gen",
+ os.path.abspath(os.path.join(SCRIPT_DIR, "sphinx_changelog_gen.py")),
+ )
+ sphinx_changelog_gen = importlib.util.module_from_spec(spec)
+ spec.loader.exec_module(sphinx_changelog_gen)
+
+ API_DUMP_INDEX_FILEPATH = ARGS.api_dump_index_path
+ API_DUMP_ROOT = os.path.dirname(API_DUMP_INDEX_FILEPATH)
+ API_DUMP_FILEPATH = os.path.abspath(os.path.join(API_DUMP_ROOT, BLENDER_VERSION_DOTS, "api_dump.json"))
+ API_CHANGELOG_FILEPATH = os.path.abspath(os.path.join(SPHINX_IN_TMP, "change_log.rst"))
+
+ sphinx_changelog_gen.main((
+ "--",
+ "--indexpath",
+ API_DUMP_INDEX_FILEPATH,
+ "dump",
+ "--filepath-out",
+ API_DUMP_FILEPATH,
+ ))
+
+ sphinx_changelog_gen.main((
+ "--",
+ "--indexpath",
+ API_DUMP_INDEX_FILEPATH,
+ "changelog",
+ "--filepath-out",
+ API_CHANGELOG_FILEPATH,
+ ))
+
+
# --------------------------------API DUMP--------------------------------------
# Lame, python won't give some access.
@@ -531,7 +606,7 @@ def import_value_from_module(module_name, import_name):
def execfile(filepath):
global_namespace = {"__file__": filepath, "__name__": "__main__"}
- with open(filepath) as file_handle:
+ with open(filepath, encoding="utf-8") as file_handle:
exec(compile(file_handle.read(), filepath, 'exec'), global_namespace)
@@ -693,26 +768,6 @@ def write_indented_lines(ident, fn, text, strip=True):
fn(ident + l + "\n")
-def pymethod2sphinx(ident, fw, identifier, py_func):
- """
- class method to sphinx
- """
- arg_str = inspect.formatargspec(*inspect.getargspec(py_func))
- if arg_str.startswith("(self, "):
- arg_str = "(" + arg_str[7:]
- func_type = "method"
- elif arg_str.startswith("(cls, "):
- arg_str = "(" + arg_str[6:]
- func_type = "classmethod"
- else:
- func_type = "staticmethod"
-
- fw(ident + ".. %s:: %s%s\n\n" % (func_type, identifier, arg_str))
- if py_func.__doc__:
- write_indented_lines(ident + " ", fw, py_func.__doc__)
- fw("\n")
-
-
def pyfunc2sphinx(ident, fw, module_name, type_name, identifier, py_func, is_class=True):
"""
function or class method to sphinx
@@ -1220,15 +1275,23 @@ def pycontext2sphinx(basepath):
# No need to check if there are duplicates yet as it's known there wont be.
unique.add(prop.identifier)
+ enum_descr_override = None
+ if USE_SHARED_RNA_ENUM_ITEMS_STATIC:
+ enum_descr_override = pyrna_enum2sphinx_shared_link(prop)
+
type_descr = prop.get_type_description(
- class_fmt=":class:`bpy.types.%s`", collection_id=_BPY_PROP_COLLECTION_ID)
+ class_fmt=":class:`bpy.types.%s`",
+ collection_id=_BPY_PROP_COLLECTION_ID,
+ enum_descr_override=enum_descr_override,
+ )
fw(".. data:: %s\n\n" % prop.identifier)
if prop.description:
fw(" %s\n\n" % prop.description)
# Special exception, can't use generic code here for enums.
if prop.type == "enum":
- enum_text = pyrna_enum2sphinx(prop)
+ # If the link has been written, no need to inline the enum items.
+ enum_text = "" if enum_descr_override else pyrna_enum2sphinx(prop)
if enum_text:
write_indented_lines(" ", fw, enum_text)
fw("\n")
@@ -1290,6 +1353,11 @@ def pyrna_enum2sphinx(prop, use_empty_descriptions=False):
Write a bullet point list of enum + descriptions.
"""
+ # Write a link to the enum if this is part of `rna_enum_pointer_map`.
+ if USE_SHARED_RNA_ENUM_ITEMS_STATIC:
+ if (result := pyrna_enum2sphinx_shared_link(prop)) is not None:
+ return result
+
if use_empty_descriptions:
ok = True
else:
@@ -1368,10 +1436,15 @@ def pyrna2sphinx(basepath):
kwargs["collection_id"] = _BPY_PROP_COLLECTION_ID
- type_descr = prop.get_type_description(**kwargs)
+ enum_descr_override = None
+ if USE_SHARED_RNA_ENUM_ITEMS_STATIC:
+ enum_descr_override = pyrna_enum2sphinx_shared_link(prop)
+ kwargs["enum_descr_override"] = enum_descr_override
- enum_text = pyrna_enum2sphinx(prop)
+ type_descr = prop.get_type_description(**kwargs)
+ # If the link has been written, no need to inline the enum items.
+ enum_text = "" if enum_descr_override else pyrna_enum2sphinx(prop)
if prop.name or prop.description or enum_text:
fw(ident + ":%s%s:\n\n" % (id_name, identifier))
@@ -1456,7 +1529,8 @@ def pyrna2sphinx(basepath):
else:
fw(".. class:: %s\n\n" % struct_id)
- fw(" %s\n\n" % struct.description)
+ write_indented_lines(" ", fw, struct.description, False)
+ fw("\n")
# Properties sorted in alphabetical order.
sorted_struct_properties = struct.properties[:]
@@ -1472,7 +1546,15 @@ def pyrna2sphinx(basepath):
if identifier in struct_blacklist:
continue
- type_descr = prop.get_type_description(class_fmt=":class:`%s`", collection_id=_BPY_PROP_COLLECTION_ID)
+ enum_descr_override = None
+ if USE_SHARED_RNA_ENUM_ITEMS_STATIC:
+ enum_descr_override = pyrna_enum2sphinx_shared_link(prop)
+
+ type_descr = prop.get_type_description(
+ class_fmt=":class:`%s`",
+ collection_id=_BPY_PROP_COLLECTION_ID,
+ enum_descr_override=enum_descr_override,
+ )
# Read-only properties use "data" directive, variables properties use "attribute" directive.
if "readonly" in type_descr:
fw(" .. data:: %s\n" % identifier)
@@ -1489,7 +1571,8 @@ def pyrna2sphinx(basepath):
# Special exception, can't use generic code here for enums.
if prop.type == "enum":
- enum_text = pyrna_enum2sphinx(prop)
+ # If the link has been written, no need to inline the enum items.
+ enum_text = "" if enum_descr_override else pyrna_enum2sphinx(prop)
if enum_text:
write_indented_lines(" ", fw, enum_text)
fw("\n")
@@ -1528,8 +1611,16 @@ def pyrna2sphinx(basepath):
for prop in func.return_values:
# TODO: pyrna_enum2sphinx for multiple return values... actually don't
# think we even use this but still!
+
+ enum_descr_override = None
+ if USE_SHARED_RNA_ENUM_ITEMS_STATIC:
+ enum_descr_override = pyrna_enum2sphinx_shared_link(prop)
+
type_descr = prop.get_type_description(
- as_ret=True, class_fmt=":class:`%s`", collection_id=_BPY_PROP_COLLECTION_ID)
+ as_ret=True, class_fmt=":class:`%s`",
+ collection_id=_BPY_PROP_COLLECTION_ID,
+ enum_descr_override=enum_descr_override,
+ )
descr = prop.description
if not descr:
descr = prop.name
@@ -1778,11 +1869,16 @@ def write_sphinx_conf_py(basepath):
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("master_doc = 'index'\n")
- fw("copyright = u'Blender Foundation'\n")
+ fw("root_doc = 'index'\n")
+ fw("copyright = 'Blender Foundation'\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")
@@ -1978,6 +2074,14 @@ def write_rst_types_index(basepath):
fw(".. toctree::\n")
fw(" :glob:\n\n")
fw(" bpy.types.*\n\n")
+
+ # This needs to be included somewhere, while it's hidden, list to avoid warnings.
+ if USE_SHARED_RNA_ENUM_ITEMS_STATIC:
+ fw(".. toctree::\n")
+ fw(" :hidden:\n")
+ fw(" :maxdepth: 1\n\n")
+ fw(" Shared Enum Types <bpy_types_enum_items/index>\n\n")
+
file.close()
@@ -2048,6 +2152,81 @@ def write_rst_data(basepath):
EXAMPLE_SET_USED.add("bpy.data")
+def pyrna_enum2sphinx_shared_link(prop):
+ """
+ Return a reference to the enum used by ``prop`` or None when not found.
+ """
+ if (
+ (prop.type == "enum") and
+ (pointer := prop.enum_pointer) and
+ (identifier := rna_enum_pointer_to_id_map.get(pointer))
+ ):
+ return ":ref:`%s`" % identifier
+ return None
+
+
+def write_rst_enum_items(basepath, key, key_no_prefix, enum_items):
+ """
+ Write a single page for a static enum in RST.
+
+ This helps avoiding very large lists being in-lined in many places which is an issue
+ especially with icons in ``bpy.types.UILayout``. See T87008.
+ """
+ filepath = os.path.join(basepath, "%s.rst" % key_no_prefix)
+ with open(filepath, "w", encoding="utf-8") as fh:
+ fw = fh.write
+ # fw(".. noindex::\n\n")
+ fw(".. _%s:\n\n" % key)
+
+ fw(title_string(key_no_prefix.replace("_", " ").title(), "#"))
+ # fw(".. rubric:: %s\n\n" % key_no_prefix.replace("_", " ").title())
+
+ for item in enum_items:
+ identifier = item.identifier
+ name = item.name
+ description = item.description
+ if identifier:
+ fw(":%s: %s\n" % (item.identifier, (escape_rst(name) + ".") if name else ""))
+ if description:
+ fw("\n")
+ write_indented_lines(" ", fw, escape_rst(description) + ".")
+ else:
+ fw("\n")
+ else:
+ if name:
+ fw("\n\n**%s**\n\n" % name)
+ else:
+ fw("\n\n----\n\n")
+
+ if description:
+ fw(escape_rst(description) + ".")
+ fw("\n\n")
+
+
+def write_rst_enum_items_and_index(basepath):
+ """
+ Write shared enum items.
+ """
+ subdir = "bpy_types_enum_items"
+ basepath_bpy_types_rna_enum = os.path.join(basepath, subdir)
+ os.makedirs(basepath_bpy_types_rna_enum, exist_ok=True)
+ with open(os.path.join(basepath_bpy_types_rna_enum, "index.rst"), "w", encoding="utf-8") as fh:
+ fw = fh.write
+ fw(title_string("Shared Enum Items", "#"))
+ fw(".. toctree::\n")
+ fw("\n")
+ for key, enum_items in rna_enum_dict.items():
+ if not key.startswith("rna_enum_"):
+ raise Exception("Found RNA enum identifier that doesn't use the 'rna_enum_' prefix, found %r!" % key)
+ key_no_prefix = key.removeprefix("rna_enum_")
+ fw(" %s\n" % key_no_prefix)
+
+ for key, enum_items in rna_enum_dict.items():
+ key_no_prefix = key.removeprefix("rna_enum_")
+ write_rst_enum_items(basepath_bpy_types_rna_enum, key, key_no_prefix, enum_items)
+ fw("\n")
+
+
def write_rst_importable_modules(basepath):
"""
Write the RST files of importable modules.
@@ -2212,6 +2391,10 @@ def rna2sphinx(basepath):
write_rst_data(basepath) # bpy.data
write_rst_importable_modules(basepath)
+ # `bpy_types_enum_items/*` (referenced from `bpy.types`).
+ if USE_SHARED_RNA_ENUM_ITEMS_STATIC:
+ write_rst_enum_items_and_index(basepath)
+
# copy the other rsts
copy_handwritten_rsts(basepath)
@@ -2285,8 +2468,6 @@ def setup_monkey_patch():
# Avoid adding too many changes here.
def setup_blender():
- import bpy
-
# Remove handlers since the functions get included
# in the doc-string and don't have meaningful names.
lists_to_restore = []
@@ -2349,6 +2530,9 @@ def main():
rna2sphinx(SPHINX_IN_TMP)
+ if ARGS.changelog:
+ generate_changelog()
+
if ARGS.full_rebuild:
# Only for full updates.
shutil.rmtree(SPHINX_IN, True)