2022-02-10 23:07:11 +01:00
|
|
|
# SPDX-License-Identifier: GPL-2.0-or-later
|
2009-12-25 16:50:53 +01:00
|
|
|
|
2022-05-21 08:33:35 +02:00
|
|
|
"""
|
2012-03-02 15:39:18 +01:00
|
|
|
API dump in RST files
|
|
|
|
---------------------
|
2016-07-31 06:31:39 +02:00
|
|
|
Run this script from Blender's root path once you have compiled Blender
|
2010-10-13 12:42:33 +02:00
|
|
|
|
2016-10-13 17:53:30 +02:00
|
|
|
blender --background --factory-startup -noaudio --python doc/python_api/sphinx_doc_gen.py
|
2010-01-28 11:48:17 +01:00
|
|
|
|
2011-06-27 06:50:08 +02:00
|
|
|
This will generate python files in doc/python_api/sphinx-in/
|
2016-10-13 17:53:30 +02:00
|
|
|
providing ./blender is or links to the blender executable
|
2010-01-28 11:48:17 +01:00
|
|
|
|
2012-03-05 15:12:38 +01:00
|
|
|
To choose sphinx-in directory:
|
2022-05-23 03:43:47 +02:00
|
|
|
blender --background --factory-startup --python doc/python_api/sphinx_doc_gen.py -- --output=../python_api
|
2012-03-05 15:12:38 +01:00
|
|
|
|
|
|
|
For quick builds:
|
2022-05-23 03:43:47 +02:00
|
|
|
blender --background --factory-startup --python doc/python_api/sphinx_doc_gen.py -- --partial=bmesh.*
|
2012-03-02 15:39:18 +01:00
|
|
|
|
2012-03-13 07:22:43 +01:00
|
|
|
|
2012-03-02 15:39:18 +01:00
|
|
|
Sphinx: HTML generation
|
|
|
|
-----------------------
|
2012-03-05 15:12:38 +01:00
|
|
|
After you have built doc/python_api/sphinx-in (see above),
|
|
|
|
generate html docs by running:
|
2011-02-04 10:27:25 +01:00
|
|
|
|
2020-10-07 16:14:42 +02:00
|
|
|
sphinx-build doc/python_api/sphinx-in doc/python_api/sphinx-out
|
2010-10-13 12:42:33 +02:00
|
|
|
|
2012-03-13 07:22:43 +01:00
|
|
|
|
2012-03-02 15:39:18 +01:00
|
|
|
Sphinx: PDF generation
|
|
|
|
----------------------
|
2012-03-05 15:12:38 +01:00
|
|
|
After you have built doc/python_api/sphinx-in (see above),
|
|
|
|
generate the pdf doc by running:
|
2010-01-28 11:48:17 +01:00
|
|
|
|
2010-10-13 12:42:33 +02:00
|
|
|
sphinx-build -b latex doc/python_api/sphinx-in doc/python_api/sphinx-out
|
|
|
|
cd doc/python_api/sphinx-out
|
2010-01-28 11:48:17 +01:00
|
|
|
make
|
2012-03-02 15:39:18 +01:00
|
|
|
"""
|
2009-12-25 16:50:53 +01:00
|
|
|
|
2012-03-02 15:39:18 +01:00
|
|
|
try:
|
2022-05-21 08:33:35 +02:00
|
|
|
import bpy # Blender module.
|
2014-04-24 21:31:20 +02:00
|
|
|
except ImportError:
|
2012-03-05 15:12:38 +01:00
|
|
|
print("\nERROR: this script must run from inside Blender")
|
2022-05-21 08:33:35 +02:00
|
|
|
print(__doc__)
|
2011-06-27 06:50:08 +02:00
|
|
|
import sys
|
|
|
|
sys.exit()
|
|
|
|
|
2022-05-21 08:33:35 +02:00
|
|
|
import rna_info # Blender module.
|
2012-03-02 15:39:18 +01:00
|
|
|
|
2013-04-29 18:20:49 +02:00
|
|
|
|
|
|
|
def rna_info_BuildRNAInfo_cache():
|
|
|
|
if rna_info_BuildRNAInfo_cache.ret is None:
|
|
|
|
rna_info_BuildRNAInfo_cache.ret = rna_info.BuildRNAInfo()
|
|
|
|
return rna_info_BuildRNAInfo_cache.ret
|
2018-07-03 06:47:49 +02:00
|
|
|
|
|
|
|
|
2013-04-29 18:20:49 +02:00
|
|
|
rna_info_BuildRNAInfo_cache.ret = None
|
|
|
|
# --- end rna_info cache
|
|
|
|
|
2012-03-02 15:39:18 +01:00
|
|
|
import os
|
|
|
|
import sys
|
|
|
|
import inspect
|
|
|
|
import shutil
|
2022-04-06 11:42:36 +02:00
|
|
|
import time
|
2012-03-16 11:49:39 +01:00
|
|
|
import logging
|
2021-02-19 07:58:16 +01:00
|
|
|
import warnings
|
2012-03-02 15:39:18 +01:00
|
|
|
|
2020-06-18 08:26:16 +02:00
|
|
|
from textwrap import indent
|
|
|
|
|
2012-03-08 19:36:23 +01:00
|
|
|
SCRIPT_DIR = os.path.abspath(os.path.dirname(__file__))
|
2012-03-02 15:39:18 +01:00
|
|
|
|
2022-05-21 08:33:35 +02:00
|
|
|
# For now, ignore add-ons and internal sub-classes of `bpy.types.PropertyGroup`.
|
2019-11-15 16:12:20 +01:00
|
|
|
#
|
|
|
|
# Besides disabling this line, the main change will be to add a
|
2022-05-21 08:33:35 +02:00
|
|
|
# 'toctree' to 'write_rst_index' which contains the generated RST files.
|
2019-11-15 16:12:20 +01:00
|
|
|
# This 'toctree' can be generated automatically.
|
|
|
|
#
|
|
|
|
# See: D6261 for reference.
|
|
|
|
USE_ONLY_BUILTIN_RNA_TYPES = True
|
2012-03-08 06:36:05 +01:00
|
|
|
|
2022-05-31 06:07:10 +02:00
|
|
|
# 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
|
|
|
|
|
2022-05-31 06:07:14 +02:00
|
|
|
# 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))
|
|
|
|
}
|
|
|
|
|
2020-10-02 02:10:01 +02:00
|
|
|
|
2012-03-05 15:12:38 +01:00
|
|
|
def handle_args():
|
2022-05-21 08:33:35 +02:00
|
|
|
"""
|
2012-03-07 18:36:38 +01:00
|
|
|
Parse the args passed to Blender after "--", ignored by Blender
|
2022-05-21 08:33:35 +02:00
|
|
|
"""
|
2012-03-05 15:12:38 +01:00
|
|
|
import argparse
|
|
|
|
|
|
|
|
# When --help is given, print the usage text
|
|
|
|
parser = argparse.ArgumentParser(
|
2012-03-08 19:36:23 +01:00
|
|
|
formatter_class=argparse.RawTextHelpFormatter,
|
2022-05-21 08:33:35 +02:00
|
|
|
usage=__doc__
|
2012-03-05 15:12:38 +01:00
|
|
|
)
|
|
|
|
|
2012-03-16 11:49:39 +01:00
|
|
|
# optional arguments
|
2022-05-21 08:33:35 +02:00
|
|
|
parser.add_argument(
|
|
|
|
"-p", "--partial",
|
|
|
|
dest="partial",
|
|
|
|
type=str,
|
|
|
|
default="",
|
|
|
|
help="Use a wildcard to only build specific module(s)\n"
|
2022-05-23 03:43:47 +02:00
|
|
|
"Example: --partial\"=bmesh*\"\n",
|
|
|
|
required=False,
|
|
|
|
)
|
2022-05-21 08:33:35 +02:00
|
|
|
|
|
|
|
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,
|
|
|
|
)
|
|
|
|
|
2022-06-15 15:43:13 +02:00
|
|
|
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,
|
|
|
|
)
|
|
|
|
|
2022-05-21 08:33:35 +02:00
|
|
|
parser.add_argument(
|
|
|
|
"-o", "--output",
|
|
|
|
dest="output_dir",
|
|
|
|
type=str,
|
|
|
|
default=SCRIPT_DIR,
|
|
|
|
help="Path of the API docs (default=<script dir>)",
|
|
|
|
required=False,
|
|
|
|
)
|
|
|
|
|
|
|
|
parser.add_argument(
|
|
|
|
"-B", "--sphinx-build",
|
|
|
|
dest="sphinx_build",
|
|
|
|
default=False,
|
|
|
|
action='store_true',
|
|
|
|
help="Build the html docs by running:\n"
|
|
|
|
"sphinx-build SPHINX_IN SPHINX_OUT\n"
|
|
|
|
"(default=False; does not depend on -P)",
|
|
|
|
required=False,
|
|
|
|
)
|
|
|
|
|
|
|
|
parser.add_argument(
|
|
|
|
"-P", "--sphinx-build-pdf",
|
|
|
|
dest="sphinx_build_pdf",
|
|
|
|
default=False,
|
|
|
|
action='store_true',
|
|
|
|
help="Build the pdf by running:\n"
|
|
|
|
"sphinx-build -b latex SPHINX_IN SPHINX_OUT_PDF\n"
|
|
|
|
"(default=False; does not depend on -B)",
|
|
|
|
required=False,
|
|
|
|
)
|
|
|
|
|
|
|
|
parser.add_argument(
|
|
|
|
"-R", "--pack-reference",
|
|
|
|
dest="pack_reference",
|
|
|
|
default=False,
|
|
|
|
action='store_true',
|
|
|
|
help="Pack all necessary files in the deployed dir.\n"
|
|
|
|
"(default=False; use with -B and -P)",
|
|
|
|
required=False,
|
|
|
|
)
|
|
|
|
|
|
|
|
parser.add_argument(
|
|
|
|
"-l", "--log",
|
|
|
|
dest="log",
|
|
|
|
default=False,
|
|
|
|
action='store_true',
|
|
|
|
help=(
|
|
|
|
"Log the output of the API dump and sphinx|latex "
|
|
|
|
"warnings and errors (default=False).\n"
|
|
|
|
"If given, save logs in:\n"
|
|
|
|
"* OUTPUT_DIR/.bpy.log\n"
|
|
|
|
"* OUTPUT_DIR/.sphinx-build.log\n"
|
|
|
|
"* OUTPUT_DIR/.sphinx-build_pdf.log\n"
|
|
|
|
"* OUTPUT_DIR/.latex_make.log"
|
|
|
|
),
|
|
|
|
required=False,
|
|
|
|
)
|
|
|
|
|
|
|
|
# Parse only the arguments passed after "--".
|
2012-03-05 15:12:38 +01:00
|
|
|
argv = []
|
|
|
|
if "--" in sys.argv:
|
2022-05-21 08:33:35 +02:00
|
|
|
argv = sys.argv[sys.argv.index("--") + 1:] # Get all arguments after "--".
|
2012-03-05 15:12:38 +01:00
|
|
|
|
|
|
|
return parser.parse_args(argv)
|
|
|
|
|
|
|
|
|
2012-03-07 18:36:38 +01:00
|
|
|
ARGS = handle_args()
|
|
|
|
|
|
|
|
# ----------------------------------BPY-----------------------------------------
|
|
|
|
|
2022-05-21 08:33:35 +02:00
|
|
|
BPY_LOGGER = logging.getLogger("bpy")
|
2012-03-16 11:49:39 +01:00
|
|
|
BPY_LOGGER.setLevel(logging.DEBUG)
|
|
|
|
|
2012-03-05 15:12:38 +01:00
|
|
|
"""
|
2012-03-07 18:36:38 +01:00
|
|
|
# for quick rebuilds
|
2012-03-05 15:12:38 +01:00
|
|
|
rm -rf /b/doc/python_api/sphinx-* && \
|
2016-10-13 17:53:30 +02:00
|
|
|
./blender -b -noaudio --factory-startup -P doc/python_api/sphinx_doc_gen.py && \
|
2012-03-05 15:12:38 +01:00
|
|
|
sphinx-build doc/python_api/sphinx-in doc/python_api/sphinx-out
|
2012-03-16 11:49:39 +01:00
|
|
|
|
|
|
|
or
|
|
|
|
|
2016-10-13 17:53:30 +02:00
|
|
|
./blender -b -noaudio --factory-startup -P doc/python_api/sphinx_doc_gen.py -- -f -B
|
2012-03-05 15:12:38 +01:00
|
|
|
"""
|
2011-06-27 06:50:08 +02:00
|
|
|
|
2022-05-21 08:33:35 +02:00
|
|
|
# Switch for quick testing so doc-builds don't take so long.
|
2012-03-13 07:22:43 +01:00
|
|
|
if not ARGS.partial:
|
2011-02-16 06:18:10 +01:00
|
|
|
# full build
|
2012-03-07 18:36:38 +01:00
|
|
|
FILTER_BPY_OPS = None
|
|
|
|
FILTER_BPY_TYPES = None
|
2011-08-25 06:25:33 +02:00
|
|
|
EXCLUDE_INFO_DOCS = False
|
2016-07-31 06:37:25 +02:00
|
|
|
EXCLUDE_MODULES = []
|
2011-02-16 06:18:10 +01:00
|
|
|
|
|
|
|
else:
|
2012-03-13 07:22:43 +01:00
|
|
|
# can manually edit this too:
|
2016-07-30 10:39:22 +02:00
|
|
|
# FILTER_BPY_OPS = ("import.scene", ) # allow
|
|
|
|
# FILTER_BPY_TYPES = ("bpy_struct", "Operator", "ID") # allow
|
2012-02-24 10:53:29 +01:00
|
|
|
EXCLUDE_INFO_DOCS = True
|
2012-04-23 01:51:50 +02:00
|
|
|
EXCLUDE_MODULES = [
|
2012-03-07 18:36:38 +01:00
|
|
|
"aud",
|
|
|
|
"bgl",
|
|
|
|
"blf",
|
2020-07-22 06:36:30 +02:00
|
|
|
"bl_math",
|
2020-02-26 05:16:10 +01:00
|
|
|
"imbuf",
|
2021-02-25 02:00:45 +01:00
|
|
|
"imbuf.types",
|
2012-03-13 07:22:43 +01:00
|
|
|
"bmesh",
|
2012-11-27 07:56:51 +01:00
|
|
|
"bmesh.ops",
|
2012-03-13 07:22:43 +01:00
|
|
|
"bmesh.types",
|
|
|
|
"bmesh.utils",
|
2013-12-06 11:13:11 +01:00
|
|
|
"bmesh.geometry",
|
2012-02-24 10:53:29 +01:00
|
|
|
"bpy.app",
|
|
|
|
"bpy.app.handlers",
|
2018-11-27 10:56:17 +01:00
|
|
|
"bpy.app.timers",
|
2013-04-19 15:00:21 +02:00
|
|
|
"bpy.app.translations",
|
2012-03-07 18:36:38 +01:00
|
|
|
"bpy.context",
|
2011-02-16 06:18:10 +01:00
|
|
|
"bpy.data",
|
2012-03-07 18:36:38 +01:00
|
|
|
"bpy.ops", # supports filtering
|
|
|
|
"bpy.path",
|
2011-03-22 05:28:51 +01:00
|
|
|
"bpy.props",
|
2011-11-04 05:27:46 +01:00
|
|
|
"bpy.types", # supports filtering
|
2012-03-07 18:36:38 +01:00
|
|
|
"bpy.utils",
|
2015-05-12 10:06:31 +02:00
|
|
|
"bpy.utils.previews",
|
2020-10-12 00:38:55 +02:00
|
|
|
"bpy.utils.units",
|
2011-07-01 13:16:42 +02:00
|
|
|
"bpy_extras",
|
2011-10-13 08:59:09 +02:00
|
|
|
"gpu",
|
2018-09-06 02:10:42 +02:00
|
|
|
"gpu.types",
|
|
|
|
"gpu.matrix",
|
|
|
|
"gpu.select",
|
2021-02-25 13:16:48 +01:00
|
|
|
"gpu.shader",
|
|
|
|
"gpu.state",
|
2021-03-01 21:35:10 +01:00
|
|
|
"gpu.texture",
|
2021-08-17 06:44:28 +02:00
|
|
|
"gpu.platform",
|
2021-08-17 06:55:05 +02:00
|
|
|
"gpu.capabilities",
|
2018-11-27 10:56:17 +01:00
|
|
|
"gpu_extras",
|
2016-07-31 07:39:30 +02:00
|
|
|
"idprop.types",
|
2011-02-16 06:18:10 +01:00
|
|
|
"mathutils",
|
2015-07-29 13:16:28 +02:00
|
|
|
"mathutils.bvhtree",
|
2016-03-03 22:40:22 +01:00
|
|
|
"mathutils.geometry",
|
|
|
|
"mathutils.interpolate",
|
2014-01-06 10:32:34 +01:00
|
|
|
"mathutils.kdtree",
|
2011-12-04 07:55:32 +01:00
|
|
|
"mathutils.noise",
|
2013-04-07 13:22:54 +02:00
|
|
|
"freestyle",
|
2014-09-18 08:44:53 +02:00
|
|
|
"freestyle.chainingiterators",
|
|
|
|
"freestyle.functions",
|
|
|
|
"freestyle.predicates",
|
|
|
|
"freestyle.shaders",
|
|
|
|
"freestyle.types",
|
|
|
|
"freestyle.utils",
|
2016-07-30 10:39:22 +02:00
|
|
|
]
|
2011-02-16 06:18:10 +01:00
|
|
|
|
2012-03-13 07:22:43 +01:00
|
|
|
# ------
|
|
|
|
# Filter
|
|
|
|
#
|
2022-05-21 08:33:35 +02:00
|
|
|
# TODO: support `bpy.ops` and `bpy.types` filtering.
|
2012-03-13 07:22:43 +01:00
|
|
|
import fnmatch
|
|
|
|
m = None
|
2012-04-23 01:51:50 +02:00
|
|
|
EXCLUDE_MODULES = [m for m in EXCLUDE_MODULES if not fnmatch.fnmatchcase(m, ARGS.partial)]
|
|
|
|
|
|
|
|
# special support for bpy.types.XXX
|
|
|
|
FILTER_BPY_OPS = tuple([m[8:] for m in ARGS.partial.split(":") if m.startswith("bpy.ops.")])
|
|
|
|
if FILTER_BPY_OPS:
|
|
|
|
EXCLUDE_MODULES.remove("bpy.ops")
|
|
|
|
|
|
|
|
FILTER_BPY_TYPES = tuple([m[10:] for m in ARGS.partial.split(":") if m.startswith("bpy.types.")])
|
|
|
|
if FILTER_BPY_TYPES:
|
|
|
|
EXCLUDE_MODULES.remove("bpy.types")
|
|
|
|
|
2022-05-23 03:43:47 +02:00
|
|
|
# print(FILTER_BPY_TYPES)
|
2012-03-23 01:28:29 +01:00
|
|
|
|
|
|
|
EXCLUDE_INFO_DOCS = (not fnmatch.fnmatchcase("info", ARGS.partial))
|
|
|
|
|
2012-03-13 07:22:43 +01:00
|
|
|
del m
|
|
|
|
del fnmatch
|
|
|
|
|
2016-07-30 10:39:22 +02:00
|
|
|
BPY_LOGGER.debug(
|
2022-05-22 05:30:17 +02:00
|
|
|
"Partial Doc Build, Skipping: %s\n",
|
2016-07-30 10:39:22 +02:00
|
|
|
"\n ".join(sorted(EXCLUDE_MODULES)))
|
2012-03-13 07:22:43 +01:00
|
|
|
|
|
|
|
#
|
|
|
|
# done filtering
|
|
|
|
# --------------
|
|
|
|
|
2012-03-05 15:12:38 +01:00
|
|
|
try:
|
|
|
|
__import__("aud")
|
|
|
|
except ImportError:
|
2022-05-21 08:33:35 +02:00
|
|
|
BPY_LOGGER.debug("Warning: Built without \"aud\" module, docs incomplete...")
|
2016-07-31 06:37:25 +02:00
|
|
|
EXCLUDE_MODULES.append("aud")
|
2011-02-16 06:18:10 +01:00
|
|
|
|
2013-04-29 18:20:49 +02:00
|
|
|
try:
|
|
|
|
__import__("freestyle")
|
|
|
|
except ImportError:
|
2022-05-21 08:33:35 +02:00
|
|
|
BPY_LOGGER.debug("Warning: Built without \"freestyle\" module, docs incomplete...")
|
2016-07-31 06:37:25 +02:00
|
|
|
EXCLUDE_MODULES.extend([
|
|
|
|
"freestyle",
|
|
|
|
"freestyle.chainingiterators",
|
|
|
|
"freestyle.functions",
|
|
|
|
"freestyle.predicates",
|
|
|
|
"freestyle.shaders",
|
|
|
|
"freestyle.types",
|
|
|
|
"freestyle.utils",
|
|
|
|
])
|
2013-04-29 18:20:49 +02:00
|
|
|
|
2016-06-12 03:20:13 +02:00
|
|
|
# Source files we use, and need to copy to the OUTPUT_DIR
|
|
|
|
# to have working out-of-source builds.
|
|
|
|
# Note that ".." is replaced by "__" in the RST files,
|
|
|
|
# to avoid having to match Blender's source tree.
|
|
|
|
EXTRA_SOURCE_FILES = (
|
|
|
|
"../../../release/scripts/templates_py/bmesh_simple.py",
|
2018-07-15 19:49:07 +02:00
|
|
|
"../../../release/scripts/templates_py/gizmo_operator.py",
|
|
|
|
"../../../release/scripts/templates_py/gizmo_operator_target.py",
|
|
|
|
"../../../release/scripts/templates_py/gizmo_simple.py",
|
2016-06-12 03:20:13 +02:00
|
|
|
"../../../release/scripts/templates_py/operator_simple.py",
|
|
|
|
"../../../release/scripts/templates_py/ui_panel_simple.py",
|
|
|
|
"../../../release/scripts/templates_py/ui_previews_custom_icon.py",
|
|
|
|
"../examples/bmesh.ops.1.py",
|
|
|
|
"../examples/bpy.app.translations.py",
|
2016-07-30 10:39:22 +02:00
|
|
|
)
|
2016-06-12 03:20:13 +02:00
|
|
|
|
|
|
|
|
2012-03-05 15:12:38 +01:00
|
|
|
# examples
|
|
|
|
EXAMPLES_DIR = os.path.abspath(os.path.join(SCRIPT_DIR, "examples"))
|
2022-05-22 05:30:17 +02:00
|
|
|
EXAMPLE_SET = set(os.path.splitext(f)[0] for f in os.listdir(EXAMPLES_DIR) if f.endswith(".py"))
|
2012-03-05 15:12:38 +01:00
|
|
|
EXAMPLE_SET_USED = set()
|
|
|
|
|
2022-05-21 08:33:35 +02:00
|
|
|
# RST files directory.
|
2012-03-05 15:12:38 +01:00
|
|
|
RST_DIR = os.path.abspath(os.path.join(SCRIPT_DIR, "rst"))
|
2011-02-16 06:18:10 +01:00
|
|
|
|
2022-09-15 06:31:06 +02:00
|
|
|
# Extra info, not api reference docs stored in `./rst/info_*`.
|
|
|
|
# Pairs of (file, description), the title makes from the RST files are displayed before the description.
|
2011-08-25 06:25:33 +02:00
|
|
|
INFO_DOCS = (
|
2016-07-30 10:39:22 +02:00
|
|
|
("info_quickstart.rst",
|
2022-09-15 06:31:06 +02:00
|
|
|
"New to Blender or scripting and want to get your feet wet?"),
|
2016-07-30 10:39:22 +02:00
|
|
|
("info_overview.rst",
|
2022-09-15 06:31:06 +02:00
|
|
|
"A more complete explanation of Python integration."),
|
2016-07-30 10:39:22 +02:00
|
|
|
("info_api_reference.rst",
|
2022-09-15 06:31:06 +02:00
|
|
|
"Examples of how to use the API reference docs."),
|
2016-07-30 10:39:22 +02:00
|
|
|
("info_best_practice.rst",
|
2022-09-15 06:31:06 +02:00
|
|
|
"Conventions to follow for writing good scripts."),
|
2016-07-30 10:39:22 +02:00
|
|
|
("info_tips_and_tricks.rst",
|
2022-09-15 06:31:06 +02:00
|
|
|
"Hints to help you while writing scripts for Blender."),
|
2016-07-30 10:39:22 +02:00
|
|
|
("info_gotcha.rst",
|
2022-09-15 06:31:06 +02:00
|
|
|
"Some of the problems you may encounter when writing scripts."),
|
2022-09-14 07:11:50 +02:00
|
|
|
("info_advanced.rst",
|
2022-09-15 06:31:06 +02:00
|
|
|
"Topics which may not be required for typical usage."),
|
|
|
|
("change_log.rst",
|
|
|
|
"List of changes since last Blender release"),
|
2016-07-30 10:39:22 +02:00
|
|
|
)
|
2022-09-14 07:11:50 +02:00
|
|
|
# Referenced indirectly.
|
|
|
|
INFO_DOCS_OTHER = (
|
|
|
|
# Included by: `info_advanced.rst`.
|
|
|
|
"info_advanced_blender_as_bpy.rst",
|
|
|
|
)
|
2011-02-16 06:18:10 +01:00
|
|
|
|
2022-09-15 06:31:06 +02:00
|
|
|
# Hide the actual TOC, use a separate list that links to the items.
|
|
|
|
# This is done so a short description can be included with each link.
|
|
|
|
USE_INFO_DOCS_FANCY_INDEX = True
|
|
|
|
|
2011-11-24 23:24:07 +01:00
|
|
|
# only support for properties atm.
|
|
|
|
RNA_BLACKLIST = {
|
2012-03-07 18:36:38 +01:00
|
|
|
# XXX messes up PDF!, really a bug but for now just workaround.
|
2018-12-21 02:47:44 +01:00
|
|
|
"PreferencesSystem": {"language", }
|
2016-07-30 10:39:22 +02:00
|
|
|
}
|
2011-11-24 23:24:07 +01:00
|
|
|
|
2022-05-25 05:39:47 +02:00
|
|
|
# Support suppressing errors when attributes collide with methods,
|
|
|
|
# use `noindex` on the attributes / data declarations.
|
|
|
|
#
|
|
|
|
# NOTE: in general this should be avoided but changing it would break the API,
|
|
|
|
# so explicitly suppress warnings instead.
|
|
|
|
#
|
|
|
|
# NOTE: Currently some API generation doesn't support this is it is not used yet,
|
|
|
|
# see references to `RST_NOINDEX_ATTR` in code comments.
|
|
|
|
#
|
|
|
|
# A set of tuple identifiers: `(module, type, attr)`.
|
|
|
|
RST_NOINDEX_ATTR = {
|
|
|
|
# Render is both a method and an attribute, from looking into this
|
|
|
|
# having both doesn't cause problems in practice since the `render` method
|
|
|
|
# is registered and called from C code where the attribute is accessed from the instance.
|
|
|
|
("bpy.types", "RenderEngine", "render"),
|
|
|
|
}
|
|
|
|
|
2012-03-21 06:33:37 +01:00
|
|
|
MODULE_GROUPING = {
|
|
|
|
"bmesh.types": (
|
2022-05-21 08:33:35 +02:00
|
|
|
("Base Mesh Type", "-"),
|
2016-07-30 10:39:22 +02:00
|
|
|
"BMesh",
|
2022-05-21 08:33:35 +02:00
|
|
|
("Mesh Elements", "-"),
|
2016-07-30 10:39:22 +02:00
|
|
|
"BMVert",
|
|
|
|
"BMEdge",
|
|
|
|
"BMFace",
|
|
|
|
"BMLoop",
|
2022-05-21 08:33:35 +02:00
|
|
|
("Sequence Accessors", "-"),
|
2016-07-30 10:39:22 +02:00
|
|
|
"BMElemSeq",
|
|
|
|
"BMVertSeq",
|
|
|
|
"BMEdgeSeq",
|
|
|
|
"BMFaceSeq",
|
|
|
|
"BMLoopSeq",
|
|
|
|
"BMIter",
|
2022-05-21 08:33:35 +02:00
|
|
|
("Selection History", "-"),
|
2016-07-30 10:39:22 +02:00
|
|
|
"BMEditSelSeq",
|
|
|
|
"BMEditSelIter",
|
2022-05-21 08:33:35 +02:00
|
|
|
("Custom-Data Layer Access", "-"),
|
2016-07-30 10:39:22 +02:00
|
|
|
"BMLayerAccessVert",
|
|
|
|
"BMLayerAccessEdge",
|
|
|
|
"BMLayerAccessFace",
|
|
|
|
"BMLayerAccessLoop",
|
|
|
|
"BMLayerCollection",
|
|
|
|
"BMLayerItem",
|
2022-05-21 08:33:35 +02:00
|
|
|
("Custom-Data Layer Types", "-"),
|
2016-07-30 10:39:22 +02:00
|
|
|
"BMLoopUV",
|
2022-05-21 08:33:35 +02:00
|
|
|
"BMDeformVert",
|
2016-07-30 10:39:22 +02:00
|
|
|
)
|
|
|
|
}
|
2012-03-07 18:36:38 +01:00
|
|
|
|
2012-03-16 11:49:39 +01:00
|
|
|
# --------------------configure compile time options----------------------------
|
|
|
|
|
|
|
|
# -------------------------------BLENDER----------------------------------------
|
|
|
|
|
2016-10-13 17:53:30 +02:00
|
|
|
# converting bytes to strings, due to T30154
|
2013-11-29 00:00:57 +01:00
|
|
|
BLENDER_REVISION = str(bpy.app.build_hash, 'utf_8')
|
2022-04-06 11:42:36 +02:00
|
|
|
BLENDER_REVISION_TIMESTAMP = bpy.app.build_commit_timestamp
|
2012-03-16 11:49:39 +01:00
|
|
|
|
Blender: change bugfix release versioning from a/b/c to .1/.2/.3
The file subversion is no longer used in the Python API or user interface,
and is now internal to Blender.
User interface, Python API and file I/O metadata now use more consistent
formatting for version numbers. Official releases use "2.83.0", "2.83.1",
and releases under development use "2.90.0 Alpha", "2.90.0 Beta".
Some Python add-ons may need to lower the Blender version in bl_info to
(2, 83, 0) or (2, 90, 0) if they used a subversion number higher than 0.
https://wiki.blender.org/wiki/Reference/Release_Notes/2.83/Python_API#Compatibility
This change is in preparation of LTS releases, and also brings us more
in line with semantic versioning.
Fixes T76058.
Differential Revision: https://developer.blender.org/D7748
2020-05-25 10:49:04 +02:00
|
|
|
# '2.83.0 Beta' or '2.83.0' or '2.83.1'
|
2022-02-03 20:25:16 +01:00
|
|
|
BLENDER_VERSION_STRING = bpy.app.version_string
|
|
|
|
BLENDER_VERSION_DOTS = "%d.%d" % (bpy.app.version[0], bpy.app.version[1])
|
2019-07-09 02:52:29 +02:00
|
|
|
|
2012-03-16 11:49:39 +01:00
|
|
|
if BLENDER_REVISION != "Unknown":
|
Blender: change bugfix release versioning from a/b/c to .1/.2/.3
The file subversion is no longer used in the Python API or user interface,
and is now internal to Blender.
User interface, Python API and file I/O metadata now use more consistent
formatting for version numbers. Official releases use "2.83.0", "2.83.1",
and releases under development use "2.90.0 Alpha", "2.90.0 Beta".
Some Python add-ons may need to lower the Blender version in bl_info to
(2, 83, 0) or (2, 90, 0) if they used a subversion number higher than 0.
https://wiki.blender.org/wiki/Reference/Release_Notes/2.83/Python_API#Compatibility
This change is in preparation of LTS releases, and also brings us more
in line with semantic versioning.
Fixes T76058.
Differential Revision: https://developer.blender.org/D7748
2020-05-25 10:49:04 +02:00
|
|
|
# SHA1 Git hash
|
2019-07-09 02:52:29 +02:00
|
|
|
BLENDER_VERSION_HASH = BLENDER_REVISION
|
2022-04-20 07:47:24 +02:00
|
|
|
BLENDER_VERSION_HASH_HTML_LINK = "<a href=https://developer.blender.org/rB%s>%s</a>" % (
|
|
|
|
BLENDER_VERSION_HASH, BLENDER_VERSION_HASH,
|
|
|
|
)
|
2022-04-06 11:42:36 +02:00
|
|
|
BLENDER_VERSION_DATE = time.strftime("%d/%m/%Y", time.localtime(BLENDER_REVISION_TIMESTAMP))
|
2019-07-09 02:52:29 +02:00
|
|
|
else:
|
|
|
|
# Fallback: Should not be used
|
|
|
|
BLENDER_VERSION_HASH = "Hash Unknown"
|
2022-04-06 11:42:36 +02:00
|
|
|
BLENDER_VERSION_HASH_HTML_LINK = BLENDER_VERSION_HASH
|
|
|
|
BLENDER_VERSION_DATE = time.strftime("%Y-%m-%d")
|
2012-03-16 11:49:39 +01:00
|
|
|
|
2022-05-21 08:33:35 +02:00
|
|
|
# Example: `2_83`.
|
Blender: change bugfix release versioning from a/b/c to .1/.2/.3
The file subversion is no longer used in the Python API or user interface,
and is now internal to Blender.
User interface, Python API and file I/O metadata now use more consistent
formatting for version numbers. Official releases use "2.83.0", "2.83.1",
and releases under development use "2.90.0 Alpha", "2.90.0 Beta".
Some Python add-ons may need to lower the Blender version in bl_info to
(2, 83, 0) or (2, 90, 0) if they used a subversion number higher than 0.
https://wiki.blender.org/wiki/Reference/Release_Notes/2.83/Python_API#Compatibility
This change is in preparation of LTS releases, and also brings us more
in line with semantic versioning.
Fixes T76058.
Differential Revision: https://developer.blender.org/D7748
2020-05-25 10:49:04 +02:00
|
|
|
BLENDER_VERSION_PATH = "%d_%d" % (bpy.app.version[0], bpy.app.version[1])
|
2012-03-16 11:49:39 +01:00
|
|
|
|
|
|
|
# --------------------------DOWNLOADABLE FILES----------------------------------
|
|
|
|
|
|
|
|
REFERENCE_NAME = "blender_python_reference_%s" % BLENDER_VERSION_PATH
|
|
|
|
REFERENCE_PATH = os.path.join(ARGS.output_dir, REFERENCE_NAME)
|
|
|
|
BLENDER_PDF_FILENAME = "%s.pdf" % REFERENCE_NAME
|
|
|
|
BLENDER_ZIP_FILENAME = "%s.zip" % REFERENCE_NAME
|
|
|
|
|
2012-03-07 18:36:38 +01:00
|
|
|
# -------------------------------SPHINX-----------------------------------------
|
|
|
|
|
2012-03-08 19:36:23 +01:00
|
|
|
SPHINX_IN = os.path.join(ARGS.output_dir, "sphinx-in")
|
|
|
|
SPHINX_IN_TMP = SPHINX_IN + "-tmp"
|
|
|
|
SPHINX_OUT = os.path.join(ARGS.output_dir, "sphinx-out")
|
2012-03-08 06:36:05 +01:00
|
|
|
|
2012-03-16 11:49:39 +01:00
|
|
|
# html build
|
|
|
|
if ARGS.sphinx_build:
|
|
|
|
SPHINX_BUILD = ["sphinx-build", SPHINX_IN, SPHINX_OUT]
|
|
|
|
|
|
|
|
if ARGS.log:
|
|
|
|
SPHINX_BUILD_LOG = os.path.join(ARGS.output_dir, ".sphinx-build.log")
|
2020-06-23 15:30:51 +02:00
|
|
|
SPHINX_BUILD = [
|
|
|
|
"sphinx-build",
|
|
|
|
"-w", SPHINX_BUILD_LOG,
|
|
|
|
SPHINX_IN, SPHINX_OUT,
|
|
|
|
]
|
2012-03-16 11:49:39 +01:00
|
|
|
|
|
|
|
# pdf build
|
|
|
|
if ARGS.sphinx_build_pdf:
|
|
|
|
SPHINX_OUT_PDF = os.path.join(ARGS.output_dir, "sphinx-out_pdf")
|
2020-06-23 15:30:51 +02:00
|
|
|
SPHINX_BUILD_PDF = [
|
|
|
|
"sphinx-build",
|
|
|
|
"-b", "latex",
|
|
|
|
SPHINX_IN, SPHINX_OUT_PDF,
|
|
|
|
]
|
2012-03-16 11:49:39 +01:00
|
|
|
SPHINX_MAKE_PDF = ["make", "-C", SPHINX_OUT_PDF]
|
|
|
|
SPHINX_MAKE_PDF_STDOUT = None
|
|
|
|
|
|
|
|
if ARGS.log:
|
|
|
|
SPHINX_BUILD_PDF_LOG = os.path.join(ARGS.output_dir, ".sphinx-build_pdf.log")
|
2020-06-23 15:30:51 +02:00
|
|
|
SPHINX_BUILD_PDF = [
|
|
|
|
"sphinx-build", "-b", "latex",
|
|
|
|
"-w", SPHINX_BUILD_PDF_LOG,
|
|
|
|
SPHINX_IN, SPHINX_OUT_PDF,
|
|
|
|
]
|
2012-03-16 11:49:39 +01:00
|
|
|
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")
|
2012-03-07 18:36:38 +01:00
|
|
|
|
2022-06-15 15:43:13 +02:00
|
|
|
|
|
|
|
# --------------------------------CHANGELOG GENERATION--------------------------------------
|
|
|
|
|
|
|
|
def generate_changelog():
|
|
|
|
import importlib.util
|
2022-06-29 02:18:24 +02:00
|
|
|
spec = importlib.util.spec_from_file_location(
|
|
|
|
"sphinx_changelog_gen",
|
|
|
|
os.path.abspath(os.path.join(SCRIPT_DIR, "sphinx_changelog_gen.py")),
|
|
|
|
)
|
2022-06-15 15:43:13 +02:00
|
|
|
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"))
|
|
|
|
|
2022-06-29 02:18:24 +02:00
|
|
|
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,
|
|
|
|
))
|
2022-06-15 15:43:13 +02:00
|
|
|
|
|
|
|
|
2012-03-07 18:36:38 +01:00
|
|
|
# --------------------------------API DUMP--------------------------------------
|
|
|
|
|
2022-05-21 08:33:35 +02:00
|
|
|
# Lame, python won't give some access.
|
|
|
|
ClassMethodDescriptorType = type(dict.__dict__["fromkeys"])
|
2010-04-06 00:37:09 +02:00
|
|
|
MethodDescriptorType = type(dict.get)
|
|
|
|
GetSetDescriptorType = type(int.real)
|
2013-04-07 08:56:49 +02:00
|
|
|
StaticMethodType = type(staticmethod(lambda: None))
|
2015-05-17 09:17:31 +02:00
|
|
|
from types import (
|
2016-07-30 10:39:22 +02:00
|
|
|
MemberDescriptorType,
|
|
|
|
MethodType,
|
|
|
|
FunctionType,
|
|
|
|
)
|
2010-04-06 00:37:09 +02:00
|
|
|
|
2010-04-10 20:35:50 +02:00
|
|
|
_BPY_STRUCT_FAKE = "bpy_struct"
|
2011-03-22 05:28:51 +01:00
|
|
|
_BPY_PROP_COLLECTION_FAKE = "bpy_prop_collection"
|
2010-04-10 20:35:50 +02:00
|
|
|
|
2011-03-22 05:28:51 +01:00
|
|
|
if _BPY_PROP_COLLECTION_FAKE:
|
|
|
|
_BPY_PROP_COLLECTION_ID = ":class:`%s`" % _BPY_PROP_COLLECTION_FAKE
|
|
|
|
else:
|
|
|
|
_BPY_PROP_COLLECTION_ID = "collection"
|
2010-12-10 01:20:32 +01:00
|
|
|
|
2020-05-11 08:56:09 +02:00
|
|
|
if _BPY_STRUCT_FAKE:
|
|
|
|
bpy_struct = bpy.types.bpy_struct
|
|
|
|
else:
|
|
|
|
bpy_struct = None
|
|
|
|
|
2011-03-27 07:23:14 +02:00
|
|
|
|
2020-10-09 02:56:11 +02:00
|
|
|
def import_value_from_module(module_name, import_name):
|
|
|
|
ns = {}
|
|
|
|
exec_str = "from %s import %s as value" % (module_name, import_name)
|
|
|
|
exec(exec_str, ns, ns)
|
|
|
|
return ns["value"]
|
|
|
|
|
|
|
|
|
2022-05-22 05:30:17 +02:00
|
|
|
def execfile(filepath):
|
|
|
|
global_namespace = {"__file__": filepath, "__name__": "__main__"}
|
2022-06-01 07:48:20 +02:00
|
|
|
with open(filepath, encoding="utf-8") as file_handle:
|
2022-05-22 05:30:17 +02:00
|
|
|
exec(compile(file_handle.read(), filepath, 'exec'), global_namespace)
|
|
|
|
|
|
|
|
|
2015-09-17 17:47:58 +02:00
|
|
|
def escape_rst(text):
|
2022-05-21 08:33:35 +02:00
|
|
|
"""
|
|
|
|
Escape plain text which may contain characters used by RST.
|
2015-09-17 17:47:58 +02:00
|
|
|
"""
|
|
|
|
return text.translate(escape_rst.trans)
|
2018-07-03 06:47:49 +02:00
|
|
|
|
|
|
|
|
2015-09-17 17:47:58 +02:00
|
|
|
escape_rst.trans = str.maketrans({
|
|
|
|
"`": "\\`",
|
|
|
|
"|": "\\|",
|
|
|
|
"*": "\\*",
|
|
|
|
"\\": "\\\\",
|
2016-07-30 10:39:22 +02:00
|
|
|
})
|
2015-09-17 17:47:58 +02:00
|
|
|
|
|
|
|
|
2011-11-04 05:27:46 +01:00
|
|
|
def is_struct_seq(value):
|
2022-05-22 05:30:17 +02:00
|
|
|
return isinstance(value, tuple) and type(value) != tuple and hasattr(value, "n_fields")
|
2011-11-04 05:27:46 +01:00
|
|
|
|
2012-03-24 08:36:32 +01:00
|
|
|
|
2010-06-15 04:06:01 +02:00
|
|
|
def undocumented_message(module_name, type_name, identifier):
|
2022-05-22 05:30:17 +02:00
|
|
|
BPY_LOGGER.debug(
|
|
|
|
"Undocumented: module %s, type: %s, id: %s is not documented",
|
|
|
|
module_name, type_name, identifier,
|
|
|
|
)
|
|
|
|
|
2020-05-12 03:35:33 +02:00
|
|
|
return "Undocumented, consider `contributing <https://developer.blender.org/T51061>`__."
|
2010-06-15 04:06:01 +02:00
|
|
|
|
2010-06-12 00:41:13 +02:00
|
|
|
|
2009-12-25 16:50:53 +01:00
|
|
|
def range_str(val):
|
2022-05-21 08:33:35 +02:00
|
|
|
"""
|
2010-06-15 04:06:01 +02:00
|
|
|
Converts values to strings for the range directive.
|
|
|
|
(unused function it seems)
|
2022-05-21 08:33:35 +02:00
|
|
|
"""
|
2011-02-04 10:27:25 +01:00
|
|
|
if val < -10000000:
|
2022-05-21 08:33:35 +02:00
|
|
|
return "-inf"
|
2011-02-04 10:27:25 +01:00
|
|
|
elif val > 10000000:
|
2022-05-21 08:33:35 +02:00
|
|
|
return "inf"
|
2011-02-04 10:27:25 +01:00
|
|
|
elif type(val) == float:
|
2022-05-21 08:33:35 +02:00
|
|
|
return "%g" % val
|
2009-12-25 16:50:53 +01:00
|
|
|
else:
|
|
|
|
return str(val)
|
|
|
|
|
2010-02-28 14:45:08 +01:00
|
|
|
|
2011-02-16 18:31:04 +01:00
|
|
|
def example_extract_docstring(filepath):
|
2022-05-21 08:33:35 +02:00
|
|
|
"""
|
2021-05-17 09:56:18 +02:00
|
|
|
Return (text, line_no, line_no_has_content) where:
|
|
|
|
- ``text`` is the doc-string text.
|
|
|
|
- ``line_no`` is the line the doc-string text ends.
|
|
|
|
- ``line_no_has_content`` when False, this file only contains a doc-string.
|
|
|
|
There is no need to include the remainder.
|
2022-05-21 08:33:35 +02:00
|
|
|
"""
|
2011-09-29 14:11:58 +02:00
|
|
|
file = open(filepath, "r", encoding="utf-8")
|
2011-02-16 18:31:04 +01:00
|
|
|
line = file.readline()
|
|
|
|
line_no = 0
|
|
|
|
text = []
|
|
|
|
if line.startswith('"""'): # assume nothing here
|
|
|
|
line_no += 1
|
|
|
|
else:
|
|
|
|
file.close()
|
2021-05-17 09:56:18 +02:00
|
|
|
return "", 0, True
|
2011-02-16 18:31:04 +01:00
|
|
|
|
2020-12-08 10:49:28 +01:00
|
|
|
for line in file:
|
2011-02-16 18:31:04 +01:00
|
|
|
line_no += 1
|
|
|
|
if line.startswith('"""'):
|
|
|
|
break
|
2022-05-22 05:30:17 +02:00
|
|
|
text.append(line.rstrip())
|
2011-02-16 18:31:04 +01:00
|
|
|
|
|
|
|
line_no += 1
|
2021-03-31 08:44:16 +02:00
|
|
|
line_no_has_content = False
|
2020-12-08 10:49:28 +01:00
|
|
|
|
|
|
|
# Skip over blank lines so the Python code doesn't have blank lines at the top.
|
|
|
|
for line in file:
|
|
|
|
if line.strip():
|
2021-03-31 08:44:16 +02:00
|
|
|
line_no_has_content = True
|
2020-12-08 10:49:28 +01:00
|
|
|
break
|
|
|
|
line_no += 1
|
|
|
|
|
2011-02-16 18:31:04 +01:00
|
|
|
file.close()
|
2022-04-19 04:17:21 +02:00
|
|
|
return "\n".join(text).rstrip("\n"), line_no, line_no_has_content
|
2011-02-16 18:31:04 +01:00
|
|
|
|
|
|
|
|
2012-03-21 05:43:17 +01:00
|
|
|
def title_string(text, heading_char, double=False):
|
|
|
|
filler = len(text) * heading_char
|
2012-03-24 08:36:32 +01:00
|
|
|
|
2012-03-21 05:43:17 +01:00
|
|
|
if double:
|
|
|
|
return "%s\n%s\n%s\n\n" % (filler, text, filler)
|
|
|
|
else:
|
|
|
|
return "%s\n%s\n\n" % (text, filler)
|
2011-03-22 05:28:51 +01:00
|
|
|
|
|
|
|
|
2010-06-15 04:06:01 +02:00
|
|
|
def write_example_ref(ident, fw, example_id, ext="py"):
|
2010-02-28 14:45:08 +01:00
|
|
|
if example_id in EXAMPLE_SET:
|
2011-02-18 09:47:37 +01:00
|
|
|
|
2022-05-21 08:33:35 +02:00
|
|
|
# Extract the comment.
|
2012-03-16 11:49:39 +01:00
|
|
|
filepath = os.path.join("..", "examples", "%s.%s" % (example_id, ext))
|
2011-02-16 18:31:04 +01:00
|
|
|
filepath_full = os.path.join(os.path.dirname(fw.__self__.name), filepath)
|
2011-02-18 09:47:37 +01:00
|
|
|
|
2021-03-31 08:44:16 +02:00
|
|
|
text, line_no, line_no_has_content = example_extract_docstring(filepath_full)
|
2022-04-19 04:17:21 +02:00
|
|
|
if text:
|
|
|
|
# Ensure a blank line, needed since in some cases the indentation doesn't match the previous line.
|
|
|
|
# which causes Sphinx not to warn about bad indentation.
|
|
|
|
fw("\n")
|
|
|
|
for line in text.split("\n"):
|
|
|
|
fw("%s\n" % (ident + line).rstrip())
|
2011-02-18 09:47:37 +01:00
|
|
|
|
2011-02-16 18:31:04 +01:00
|
|
|
fw("\n")
|
|
|
|
|
2021-03-31 08:44:16 +02:00
|
|
|
# Some files only contain a doc-string.
|
|
|
|
if line_no_has_content:
|
|
|
|
fw("%s.. literalinclude:: %s\n" % (ident, filepath))
|
|
|
|
if line_no > 0:
|
|
|
|
fw("%s :lines: %d-\n" % (ident, line_no))
|
|
|
|
fw("\n")
|
2010-02-28 14:45:08 +01:00
|
|
|
EXAMPLE_SET_USED.add(example_id)
|
|
|
|
else:
|
|
|
|
if bpy.app.debug:
|
2022-05-22 05:30:17 +02:00
|
|
|
BPY_LOGGER.debug("\tskipping example: %s", example_id)
|
2010-02-28 14:45:08 +01:00
|
|
|
|
2022-05-21 08:33:35 +02:00
|
|
|
# Support for numbered files `bpy.types.Operator` -> `bpy.types.Operator.1.py`.
|
2011-02-16 18:31:04 +01:00
|
|
|
i = 1
|
|
|
|
while True:
|
|
|
|
example_id_num = "%s.%d" % (example_id, i)
|
|
|
|
if example_id_num in EXAMPLE_SET:
|
|
|
|
write_example_ref(ident, fw, example_id_num, ext)
|
|
|
|
i += 1
|
|
|
|
else:
|
|
|
|
break
|
|
|
|
|
2010-02-28 14:45:08 +01:00
|
|
|
|
2010-01-31 22:52:26 +01:00
|
|
|
def write_indented_lines(ident, fn, text, strip=True):
|
2022-05-21 08:33:35 +02:00
|
|
|
"""
|
|
|
|
Apply same indentation to all lines in a multi-lines text.
|
|
|
|
"""
|
2009-12-25 16:50:53 +01:00
|
|
|
if text is None:
|
|
|
|
return
|
2011-05-28 09:47:58 +02:00
|
|
|
|
|
|
|
lines = text.split("\n")
|
|
|
|
|
2022-05-21 08:33:35 +02:00
|
|
|
# Strip empty lines from the start/end.
|
2011-05-28 09:47:58 +02:00
|
|
|
while lines and not lines[0].strip():
|
|
|
|
del lines[0]
|
|
|
|
while lines and not lines[-1].strip():
|
|
|
|
del lines[-1]
|
|
|
|
|
|
|
|
if strip:
|
2022-05-21 08:33:35 +02:00
|
|
|
# Set indentation to `<indent>`.
|
2011-05-28 09:47:58 +02:00
|
|
|
ident_strip = 1000
|
|
|
|
for l in lines:
|
|
|
|
if l.strip():
|
|
|
|
ident_strip = min(ident_strip, len(l) - len(l.lstrip()))
|
|
|
|
for l in lines:
|
|
|
|
fn(ident + l[ident_strip:] + "\n")
|
|
|
|
else:
|
2022-05-21 08:33:35 +02:00
|
|
|
# Add <indent> number of blanks to the current indentation.
|
2011-05-28 09:47:58 +02:00
|
|
|
for l in lines:
|
2010-01-31 22:52:26 +01:00
|
|
|
fn(ident + l + "\n")
|
2009-12-25 16:50:53 +01:00
|
|
|
|
2010-01-22 03:04:25 +01:00
|
|
|
|
2013-10-15 07:55:51 +02:00
|
|
|
def pyfunc2sphinx(ident, fw, module_name, type_name, identifier, py_func, is_class=True):
|
2022-05-21 08:33:35 +02:00
|
|
|
"""
|
2010-01-22 03:04:25 +01:00
|
|
|
function or class method to sphinx
|
2022-05-21 08:33:35 +02:00
|
|
|
"""
|
2013-01-16 00:17:45 +01:00
|
|
|
|
2013-10-15 07:55:51 +02:00
|
|
|
if type(py_func) == MethodType:
|
2012-12-29 19:25:03 +01:00
|
|
|
return
|
2013-01-16 00:17:45 +01:00
|
|
|
|
2018-09-13 08:33:34 +02:00
|
|
|
arg_str = str(inspect.signature(py_func))
|
2010-01-22 03:04:25 +01:00
|
|
|
|
|
|
|
if not is_class:
|
|
|
|
func_type = "function"
|
2011-02-04 10:27:25 +01:00
|
|
|
|
2022-05-21 08:33:35 +02:00
|
|
|
# The rest are class methods.
|
2015-05-12 08:56:40 +02:00
|
|
|
elif arg_str.startswith("(self, ") or arg_str == "(self)":
|
|
|
|
arg_str = "()" if (arg_str == "(self)") else ("(" + arg_str[7:])
|
2010-01-22 03:04:25 +01:00
|
|
|
func_type = "method"
|
|
|
|
elif arg_str.startswith("(cls, "):
|
2015-05-12 08:56:40 +02:00
|
|
|
arg_str = "()" if (arg_str == "(cls)") else ("(" + arg_str[6:])
|
2010-01-22 03:04:25 +01:00
|
|
|
func_type = "classmethod"
|
|
|
|
else:
|
|
|
|
func_type = "staticmethod"
|
|
|
|
|
2015-05-12 10:06:31 +02:00
|
|
|
doc = py_func.__doc__
|
|
|
|
if (not doc) or (not doc.startswith(".. %s:: " % func_type)):
|
|
|
|
fw(ident + ".. %s:: %s%s\n\n" % (func_type, identifier, arg_str))
|
|
|
|
ident_temp = ident + " "
|
|
|
|
else:
|
|
|
|
ident_temp = ident
|
|
|
|
|
|
|
|
if doc:
|
|
|
|
write_indented_lines(ident_temp, fw, doc)
|
2010-01-22 03:04:25 +01:00
|
|
|
fw("\n")
|
2015-05-12 10:06:31 +02:00
|
|
|
del doc, ident_temp
|
2010-01-22 03:04:25 +01:00
|
|
|
|
2013-10-15 07:55:51 +02:00
|
|
|
if is_class:
|
|
|
|
write_example_ref(ident + " ", fw, module_name + "." + type_name + "." + identifier)
|
|
|
|
else:
|
|
|
|
write_example_ref(ident + " ", fw, module_name + "." + identifier)
|
|
|
|
|
2010-04-10 20:35:50 +02:00
|
|
|
|
2011-02-04 10:27:25 +01:00
|
|
|
def py_descr2sphinx(ident, fw, descr, module_name, type_name, identifier):
|
2010-07-19 15:36:10 +02:00
|
|
|
if identifier.startswith("_"):
|
|
|
|
return
|
2011-02-04 10:27:25 +01:00
|
|
|
|
2010-04-10 20:35:50 +02:00
|
|
|
doc = descr.__doc__
|
|
|
|
if not doc:
|
2010-06-15 04:06:01 +02:00
|
|
|
doc = undocumented_message(module_name, type_name, identifier)
|
2011-02-04 10:27:25 +01:00
|
|
|
|
2010-04-10 20:35:50 +02:00
|
|
|
if type(descr) == GetSetDescriptorType:
|
2020-07-27 19:01:28 +02:00
|
|
|
fw(ident + ".. attribute:: %s\n\n" % identifier)
|
2022-05-25 05:39:47 +02:00
|
|
|
# NOTE: `RST_NOINDEX_ATTR` currently not supported (as it's not used).
|
2010-05-30 02:24:32 +02:00
|
|
|
write_indented_lines(ident + " ", fw, doc, False)
|
2011-05-28 09:47:58 +02:00
|
|
|
fw("\n")
|
2022-05-21 08:33:35 +02:00
|
|
|
elif type(descr) == MemberDescriptorType: # same as above but use "data"
|
2020-07-27 19:01:28 +02:00
|
|
|
fw(ident + ".. data:: %s\n\n" % identifier)
|
2022-05-25 05:39:47 +02:00
|
|
|
# NOTE: `RST_NOINDEX_ATTR` currently not supported (as it's not used).
|
2011-11-04 05:27:46 +01:00
|
|
|
write_indented_lines(ident + " ", fw, doc, False)
|
|
|
|
fw("\n")
|
2014-09-18 09:45:31 +02:00
|
|
|
elif type(descr) in {MethodDescriptorType, ClassMethodDescriptorType}:
|
2010-04-10 20:35:50 +02:00
|
|
|
write_indented_lines(ident, fw, doc, False)
|
2011-05-28 09:47:58 +02:00
|
|
|
fw("\n")
|
2010-04-10 20:35:50 +02:00
|
|
|
else:
|
2010-08-11 19:13:39 +02:00
|
|
|
raise TypeError("type was not GetSetDescriptorType, MethodDescriptorType or ClassMethodDescriptorType")
|
2010-04-10 20:35:50 +02:00
|
|
|
|
2011-03-31 14:45:54 +02:00
|
|
|
write_example_ref(ident + " ", fw, module_name + "." + type_name + "." + identifier)
|
2010-04-10 20:35:50 +02:00
|
|
|
fw("\n")
|
|
|
|
|
2010-06-15 04:06:01 +02:00
|
|
|
|
|
|
|
def py_c_func2sphinx(ident, fw, module_name, type_name, identifier, py_func, is_class=True):
|
2022-05-21 08:33:35 +02:00
|
|
|
"""
|
|
|
|
C defined function to sphinx.
|
|
|
|
"""
|
2011-02-04 10:27:25 +01:00
|
|
|
|
2022-05-21 08:33:35 +02:00
|
|
|
# Dump the doc-string, assume its formatted correctly.
|
2010-01-22 03:04:25 +01:00
|
|
|
if py_func.__doc__:
|
2010-01-31 22:52:26 +01:00
|
|
|
write_indented_lines(ident, fw, py_func.__doc__, False)
|
2010-01-22 03:04:25 +01:00
|
|
|
fw("\n")
|
|
|
|
else:
|
|
|
|
fw(ident + ".. function:: %s()\n\n" % identifier)
|
2010-06-15 04:06:01 +02:00
|
|
|
fw(ident + " " + undocumented_message(module_name, type_name, identifier))
|
|
|
|
|
2011-03-14 11:31:50 +01:00
|
|
|
if is_class:
|
|
|
|
write_example_ref(ident + " ", fw, module_name + "." + type_name + "." + identifier)
|
|
|
|
else:
|
|
|
|
write_example_ref(ident + " ", fw, module_name + "." + identifier)
|
|
|
|
|
|
|
|
fw("\n")
|
|
|
|
|
2010-01-22 03:04:25 +01:00
|
|
|
|
|
|
|
def pyprop2sphinx(ident, fw, identifier, py_prop):
|
2022-05-21 08:33:35 +02:00
|
|
|
"""
|
2016-07-31 06:31:39 +02:00
|
|
|
Python property to sphinx
|
2022-05-21 08:33:35 +02:00
|
|
|
"""
|
|
|
|
# Read-only properties use "data" directive, variables use "attribute" directive.
|
2010-06-28 02:06:23 +02:00
|
|
|
if py_prop.fset is None:
|
2020-07-27 19:01:28 +02:00
|
|
|
fw(ident + ".. data:: %s\n\n" % identifier)
|
2010-06-28 02:06:23 +02:00
|
|
|
else:
|
2020-07-27 19:01:28 +02:00
|
|
|
fw(ident + ".. attribute:: %s\n\n" % identifier)
|
2022-05-25 05:39:47 +02:00
|
|
|
|
|
|
|
# NOTE: `RST_NOINDEX_ATTR` currently not supported (as it's not used).
|
2010-01-22 03:04:25 +01:00
|
|
|
write_indented_lines(ident + " ", fw, py_prop.__doc__)
|
2020-10-07 21:51:03 +02:00
|
|
|
fw("\n")
|
2010-01-22 03:04:25 +01:00
|
|
|
if py_prop.fset is None:
|
|
|
|
fw(ident + " (readonly)\n\n")
|
|
|
|
|
|
|
|
|
2020-10-09 02:56:11 +02:00
|
|
|
def pymodule2sphinx(basepath, module_name, module, title, module_all_extra):
|
2010-01-22 03:04:25 +01:00
|
|
|
import types
|
2010-04-10 21:06:18 +02:00
|
|
|
attribute_set = set()
|
2012-03-07 18:36:38 +01:00
|
|
|
filepath = os.path.join(basepath, module_name + ".rst")
|
2011-02-04 10:27:25 +01:00
|
|
|
|
2011-05-28 09:47:58 +02:00
|
|
|
module_all = getattr(module, "__all__", None)
|
|
|
|
module_dir = sorted(dir(module))
|
|
|
|
|
|
|
|
if module_all:
|
|
|
|
module_dir = module_all
|
|
|
|
|
2022-05-21 08:33:35 +02:00
|
|
|
# TODO: currently only used for classes.
|
|
|
|
# Grouping support.
|
2012-03-21 06:33:37 +01:00
|
|
|
module_grouping = MODULE_GROUPING.get(module_name)
|
2012-03-24 08:36:32 +01:00
|
|
|
|
2012-03-21 06:33:37 +01:00
|
|
|
def module_grouping_index(name):
|
|
|
|
if module_grouping is not None:
|
|
|
|
try:
|
|
|
|
return module_grouping.index(name)
|
|
|
|
except ValueError:
|
|
|
|
pass
|
|
|
|
return -1
|
|
|
|
|
|
|
|
def module_grouping_heading(name):
|
|
|
|
if module_grouping is not None:
|
|
|
|
i = module_grouping_index(name) - 1
|
|
|
|
if i >= 0 and type(module_grouping[i]) == tuple:
|
|
|
|
return module_grouping[i]
|
|
|
|
return None, None
|
2012-03-24 08:36:32 +01:00
|
|
|
|
2012-03-21 06:33:37 +01:00
|
|
|
def module_grouping_sort_key(name):
|
|
|
|
return module_grouping_index(name)
|
2022-05-21 08:33:35 +02:00
|
|
|
# Done grouping support.
|
2012-03-21 06:33:37 +01:00
|
|
|
|
2011-09-29 14:11:58 +02:00
|
|
|
file = open(filepath, "w", encoding="utf-8")
|
2010-01-27 22:33:39 +01:00
|
|
|
|
2010-01-22 03:04:25 +01:00
|
|
|
fw = file.write
|
2011-02-04 10:27:25 +01:00
|
|
|
|
2012-03-21 05:43:17 +01:00
|
|
|
fw(title_string("%s (%s)" % (title, module_name), "="))
|
2011-02-04 10:27:25 +01:00
|
|
|
|
2010-01-22 03:04:25 +01:00
|
|
|
fw(".. module:: %s\n\n" % module_name)
|
2011-02-04 10:27:25 +01:00
|
|
|
|
2010-01-22 03:04:25 +01:00
|
|
|
if module.__doc__:
|
2018-09-03 16:49:08 +02:00
|
|
|
# Note, may contain sphinx syntax, don't mangle!
|
2010-01-22 03:04:25 +01:00
|
|
|
fw(module.__doc__.strip())
|
|
|
|
fw("\n\n")
|
2011-02-04 10:27:25 +01:00
|
|
|
|
2022-05-21 08:33:35 +02:00
|
|
|
# Write sub-modules.
|
|
|
|
# We could also scan files but this ensures `__all__` is used correctly.
|
2020-10-09 02:56:11 +02:00
|
|
|
if module_all or module_all_extra:
|
2011-05-28 09:47:58 +02:00
|
|
|
submod_name = None
|
|
|
|
submod = None
|
|
|
|
submod_ls = []
|
2020-10-09 02:56:11 +02:00
|
|
|
for submod_name in (module_all or ()):
|
|
|
|
submod = import_value_from_module(module_name, submod_name)
|
2011-05-28 09:47:58 +02:00
|
|
|
if type(submod) == types.ModuleType:
|
|
|
|
submod_ls.append((submod_name, submod))
|
|
|
|
|
2020-10-09 02:56:11 +02:00
|
|
|
for submod_name in module_all_extra:
|
|
|
|
if submod_name in attribute_set:
|
|
|
|
continue
|
|
|
|
submod = import_value_from_module(module_name, submod_name)
|
|
|
|
# No type checks, since there are non-module types we treat as modules
|
|
|
|
# such as `bpy.app.translations` & `bpy.app.handlers`.
|
|
|
|
submod_ls.append((submod_name, submod))
|
|
|
|
|
2011-05-28 09:47:58 +02:00
|
|
|
del submod_name
|
|
|
|
del submod
|
|
|
|
|
|
|
|
if submod_ls:
|
|
|
|
fw(".. toctree::\n")
|
2020-10-11 22:09:42 +02:00
|
|
|
fw(" :maxdepth: 1\n")
|
|
|
|
fw(" :caption: Submodules\n\n")
|
2011-05-28 09:47:58 +02:00
|
|
|
|
|
|
|
for submod_name, submod in submod_ls:
|
|
|
|
submod_name_full = "%s.%s" % (module_name, submod_name)
|
2020-10-09 02:56:11 +02:00
|
|
|
fw(" %s.rst\n" % submod_name_full)
|
2011-05-28 09:47:58 +02:00
|
|
|
|
2020-10-09 02:56:11 +02:00
|
|
|
pymodule2sphinx(basepath, submod_name_full, submod, "%s submodule" % module_name, ())
|
|
|
|
fw("\n")
|
2011-05-28 09:47:58 +02:00
|
|
|
del submod_ls
|
2022-05-21 08:33:35 +02:00
|
|
|
# Done writing sub-modules!
|
2011-05-28 09:47:58 +02:00
|
|
|
|
2020-10-09 02:56:11 +02:00
|
|
|
write_example_ref("", fw, module_name)
|
|
|
|
|
2010-01-31 22:52:26 +01:00
|
|
|
# write members of the module
|
|
|
|
# only tested with PyStructs which are not exactly modules
|
2010-04-10 20:35:50 +02:00
|
|
|
for key, descr in sorted(type(module).__dict__.items()):
|
2010-12-06 13:36:55 +01:00
|
|
|
if key.startswith("__"):
|
|
|
|
continue
|
2020-10-09 02:56:11 +02:00
|
|
|
if key in module_all_extra:
|
|
|
|
continue
|
2022-05-21 08:33:35 +02:00
|
|
|
# Naughty! We also add `getset` to `PyStruct`, this is not typical Python but also not incorrect.
|
2011-11-04 05:27:46 +01:00
|
|
|
|
2022-05-21 08:33:35 +02:00
|
|
|
# `type_name` is only used for examples and messages:
|
|
|
|
# `<class 'bpy.app.handlers'>` -> `bpy.app.handlers`.
|
2016-07-30 10:39:22 +02:00
|
|
|
type_name = str(type(module)).strip("<>").split(" ", 1)[-1][1:-1]
|
2011-11-04 05:27:46 +01:00
|
|
|
if type(descr) == types.GetSetDescriptorType:
|
|
|
|
py_descr2sphinx("", fw, descr, module_name, type_name, key)
|
2010-12-06 13:36:55 +01:00
|
|
|
attribute_set.add(key)
|
2011-11-04 05:27:46 +01:00
|
|
|
descr_sorted = []
|
2010-12-06 13:36:55 +01:00
|
|
|
for key, descr in sorted(type(module).__dict__.items()):
|
|
|
|
if key.startswith("__"):
|
|
|
|
continue
|
|
|
|
|
2011-11-04 05:27:46 +01:00
|
|
|
if type(descr) == MemberDescriptorType:
|
2010-01-31 22:52:26 +01:00
|
|
|
if descr.__doc__:
|
2011-11-04 05:27:46 +01:00
|
|
|
value = getattr(module, key, None)
|
2011-11-05 02:48:10 +01:00
|
|
|
|
2011-11-04 05:27:46 +01:00
|
|
|
value_type = type(value)
|
|
|
|
descr_sorted.append((key, descr, value, type(value)))
|
2022-05-21 08:33:35 +02:00
|
|
|
# Sort by the value type.
|
2011-11-04 05:27:46 +01:00
|
|
|
descr_sorted.sort(key=lambda descr_data: str(descr_data[3]))
|
|
|
|
for key, descr, value, value_type in descr_sorted:
|
2020-10-09 02:56:11 +02:00
|
|
|
if key in module_all_extra:
|
|
|
|
continue
|
2011-11-04 05:27:46 +01:00
|
|
|
|
2022-05-21 08:33:35 +02:00
|
|
|
# Must be documented as a sub-module.
|
2011-11-04 05:27:46 +01:00
|
|
|
if is_struct_seq(value):
|
2011-11-05 02:48:10 +01:00
|
|
|
continue
|
|
|
|
|
|
|
|
type_name = value_type.__name__
|
|
|
|
py_descr2sphinx("", fw, descr, module_name, type_name, key)
|
2011-11-04 05:27:46 +01:00
|
|
|
|
|
|
|
attribute_set.add(key)
|
|
|
|
|
|
|
|
del key, descr, descr_sorted
|
2011-02-04 10:27:25 +01:00
|
|
|
|
Mathutils refactor & include in sphinx generated docs, (TODO, include getset'ers in docs)
- Mathutils.MidpointVecs --> vector.lerp(other, fac)
- Mathutils.AngleBetweenVecs --> vector.angle(other)
- Mathutils.ProjectVecs --> vector.project(other)
- Mathutils.DifferenceQuats --> quat.difference(other)
- Mathutils.Slerp --> quat.slerp(other, fac)
- Mathutils.Rand: removed, use pythons random module
- Mathutils.RotationMatrix(angle, size, axis_flag, axis) --> Mathutils.RotationMatrix(angle, size, axis); merge axis & axis_flag args
- Matrix.scalePart --> Matrix.scale_part
- Matrix.translationPart --> Matrix.translation_part
- Matrix.rotationPart --> Matrix.rotation_part
- toMatrix --> to_matrix
- toEuler --> to_euler
- toQuat --> to_quat
- Vector.toTrackQuat --> Vector.to_track_quat
2010-01-25 10:44:04 +01:00
|
|
|
classes = []
|
2011-07-31 05:15:37 +02:00
|
|
|
submodules = []
|
2010-01-22 03:04:25 +01:00
|
|
|
|
2022-05-21 08:33:35 +02:00
|
|
|
# Use this list so we can sort by type.
|
2011-11-04 05:27:46 +01:00
|
|
|
module_dir_value_type = []
|
|
|
|
|
2011-05-28 09:47:58 +02:00
|
|
|
for attribute in module_dir:
|
2011-11-04 05:27:46 +01:00
|
|
|
if attribute.startswith("_"):
|
|
|
|
continue
|
2010-04-10 21:06:18 +02:00
|
|
|
|
2011-11-04 05:27:46 +01:00
|
|
|
if attribute in attribute_set:
|
|
|
|
continue
|
2011-02-04 10:27:25 +01:00
|
|
|
|
2022-05-21 08:33:35 +02:00
|
|
|
if attribute.startswith("n_"): # Annoying exception, needed for `bpy.app`.
|
2011-11-04 05:27:46 +01:00
|
|
|
continue
|
|
|
|
|
|
|
|
# workaround for bpy.app documenting .index() and .count()
|
|
|
|
if isinstance(module, tuple) and hasattr(tuple, attribute):
|
|
|
|
continue
|
|
|
|
|
|
|
|
value = getattr(module, attribute)
|
|
|
|
|
|
|
|
module_dir_value_type.append((attribute, value, type(value)))
|
|
|
|
|
|
|
|
# sort by str of each type
|
|
|
|
# this way lists, functions etc are grouped.
|
|
|
|
module_dir_value_type.sort(key=lambda triple: str(triple[2]))
|
|
|
|
|
|
|
|
for attribute, value, value_type in module_dir_value_type:
|
2020-10-09 02:56:11 +02:00
|
|
|
if attribute in module_all_extra:
|
|
|
|
continue
|
|
|
|
|
2015-05-17 09:17:31 +02:00
|
|
|
if value_type == FunctionType:
|
2013-10-15 07:55:51 +02:00
|
|
|
pyfunc2sphinx("", fw, module_name, None, attribute, value, is_class=False)
|
2022-05-21 08:33:35 +02:00
|
|
|
# Both the same at the moment but to be future proof.
|
2016-07-30 10:39:22 +02:00
|
|
|
elif value_type in {types.BuiltinMethodType, types.BuiltinFunctionType}:
|
2022-05-21 08:33:35 +02:00
|
|
|
# NOTE: can't get args from these, so dump the string as is
|
|
|
|
# this means any module used like this must have fully formatted doc-strings.
|
2011-11-04 05:27:46 +01:00
|
|
|
py_c_func2sphinx("", fw, module_name, None, attribute, value, is_class=False)
|
|
|
|
elif value_type == type:
|
|
|
|
classes.append((attribute, value))
|
|
|
|
elif issubclass(value_type, types.ModuleType):
|
|
|
|
submodules.append((attribute, value))
|
2013-04-19 15:00:21 +02:00
|
|
|
elif issubclass(value_type, (bool, int, float, str, tuple)):
|
2022-05-21 08:33:35 +02:00
|
|
|
# Constant, not much fun we can do here except to list it.
|
|
|
|
# TODO: figure out some way to document these!
|
2020-07-27 19:01:28 +02:00
|
|
|
fw(".. data:: %s\n\n" % attribute)
|
2021-05-30 17:09:01 +02:00
|
|
|
write_indented_lines(" ", fw, "Constant value %s" % repr(value), False)
|
2011-11-04 05:27:46 +01:00
|
|
|
fw("\n")
|
|
|
|
else:
|
2022-05-23 03:41:39 +02:00
|
|
|
BPY_LOGGER.debug("\tnot documenting %s.%s of %r type", module_name, attribute, value_type.__name__)
|
2011-11-04 05:27:46 +01:00
|
|
|
continue
|
2010-04-10 21:06:18 +02:00
|
|
|
|
2011-11-04 05:27:46 +01:00
|
|
|
attribute_set.add(attribute)
|
2022-05-21 08:33:35 +02:00
|
|
|
# TODO: more types.
|
2011-11-04 05:27:46 +01:00
|
|
|
del module_dir_value_type
|
2010-04-10 20:35:50 +02:00
|
|
|
|
2022-05-21 08:33:35 +02:00
|
|
|
# TODO: `bpy_extras` does this already, `mathutils` not.
|
|
|
|
"""
|
2011-07-31 05:15:37 +02:00
|
|
|
if submodules:
|
|
|
|
fw("\n"
|
|
|
|
"**********\n"
|
|
|
|
"Submodules\n"
|
|
|
|
"**********\n"
|
|
|
|
"\n"
|
|
|
|
)
|
|
|
|
for attribute, submod in submodules:
|
|
|
|
fw("* :mod:`%s.%s`\n" % (module_name, attribute))
|
|
|
|
fw("\n")
|
2022-05-21 08:33:35 +02:00
|
|
|
"""
|
2011-07-31 05:15:37 +02:00
|
|
|
|
2012-03-21 06:33:37 +01:00
|
|
|
if module_grouping is not None:
|
|
|
|
classes.sort(key=lambda pair: module_grouping_sort_key(pair[0]))
|
|
|
|
|
2022-05-21 08:33:35 +02:00
|
|
|
# Write collected classes now.
|
2010-04-10 20:35:50 +02:00
|
|
|
for (type_name, value) in classes:
|
2012-03-24 08:36:32 +01:00
|
|
|
|
2012-03-21 06:33:37 +01:00
|
|
|
if module_grouping is not None:
|
|
|
|
heading, heading_char = module_grouping_heading(type_name)
|
|
|
|
if heading:
|
|
|
|
fw(title_string(heading, heading_char))
|
|
|
|
|
2022-05-21 08:33:35 +02:00
|
|
|
# May need to be its own function.
|
Mathutils refactor & include in sphinx generated docs, (TODO, include getset'ers in docs)
- Mathutils.MidpointVecs --> vector.lerp(other, fac)
- Mathutils.AngleBetweenVecs --> vector.angle(other)
- Mathutils.ProjectVecs --> vector.project(other)
- Mathutils.DifferenceQuats --> quat.difference(other)
- Mathutils.Slerp --> quat.slerp(other, fac)
- Mathutils.Rand: removed, use pythons random module
- Mathutils.RotationMatrix(angle, size, axis_flag, axis) --> Mathutils.RotationMatrix(angle, size, axis); merge axis & axis_flag args
- Matrix.scalePart --> Matrix.scale_part
- Matrix.translationPart --> Matrix.translation_part
- Matrix.rotationPart --> Matrix.rotation_part
- toMatrix --> to_matrix
- toEuler --> to_euler
- toQuat --> to_quat
- Vector.toTrackQuat --> Vector.to_track_quat
2010-01-25 10:44:04 +01:00
|
|
|
if value.__doc__:
|
2015-02-01 14:00:37 +01:00
|
|
|
if value.__doc__.startswith(".. class::"):
|
|
|
|
fw(value.__doc__)
|
|
|
|
else:
|
|
|
|
fw(".. class:: %s\n\n" % type_name)
|
2015-05-12 10:06:31 +02:00
|
|
|
write_indented_lines(" ", fw, value.__doc__, True)
|
2015-02-01 14:00:37 +01:00
|
|
|
else:
|
|
|
|
fw(".. class:: %s\n\n" % type_name)
|
|
|
|
fw("\n")
|
|
|
|
|
2010-04-10 20:35:50 +02:00
|
|
|
write_example_ref(" ", fw, module_name + "." + type_name)
|
Mathutils refactor & include in sphinx generated docs, (TODO, include getset'ers in docs)
- Mathutils.MidpointVecs --> vector.lerp(other, fac)
- Mathutils.AngleBetweenVecs --> vector.angle(other)
- Mathutils.ProjectVecs --> vector.project(other)
- Mathutils.DifferenceQuats --> quat.difference(other)
- Mathutils.Slerp --> quat.slerp(other, fac)
- Mathutils.Rand: removed, use pythons random module
- Mathutils.RotationMatrix(angle, size, axis_flag, axis) --> Mathutils.RotationMatrix(angle, size, axis); merge axis & axis_flag args
- Matrix.scalePart --> Matrix.scale_part
- Matrix.translationPart --> Matrix.translation_part
- Matrix.rotationPart --> Matrix.rotation_part
- toMatrix --> to_matrix
- toEuler --> to_euler
- toQuat --> to_quat
- Vector.toTrackQuat --> Vector.to_track_quat
2010-01-25 10:44:04 +01:00
|
|
|
|
2015-05-12 08:56:40 +02:00
|
|
|
descr_items = [(key, descr) for key, descr in sorted(value.__dict__.items()) if not key.startswith("_")]
|
2010-04-10 20:35:50 +02:00
|
|
|
|
2010-08-11 19:13:39 +02:00
|
|
|
for key, descr in descr_items:
|
2010-08-12 00:36:43 +02:00
|
|
|
if type(descr) == ClassMethodDescriptorType:
|
2010-08-11 19:13:39 +02:00
|
|
|
py_descr2sphinx(" ", fw, descr, module_name, type_name, key)
|
|
|
|
|
2022-05-21 08:33:35 +02:00
|
|
|
# Needed for pure Python classes.
|
2015-05-12 10:06:31 +02:00
|
|
|
for key, descr in descr_items:
|
2015-05-17 09:17:31 +02:00
|
|
|
if type(descr) == FunctionType:
|
2015-05-12 10:06:31 +02:00
|
|
|
pyfunc2sphinx(" ", fw, module_name, type_name, key, descr, is_class=True)
|
|
|
|
|
2010-04-10 20:35:50 +02:00
|
|
|
for key, descr in descr_items:
|
2010-08-12 00:36:43 +02:00
|
|
|
if type(descr) == MethodDescriptorType:
|
2010-04-10 20:35:50 +02:00
|
|
|
py_descr2sphinx(" ", fw, descr, module_name, type_name, key)
|
|
|
|
|
|
|
|
for key, descr in descr_items:
|
|
|
|
if type(descr) == GetSetDescriptorType:
|
|
|
|
py_descr2sphinx(" ", fw, descr, module_name, type_name, key)
|
|
|
|
|
2013-04-07 08:56:49 +02:00
|
|
|
for key, descr in descr_items:
|
|
|
|
if type(descr) == StaticMethodType:
|
|
|
|
descr = getattr(value, key)
|
|
|
|
write_indented_lines(" ", fw, descr.__doc__ or "Undocumented", False)
|
|
|
|
fw("\n")
|
|
|
|
|
Mathutils refactor & include in sphinx generated docs, (TODO, include getset'ers in docs)
- Mathutils.MidpointVecs --> vector.lerp(other, fac)
- Mathutils.AngleBetweenVecs --> vector.angle(other)
- Mathutils.ProjectVecs --> vector.project(other)
- Mathutils.DifferenceQuats --> quat.difference(other)
- Mathutils.Slerp --> quat.slerp(other, fac)
- Mathutils.Rand: removed, use pythons random module
- Mathutils.RotationMatrix(angle, size, axis_flag, axis) --> Mathutils.RotationMatrix(angle, size, axis); merge axis & axis_flag args
- Matrix.scalePart --> Matrix.scale_part
- Matrix.translationPart --> Matrix.translation_part
- Matrix.rotationPart --> Matrix.rotation_part
- toMatrix --> to_matrix
- toEuler --> to_euler
- toQuat --> to_quat
- Vector.toTrackQuat --> Vector.to_track_quat
2010-01-25 10:44:04 +01:00
|
|
|
fw("\n\n")
|
2010-01-22 03:04:25 +01:00
|
|
|
|
|
|
|
file.close()
|
|
|
|
|
2018-07-03 06:47:49 +02:00
|
|
|
|
2022-05-21 08:33:35 +02:00
|
|
|
# Changes In Blender will force errors here.
|
2012-11-03 08:28:51 +01:00
|
|
|
context_type_map = {
|
2018-10-19 20:13:50 +02:00
|
|
|
# context_member: (RNA type, is_collection)
|
2022-07-26 04:17:16 +02:00
|
|
|
"active_action": ("Action", False),
|
2020-01-13 15:57:15 +01:00
|
|
|
"active_annotation_layer": ("GPencilLayer", False),
|
2012-11-03 10:14:06 +01:00
|
|
|
"active_bone": ("EditBone", False),
|
2021-09-01 16:27:29 +02:00
|
|
|
"active_file": ("FileSelectEntry", False),
|
2014-12-29 12:15:14 +01:00
|
|
|
"active_gpencil_frame": ("GreasePencilLayer", True),
|
|
|
|
"active_gpencil_layer": ("GPencilLayer", True),
|
|
|
|
"active_node": ("Node", False),
|
2012-11-03 08:28:51 +01:00
|
|
|
"active_object": ("Object", False),
|
|
|
|
"active_operator": ("Operator", False),
|
|
|
|
"active_pose_bone": ("PoseBone", False),
|
2021-08-31 00:17:11 +02:00
|
|
|
"active_sequence_strip": ("Sequence", False),
|
2019-06-24 12:05:36 +02:00
|
|
|
"active_editable_fcurve": ("FCurve", False),
|
2021-08-31 03:46:46 +02:00
|
|
|
"active_nla_strip": ("NlaStrip", False),
|
|
|
|
"active_nla_track": ("NlaTrack", False),
|
2020-01-13 15:57:15 +01:00
|
|
|
"annotation_data": ("GreasePencil", False),
|
|
|
|
"annotation_data_owner": ("ID", False),
|
2012-11-03 08:28:51 +01:00
|
|
|
"armature": ("Armature", False),
|
2021-08-10 09:14:55 +02:00
|
|
|
"asset_library_ref": ("AssetLibraryReference", False),
|
2012-11-03 08:28:51 +01:00
|
|
|
"bone": ("Bone", False),
|
|
|
|
"brush": ("Brush", False),
|
|
|
|
"camera": ("Camera", False),
|
|
|
|
"cloth": ("ClothModifier", False),
|
2017-06-24 08:56:00 +02:00
|
|
|
"collection": ("LayerCollection", False),
|
2012-11-03 08:28:51 +01:00
|
|
|
"collision": ("CollisionModifier", False),
|
|
|
|
"curve": ("Curve", False),
|
|
|
|
"dynamic_paint": ("DynamicPaintModifier", False),
|
|
|
|
"edit_bone": ("EditBone", False),
|
|
|
|
"edit_image": ("Image", False),
|
|
|
|
"edit_mask": ("Mask", False),
|
|
|
|
"edit_movieclip": ("MovieClip", False),
|
|
|
|
"edit_object": ("Object", False),
|
|
|
|
"edit_text": ("Text", False),
|
|
|
|
"editable_bones": ("EditBone", True),
|
2014-12-29 12:15:14 +01:00
|
|
|
"editable_gpencil_layers": ("GPencilLayer", True),
|
|
|
|
"editable_gpencil_strokes": ("GPencilStroke", True),
|
2016-02-27 18:23:58 +01:00
|
|
|
"editable_objects": ("Object", True),
|
2019-06-24 12:05:36 +02:00
|
|
|
"editable_fcurves": ("FCurve", True),
|
2012-11-03 08:28:51 +01:00
|
|
|
"fluid": ("FluidSimulationModifier", False),
|
2019-03-18 14:42:07 +01:00
|
|
|
"gpencil": ("GreasePencil", False),
|
|
|
|
"gpencil_data": ("GreasePencil", False),
|
2014-12-29 12:15:14 +01:00
|
|
|
"gpencil_data_owner": ("ID", False),
|
2022-02-10 22:51:00 +01:00
|
|
|
"curves": ("Hair Curves", False),
|
2021-09-01 16:27:29 +02:00
|
|
|
"id": ("ID", False),
|
2012-11-03 08:28:51 +01:00
|
|
|
"image_paint_object": ("Object", False),
|
|
|
|
"lattice": ("Lattice", False),
|
2018-12-17 07:55:18 +01:00
|
|
|
"light": ("Light", False),
|
2017-06-24 08:56:00 +02:00
|
|
|
"lightprobe": ("LightProbe", False),
|
2014-05-07 23:08:54 +02:00
|
|
|
"line_style": ("FreestyleLineStyle", False),
|
2012-11-03 08:28:51 +01:00
|
|
|
"material": ("Material", False),
|
|
|
|
"material_slot": ("MaterialSlot", False),
|
|
|
|
"mesh": ("Mesh", False),
|
|
|
|
"meta_ball": ("MetaBall", False),
|
|
|
|
"object": ("Object", False),
|
2018-12-17 07:55:18 +01:00
|
|
|
"objects_in_mode": ("Object", True),
|
|
|
|
"objects_in_mode_unique_data": ("Object", True),
|
2012-11-03 08:28:51 +01:00
|
|
|
"particle_edit_object": ("Object", False),
|
|
|
|
"particle_settings": ("ParticleSettings", False),
|
|
|
|
"particle_system": ("ParticleSystem", False),
|
|
|
|
"particle_system_editable": ("ParticleSystem", False),
|
2020-06-23 15:30:09 +02:00
|
|
|
"pointcloud": ("PointCloud", False),
|
2012-11-03 08:28:51 +01:00
|
|
|
"pose_bone": ("PoseBone", False),
|
2019-03-12 00:18:51 +01:00
|
|
|
"pose_object": ("Object", False),
|
2012-11-03 08:28:51 +01:00
|
|
|
"scene": ("Scene", False),
|
|
|
|
"sculpt_object": ("Object", False),
|
|
|
|
"selectable_objects": ("Object", True),
|
2021-10-03 23:58:20 +02:00
|
|
|
"selected_asset_files": ("FileSelectEntry", True),
|
2012-11-03 10:14:06 +01:00
|
|
|
"selected_bones": ("EditBone", True),
|
2021-07-16 11:36:57 +02:00
|
|
|
"selected_editable_actions": ("Action", True),
|
2012-11-03 10:14:06 +01:00
|
|
|
"selected_editable_bones": ("EditBone", True),
|
2018-12-13 22:13:55 +01:00
|
|
|
"selected_editable_fcurves": ("FCurve", True),
|
2020-10-16 11:51:58 +02:00
|
|
|
"selected_editable_keyframes": ("Keyframe", True),
|
2012-11-03 08:28:51 +01:00
|
|
|
"selected_editable_objects": ("Object", True),
|
|
|
|
"selected_editable_sequences": ("Sequence", True),
|
2021-09-01 16:27:29 +02:00
|
|
|
"selected_files": ("FileSelectEntry", True),
|
2021-10-28 10:31:29 +02:00
|
|
|
"selected_ids": ("ID", True),
|
2020-04-06 12:15:33 +02:00
|
|
|
"selected_nla_strips": ("NlaStrip", True),
|
2021-10-19 16:24:23 +02:00
|
|
|
"selected_movieclip_tracks": ("MovieTrackingTrack", True),
|
2012-11-03 08:28:51 +01:00
|
|
|
"selected_nodes": ("Node", True),
|
|
|
|
"selected_objects": ("Object", True),
|
|
|
|
"selected_pose_bones": ("PoseBone", True),
|
2018-10-19 20:13:50 +02:00
|
|
|
"selected_pose_bones_from_active_object": ("PoseBone", True),
|
2012-11-03 08:28:51 +01:00
|
|
|
"selected_sequences": ("Sequence", True),
|
2021-07-16 11:36:57 +02:00
|
|
|
"selected_visible_actions": ("Action", True),
|
2019-06-24 12:05:36 +02:00
|
|
|
"selected_visible_fcurves": ("FCurve", True),
|
2012-11-03 08:28:51 +01:00
|
|
|
"sequences": ("Sequence", True),
|
|
|
|
"soft_body": ("SoftBodyModifier", False),
|
|
|
|
"speaker": ("Speaker", False),
|
|
|
|
"texture": ("Texture", False),
|
2021-12-07 04:07:38 +01:00
|
|
|
"texture_slot": ("TextureSlot", False),
|
2012-11-03 08:28:51 +01:00
|
|
|
"texture_user": ("ID", False),
|
2013-04-29 18:20:49 +02:00
|
|
|
"texture_user_property": ("Property", False),
|
2021-07-20 22:07:20 +02:00
|
|
|
"ui_list": ("UIList", False),
|
2012-11-03 08:28:51 +01:00
|
|
|
"vertex_paint_object": ("Object", False),
|
2018-12-17 07:55:18 +01:00
|
|
|
"view_layer": ("ViewLayer", False),
|
2012-11-03 10:14:06 +01:00
|
|
|
"visible_bones": ("EditBone", True),
|
2014-12-29 12:15:14 +01:00
|
|
|
"visible_gpencil_layers": ("GPencilLayer", True),
|
2012-11-03 08:28:51 +01:00
|
|
|
"visible_objects": ("Object", True),
|
|
|
|
"visible_pose_bones": ("PoseBone", True),
|
2019-06-24 12:05:36 +02:00
|
|
|
"visible_fcurves": ("FCurve", True),
|
2012-11-03 08:28:51 +01:00
|
|
|
"weight_paint_object": ("Object", False),
|
2020-03-20 10:47:56 +01:00
|
|
|
"volume": ("Volume", False),
|
2012-11-03 08:28:51 +01:00
|
|
|
"world": ("World", False),
|
|
|
|
}
|
2010-01-22 03:04:25 +01:00
|
|
|
|
2013-01-16 00:17:45 +01:00
|
|
|
|
2012-03-07 18:36:38 +01:00
|
|
|
def pycontext2sphinx(basepath):
|
2022-05-21 08:33:35 +02:00
|
|
|
# Only use once. very irregular.
|
2011-02-15 16:37:40 +01:00
|
|
|
|
2012-03-07 18:36:38 +01:00
|
|
|
filepath = os.path.join(basepath, "bpy.context.rst")
|
2011-09-29 14:11:58 +02:00
|
|
|
file = open(filepath, "w", encoding="utf-8")
|
2011-02-15 16:37:40 +01:00
|
|
|
fw = file.write
|
2012-03-21 05:43:17 +01:00
|
|
|
fw(title_string("Context Access (bpy.context)", "="))
|
2011-02-15 16:37:40 +01:00
|
|
|
fw(".. module:: bpy.context\n")
|
|
|
|
fw("\n")
|
2016-07-31 06:31:39 +02:00
|
|
|
fw("The context members available depend on the area of Blender which is currently being accessed.\n")
|
2011-02-15 16:37:40 +01:00
|
|
|
fw("\n")
|
2016-07-30 10:39:22 +02:00
|
|
|
fw("Note that all context values are readonly,\n")
|
2020-09-26 04:24:58 +02:00
|
|
|
fw("but may be modified through the data API or by running operators\n\n")
|
2011-02-15 16:37:40 +01:00
|
|
|
|
2022-04-19 04:17:25 +02:00
|
|
|
# Track all unique properties to properly use `noindex`.
|
|
|
|
unique = set()
|
|
|
|
|
2013-04-29 18:20:49 +02:00
|
|
|
def write_contex_cls():
|
|
|
|
|
|
|
|
fw(title_string("Global Context", "-"))
|
2018-09-03 16:49:08 +02:00
|
|
|
fw("These properties are available in any contexts.\n\n")
|
2013-04-29 18:20:49 +02:00
|
|
|
|
2022-05-21 08:33:35 +02:00
|
|
|
# Very silly. could make these global and only access once:
|
|
|
|
# `structs, funcs, ops, props = rna_info.BuildRNAInfo()`.
|
2013-04-29 18:20:49 +02:00
|
|
|
structs, funcs, ops, props = rna_info_BuildRNAInfo_cache()
|
|
|
|
struct = structs[("", "Context")]
|
|
|
|
struct_blacklist = RNA_BLACKLIST.get(struct.identifier, ())
|
|
|
|
del structs, funcs, ops, props
|
|
|
|
|
|
|
|
sorted_struct_properties = struct.properties[:]
|
|
|
|
sorted_struct_properties.sort(key=lambda prop: prop.identifier)
|
|
|
|
|
2022-05-21 08:33:35 +02:00
|
|
|
# First write RNA.
|
2013-04-29 18:20:49 +02:00
|
|
|
for prop in sorted_struct_properties:
|
2022-04-19 04:17:25 +02:00
|
|
|
# Support blacklisting props.
|
2013-04-29 18:20:49 +02:00
|
|
|
if prop.identifier in struct_blacklist:
|
|
|
|
continue
|
2022-04-19 04:17:25 +02:00
|
|
|
# No need to check if there are duplicates yet as it's known there wont be.
|
|
|
|
unique.add(prop.identifier)
|
2013-04-29 18:20:49 +02:00
|
|
|
|
2022-05-31 06:07:14 +02:00
|
|
|
enum_descr_override = None
|
|
|
|
if USE_SHARED_RNA_ENUM_ITEMS_STATIC:
|
|
|
|
enum_descr_override = pyrna_enum2sphinx_shared_link(prop)
|
|
|
|
|
2016-07-30 10:39:22 +02:00
|
|
|
type_descr = prop.get_type_description(
|
2022-05-31 06:07:14 +02:00
|
|
|
class_fmt=":class:`bpy.types.%s`",
|
2022-11-26 23:15:12 +01:00
|
|
|
mathutils_fmt=":class:`mathutils.%s`",
|
2022-05-31 06:07:14 +02:00
|
|
|
collection_id=_BPY_PROP_COLLECTION_ID,
|
|
|
|
enum_descr_override=enum_descr_override,
|
|
|
|
)
|
2020-07-27 19:01:28 +02:00
|
|
|
fw(".. data:: %s\n\n" % prop.identifier)
|
2013-04-29 18:20:49 +02:00
|
|
|
if prop.description:
|
|
|
|
fw(" %s\n\n" % prop.description)
|
|
|
|
|
2022-05-21 08:33:35 +02:00
|
|
|
# Special exception, can't use generic code here for enums.
|
2013-04-29 18:20:49 +02:00
|
|
|
if prop.type == "enum":
|
2022-05-31 06:07:14 +02:00
|
|
|
# If the link has been written, no need to inline the enum items.
|
|
|
|
enum_text = "" if enum_descr_override else pyrna_enum2sphinx(prop)
|
2013-04-29 18:20:49 +02:00
|
|
|
if enum_text:
|
|
|
|
write_indented_lines(" ", fw, enum_text)
|
|
|
|
fw("\n")
|
|
|
|
del enum_text
|
2022-05-21 08:33:35 +02:00
|
|
|
# End enum exception.
|
2013-04-29 18:20:49 +02:00
|
|
|
|
|
|
|
fw(" :type: %s\n\n" % type_descr)
|
|
|
|
|
|
|
|
write_contex_cls()
|
|
|
|
del write_contex_cls
|
|
|
|
# end
|
|
|
|
|
2022-05-18 08:59:16 +02:00
|
|
|
# Internal API call only intended to be used to extract context members.
|
|
|
|
from _bpy import context_members
|
|
|
|
context_member_map = context_members()
|
|
|
|
del context_members
|
2011-02-15 16:37:40 +01:00
|
|
|
|
2022-04-19 04:17:25 +02:00
|
|
|
# Track unique for `context_strings` to validate `context_type_map`.
|
|
|
|
unique_context_strings = set()
|
2022-05-18 08:59:16 +02:00
|
|
|
for ctx_str, ctx_members in sorted(context_member_map.items()):
|
2011-02-15 16:37:40 +01:00
|
|
|
subsection = "%s Context" % ctx_str.split("_")[0].title()
|
2022-05-21 08:33:35 +02:00
|
|
|
fw("\n%s\n%s\n\n" % (subsection, (len(subsection) * "-")))
|
2022-05-18 08:59:16 +02:00
|
|
|
for member in ctx_members:
|
2022-04-19 04:17:25 +02:00
|
|
|
unique_all_len = len(unique)
|
|
|
|
unique.add(member)
|
|
|
|
member_visited = unique_all_len == len(unique)
|
|
|
|
|
|
|
|
unique_context_strings.add(member)
|
|
|
|
|
|
|
|
fw(".. data:: %s\n" % member)
|
|
|
|
# Avoid warnings about the member being included multiple times.
|
|
|
|
if member_visited:
|
|
|
|
fw(" :noindex:\n")
|
|
|
|
fw("\n")
|
|
|
|
|
2021-10-28 10:30:13 +02:00
|
|
|
try:
|
|
|
|
member_type, is_seq = context_type_map[member]
|
|
|
|
except KeyError:
|
2022-04-20 07:47:24 +02:00
|
|
|
raise SystemExit(
|
|
|
|
"Error: context key %r not found in context_type_map; update %s" %
|
|
|
|
(member, __file__)) from None
|
2011-02-15 16:37:40 +01:00
|
|
|
fw(" :type: %s :class:`bpy.types.%s`\n\n" % ("sequence of " if is_seq else "", member_type))
|
|
|
|
|
2022-05-21 08:33:35 +02:00
|
|
|
# Generate type-map:
|
2022-04-19 04:17:25 +02:00
|
|
|
# for member in sorted(unique_context_strings):
|
2011-02-15 16:37:40 +01:00
|
|
|
# print(' "%s": ("", False),' % member)
|
2022-04-19 04:17:25 +02:00
|
|
|
if len(context_type_map) > len(unique_context_strings):
|
2021-02-19 04:51:24 +01:00
|
|
|
warnings.warn(
|
2016-07-30 10:39:22 +02:00
|
|
|
"Some types are not used: %s" %
|
2022-04-19 04:17:25 +02:00
|
|
|
str([member for member in context_type_map if member not in unique_context_strings]))
|
2011-02-15 16:37:40 +01:00
|
|
|
else:
|
2022-05-21 08:33:35 +02:00
|
|
|
pass # Will have raised an error above.
|
2011-02-15 16:37:40 +01:00
|
|
|
|
2011-02-22 06:23:20 +01:00
|
|
|
file.close()
|
|
|
|
|
2011-02-15 16:37:40 +01:00
|
|
|
|
2011-09-16 04:42:50 +02:00
|
|
|
def pyrna_enum2sphinx(prop, use_empty_descriptions=False):
|
2022-05-21 08:33:35 +02:00
|
|
|
"""
|
|
|
|
Write a bullet point list of enum + descriptions.
|
2012-03-15 04:50:52 +01:00
|
|
|
"""
|
2011-09-15 18:15:24 +02:00
|
|
|
|
2022-05-31 06:07:14 +02:00
|
|
|
# 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
|
|
|
|
|
2011-09-15 18:15:24 +02:00
|
|
|
if use_empty_descriptions:
|
|
|
|
ok = True
|
|
|
|
else:
|
|
|
|
ok = False
|
|
|
|
for identifier, name, description in prop.enum_items:
|
|
|
|
if description:
|
|
|
|
ok = True
|
|
|
|
break
|
|
|
|
|
|
|
|
if ok:
|
2020-06-18 08:26:16 +02:00
|
|
|
return "".join([
|
|
|
|
"* ``%s``\n"
|
|
|
|
"%s.\n" % (
|
|
|
|
identifier,
|
|
|
|
# Account for multi-line enum descriptions, allowing this to be a block of text.
|
2021-05-29 18:16:13 +02:00
|
|
|
indent(" -- ".join(escape_rst(val) for val in (name, description) if val) or "Undocumented", " "),
|
2020-06-18 08:26:16 +02:00
|
|
|
)
|
|
|
|
for identifier, name, description in prop.enum_items
|
|
|
|
])
|
2011-09-15 18:15:24 +02:00
|
|
|
else:
|
|
|
|
return ""
|
|
|
|
|
|
|
|
|
2012-03-07 18:36:38 +01:00
|
|
|
def pyrna2sphinx(basepath):
|
2012-03-15 04:50:52 +01:00
|
|
|
"""
|
2022-05-21 08:33:35 +02:00
|
|
|
``bpy.types`` and ``bpy.ops``.
|
|
|
|
"""
|
|
|
|
# `structs, funcs, ops, props = rna_info.BuildRNAInfo()`
|
2022-05-22 05:30:17 +02:00
|
|
|
structs, _funcs, ops, _props = rna_info_BuildRNAInfo_cache()
|
2013-04-29 18:20:49 +02:00
|
|
|
|
2019-11-15 16:12:20 +01:00
|
|
|
if USE_ONLY_BUILTIN_RNA_TYPES:
|
2022-05-21 08:33:35 +02:00
|
|
|
# Ignore properties that use non `bpy.types` properties.
|
2019-11-15 16:12:20 +01:00
|
|
|
structs_blacklist = {
|
|
|
|
v.identifier for v in structs.values()
|
|
|
|
if v.module_name != "bpy.types"
|
|
|
|
}
|
|
|
|
for k, v in structs.items():
|
|
|
|
for p in v.properties:
|
|
|
|
for identifier in (
|
|
|
|
getattr(p.srna, "identifier", None),
|
|
|
|
getattr(p.fixed_type, "identifier", None),
|
|
|
|
):
|
|
|
|
if identifier is not None:
|
|
|
|
if identifier in structs_blacklist:
|
|
|
|
RNA_BLACKLIST.setdefault(k, set()).add(identifier)
|
|
|
|
del structs_blacklist
|
|
|
|
|
|
|
|
structs = {
|
|
|
|
k: v for k, v in structs.items()
|
|
|
|
if v.module_name == "bpy.types"
|
|
|
|
}
|
|
|
|
|
2011-02-16 06:18:10 +01:00
|
|
|
if FILTER_BPY_TYPES is not None:
|
2019-11-15 16:12:20 +01:00
|
|
|
structs = {
|
|
|
|
k: v for k, v in structs.items()
|
|
|
|
if k[1] in FILTER_BPY_TYPES
|
|
|
|
if v.module_name == "bpy.types"
|
|
|
|
}
|
2009-12-25 16:50:53 +01:00
|
|
|
|
2011-02-16 06:18:10 +01:00
|
|
|
if FILTER_BPY_OPS is not None:
|
|
|
|
ops = {k: v for k, v in ops.items() if v.module_name in FILTER_BPY_OPS}
|
2009-12-25 16:50:53 +01:00
|
|
|
|
2009-12-26 17:47:25 +01:00
|
|
|
def write_param(ident, fw, prop, is_return=False):
|
|
|
|
if is_return:
|
|
|
|
id_name = "return"
|
|
|
|
id_type = "rtype"
|
2011-03-22 05:28:51 +01:00
|
|
|
kwargs = {"as_ret": True}
|
2010-01-18 11:45:54 +01:00
|
|
|
identifier = ""
|
2009-12-25 16:50:53 +01:00
|
|
|
else:
|
2009-12-26 17:47:25 +01:00
|
|
|
id_name = "arg"
|
|
|
|
id_type = "type"
|
2011-03-22 05:28:51 +01:00
|
|
|
kwargs = {"as_arg": True}
|
2010-01-18 11:45:54 +01:00
|
|
|
identifier = " %s" % prop.identifier
|
2009-12-25 16:50:53 +01:00
|
|
|
|
2011-03-22 05:28:51 +01:00
|
|
|
kwargs["class_fmt"] = ":class:`%s`"
|
2022-11-26 23:15:12 +01:00
|
|
|
kwargs["mathutils_fmt"] = ":class:`mathutils.%s`"
|
2011-03-22 05:28:51 +01:00
|
|
|
|
|
|
|
kwargs["collection_id"] = _BPY_PROP_COLLECTION_ID
|
|
|
|
|
2022-05-31 06:07:14 +02:00
|
|
|
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
|
2011-09-15 18:15:24 +02:00
|
|
|
|
2022-05-31 06:07:14 +02:00
|
|
|
type_descr = prop.get_type_description(**kwargs)
|
2011-09-15 18:15:24 +02:00
|
|
|
|
2022-05-31 06:07:14 +02:00
|
|
|
# If the link has been written, no need to inline the enum items.
|
|
|
|
enum_text = "" if enum_descr_override else pyrna_enum2sphinx(prop)
|
2011-09-15 18:15:24 +02:00
|
|
|
if prop.name or prop.description or enum_text:
|
2022-11-04 04:36:12 +01:00
|
|
|
fw(ident + ":%s%s: " % (id_name, identifier))
|
2011-09-15 18:15:24 +02:00
|
|
|
|
|
|
|
if prop.name or prop.description:
|
2022-11-04 04:36:12 +01:00
|
|
|
fw(", ".join(val for val in (prop.name, prop.description.replace("\n", "")) if val) + "\n")
|
2011-09-15 18:15:24 +02:00
|
|
|
|
2022-05-21 08:33:35 +02:00
|
|
|
# Special exception, can't use generic code here for enums.
|
2011-09-15 18:15:24 +02:00
|
|
|
if enum_text:
|
|
|
|
fw("\n")
|
2022-11-04 04:36:12 +01:00
|
|
|
write_indented_lines(ident + " ", fw, enum_text)
|
2011-09-15 18:15:24 +02:00
|
|
|
del enum_text
|
|
|
|
# end enum exception
|
|
|
|
|
2010-01-18 11:45:54 +01:00
|
|
|
fw(ident + ":%s%s: %s\n" % (id_type, identifier, type_descr))
|
2009-12-25 16:50:53 +01:00
|
|
|
|
|
|
|
def write_struct(struct):
|
2016-07-30 10:39:22 +02:00
|
|
|
# if not struct.identifier.startswith("Sc") and not struct.identifier.startswith("I"):
|
|
|
|
# return
|
2009-12-25 16:50:53 +01:00
|
|
|
|
2016-07-30 10:39:22 +02:00
|
|
|
# if not struct.identifier == "Object":
|
|
|
|
# return
|
2009-12-25 16:50:53 +01:00
|
|
|
|
2019-11-15 16:12:20 +01:00
|
|
|
struct_module_name = struct.module_name
|
|
|
|
if USE_ONLY_BUILTIN_RNA_TYPES:
|
2022-09-14 08:18:59 +02:00
|
|
|
assert struct_module_name == "bpy.types"
|
2019-11-15 16:12:20 +01:00
|
|
|
filepath = os.path.join(basepath, "%s.%s.rst" % (struct_module_name, struct.identifier))
|
2011-09-29 14:11:58 +02:00
|
|
|
file = open(filepath, "w", encoding="utf-8")
|
2009-12-25 16:50:53 +01:00
|
|
|
fw = file.write
|
2011-02-04 10:27:25 +01:00
|
|
|
|
2010-04-10 20:35:50 +02:00
|
|
|
base_id = getattr(struct.base, "identifier", "")
|
2011-11-24 23:24:07 +01:00
|
|
|
struct_id = struct.identifier
|
2010-04-10 20:35:50 +02:00
|
|
|
|
|
|
|
if _BPY_STRUCT_FAKE:
|
|
|
|
if not base_id:
|
|
|
|
base_id = _BPY_STRUCT_FAKE
|
|
|
|
|
|
|
|
if base_id:
|
2011-11-24 23:24:07 +01:00
|
|
|
title = "%s(%s)" % (struct_id, base_id)
|
2009-12-25 16:50:53 +01:00
|
|
|
else:
|
2011-11-24 23:24:07 +01:00
|
|
|
title = struct_id
|
2009-12-25 16:50:53 +01:00
|
|
|
|
2012-03-21 05:43:17 +01:00
|
|
|
fw(title_string(title, "="))
|
2011-02-04 10:27:25 +01:00
|
|
|
|
2020-10-08 07:20:45 +02:00
|
|
|
fw(".. currentmodule:: %s\n\n" % struct_module_name)
|
2011-02-04 10:27:25 +01:00
|
|
|
|
2011-02-18 09:47:37 +01:00
|
|
|
# docs first?, ok
|
2019-11-15 16:12:20 +01:00
|
|
|
write_example_ref("", fw, "%s.%s" % (struct_module_name, struct_id))
|
2011-02-16 18:31:04 +01:00
|
|
|
|
2010-04-10 20:35:50 +02:00
|
|
|
base_ids = [base.identifier for base in struct.get_bases()]
|
|
|
|
|
|
|
|
if _BPY_STRUCT_FAKE:
|
|
|
|
base_ids.append(_BPY_STRUCT_FAKE)
|
|
|
|
|
|
|
|
base_ids.reverse()
|
2011-02-04 10:27:25 +01:00
|
|
|
|
2010-04-10 20:35:50 +02:00
|
|
|
if base_ids:
|
|
|
|
if len(base_ids) > 1:
|
2009-12-25 16:50:53 +01:00
|
|
|
fw("base classes --- ")
|
|
|
|
else:
|
|
|
|
fw("base class --- ")
|
|
|
|
|
2010-09-19 09:07:14 +02:00
|
|
|
fw(", ".join((":class:`%s`" % base_id) for base_id in base_ids))
|
2009-12-25 16:50:53 +01:00
|
|
|
fw("\n\n")
|
2011-02-04 10:27:25 +01:00
|
|
|
|
2016-07-30 10:39:22 +02:00
|
|
|
subclass_ids = [
|
|
|
|
s.identifier for s in structs.values()
|
|
|
|
if s.base is struct
|
|
|
|
if not rna_info.rna_id_ignore(s.identifier)
|
|
|
|
]
|
2015-05-12 08:56:40 +02:00
|
|
|
subclass_ids.sort()
|
2010-04-10 20:35:50 +02:00
|
|
|
if subclass_ids:
|
2010-09-19 09:07:14 +02:00
|
|
|
fw("subclasses --- \n" + ", ".join((":class:`%s`" % s) for s in subclass_ids) + "\n\n")
|
2011-02-04 10:27:25 +01:00
|
|
|
|
2010-04-10 20:35:50 +02:00
|
|
|
base_id = getattr(struct.base, "identifier", "")
|
2011-02-04 10:27:25 +01:00
|
|
|
|
2010-04-10 20:35:50 +02:00
|
|
|
if _BPY_STRUCT_FAKE:
|
|
|
|
if not base_id:
|
|
|
|
base_id = _BPY_STRUCT_FAKE
|
2009-12-25 16:50:53 +01:00
|
|
|
|
2010-04-10 20:35:50 +02:00
|
|
|
if base_id:
|
2011-11-24 23:24:07 +01:00
|
|
|
fw(".. class:: %s(%s)\n\n" % (struct_id, base_id))
|
2009-12-25 16:50:53 +01:00
|
|
|
else:
|
2011-11-24 23:24:07 +01:00
|
|
|
fw(".. class:: %s\n\n" % struct_id)
|
2009-12-25 16:50:53 +01:00
|
|
|
|
2022-07-18 11:55:51 +02:00
|
|
|
write_indented_lines(" ", fw, struct.description, False)
|
|
|
|
fw("\n")
|
2011-02-04 10:27:25 +01:00
|
|
|
|
2022-05-21 08:33:35 +02:00
|
|
|
# Properties sorted in alphabetical order.
|
2010-07-15 13:51:43 +02:00
|
|
|
sorted_struct_properties = struct.properties[:]
|
|
|
|
sorted_struct_properties.sort(key=lambda prop: prop.identifier)
|
|
|
|
|
2022-05-21 08:33:35 +02:00
|
|
|
# Support blacklisting props.
|
2011-11-24 23:24:07 +01:00
|
|
|
struct_blacklist = RNA_BLACKLIST.get(struct_id, ())
|
|
|
|
|
2010-06-28 02:06:23 +02:00
|
|
|
for prop in sorted_struct_properties:
|
2022-05-25 05:39:47 +02:00
|
|
|
identifier = prop.identifier
|
2011-11-24 23:24:07 +01:00
|
|
|
|
2022-05-21 08:33:35 +02:00
|
|
|
# Support blacklisting props.
|
2022-05-25 05:39:47 +02:00
|
|
|
if identifier in struct_blacklist:
|
2011-11-24 23:24:07 +01:00
|
|
|
continue
|
|
|
|
|
2022-05-31 06:07:14 +02:00
|
|
|
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`",
|
2022-11-26 23:15:12 +01:00
|
|
|
mathutils_fmt=":class:`mathutils.%s`",
|
2022-05-31 06:07:14 +02:00
|
|
|
collection_id=_BPY_PROP_COLLECTION_ID,
|
|
|
|
enum_descr_override=enum_descr_override,
|
|
|
|
)
|
2022-05-21 08:33:35 +02:00
|
|
|
# Read-only properties use "data" directive, variables properties use "attribute" directive.
|
|
|
|
if "readonly" in type_descr:
|
2022-05-25 05:39:47 +02:00
|
|
|
fw(" .. data:: %s\n" % identifier)
|
2010-06-28 02:06:23 +02:00
|
|
|
else:
|
2022-05-25 05:39:47 +02:00
|
|
|
fw(" .. attribute:: %s\n" % identifier)
|
|
|
|
# Also write `noindex` on requerst.
|
|
|
|
if ("bpy.types", struct_id, identifier) in RST_NOINDEX_ATTR:
|
|
|
|
fw(" :noindex:\n")
|
|
|
|
fw("\n")
|
|
|
|
|
2009-12-26 17:47:25 +01:00
|
|
|
if prop.description:
|
2021-03-31 08:43:30 +02:00
|
|
|
write_indented_lines(" ", fw, prop.description, False)
|
|
|
|
fw("\n")
|
2011-09-20 20:29:19 +02:00
|
|
|
|
2022-05-21 08:33:35 +02:00
|
|
|
# Special exception, can't use generic code here for enums.
|
2011-09-15 18:15:24 +02:00
|
|
|
if prop.type == "enum":
|
2022-05-31 06:07:14 +02:00
|
|
|
# If the link has been written, no need to inline the enum items.
|
|
|
|
enum_text = "" if enum_descr_override else pyrna_enum2sphinx(prop)
|
2011-09-15 18:15:24 +02:00
|
|
|
if enum_text:
|
|
|
|
write_indented_lines(" ", fw, enum_text)
|
|
|
|
fw("\n")
|
|
|
|
del enum_text
|
2022-05-21 08:33:35 +02:00
|
|
|
# End enum exception.
|
2011-09-15 18:15:24 +02:00
|
|
|
|
2010-06-02 23:28:17 +02:00
|
|
|
fw(" :type: %s\n\n" % type_descr)
|
2011-02-04 10:27:25 +01:00
|
|
|
|
2022-05-21 08:33:35 +02:00
|
|
|
# Python attributes.
|
2009-12-25 16:50:53 +01:00
|
|
|
py_properties = struct.get_py_properties()
|
|
|
|
py_prop = None
|
|
|
|
for identifier, py_prop in py_properties:
|
2010-01-22 03:04:25 +01:00
|
|
|
pyprop2sphinx(" ", fw, identifier, py_prop)
|
2009-12-25 16:50:53 +01:00
|
|
|
del py_properties, py_prop
|
|
|
|
|
2022-05-18 13:43:38 +02:00
|
|
|
# C/Python attributes: `GetSetDescriptorType`.
|
|
|
|
key = descr = None
|
|
|
|
for key, descr in sorted(struct.get_py_c_properties_getset()):
|
|
|
|
py_descr2sphinx(" ", fw, descr, "bpy.types", struct_id, key)
|
|
|
|
del key, descr
|
|
|
|
|
2009-12-25 16:50:53 +01:00
|
|
|
for func in struct.functions:
|
2010-09-19 09:07:14 +02:00
|
|
|
args_str = ", ".join(prop.get_arg_default(force=False) for prop in func.args)
|
2009-12-25 16:50:53 +01:00
|
|
|
|
2016-07-30 10:39:22 +02:00
|
|
|
fw(" .. %s:: %s(%s)\n\n" %
|
|
|
|
("classmethod" if func.is_classmethod else "method", func.identifier, args_str))
|
2009-12-25 16:50:53 +01:00
|
|
|
fw(" %s\n\n" % func.description)
|
2011-02-04 10:27:25 +01:00
|
|
|
|
2009-12-25 16:50:53 +01:00
|
|
|
for prop in func.args:
|
2009-12-26 17:47:25 +01:00
|
|
|
write_param(" ", fw, prop)
|
|
|
|
|
2010-01-02 19:55:07 +01:00
|
|
|
if len(func.return_values) == 1:
|
|
|
|
write_param(" ", fw, func.return_values[0], is_return=True)
|
2022-05-21 08:33:35 +02:00
|
|
|
elif func.return_values: # Multiple return values.
|
2010-09-19 09:07:14 +02:00
|
|
|
fw(" :return (%s):\n" % ", ".join(prop.identifier for prop in func.return_values))
|
2010-01-02 19:55:07 +01:00
|
|
|
for prop in func.return_values:
|
2022-05-21 08:33:35 +02:00
|
|
|
# TODO: pyrna_enum2sphinx for multiple return values... actually don't
|
|
|
|
# think we even use this but still!
|
2022-05-31 06:07:14 +02:00
|
|
|
|
|
|
|
enum_descr_override = None
|
|
|
|
if USE_SHARED_RNA_ENUM_ITEMS_STATIC:
|
|
|
|
enum_descr_override = pyrna_enum2sphinx_shared_link(prop)
|
|
|
|
|
2016-07-30 10:39:22 +02:00
|
|
|
type_descr = prop.get_type_description(
|
2022-05-31 06:07:14 +02:00
|
|
|
as_ret=True, class_fmt=":class:`%s`",
|
2022-11-26 23:15:12 +01:00
|
|
|
mathutils_fmt=":class:`mathutils.%s`",
|
2022-05-31 06:07:14 +02:00
|
|
|
collection_id=_BPY_PROP_COLLECTION_ID,
|
|
|
|
enum_descr_override=enum_descr_override,
|
|
|
|
)
|
2010-01-02 19:55:07 +01:00
|
|
|
descr = prop.description
|
|
|
|
if not descr:
|
|
|
|
descr = prop.name
|
2022-05-21 08:33:35 +02:00
|
|
|
# In rare cases `descr` may be empty.
|
2016-01-25 10:25:28 +01:00
|
|
|
fw(" `%s`, %s\n\n" %
|
|
|
|
(prop.identifier,
|
|
|
|
", ".join((val for val in (descr, type_descr) if val))))
|
2010-01-02 19:55:07 +01:00
|
|
|
|
2019-11-15 16:12:20 +01:00
|
|
|
write_example_ref(" ", fw, struct_module_name + "." + struct_id + "." + func.identifier)
|
2011-08-23 13:28:18 +02:00
|
|
|
|
2009-12-26 17:47:25 +01:00
|
|
|
fw("\n")
|
2009-12-25 16:50:53 +01:00
|
|
|
|
2022-05-21 08:33:35 +02:00
|
|
|
# Python methods.
|
2009-12-25 16:50:53 +01:00
|
|
|
py_funcs = struct.get_py_functions()
|
|
|
|
py_func = None
|
2011-02-04 10:27:25 +01:00
|
|
|
|
2009-12-25 16:50:53 +01:00
|
|
|
for identifier, py_func in py_funcs:
|
2013-10-15 07:55:51 +02:00
|
|
|
pyfunc2sphinx(" ", fw, "bpy.types", struct_id, identifier, py_func, is_class=True)
|
2009-12-25 16:50:53 +01:00
|
|
|
del py_funcs, py_func
|
|
|
|
|
2011-03-14 11:31:50 +01:00
|
|
|
py_funcs = struct.get_py_c_functions()
|
|
|
|
py_func = None
|
|
|
|
|
|
|
|
for identifier, py_func in py_funcs:
|
2011-11-24 23:24:07 +01:00
|
|
|
py_c_func2sphinx(" ", fw, "bpy.types", struct_id, identifier, py_func, is_class=True)
|
2011-03-14 11:31:50 +01:00
|
|
|
|
2010-04-09 22:43:58 +02:00
|
|
|
lines = []
|
|
|
|
|
2010-04-10 20:35:50 +02:00
|
|
|
if struct.base or _BPY_STRUCT_FAKE:
|
2010-04-09 22:43:58 +02:00
|
|
|
bases = list(reversed(struct.get_bases()))
|
2010-04-10 20:35:50 +02:00
|
|
|
|
2022-05-21 08:33:35 +02:00
|
|
|
# Properties.
|
2012-11-14 10:45:15 +01:00
|
|
|
del lines[:]
|
2011-02-04 10:27:25 +01:00
|
|
|
|
2010-04-10 20:35:50 +02:00
|
|
|
if _BPY_STRUCT_FAKE:
|
2016-07-30 10:39:22 +02:00
|
|
|
descr_items = [
|
2020-05-11 08:56:09 +02:00
|
|
|
(key, descr) for key, descr in sorted(bpy_struct.__dict__.items())
|
2016-07-30 10:39:22 +02:00
|
|
|
if not key.startswith("__")
|
|
|
|
]
|
2011-02-04 10:27:25 +01:00
|
|
|
|
2010-04-10 20:35:50 +02:00
|
|
|
if _BPY_STRUCT_FAKE:
|
|
|
|
for key, descr in descr_items:
|
|
|
|
if type(descr) == GetSetDescriptorType:
|
2010-09-03 11:21:40 +02:00
|
|
|
lines.append(" * :class:`%s.%s`\n" % (_BPY_STRUCT_FAKE, key))
|
2010-04-10 20:35:50 +02:00
|
|
|
|
2010-04-09 22:43:58 +02:00
|
|
|
for base in bases:
|
|
|
|
for prop in base.properties:
|
2010-09-03 11:21:40 +02:00
|
|
|
lines.append(" * :class:`%s.%s`\n" % (base.identifier, prop.identifier))
|
2010-04-09 22:43:58 +02:00
|
|
|
|
|
|
|
for identifier, py_prop in base.get_py_properties():
|
2010-09-03 11:21:40 +02:00
|
|
|
lines.append(" * :class:`%s.%s`\n" % (base.identifier, identifier))
|
2011-02-04 10:27:25 +01:00
|
|
|
|
2010-04-09 22:43:58 +02:00
|
|
|
if lines:
|
|
|
|
fw(".. rubric:: Inherited Properties\n\n")
|
2010-09-03 11:21:40 +02:00
|
|
|
|
|
|
|
fw(".. hlist::\n")
|
2010-09-03 16:53:54 +02:00
|
|
|
fw(" :columns: 2\n\n")
|
2010-09-03 11:21:40 +02:00
|
|
|
|
2010-04-09 22:43:58 +02:00
|
|
|
for line in lines:
|
|
|
|
fw(line)
|
|
|
|
fw("\n")
|
|
|
|
|
2022-05-21 08:33:35 +02:00
|
|
|
# Functions.
|
2012-11-14 10:45:15 +01:00
|
|
|
del lines[:]
|
2010-04-10 20:35:50 +02:00
|
|
|
|
|
|
|
if _BPY_STRUCT_FAKE:
|
|
|
|
for key, descr in descr_items:
|
|
|
|
if type(descr) == MethodDescriptorType:
|
2010-09-03 11:21:40 +02:00
|
|
|
lines.append(" * :class:`%s.%s`\n" % (_BPY_STRUCT_FAKE, key))
|
2010-04-10 20:35:50 +02:00
|
|
|
|
2010-04-09 22:43:58 +02:00
|
|
|
for base in bases:
|
|
|
|
for func in base.functions:
|
2010-09-03 11:21:40 +02:00
|
|
|
lines.append(" * :class:`%s.%s`\n" % (base.identifier, func.identifier))
|
2010-04-09 22:43:58 +02:00
|
|
|
for identifier, py_func in base.get_py_functions():
|
2010-09-03 11:21:40 +02:00
|
|
|
lines.append(" * :class:`%s.%s`\n" % (base.identifier, identifier))
|
2020-05-11 08:43:49 +02:00
|
|
|
for identifier, py_func in base.get_py_c_functions():
|
|
|
|
lines.append(" * :class:`%s.%s`\n" % (base.identifier, identifier))
|
2010-04-09 22:43:58 +02:00
|
|
|
|
|
|
|
if lines:
|
|
|
|
fw(".. rubric:: Inherited Functions\n\n")
|
2010-09-03 11:21:40 +02:00
|
|
|
|
|
|
|
fw(".. hlist::\n")
|
2010-09-03 16:53:54 +02:00
|
|
|
fw(" :columns: 2\n\n")
|
2010-09-03 11:21:40 +02:00
|
|
|
|
2010-04-09 22:43:58 +02:00
|
|
|
for line in lines:
|
|
|
|
fw(line)
|
|
|
|
fw("\n")
|
|
|
|
|
2012-11-14 10:45:15 +01:00
|
|
|
del lines[:]
|
2010-04-09 22:43:58 +02:00
|
|
|
|
2009-12-25 16:50:53 +01:00
|
|
|
if struct.references:
|
|
|
|
# use this otherwise it gets in the index for a normal heading.
|
|
|
|
fw(".. rubric:: References\n\n")
|
|
|
|
|
2010-09-03 11:21:40 +02:00
|
|
|
fw(".. hlist::\n")
|
2010-09-03 16:53:54 +02:00
|
|
|
fw(" :columns: 2\n\n")
|
2010-09-03 11:21:40 +02:00
|
|
|
|
2021-05-12 14:01:55 +02:00
|
|
|
# Context does its own thing.
|
|
|
|
# "active_object": ("Object", False),
|
2012-11-03 08:28:51 +01:00
|
|
|
for ref_attr, (ref_type, ref_is_seq) in sorted(context_type_map.items()):
|
|
|
|
if ref_type == struct_id:
|
|
|
|
fw(" * :mod:`bpy.context.%s`\n" % ref_attr)
|
|
|
|
del ref_attr, ref_type, ref_is_seq
|
|
|
|
|
2009-12-25 16:50:53 +01:00
|
|
|
for ref in struct.references:
|
|
|
|
ref_split = ref.split(".")
|
|
|
|
if len(ref_split) > 2:
|
|
|
|
ref = ref_split[-2] + "." + ref_split[-1]
|
2010-09-03 11:21:40 +02:00
|
|
|
fw(" * :class:`%s`\n" % ref)
|
2009-12-25 16:50:53 +01:00
|
|
|
fw("\n")
|
|
|
|
|
2011-02-18 09:47:37 +01:00
|
|
|
# docs last?, disable for now
|
2011-11-24 23:24:07 +01:00
|
|
|
# write_example_ref("", fw, "bpy.types.%s" % struct_id)
|
2011-02-22 06:23:20 +01:00
|
|
|
file.close()
|
2011-02-18 09:47:37 +01:00
|
|
|
|
2011-02-16 06:18:10 +01:00
|
|
|
if "bpy.types" not in EXCLUDE_MODULES:
|
|
|
|
for struct in structs.values():
|
2022-05-21 08:33:35 +02:00
|
|
|
# TODO: rna_info should filter these out!
|
2011-02-16 06:18:10 +01:00
|
|
|
if "_OT_" in struct.identifier:
|
|
|
|
continue
|
|
|
|
write_struct(struct)
|
2011-02-04 10:27:25 +01:00
|
|
|
|
2019-11-15 16:12:20 +01:00
|
|
|
def fake_bpy_type(class_module_name, class_value, class_name, descr_str, use_subclasses=True):
|
|
|
|
filepath = os.path.join(basepath, "%s.%s.rst" % (class_module_name, class_name))
|
2011-09-29 14:11:58 +02:00
|
|
|
file = open(filepath, "w", encoding="utf-8")
|
2011-02-16 06:18:10 +01:00
|
|
|
fw = file.write
|
2010-04-10 20:35:50 +02:00
|
|
|
|
2012-03-21 05:43:17 +01:00
|
|
|
fw(title_string(class_name, "="))
|
2011-03-22 05:28:51 +01:00
|
|
|
|
2020-10-08 07:20:45 +02:00
|
|
|
fw(".. currentmodule:: %s\n\n" % class_module_name)
|
2010-04-10 20:35:50 +02:00
|
|
|
|
2011-03-22 05:28:51 +01:00
|
|
|
if use_subclasses:
|
2016-07-30 10:39:22 +02:00
|
|
|
subclass_ids = [
|
|
|
|
s.identifier for s in structs.values()
|
|
|
|
if s.base is None
|
|
|
|
if not rna_info.rna_id_ignore(s.identifier)
|
|
|
|
]
|
2011-03-22 05:28:51 +01:00
|
|
|
if subclass_ids:
|
|
|
|
fw("subclasses --- \n" + ", ".join((":class:`%s`" % s) for s in sorted(subclass_ids)) + "\n\n")
|
2010-04-10 20:35:50 +02:00
|
|
|
|
2011-03-22 05:28:51 +01:00
|
|
|
fw(".. class:: %s\n\n" % class_name)
|
|
|
|
fw(" %s\n\n" % descr_str)
|
2011-02-16 06:18:10 +01:00
|
|
|
fw(" .. note::\n\n")
|
2020-10-07 23:00:57 +02:00
|
|
|
fw(" Note that :class:`%s.%s` is not actually available from within Blender,\n"
|
2019-11-15 16:12:20 +01:00
|
|
|
" it only exists for the purpose of documentation.\n\n" % (class_module_name, class_name))
|
2010-04-10 20:35:50 +02:00
|
|
|
|
2016-07-30 10:39:22 +02:00
|
|
|
descr_items = [
|
|
|
|
(key, descr) for key, descr in sorted(class_value.__dict__.items())
|
|
|
|
if not key.startswith("__")
|
|
|
|
]
|
2010-04-10 20:35:50 +02:00
|
|
|
|
2011-02-16 06:18:10 +01:00
|
|
|
for key, descr in descr_items:
|
2022-05-21 08:33:35 +02:00
|
|
|
# `GetSetDescriptorType`, `GetSetDescriptorType` types are not documented yet.
|
2016-07-30 10:39:22 +02:00
|
|
|
if type(descr) == MethodDescriptorType:
|
2011-03-22 05:28:51 +01:00
|
|
|
py_descr2sphinx(" ", fw, descr, "bpy.types", class_name, key)
|
2010-04-10 20:35:50 +02:00
|
|
|
|
2011-02-16 06:18:10 +01:00
|
|
|
for key, descr in descr_items:
|
|
|
|
if type(descr) == GetSetDescriptorType:
|
2011-03-22 05:28:51 +01:00
|
|
|
py_descr2sphinx(" ", fw, descr, "bpy.types", class_name, key)
|
2011-02-22 06:23:20 +01:00
|
|
|
file.close()
|
2010-04-10 20:35:50 +02:00
|
|
|
|
2011-03-22 05:28:51 +01:00
|
|
|
# write fake classes
|
|
|
|
if _BPY_STRUCT_FAKE:
|
2020-05-11 08:56:09 +02:00
|
|
|
class_value = bpy_struct
|
2016-07-30 10:39:22 +02:00
|
|
|
fake_bpy_type(
|
2019-11-15 16:12:20 +01:00
|
|
|
"bpy.types", class_value, _BPY_STRUCT_FAKE,
|
|
|
|
"built-in base class for all classes in bpy.types.", use_subclasses=True,
|
|
|
|
)
|
2011-03-22 05:28:51 +01:00
|
|
|
|
|
|
|
if _BPY_PROP_COLLECTION_FAKE:
|
|
|
|
class_value = bpy.data.objects.__class__
|
2016-07-30 10:39:22 +02:00
|
|
|
fake_bpy_type(
|
2019-11-15 16:12:20 +01:00
|
|
|
"bpy.types", class_value, _BPY_PROP_COLLECTION_FAKE,
|
|
|
|
"built-in class used for all collections.", use_subclasses=False,
|
|
|
|
)
|
2011-03-22 05:28:51 +01:00
|
|
|
|
2010-06-04 15:47:56 +02:00
|
|
|
# operators
|
2009-12-25 16:50:53 +01:00
|
|
|
def write_ops():
|
2018-08-13 23:25:01 +02:00
|
|
|
API_BASEURL = "https://developer.blender.org/diffusion/B/browse/master/release/scripts"
|
2017-01-02 01:42:37 +01:00
|
|
|
API_BASEURL_ADDON = "https://developer.blender.org/diffusion/BA"
|
|
|
|
API_BASEURL_ADDON_CONTRIB = "https://developer.blender.org/diffusion/BAC"
|
2011-02-04 10:27:25 +01:00
|
|
|
|
2011-02-22 06:23:20 +01:00
|
|
|
op_modules = {}
|
2022-05-22 05:30:17 +02:00
|
|
|
op = None
|
2011-02-22 06:23:20 +01:00
|
|
|
for op in ops.values():
|
|
|
|
op_modules.setdefault(op.module_name, []).append(op)
|
|
|
|
del op
|
2011-02-25 17:19:50 +01:00
|
|
|
|
2011-02-22 06:23:20 +01:00
|
|
|
for op_module_name, ops_mod in op_modules.items():
|
2012-03-07 18:36:38 +01:00
|
|
|
filepath = os.path.join(basepath, "bpy.ops.%s.rst" % op_module_name)
|
2011-09-29 14:11:58 +02:00
|
|
|
file = open(filepath, "w", encoding="utf-8")
|
2011-02-22 06:23:20 +01:00
|
|
|
fw = file.write
|
|
|
|
|
|
|
|
title = "%s Operators" % op_module_name.replace("_", " ").title()
|
2011-03-22 05:28:51 +01:00
|
|
|
|
2012-03-21 05:43:17 +01:00
|
|
|
fw(title_string(title, "="))
|
2011-02-04 10:27:25 +01:00
|
|
|
|
2011-02-22 06:23:20 +01:00
|
|
|
fw(".. module:: bpy.ops.%s\n\n" % op_module_name)
|
2011-02-04 10:27:25 +01:00
|
|
|
|
2011-02-22 06:23:20 +01:00
|
|
|
ops_mod.sort(key=lambda op: op.func_name)
|
2011-02-04 10:27:25 +01:00
|
|
|
|
2011-02-22 06:23:20 +01:00
|
|
|
for op in ops_mod:
|
|
|
|
args_str = ", ".join(prop.get_arg_default(force=True) for prop in op.args)
|
|
|
|
fw(".. function:: %s(%s)\n\n" % (op.func_name, args_str))
|
2010-04-10 20:35:50 +02:00
|
|
|
|
2011-02-22 06:23:20 +01:00
|
|
|
# if the description isn't valid, we output the standard warning
|
|
|
|
# with a link to the wiki so that people can help
|
|
|
|
if not op.description or op.description == "(undocumented operator)":
|
2022-05-21 08:33:35 +02:00
|
|
|
operator_description = undocumented_message("bpy.ops", op.module_name, op.func_name)
|
2011-02-22 06:23:20 +01:00
|
|
|
else:
|
|
|
|
operator_description = op.description
|
2010-06-12 00:41:13 +02:00
|
|
|
|
2011-02-22 06:23:20 +01:00
|
|
|
fw(" %s\n\n" % operator_description)
|
|
|
|
for prop in op.args:
|
|
|
|
write_param(" ", fw, prop)
|
|
|
|
if op.args:
|
|
|
|
fw("\n")
|
2010-06-12 00:41:13 +02:00
|
|
|
|
2011-02-22 06:23:20 +01:00
|
|
|
location = op.get_location()
|
|
|
|
if location != (None, None):
|
2011-05-05 16:45:24 +02:00
|
|
|
if location[0].startswith("addons_contrib" + os.sep):
|
|
|
|
url_base = API_BASEURL_ADDON_CONTRIB
|
|
|
|
elif location[0].startswith("addons" + os.sep):
|
|
|
|
url_base = API_BASEURL_ADDON
|
|
|
|
else:
|
|
|
|
url_base = API_BASEURL
|
|
|
|
|
2017-06-17 18:33:13 +02:00
|
|
|
fw(" :file: `%s\\:%d <%s/%s$%d>`_\n\n" %
|
|
|
|
(location[0], location[1], url_base, location[0], location[1]))
|
2009-12-26 17:47:25 +01:00
|
|
|
|
2011-02-22 06:23:20 +01:00
|
|
|
file.close()
|
2011-02-04 10:27:25 +01:00
|
|
|
|
2011-02-16 06:18:10 +01:00
|
|
|
if "bpy.ops" not in EXCLUDE_MODULES:
|
|
|
|
write_ops()
|
|
|
|
|
|
|
|
|
2012-03-07 18:36:38 +01:00
|
|
|
def write_sphinx_conf_py(basepath):
|
2022-05-21 08:33:35 +02:00
|
|
|
"""
|
|
|
|
Write sphinx's ``conf.py``.
|
|
|
|
"""
|
2012-03-07 18:36:38 +01:00
|
|
|
filepath = os.path.join(basepath, "conf.py")
|
2011-09-29 14:11:58 +02:00
|
|
|
file = open(filepath, "w", encoding="utf-8")
|
2011-02-16 06:18:10 +01:00
|
|
|
fw = file.write
|
2012-03-08 06:36:05 +01:00
|
|
|
|
2017-01-24 01:09:45 +01:00
|
|
|
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")
|
2022-02-03 20:25:16 +01:00
|
|
|
fw("project = 'Blender %s Python API'\n" % BLENDER_VERSION_STRING)
|
2022-06-02 07:02:25 +02:00
|
|
|
fw("root_doc = 'index'\n")
|
|
|
|
fw("copyright = 'Blender Foundation'\n")
|
2022-02-03 20:25:16 +01:00
|
|
|
fw("version = '%s'\n" % BLENDER_VERSION_DOTS)
|
|
|
|
fw("release = '%s'\n" % BLENDER_VERSION_DOTS)
|
2011-09-20 03:35:39 +02:00
|
|
|
|
2022-06-02 07:02:25 +02:00
|
|
|
# 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")
|
|
|
|
|
2017-07-17 05:16:37 +02:00
|
|
|
# Quiet file not in table-of-contents warnings.
|
|
|
|
fw("exclude_patterns = [\n")
|
|
|
|
fw(" 'include__bmesh.rst',\n")
|
|
|
|
fw("]\n\n")
|
|
|
|
|
2019-07-09 02:52:29 +02:00
|
|
|
fw("html_title = 'Blender Python API'\n")
|
2020-10-08 03:31:26 +02:00
|
|
|
|
|
|
|
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__('sphinx_rtd_theme')
|
|
|
|
html_theme = 'sphinx_rtd_theme'
|
|
|
|
except ModuleNotFoundError:
|
|
|
|
pass
|
|
|
|
""")
|
|
|
|
|
|
|
|
fw("if html_theme == 'sphinx_rtd_theme':\n")
|
|
|
|
fw(" html_theme_options = {\n")
|
2022-02-03 20:37:55 +01:00
|
|
|
fw(" 'display_version': False,\n")
|
2020-10-08 03:31:26 +02:00
|
|
|
# fw(" 'analytics_id': '',\n")
|
|
|
|
# fw(" 'collapse_navigation': True,\n")
|
|
|
|
fw(" 'sticky_navigation': False,\n")
|
|
|
|
fw(" 'navigation_depth': 1,\n")
|
2022-09-15 06:31:06 +02:00
|
|
|
fw(" 'includehidden': False,\n")
|
2020-10-08 03:31:26 +02:00
|
|
|
# fw(" 'titles_only': False\n")
|
2019-07-04 04:46:52 +02:00
|
|
|
fw(" }\n\n")
|
|
|
|
|
2012-03-07 18:36:38 +01:00
|
|
|
# not helpful since the source is generated, adds to upload size.
|
2011-02-16 06:18:10 +01:00
|
|
|
fw("html_copy_source = False\n")
|
2017-01-19 22:16:04 +01:00
|
|
|
fw("html_show_sphinx = False\n")
|
2021-02-19 05:55:23 +01:00
|
|
|
fw("html_baseurl = 'https://docs.blender.org/api/current/'\n")
|
2019-07-06 22:01:49 +02:00
|
|
|
fw("html_use_opensearch = 'https://docs.blender.org/api/current'\n")
|
2022-01-19 20:08:24 +01:00
|
|
|
fw("html_show_search_summary = True\n")
|
2016-10-23 17:47:56 +02:00
|
|
|
fw("html_split_index = True\n")
|
2019-07-06 21:53:39 +02:00
|
|
|
fw("html_static_path = ['static']\n")
|
2022-02-03 20:25:16 +01:00
|
|
|
fw("templates_path = ['templates']\n")
|
2022-04-06 11:42:36 +02:00
|
|
|
fw("html_context = {'commit': '%s - %s'}\n" % (BLENDER_VERSION_HASH_HTML_LINK, BLENDER_VERSION_DATE))
|
2019-07-06 02:17:42 +02:00
|
|
|
fw("html_extra_path = ['static/favicon.ico', 'static/blender_logo.svg']\n")
|
|
|
|
fw("html_favicon = 'static/favicon.ico'\n")
|
2019-07-09 02:52:29 +02:00
|
|
|
fw("html_logo = 'static/blender_logo.svg'\n")
|
2022-04-06 11:42:36 +02:00
|
|
|
# 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")
|
2022-02-03 20:25:16 +01:00
|
|
|
fw("if html_theme == 'sphinx_rtd_theme':\n")
|
|
|
|
fw(" html_css_files = ['css/version_switch.css']\n")
|
|
|
|
fw(" html_js_files = ['js/version_switch.js']\n")
|
2012-03-08 06:36:05 +01:00
|
|
|
|
2011-02-16 06:18:10 +01:00
|
|
|
# needed for latex, pdf gen
|
2017-01-02 05:54:24 +01:00
|
|
|
fw("latex_elements = {\n")
|
|
|
|
fw(" 'papersize': 'a4paper',\n")
|
|
|
|
fw("}\n\n")
|
|
|
|
|
2011-02-16 06:18:10 +01:00
|
|
|
fw("latex_documents = [ ('contents', 'contents.tex', 'Blender Index', 'Blender Foundation', 'manual'), ]\n")
|
2017-07-17 05:16:37 +02:00
|
|
|
|
|
|
|
# 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)
|
|
|
|
""")
|
|
|
|
# end workaround
|
|
|
|
|
2019-07-06 21:53:39 +02:00
|
|
|
fw("def setup(app):\n")
|
2020-06-23 15:30:09 +02:00
|
|
|
fw(" app.add_css_file('css/theme_overrides.css')\n")
|
2020-05-11 08:09:56 +02:00
|
|
|
fw(" app.add_domain(PatchedPythonDomain, override=True)\n\n")
|
2019-07-06 21:53:39 +02:00
|
|
|
|
2011-02-16 06:18:10 +01:00
|
|
|
file.close()
|
|
|
|
|
2012-03-07 18:36:38 +01:00
|
|
|
|
2020-10-11 23:02:41 +02:00
|
|
|
def write_rst_index(basepath):
|
2022-05-21 08:33:35 +02:00
|
|
|
"""
|
|
|
|
Write the RST file of the main page, needed for sphinx: ``index.html``.
|
|
|
|
"""
|
2018-08-14 01:07:50 +02:00
|
|
|
filepath = os.path.join(basepath, "index.rst")
|
2011-09-29 14:11:58 +02:00
|
|
|
file = open(filepath, "w", encoding="utf-8")
|
2011-02-16 06:18:10 +01:00
|
|
|
fw = file.write
|
|
|
|
|
2019-07-09 02:52:29 +02:00
|
|
|
fw(title_string("Blender %s Python API Documentation" % BLENDER_VERSION_DOTS, "%", double=True))
|
2011-02-16 06:18:10 +01:00
|
|
|
fw("\n")
|
2019-07-09 02:52:29 +02:00
|
|
|
fw("Welcome to the Python API documentation for `Blender <https://www.blender.org>`__, ")
|
|
|
|
fw("the free and open source 3D creation suite.\n")
|
2011-02-16 06:18:10 +01:00
|
|
|
fw("\n")
|
2012-02-10 16:00:55 +01:00
|
|
|
|
2012-03-16 11:49:39 +01:00
|
|
|
# fw("`A PDF version of this document is also available <%s>`_\n" % BLENDER_PDF_FILENAME)
|
2019-07-09 02:52:29 +02:00
|
|
|
fw("This site can be used offline: `Download the full documentation (zipped HTML files) <%s>`__\n" %
|
2016-07-30 10:39:22 +02:00
|
|
|
BLENDER_ZIP_FILENAME)
|
2011-04-13 16:43:11 +02:00
|
|
|
fw("\n")
|
|
|
|
|
2011-08-25 06:25:33 +02:00
|
|
|
if not EXCLUDE_INFO_DOCS:
|
|
|
|
fw(".. toctree::\n")
|
2022-09-15 06:31:06 +02:00
|
|
|
if USE_INFO_DOCS_FANCY_INDEX:
|
|
|
|
fw(" :hidden:\n")
|
2018-08-14 01:22:57 +02:00
|
|
|
fw(" :maxdepth: 1\n")
|
2018-11-21 17:21:28 +01:00
|
|
|
fw(" :caption: Documentation\n\n")
|
2011-08-25 06:25:33 +02:00
|
|
|
for info, info_desc in INFO_DOCS:
|
2022-09-15 06:31:06 +02:00
|
|
|
fw(" %s\n" % info)
|
2011-08-25 06:25:33 +02:00
|
|
|
fw("\n")
|
2011-02-16 06:18:10 +01:00
|
|
|
|
2022-09-15 06:31:06 +02:00
|
|
|
if USE_INFO_DOCS_FANCY_INDEX:
|
|
|
|
# Show a fake TOC, allowing for an extra description to be shown as well as the title.
|
|
|
|
fw(title_string("Documentation", "="))
|
|
|
|
for info, info_desc in INFO_DOCS:
|
|
|
|
fw("- :doc:`%s`: %s\n" % (info.removesuffix(".rst"), info_desc))
|
|
|
|
fw("\n")
|
|
|
|
|
2011-02-16 06:18:10 +01:00
|
|
|
fw(".. toctree::\n")
|
2018-08-14 00:50:58 +02:00
|
|
|
fw(" :maxdepth: 1\n")
|
2018-08-14 02:19:50 +02:00
|
|
|
fw(" :caption: Application Modules\n\n")
|
2012-03-08 06:36:05 +01:00
|
|
|
|
2012-03-21 06:33:37 +01:00
|
|
|
app_modules = (
|
2012-03-07 18:36:38 +01:00
|
|
|
"bpy.context", # note: not actually a module
|
|
|
|
"bpy.data", # note: not actually a module
|
2020-03-06 14:28:54 +01:00
|
|
|
"bpy.msgbus", # note: not actually a module
|
2012-03-07 18:36:38 +01:00
|
|
|
"bpy.ops",
|
|
|
|
"bpy.types",
|
2012-03-08 06:36:05 +01:00
|
|
|
|
2012-03-07 18:36:38 +01:00
|
|
|
# py modules
|
|
|
|
"bpy.utils",
|
|
|
|
"bpy.path",
|
|
|
|
"bpy.app",
|
2012-03-08 06:36:05 +01:00
|
|
|
|
2012-03-07 18:36:38 +01:00
|
|
|
# C modules
|
2012-03-21 06:33:37 +01:00
|
|
|
"bpy.props",
|
2016-07-30 10:39:22 +02:00
|
|
|
)
|
2012-03-21 06:33:37 +01:00
|
|
|
|
2012-03-07 18:36:38 +01:00
|
|
|
for mod in app_modules:
|
|
|
|
if mod not in EXCLUDE_MODULES:
|
2018-08-14 01:37:41 +02:00
|
|
|
fw(" %s\n" % mod)
|
|
|
|
fw("\n")
|
2012-03-08 06:36:05 +01:00
|
|
|
|
2011-02-16 06:18:10 +01:00
|
|
|
fw(".. toctree::\n")
|
2018-08-14 00:50:58 +02:00
|
|
|
fw(" :maxdepth: 1\n")
|
|
|
|
fw(" :caption: Standalone Modules\n\n")
|
2011-02-16 06:18:10 +01:00
|
|
|
|
2012-03-21 06:33:37 +01:00
|
|
|
standalone_modules = (
|
2017-06-15 07:41:28 +02:00
|
|
|
# submodules are added in parent page
|
2020-07-22 06:36:30 +02:00
|
|
|
"aud",
|
|
|
|
"bgl",
|
|
|
|
"bl_math",
|
|
|
|
"blf",
|
|
|
|
"bmesh",
|
|
|
|
"bpy_extras",
|
|
|
|
"freestyle",
|
|
|
|
"gpu",
|
|
|
|
"gpu_extras",
|
|
|
|
"idprop.types",
|
|
|
|
"imbuf",
|
|
|
|
"mathutils",
|
2016-07-30 10:39:22 +02:00
|
|
|
)
|
2012-03-21 06:33:37 +01:00
|
|
|
|
2012-03-07 18:36:38 +01:00
|
|
|
for mod in standalone_modules:
|
|
|
|
if mod not in EXCLUDE_MODULES:
|
2018-08-14 01:37:41 +02:00
|
|
|
fw(" %s\n" % mod)
|
|
|
|
fw("\n")
|
2011-02-16 06:18:10 +01:00
|
|
|
|
2020-10-11 22:54:52 +02:00
|
|
|
fw(title_string("Indices", "="))
|
|
|
|
fw("* :ref:`genindex`\n")
|
|
|
|
fw("* :ref:`modindex`\n\n")
|
|
|
|
|
2022-05-21 08:33:35 +02:00
|
|
|
# Special case, this `bmesh.ops.rst` is extracted from C source.
|
2012-11-27 07:56:51 +01:00
|
|
|
if "bmesh.ops" not in EXCLUDE_MODULES:
|
|
|
|
execfile(os.path.join(SCRIPT_DIR, "rst_from_bmesh_opdefines.py"))
|
|
|
|
|
2011-02-16 06:18:10 +01:00
|
|
|
file.close()
|
|
|
|
|
2012-03-07 18:36:38 +01:00
|
|
|
|
|
|
|
def write_rst_bpy(basepath):
|
2022-05-21 08:33:35 +02:00
|
|
|
"""
|
|
|
|
Write RST file of ``bpy`` module (disabled by default)
|
|
|
|
"""
|
2012-03-07 18:36:38 +01:00
|
|
|
if ARGS.bpy:
|
|
|
|
filepath = os.path.join(basepath, "bpy.rst")
|
2011-09-29 14:11:58 +02:00
|
|
|
file = open(filepath, "w", encoding="utf-8")
|
2011-02-16 06:18:10 +01:00
|
|
|
fw = file.write
|
2012-03-07 18:36:38 +01:00
|
|
|
|
|
|
|
fw("\n")
|
|
|
|
|
|
|
|
title = ":mod:`bpy` --- Blender Python Module"
|
|
|
|
|
2012-03-21 05:43:17 +01:00
|
|
|
fw(title_string(title, "="))
|
2012-03-07 18:36:38 +01:00
|
|
|
|
|
|
|
fw(".. module:: bpy.types\n\n")
|
2011-02-16 06:18:10 +01:00
|
|
|
file.close()
|
|
|
|
|
2012-03-07 18:36:38 +01:00
|
|
|
|
|
|
|
def write_rst_types_index(basepath):
|
2022-05-21 08:33:35 +02:00
|
|
|
"""
|
|
|
|
Write the RST file of ``bpy.types`` module (index)
|
|
|
|
"""
|
2011-02-16 06:18:10 +01:00
|
|
|
if "bpy.types" not in EXCLUDE_MODULES:
|
2012-03-07 18:36:38 +01:00
|
|
|
filepath = os.path.join(basepath, "bpy.types.rst")
|
2011-09-29 14:11:58 +02:00
|
|
|
file = open(filepath, "w", encoding="utf-8")
|
2011-02-16 06:18:10 +01:00
|
|
|
fw = file.write
|
2012-03-21 05:43:17 +01:00
|
|
|
fw(title_string("Types (bpy.types)", "="))
|
2020-10-08 07:20:45 +02:00
|
|
|
fw(".. module:: bpy.types\n\n")
|
2011-02-16 06:18:10 +01:00
|
|
|
fw(".. toctree::\n")
|
|
|
|
fw(" :glob:\n\n")
|
|
|
|
fw(" bpy.types.*\n\n")
|
2022-05-31 06:07:10 +02:00
|
|
|
|
|
|
|
# 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")
|
|
|
|
|
2011-02-16 06:18:10 +01:00
|
|
|
file.close()
|
|
|
|
|
2012-03-07 18:36:38 +01:00
|
|
|
|
|
|
|
def write_rst_ops_index(basepath):
|
2022-05-21 08:33:35 +02:00
|
|
|
"""
|
|
|
|
Write the RST file of bpy.ops module (index)
|
|
|
|
"""
|
2012-03-07 18:36:38 +01:00
|
|
|
if "bpy.ops" not in EXCLUDE_MODULES:
|
|
|
|
filepath = os.path.join(basepath, "bpy.ops.rst")
|
|
|
|
file = open(filepath, "w", encoding="utf-8")
|
|
|
|
fw = file.write
|
2012-03-21 05:43:17 +01:00
|
|
|
fw(title_string("Operators (bpy.ops)", "="))
|
2020-10-08 07:20:45 +02:00
|
|
|
fw(".. module:: bpy.ops\n\n")
|
2012-03-07 18:36:38 +01:00
|
|
|
write_example_ref("", fw, "bpy.ops")
|
|
|
|
fw(".. toctree::\n")
|
2020-10-11 22:09:42 +02:00
|
|
|
fw(" :caption: Submodules\n")
|
2012-03-07 18:36:38 +01:00
|
|
|
fw(" :glob:\n\n")
|
|
|
|
fw(" bpy.ops.*\n\n")
|
|
|
|
file.close()
|
|
|
|
|
|
|
|
|
2020-03-06 14:28:54 +01:00
|
|
|
def write_rst_msgbus(basepath):
|
|
|
|
"""
|
2022-05-21 08:33:35 +02:00
|
|
|
Write the RST files of ``bpy.msgbus`` module
|
2020-03-06 14:28:54 +01:00
|
|
|
"""
|
|
|
|
if 'bpy.msgbus' in EXCLUDE_MODULES:
|
|
|
|
return
|
|
|
|
|
|
|
|
# Write the index.
|
|
|
|
filepath = os.path.join(basepath, "bpy.msgbus.rst")
|
|
|
|
file = open(filepath, "w", encoding="utf-8")
|
|
|
|
fw = file.write
|
|
|
|
fw(title_string("Message Bus (bpy.msgbus)", "="))
|
|
|
|
write_example_ref("", fw, "bpy.msgbus")
|
|
|
|
fw(".. toctree::\n")
|
|
|
|
fw(" :glob:\n\n")
|
|
|
|
fw(" bpy.msgbus.*\n\n")
|
|
|
|
file.close()
|
|
|
|
|
|
|
|
# Write the contents.
|
2020-10-09 02:56:11 +02:00
|
|
|
pymodule2sphinx(basepath, 'bpy.msgbus', bpy.msgbus, 'Message Bus', ())
|
2020-03-06 14:28:54 +01:00
|
|
|
EXAMPLE_SET_USED.add("bpy.msgbus")
|
|
|
|
|
|
|
|
|
2012-03-07 18:36:38 +01:00
|
|
|
def write_rst_data(basepath):
|
2022-05-21 08:33:35 +02:00
|
|
|
"""
|
|
|
|
Write the RST file of ``bpy.data`` module.
|
|
|
|
"""
|
2011-02-16 06:18:10 +01:00
|
|
|
if "bpy.data" not in EXCLUDE_MODULES:
|
2022-05-21 08:33:35 +02:00
|
|
|
# Not actually a module, only write this file so we can reference in the TOC.
|
2012-03-07 18:36:38 +01:00
|
|
|
filepath = os.path.join(basepath, "bpy.data.rst")
|
2011-09-29 14:11:58 +02:00
|
|
|
file = open(filepath, "w", encoding="utf-8")
|
2011-02-16 06:18:10 +01:00
|
|
|
fw = file.write
|
2012-03-21 05:43:17 +01:00
|
|
|
fw(title_string("Data Access (bpy.data)", "="))
|
2020-10-08 07:20:45 +02:00
|
|
|
fw(".. module:: bpy.data\n")
|
2011-02-16 06:18:10 +01:00
|
|
|
fw("\n")
|
2016-07-31 06:31:39 +02:00
|
|
|
fw("This module is used for all Blender/Python access.\n")
|
2011-02-16 06:18:10 +01:00
|
|
|
fw("\n")
|
|
|
|
fw(".. data:: data\n")
|
|
|
|
fw("\n")
|
2016-07-31 06:31:39 +02:00
|
|
|
fw(" Access to Blender's internal data\n")
|
2011-02-16 06:18:10 +01:00
|
|
|
fw("\n")
|
|
|
|
fw(" :type: :class:`bpy.types.BlendData`\n")
|
|
|
|
fw("\n")
|
|
|
|
fw(".. literalinclude:: ../examples/bpy.data.py\n")
|
|
|
|
file.close()
|
|
|
|
|
2012-03-07 18:36:38 +01:00
|
|
|
EXAMPLE_SET_USED.add("bpy.data")
|
2011-02-16 06:18:10 +01:00
|
|
|
|
|
|
|
|
2022-05-31 06:07:14 +02:00
|
|
|
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
|
|
|
|
|
|
|
|
|
2022-05-31 06:07:10 +02:00
|
|
|
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_"):
|
2022-06-01 07:48:20 +02:00
|
|
|
raise Exception("Found RNA enum identifier that doesn't use the 'rna_enum_' prefix, found %r!" % key)
|
2022-05-31 06:07:10 +02:00
|
|
|
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")
|
|
|
|
|
|
|
|
|
2012-03-07 18:36:38 +01:00
|
|
|
def write_rst_importable_modules(basepath):
|
2022-05-21 08:33:35 +02:00
|
|
|
"""
|
|
|
|
Write the RST files of importable modules.
|
|
|
|
"""
|
2012-03-07 18:36:38 +01:00
|
|
|
importable_modules = {
|
2016-07-31 06:31:39 +02:00
|
|
|
# Python_modules
|
2016-07-30 10:39:22 +02:00
|
|
|
"bpy.path": "Path Utilities",
|
|
|
|
"bpy.utils": "Utilities",
|
|
|
|
"bpy_extras": "Extra Utilities",
|
2018-11-07 15:45:28 +01:00
|
|
|
"gpu_extras": "GPU Utilities",
|
2012-03-07 18:36:38 +01:00
|
|
|
|
|
|
|
# C_modules
|
2016-07-30 10:39:22 +02:00
|
|
|
"aud": "Audio System",
|
|
|
|
"blf": "Font Drawing",
|
2020-02-26 05:16:10 +01:00
|
|
|
"imbuf": "Image Buffer",
|
2021-02-25 02:00:45 +01:00
|
|
|
"imbuf.types": "Image Buffer Types",
|
2021-08-30 14:19:50 +02:00
|
|
|
"gpu": "GPU Module",
|
2018-09-06 02:10:42 +02:00
|
|
|
"gpu.types": "GPU Types",
|
2021-03-01 21:35:10 +01:00
|
|
|
"gpu.matrix": "GPU Matrix Utilities",
|
|
|
|
"gpu.select": "GPU Select Utilities",
|
|
|
|
"gpu.shader": "GPU Shader Utilities",
|
|
|
|
"gpu.state": "GPU State Utilities",
|
|
|
|
"gpu.texture": "GPU Texture Utilities",
|
2021-08-17 06:44:28 +02:00
|
|
|
"gpu.platform": "GPU Platform Utilities",
|
2021-08-17 06:55:05 +02:00
|
|
|
"gpu.capabilities": "GPU Capabilities Utilities",
|
2016-07-30 10:39:22 +02:00
|
|
|
"bmesh": "BMesh Module",
|
2020-10-09 02:56:11 +02:00
|
|
|
"bmesh.ops": "BMesh Operators",
|
2016-07-30 10:39:22 +02:00
|
|
|
"bmesh.types": "BMesh Types",
|
|
|
|
"bmesh.utils": "BMesh Utilities",
|
|
|
|
"bmesh.geometry": "BMesh Geometry Utilities",
|
|
|
|
"bpy.app": "Application Data",
|
|
|
|
"bpy.app.handlers": "Application Handlers",
|
|
|
|
"bpy.app.translations": "Application Translations",
|
2018-09-13 11:21:08 +02:00
|
|
|
"bpy.app.icons": "Application Icons",
|
2018-11-26 20:25:15 +01:00
|
|
|
"bpy.app.timers": "Application Timers",
|
2016-07-30 10:39:22 +02:00
|
|
|
"bpy.props": "Property Definitions",
|
2016-07-31 07:39:30 +02:00
|
|
|
"idprop.types": "ID Property Access",
|
2016-07-30 10:39:22 +02:00
|
|
|
"mathutils": "Math Types & Utilities",
|
|
|
|
"mathutils.geometry": "Geometry Utilities",
|
|
|
|
"mathutils.bvhtree": "BVHTree Utilities",
|
|
|
|
"mathutils.kdtree": "KDTree Utilities",
|
2016-03-03 22:40:22 +01:00
|
|
|
"mathutils.interpolate": "Interpolation Utilities",
|
2016-07-30 10:39:22 +02:00
|
|
|
"mathutils.noise": "Noise Utilities",
|
2020-07-22 06:36:30 +02:00
|
|
|
"bl_math": "Additional Math Functions",
|
2016-07-30 10:39:22 +02:00
|
|
|
"freestyle": "Freestyle Module",
|
|
|
|
"freestyle.types": "Freestyle Types",
|
|
|
|
"freestyle.predicates": "Freestyle Predicates",
|
|
|
|
"freestyle.functions": "Freestyle Functions",
|
|
|
|
"freestyle.chainingiterators": "Freestyle Chaining Iterators",
|
|
|
|
"freestyle.shaders": "Freestyle Shaders",
|
|
|
|
"freestyle.utils": "Freestyle Utilities",
|
|
|
|
}
|
2020-10-09 02:56:11 +02:00
|
|
|
|
|
|
|
# This is needed since some of the sub-modules listed above are not actual modules.
|
|
|
|
# Examples include `bpy.app.translations` & `bpy.app.handlers`.
|
|
|
|
#
|
|
|
|
# Most of these are `PyStructSequence` internally,
|
|
|
|
# however we don't want to document all of these as modules since some only contain
|
|
|
|
# a few values (version number for e.g).
|
|
|
|
#
|
|
|
|
# If we remove this logic and document all `PyStructSequence` as sub-modules it means
|
|
|
|
# `bpy.app.timers` for example would be presented on the same level as library information
|
|
|
|
# access such as `bpy.app.sdl` which doesn't seem useful since it hides more useful
|
|
|
|
# module-like objects among library data access.
|
|
|
|
importable_modules_parent_map = {}
|
2022-05-22 05:30:17 +02:00
|
|
|
for mod_name in importable_modules: # Iterate over keys.
|
2020-10-09 02:56:11 +02:00
|
|
|
if mod_name in EXCLUDE_MODULES:
|
|
|
|
continue
|
|
|
|
if "." in mod_name:
|
|
|
|
mod_name, submod_name = mod_name.rsplit(".", 1)
|
|
|
|
importable_modules_parent_map.setdefault(mod_name, []).append(submod_name)
|
|
|
|
|
2012-03-07 18:36:38 +01:00
|
|
|
for mod_name, mod_descr in importable_modules.items():
|
2020-10-09 02:56:11 +02:00
|
|
|
if mod_name in EXCLUDE_MODULES:
|
|
|
|
continue
|
|
|
|
module_all_extra = importable_modules_parent_map.get(mod_name, ())
|
|
|
|
module = __import__(mod_name, fromlist=[mod_name.rsplit(".", 1)[-1]])
|
|
|
|
pymodule2sphinx(basepath, mod_name, module, mod_descr, module_all_extra)
|
2011-02-16 06:18:10 +01:00
|
|
|
|
2011-04-07 09:53:28 +02:00
|
|
|
|
2012-03-07 18:36:38 +01:00
|
|
|
def copy_handwritten_rsts(basepath):
|
2012-03-08 06:36:05 +01:00
|
|
|
|
2022-05-21 08:33:35 +02:00
|
|
|
# Info docs.
|
2011-08-25 06:25:33 +02:00
|
|
|
if not EXCLUDE_INFO_DOCS:
|
2022-05-22 05:30:17 +02:00
|
|
|
for info, _info_desc in INFO_DOCS:
|
2012-03-07 18:36:38 +01:00
|
|
|
shutil.copy2(os.path.join(RST_DIR, info), basepath)
|
2022-09-14 07:11:50 +02:00
|
|
|
for info in INFO_DOCS_OTHER:
|
|
|
|
shutil.copy2(os.path.join(RST_DIR, info), basepath)
|
2012-03-08 06:36:05 +01:00
|
|
|
|
2022-05-21 08:33:35 +02:00
|
|
|
# TODO: put this docs in Blender's code and use import as per modules above.
|
2012-03-07 18:36:38 +01:00
|
|
|
handwritten_modules = [
|
|
|
|
"bgl", # "Blender OpenGl wrapper"
|
2012-11-27 07:56:51 +01:00
|
|
|
"bmesh.ops", # generated by rst_from_bmesh_opdefines.py
|
|
|
|
|
2012-03-13 07:22:43 +01:00
|
|
|
# includes...
|
|
|
|
"include__bmesh",
|
2012-03-07 18:36:38 +01:00
|
|
|
]
|
2018-08-13 23:29:36 +02:00
|
|
|
|
2012-03-07 18:36:38 +01:00
|
|
|
for mod_name in handwritten_modules:
|
|
|
|
if mod_name not in EXCLUDE_MODULES:
|
2022-05-21 08:33:35 +02:00
|
|
|
# Copy2 keeps time/date stamps.
|
2012-03-07 18:36:38 +01:00
|
|
|
shutil.copy2(os.path.join(RST_DIR, "%s.rst" % mod_name), basepath)
|
|
|
|
|
2022-05-21 08:33:35 +02:00
|
|
|
# Change-log.
|
2012-03-07 18:36:38 +01:00
|
|
|
shutil.copy2(os.path.join(RST_DIR, "change_log.rst"), basepath)
|
|
|
|
|
2022-05-21 08:33:35 +02:00
|
|
|
# Copy images, could be smarter but just glob for now.
|
2012-12-07 06:27:09 +01:00
|
|
|
for f in os.listdir(RST_DIR):
|
|
|
|
if f.endswith(".png"):
|
|
|
|
shutil.copy2(os.path.join(RST_DIR, f), basepath)
|
|
|
|
|
2012-03-07 18:36:38 +01:00
|
|
|
|
2016-06-12 03:20:13 +02:00
|
|
|
def copy_handwritten_extra(basepath):
|
|
|
|
for f_src in EXTRA_SOURCE_FILES:
|
|
|
|
if os.sep != "/":
|
|
|
|
f_src = os.sep.join(f_src.split("/"))
|
|
|
|
|
|
|
|
f_dst = f_src.replace("..", "__")
|
|
|
|
|
|
|
|
f_src = os.path.join(RST_DIR, f_src)
|
|
|
|
f_dst = os.path.join(basepath, f_dst)
|
|
|
|
|
|
|
|
os.makedirs(os.path.dirname(f_dst), exist_ok=True)
|
|
|
|
|
|
|
|
shutil.copy2(f_src, f_dst)
|
|
|
|
|
|
|
|
|
2019-07-06 02:17:42 +02:00
|
|
|
def copy_theme_assets(basepath):
|
2022-05-21 08:33:35 +02:00
|
|
|
shutil.copytree(
|
|
|
|
os.path.join(SCRIPT_DIR, "static"),
|
|
|
|
os.path.join(basepath, "static"),
|
|
|
|
copy_function=shutil.copy,
|
|
|
|
)
|
|
|
|
shutil.copytree(
|
|
|
|
os.path.join(SCRIPT_DIR, "templates"),
|
|
|
|
os.path.join(basepath, "templates"),
|
|
|
|
copy_function=shutil.copy,
|
|
|
|
)
|
2019-07-06 02:17:42 +02:00
|
|
|
|
|
|
|
|
2012-03-07 18:36:38 +01:00
|
|
|
def rna2sphinx(basepath):
|
2011-08-25 06:25:33 +02:00
|
|
|
|
2012-03-07 18:36:38 +01:00
|
|
|
try:
|
|
|
|
os.mkdir(basepath)
|
|
|
|
except:
|
|
|
|
pass
|
2011-03-22 05:28:51 +01:00
|
|
|
|
2012-03-07 18:36:38 +01:00
|
|
|
# sphinx setup
|
|
|
|
write_sphinx_conf_py(basepath)
|
2011-02-16 06:18:10 +01:00
|
|
|
|
2012-03-07 18:36:38 +01:00
|
|
|
# main page
|
2020-10-11 23:02:41 +02:00
|
|
|
write_rst_index(basepath)
|
2009-12-25 16:50:53 +01:00
|
|
|
|
2012-03-07 18:36:38 +01:00
|
|
|
# context
|
|
|
|
if "bpy.context" not in EXCLUDE_MODULES:
|
2022-05-18 08:59:16 +02:00
|
|
|
pycontext2sphinx(basepath)
|
2009-12-25 16:50:53 +01:00
|
|
|
|
2012-03-07 18:36:38 +01:00
|
|
|
# internal modules
|
2012-03-08 06:36:05 +01:00
|
|
|
write_rst_bpy(basepath) # bpy, disabled by default
|
2012-03-07 18:36:38 +01:00
|
|
|
write_rst_types_index(basepath) # bpy.types
|
|
|
|
write_rst_ops_index(basepath) # bpy.ops
|
2020-03-06 14:28:54 +01:00
|
|
|
write_rst_msgbus(basepath) # bpy.msgbus
|
2012-03-07 18:36:38 +01:00
|
|
|
pyrna2sphinx(basepath) # bpy.types.* and bpy.ops.*
|
|
|
|
write_rst_data(basepath) # bpy.data
|
|
|
|
write_rst_importable_modules(basepath)
|
2012-03-08 06:36:05 +01:00
|
|
|
|
2022-05-31 06:07:10 +02:00
|
|
|
# `bpy_types_enum_items/*` (referenced from `bpy.types`).
|
|
|
|
if USE_SHARED_RNA_ENUM_ITEMS_STATIC:
|
|
|
|
write_rst_enum_items_and_index(basepath)
|
|
|
|
|
2012-03-07 18:36:38 +01:00
|
|
|
# copy the other rsts
|
|
|
|
copy_handwritten_rsts(basepath)
|
|
|
|
|
2016-06-12 03:20:13 +02:00
|
|
|
# copy source files referenced
|
|
|
|
copy_handwritten_extra(basepath)
|
|
|
|
|
2019-07-06 02:17:42 +02:00
|
|
|
# copy extra files needed for theme
|
|
|
|
copy_theme_assets(basepath)
|
|
|
|
|
2012-03-07 18:36:38 +01:00
|
|
|
|
2016-06-12 03:20:13 +02:00
|
|
|
def align_sphinx_in_to_sphinx_in_tmp(dir_src, dir_dst):
|
2022-05-21 08:33:35 +02:00
|
|
|
"""
|
2012-03-07 18:36:38 +01:00
|
|
|
Move changed files from SPHINX_IN_TMP to SPHINX_IN
|
2022-05-21 08:33:35 +02:00
|
|
|
"""
|
2012-03-07 18:36:38 +01:00
|
|
|
import filecmp
|
2011-02-04 10:27:25 +01:00
|
|
|
|
2022-05-21 08:33:35 +02:00
|
|
|
# Possible the dir doesn't exist when running recursively.
|
2016-06-12 03:20:13 +02:00
|
|
|
os.makedirs(dir_dst, exist_ok=True)
|
|
|
|
|
|
|
|
sphinx_dst_files = set(os.listdir(dir_dst))
|
|
|
|
sphinx_src_files = set(os.listdir(dir_src))
|
2012-03-05 15:12:38 +01:00
|
|
|
|
2022-05-21 08:33:35 +02:00
|
|
|
# Remove deprecated files that have been removed.
|
2016-06-12 03:20:13 +02:00
|
|
|
for f in sorted(sphinx_dst_files):
|
|
|
|
if f not in sphinx_src_files:
|
2022-05-22 05:30:17 +02:00
|
|
|
BPY_LOGGER.debug("\tdeprecated: %s", f)
|
2016-06-12 03:20:13 +02:00
|
|
|
f_dst = os.path.join(dir_dst, f)
|
|
|
|
if os.path.isdir(f_dst):
|
|
|
|
shutil.rmtree(f_dst, True)
|
|
|
|
else:
|
|
|
|
os.remove(f_dst)
|
2011-06-27 06:50:08 +02:00
|
|
|
|
2012-03-07 18:36:38 +01:00
|
|
|
# freshen with new files.
|
2016-06-12 03:20:13 +02:00
|
|
|
for f in sorted(sphinx_src_files):
|
|
|
|
f_src = os.path.join(dir_src, f)
|
|
|
|
f_dst = os.path.join(dir_dst, f)
|
2011-06-27 06:50:08 +02:00
|
|
|
|
2016-06-12 03:20:13 +02:00
|
|
|
if os.path.isdir(f_src):
|
|
|
|
align_sphinx_in_to_sphinx_in_tmp(f_src, f_dst)
|
|
|
|
else:
|
|
|
|
do_copy = True
|
|
|
|
if f in sphinx_dst_files:
|
|
|
|
if filecmp.cmp(f_src, f_dst):
|
|
|
|
do_copy = False
|
2011-06-27 06:50:08 +02:00
|
|
|
|
2016-06-12 03:20:13 +02:00
|
|
|
if do_copy:
|
2022-05-22 05:30:17 +02:00
|
|
|
BPY_LOGGER.debug("\tupdating: %s", f)
|
2016-06-12 03:20:13 +02:00
|
|
|
shutil.copy(f_src, f_dst)
|
2011-06-27 06:50:08 +02:00
|
|
|
|
|
|
|
|
2012-03-16 11:49:39 +01:00
|
|
|
def refactor_sphinx_log(sphinx_logfile):
|
|
|
|
refactored_log = []
|
|
|
|
with open(sphinx_logfile, "r", encoding="utf-8") as original_logfile:
|
|
|
|
lines = set(original_logfile.readlines())
|
|
|
|
for line in lines:
|
|
|
|
if 'warning' in line.lower() or 'error' in line.lower():
|
|
|
|
line = line.strip().split(None, 2)
|
|
|
|
if len(line) == 3:
|
|
|
|
location, kind, msg = line
|
|
|
|
location = os.path.relpath(location, start=SPHINX_IN)
|
|
|
|
refactored_log.append((kind, location, msg))
|
|
|
|
with open(sphinx_logfile, "w", encoding="utf-8") as refactored_logfile:
|
|
|
|
for log in sorted(refactored_log):
|
|
|
|
refactored_logfile.write("%-12s %s\n %s\n" % log)
|
|
|
|
|
|
|
|
|
2019-11-15 16:37:40 +01:00
|
|
|
def setup_monkey_patch():
|
2013-02-05 06:09:19 +01:00
|
|
|
filepath = os.path.join(SCRIPT_DIR, "sphinx_doc_gen_monkeypatch.py")
|
2022-05-22 05:30:17 +02:00
|
|
|
execfile(filepath)
|
2013-02-05 06:09:19 +01:00
|
|
|
|
|
|
|
|
2019-11-15 16:37:40 +01:00
|
|
|
# Avoid adding too many changes here.
|
|
|
|
def setup_blender():
|
|
|
|
# Remove handlers since the functions get included
|
|
|
|
# in the doc-string and don't have meaningful names.
|
2020-11-20 08:53:27 +01:00
|
|
|
lists_to_restore = []
|
|
|
|
for var in bpy.app.handlers:
|
|
|
|
if isinstance(var, list):
|
|
|
|
lists_to_restore.append((var[:], var))
|
|
|
|
var.clear()
|
|
|
|
|
|
|
|
return {
|
|
|
|
"lists_to_restore": lists_to_restore,
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
def teardown_blender(setup_data):
|
|
|
|
for var_src, var_dst in setup_data["lists_to_restore"]:
|
|
|
|
var_dst[:] = var_src
|
2019-11-15 16:37:40 +01:00
|
|
|
|
|
|
|
|
2012-03-07 18:36:38 +01:00
|
|
|
def main():
|
2011-06-27 06:50:08 +02:00
|
|
|
|
2019-11-15 16:37:40 +01:00
|
|
|
# First monkey patch to load in fake members.
|
|
|
|
setup_monkey_patch()
|
|
|
|
|
2021-11-08 06:20:29 +01:00
|
|
|
# Perform changes to Blender itself.
|
2020-11-20 08:53:27 +01:00
|
|
|
setup_data = setup_blender()
|
2013-02-05 06:09:19 +01:00
|
|
|
|
2022-05-21 08:33:35 +02:00
|
|
|
# Eventually, create the directories.
|
2012-03-08 19:36:23 +01:00
|
|
|
for dir_path in [ARGS.output_dir, SPHINX_IN]:
|
|
|
|
if not os.path.exists(dir_path):
|
|
|
|
os.mkdir(dir_path)
|
|
|
|
|
2022-05-21 08:33:35 +02:00
|
|
|
# Eventually, log in files.
|
2012-03-16 15:49:16 +01:00
|
|
|
if ARGS.log:
|
|
|
|
bpy_logfile = os.path.join(ARGS.output_dir, ".bpy.log")
|
|
|
|
bpy_logfilehandler = logging.FileHandler(bpy_logfile, mode="w")
|
|
|
|
bpy_logfilehandler.setLevel(logging.DEBUG)
|
|
|
|
BPY_LOGGER.addHandler(bpy_logfilehandler)
|
2012-03-24 08:36:32 +01:00
|
|
|
|
2022-05-21 08:33:35 +02:00
|
|
|
# using a `FileHandler` seems to disable the `stdout`, so we add a `StreamHandler`.
|
2012-03-16 15:49:16 +01:00
|
|
|
bpy_log_stdout_handler = logging.StreamHandler(stream=sys.stdout)
|
|
|
|
bpy_log_stdout_handler.setLevel(logging.DEBUG)
|
|
|
|
BPY_LOGGER.addHandler(bpy_log_stdout_handler)
|
|
|
|
|
2022-05-21 08:33:35 +02:00
|
|
|
# In case of out-of-source build, copy the needed directories.
|
2012-03-16 11:49:39 +01:00
|
|
|
if ARGS.output_dir != SCRIPT_DIR:
|
2022-05-21 08:33:35 +02:00
|
|
|
# Examples directory.
|
2012-03-16 11:49:39 +01:00
|
|
|
examples_dir_copy = os.path.join(ARGS.output_dir, "examples")
|
|
|
|
if os.path.exists(examples_dir_copy):
|
|
|
|
shutil.rmtree(examples_dir_copy, True)
|
2022-05-21 08:33:35 +02:00
|
|
|
shutil.copytree(
|
|
|
|
EXAMPLES_DIR,
|
|
|
|
examples_dir_copy,
|
|
|
|
ignore=shutil.ignore_patterns(*(".svn",)),
|
|
|
|
copy_function=shutil.copy,
|
|
|
|
)
|
|
|
|
|
|
|
|
# Dump the API in RST files.
|
2012-03-16 11:49:39 +01:00
|
|
|
if os.path.exists(SPHINX_IN_TMP):
|
|
|
|
shutil.rmtree(SPHINX_IN_TMP, True)
|
|
|
|
|
|
|
|
rna2sphinx(SPHINX_IN_TMP)
|
|
|
|
|
2022-06-15 15:43:13 +02:00
|
|
|
if ARGS.changelog:
|
|
|
|
generate_changelog()
|
|
|
|
|
2012-03-07 18:36:38 +01:00
|
|
|
if ARGS.full_rebuild:
|
2022-05-21 08:33:35 +02:00
|
|
|
# Only for full updates.
|
2012-03-07 18:36:38 +01:00
|
|
|
shutil.rmtree(SPHINX_IN, True)
|
2022-05-21 08:33:35 +02:00
|
|
|
shutil.copytree(
|
|
|
|
SPHINX_IN_TMP,
|
|
|
|
SPHINX_IN,
|
|
|
|
copy_function=shutil.copy,
|
|
|
|
)
|
2012-03-16 11:49:39 +01:00
|
|
|
if ARGS.sphinx_build and os.path.exists(SPHINX_OUT):
|
|
|
|
shutil.rmtree(SPHINX_OUT, True)
|
|
|
|
if ARGS.sphinx_build_pdf and os.path.exists(SPHINX_OUT_PDF):
|
|
|
|
shutil.rmtree(SPHINX_OUT_PDF, True)
|
2012-03-07 18:36:38 +01:00
|
|
|
else:
|
2022-05-21 08:33:35 +02:00
|
|
|
# Move changed files in `SPHINX_IN`.
|
2016-06-12 03:20:13 +02:00
|
|
|
align_sphinx_in_to_sphinx_in_tmp(SPHINX_IN_TMP, SPHINX_IN)
|
2011-06-27 06:50:08 +02:00
|
|
|
|
2022-05-21 08:33:35 +02:00
|
|
|
# Report which example files weren't used.
|
2011-06-27 06:50:08 +02:00
|
|
|
EXAMPLE_SET_UNUSED = EXAMPLE_SET - EXAMPLE_SET_USED
|
|
|
|
if EXAMPLE_SET_UNUSED:
|
2022-05-22 05:30:17 +02:00
|
|
|
BPY_LOGGER.debug("\nUnused examples found in '%s'...", EXAMPLES_DIR)
|
2012-03-16 11:49:39 +01:00
|
|
|
for f in sorted(EXAMPLE_SET_UNUSED):
|
2022-05-22 05:30:17 +02:00
|
|
|
BPY_LOGGER.debug(" %s.py", f)
|
|
|
|
BPY_LOGGER.debug(" %d total\n", len(EXAMPLE_SET_UNUSED))
|
2012-03-08 19:36:23 +01:00
|
|
|
|
2022-05-21 08:33:35 +02:00
|
|
|
# Eventually, build the html docs.
|
2012-03-08 19:36:23 +01:00
|
|
|
if ARGS.sphinx_build:
|
|
|
|
import subprocess
|
2012-03-16 11:49:39 +01:00
|
|
|
subprocess.call(SPHINX_BUILD)
|
|
|
|
|
2022-05-21 08:33:35 +02:00
|
|
|
# Sphinx-build log cleanup+sort.
|
2012-03-16 11:49:39 +01:00
|
|
|
if ARGS.log:
|
|
|
|
if os.stat(SPHINX_BUILD_LOG).st_size:
|
|
|
|
refactor_sphinx_log(SPHINX_BUILD_LOG)
|
|
|
|
|
2022-05-21 08:33:35 +02:00
|
|
|
# Eventually, build the PDF docs.
|
2012-03-16 11:49:39 +01:00
|
|
|
if ARGS.sphinx_build_pdf:
|
|
|
|
import subprocess
|
|
|
|
subprocess.call(SPHINX_BUILD_PDF)
|
|
|
|
subprocess.call(SPHINX_MAKE_PDF, stdout=SPHINX_MAKE_PDF_STDOUT)
|
|
|
|
|
2022-05-21 08:33:35 +02:00
|
|
|
# Sphinx-build log cleanup+sort.
|
2012-03-16 11:49:39 +01:00
|
|
|
if ARGS.log:
|
|
|
|
if os.stat(SPHINX_BUILD_PDF_LOG).st_size:
|
|
|
|
refactor_sphinx_log(SPHINX_BUILD_PDF_LOG)
|
|
|
|
|
2022-05-21 08:33:35 +02:00
|
|
|
# Eventually, prepare the dir to be deployed online (REFERENCE_PATH).
|
2012-03-16 11:49:39 +01:00
|
|
|
if ARGS.pack_reference:
|
|
|
|
|
|
|
|
if ARGS.sphinx_build:
|
2022-05-21 08:33:35 +02:00
|
|
|
# Delete REFERENCE_PATH.
|
2012-03-16 11:49:39 +01:00
|
|
|
if os.path.exists(REFERENCE_PATH):
|
|
|
|
shutil.rmtree(REFERENCE_PATH, True)
|
|
|
|
|
2022-05-21 08:33:35 +02:00
|
|
|
# Copy SPHINX_OUT to the REFERENCE_PATH.
|
|
|
|
ignores = (".doctrees", ".buildinfo")
|
|
|
|
shutil.copytree(
|
|
|
|
SPHINX_OUT,
|
|
|
|
REFERENCE_PATH,
|
|
|
|
ignore=shutil.ignore_patterns(*ignores),
|
|
|
|
)
|
2012-03-16 11:49:39 +01:00
|
|
|
|
2022-05-21 08:33:35 +02:00
|
|
|
# Zip REFERENCE_PATH.
|
2012-03-16 11:49:39 +01:00
|
|
|
basename = os.path.join(ARGS.output_dir, REFERENCE_NAME)
|
2022-05-21 08:33:35 +02:00
|
|
|
tmp_path = shutil.make_archive(
|
|
|
|
basename, "zip",
|
|
|
|
root_dir=ARGS.output_dir,
|
|
|
|
base_dir=REFERENCE_NAME,
|
|
|
|
)
|
2012-03-16 11:49:39 +01:00
|
|
|
final_path = os.path.join(REFERENCE_PATH, BLENDER_ZIP_FILENAME)
|
|
|
|
os.rename(tmp_path, final_path)
|
|
|
|
|
|
|
|
if ARGS.sphinx_build_pdf:
|
2022-05-21 08:33:35 +02:00
|
|
|
# Copy the pdf to REFERENCE_PATH.
|
|
|
|
shutil.copy(
|
|
|
|
os.path.join(SPHINX_OUT_PDF, "contents.pdf"),
|
|
|
|
os.path.join(REFERENCE_PATH, BLENDER_PDF_FILENAME),
|
|
|
|
)
|
2012-03-08 19:36:23 +01:00
|
|
|
|
2020-11-20 08:53:27 +01:00
|
|
|
teardown_blender(setup_data)
|
|
|
|
|
2009-12-25 16:50:53 +01:00
|
|
|
sys.exit()
|
2010-07-19 15:36:10 +02:00
|
|
|
|
2012-03-07 18:36:38 +01:00
|
|
|
|
2010-07-19 15:36:10 +02:00
|
|
|
if __name__ == '__main__':
|
|
|
|
main()
|