# SPDX-FileCopyrightText: 2009-2023 Blender Authors # # SPDX-License-Identifier: GPL-2.0-or-later """ API dump in RST files --------------------- Run this script from Blender's root path once you have compiled Blender blender --background --factory-startup --python doc/python_api/sphinx_doc_gen.py This will generate Python files in doc/python_api/sphinx-in/ providing ./blender is or links to the Blender executable To choose sphinx-in directory: blender --background --factory-startup --python doc/python_api/sphinx_doc_gen.py -- --output=../python_api For quick builds: blender --background --factory-startup --python doc/python_api/sphinx_doc_gen.py -- --partial=bmesh.* Sphinx: HTML generation ----------------------- After you have built doc/python_api/sphinx-in (see above), generate html docs by running: sphinx-build doc/python_api/sphinx-in doc/python_api/sphinx-out Sphinx: PDF generation ---------------------- After you have built doc/python_api/sphinx-in (see above), generate the pdf doc by running: sphinx-build -b latex doc/python_api/sphinx-in doc/python_api/sphinx-out cd doc/python_api/sphinx-out make """ __all__ = ( "main", ) import argparse import sys import inspect import shutil import logging import warnings from collections.abc import ( Callable, Iterator, Sequence, ) from typing import ( Final, NamedTuple, Protocol, ) from pathlib import Path from textwrap import indent try: import bpy # type: ignore[import-not-found] # Blender module. except ImportError: print("\nERROR: this script must run from inside Blender") print(__doc__) sys.exit() import _rna_info as rna_info # type: ignore[import-not-found] # Blender module. # ---------------------------------------------------------------------------- # Type Info # Alias for a file's `.write` method, used as `fw(...)`. WriteFn = Callable[[str], int] # ---------------------------------------------------------------------------- # Inline stubs for `bpy` and `_rna_info` # # These types have no runtime stubs available, # they could be important but the code depends heavily on `bpy`, so define stubs here. class stub: """Namespace holding inline stubs for `bpy` and `_rna_info` types.""" class RnaEnumItem(Protocol): identifier: str name: str description: str def as_pointer(self) -> int: ... # Tuple form: (note, current_version, removal_version) RnaDeprecated = tuple[str, tuple[int, int, int], tuple[int, int, int]] class InfoPropertyRNA(Protocol): identifier: str name: str description: str type: str fixed_type: "stub.InfoStructRNA | None" srna: "stub.InfoStructRNA | None" # Tuples of `(identifier, name, description)`. enum_items: Sequence[tuple[str, str, str]] enum_pointer: int | None deprecated: "stub.RnaDeprecated | None" is_required: bool def get_arg_default(self, force: bool = ...) -> str: ... def get_type_description( self, *, as_arg: bool = ..., as_ret: bool = ..., class_fmt: str = ..., mathutils_fmt: str = ..., literal_fmt: str = ..., collection_id: str = ..., enum_descr_override: str | None = ..., ) -> tuple[str, list[str]]: ... class InfoFunctionRNA(Protocol): identifier: str description: str args: "Sequence[stub.InfoPropertyRNA]" return_values: "Sequence[stub.InfoPropertyRNA]" is_classmethod: bool class InfoOperatorRNA(Protocol): identifier: str module_name: str func_name: str description: str args: "Sequence[stub.InfoPropertyRNA]" def get_location(self) -> tuple[str | None, int | None]: ... class InfoStructRNA(Protocol): identifier: str module_name: str base: "stub.InfoStructRNA | None" description: str properties: "Sequence[stub.InfoPropertyRNA]" functions: "Sequence[stub.InfoFunctionRNA]" references: Sequence[str] py_class: type def get_bases(self) -> "list[stub.InfoStructRNA]": ... def get_py_properties(self) -> "list[tuple[str, stub.PyProperty]]": ... def get_py_c_properties_getset(self) -> list[tuple[str, object]]: ... def get_py_functions(self) -> list[tuple[str, Callable[..., object]]]: ... def get_py_c_functions(self) -> list[tuple[str, Callable[..., object]]]: ... def is_operator_properties(self) -> bool: ... class PyProperty(Protocol): fset: Callable[..., object] | None # Result of `_rna_info.BuildRNAInfo()`: (structs, funcs, ops, props). # Keys are `(parent_id, identifier)` tuples, see `_GetInfoRNA` in `_rna_info.py`. RnaInfo = tuple[ "dict[tuple[str, str], stub.InfoStructRNA]", "dict[tuple[str, str], stub.InfoFunctionRNA]", "dict[tuple[str, str], stub.InfoOperatorRNA]", "dict[tuple[str, str], stub.InfoPropertyRNA]", ] def rna_info_BuildRNAInfo_cache() -> stub.RnaInfo: ret: stub.RnaInfo | None = rna_info_BuildRNAInfo_cache.ret # type: ignore[attr-defined] if ret is None: ret = rna_info.BuildRNAInfo() rna_info_BuildRNAInfo_cache.ret = ret # type: ignore[attr-defined] return ret rna_info_BuildRNAInfo_cache.ret = None # type: ignore[attr-defined] # --- end rna_info cache SCRIPT_DIR = Path(__file__).resolve().parent # ---------------------------------------------------------------------------- # Global State class Global(NamedTuple): """Static configuration assembled from command-line arguments and Blender state.""" # Command line arguments. output_dir: Path partial: str full_rebuild: bool bpy: bool changelog: bool api_dump_index_path: Path | None sphinx_build: bool sphinx_build_pdf: bool pack_reference: bool log: bool # Derived from `partial` + Blender's available modules. exclude_modules: frozenset[str] filter_bpy_ops: tuple[str, ...] | None filter_bpy_types: tuple[str, ...] | None exclude_info_docs: bool # Derived from `bpy.app`. blender_revision: str blender_revision_timestamp: int blender_version_string: str blender_version_dots: str blender_version_path: str # Deployable artifact names and paths (used when `pack_reference` is set). output_base_name: str output_base_path: Path output_filename_pdf: str output_filename_zip: str # Derived from `output_dir`. sphinx_in: Path sphinx_in_tmp: Path sphinx_out: Path sphinx_out_pdf: Path | None # Set only when `sphinx_build_pdf` is True. # Sphinx command lines (only populated when the matching `sphinx_build*` flag is set). sphinx_build_cmd: tuple[str | Path, ...] sphinx_build_pdf_cmd: tuple[str | Path, ...] sphinx_make_pdf_cmd: tuple[str | Path, ...] # Sphinx log paths (set only when `log` is True alongside the matching `sphinx_build*` flag). sphinx_build_log: Path | None sphinx_build_pdf_log: Path | None sphinx_make_pdf_log: Path | None # Type-only declaration; bound by `main()`. GLOBAL: Global # ---------------------------------------------------------------------------- # Mutable State class State: # Non-fatal issues (e.g. C-API `PyGetSetDef` docstring problems) increment # this counter. `main()` returns non-zero when it ends up greater than zero. error_count: int = 0 # Set of example identifiers referenced during RST generation. # Compared against `EXAMPLE_SET` at the end of `main()` to report unused examples. example_set_used: set[str] = set() @staticmethod def reset() -> None: State.error_count = 0 State.example_set_used = set() # For now, ignore add-ons and internal sub-classes of `bpy.types.PropertyGroup`. # # Besides disabling this line, the main change will be to add a # `toctree` to `write_rst_index` which contains the generated RST files. # This `toctree` can be generated automatically. # # See: D6261 for reference. USE_ONLY_BUILTIN_RNA_TYPES: Final = True # Write a page for each static enum defined in: # `source/blender/makesrna/RNA_enum_items.hh` so the enums can be linked to instead of being expanded everywhere. USE_SHARED_RNA_ENUM_ITEMS_STATIC: Final = True # Generate a list of types which support custom properties. # This isn't listed anywhere, it's just linked to. USE_RNA_TYPES_WITH_CUSTOM_PROPERTY_INDEX: Final = True # Write additional RST so `sphinx_stub_gen.py` can produce mypy-compatible stubs # (dunder methods, valid default reprs). # Disable to fall back to the pre-stub-gen baseline RST. USE_STUB_GEN: Final = True # Other types are assumed to be `bpy.types.*`. PRIMITIVE_TYPE_NAMES = {"bool", "bytearray", "bytes", "dict", "float", "int", "list", "set", "str", "tuple"} if USE_SHARED_RNA_ENUM_ITEMS_STATIC: from _bpy import rna_enum_items_static # type: ignore[import-not-found] rna_enum_dict = rna_enum_items_static() for key in ("rna_enum_dummy_NULL_items", "rna_enum_dummy_DEFAULT_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(argv: Sequence[str]) -> argparse.Namespace: """ Parse the arguments passed in ``argv``. When invoked from Blender, callers should pass the slice of ``sys.argv`` after ``"--"`` (Blender ignores everything after ``--`` itself). """ # When --help is given, print the usage text parser = argparse.ArgumentParser( formatter_class=argparse.RawTextHelpFormatter, usage=__doc__ ) # Optional arguments. parser.add_argument( "-p", "--partial", dest="partial", type=str, default="", help="Use a wildcard to only build specific module(s)\n" "Example: --partial\"=bmesh*\"\n", required=False, ) parser.add_argument( "-f", "--fullrebuild", dest="full_rebuild", default=False, action='store_true', help="Rewrite all RST files in sphinx-in/ " "(default=False)", required=False, ) parser.add_argument( "-b", "--bpy", dest="bpy", default=False, action='store_true', help="Write the RST file of the bpy module " "(default=False)", required=False, ) 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', type=Path, 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=Path, default=SCRIPT_DIR, help="Path of the API docs (default=