Revert "Revert "Revert "TEST COMMIT: API doc generation changes."""
This reverts commit 5a30fe29ef.
This commit is contained in:
@@ -1,112 +1,59 @@
|
|||||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
"""
|
"""
|
||||||
---------------
|
Dump the python API into a text file so we can generate changelogs.
|
||||||
|
|
||||||
Dump the python API into a JSON file, or generate changelogs from those JSON API dumps.
|
output from this tool should be added into "doc/python_api/rst/change_log.rst"
|
||||||
|
|
||||||
Typically, changelog output from this tool should be added into "doc/python_api/rst/change_log.rst"
|
# dump api blender_version.py in CWD
|
||||||
|
blender --background --python doc/python_api/sphinx_changelog_gen.py -- --dump
|
||||||
|
|
||||||
API dump files are saved together with the generated API doc on the server, with a general index file.
|
# create changelog
|
||||||
This way the changelog generation simply needs to re-download the previous version's dump for the diffing process.
|
|
||||||
|
|
||||||
---------------
|
|
||||||
|
|
||||||
# Dump api blender_version.json in CWD:
|
|
||||||
blender --background --factory-startup --python doc/python_api/sphinx_changelog_gen.py -- \
|
blender --background --factory-startup --python doc/python_api/sphinx_changelog_gen.py -- \
|
||||||
--indexpath="path/to/api/docs/api_dump_index.json" \
|
--api_from blender_2_63_0.py \
|
||||||
dump --filepath-out="path/to/api/docs/<version>/api_dump.json"
|
--api_to blender_2_64_0.py \
|
||||||
|
--api_out changes.rst
|
||||||
|
|
||||||
# Create changelog:
|
|
||||||
blender --background --factory-startup --python doc/python_api/sphinx_changelog_gen.py -- \
|
|
||||||
--indexpath="path/to/api/docs/api_dump_index.json" \
|
|
||||||
changelog --filepath-out doc/python_api/rst/change_log.rst
|
|
||||||
|
|
||||||
# Api comparison can also run without blender,
|
# Api comparison can also run without blender
|
||||||
# will by default generate changeloig between the last two available versions listed in the index,
|
|
||||||
# unless input files are provided explicitely:
|
|
||||||
python doc/python_api/sphinx_changelog_gen.py -- \
|
python doc/python_api/sphinx_changelog_gen.py -- \
|
||||||
--indexpath="path/to/api/docs/api_dump_index.json" \
|
--api_from blender_api_2_63_0.py \
|
||||||
changelog --filepath-in-from blender_api_2_63_0.json \
|
--api_to blender_api_2_64_0.py \
|
||||||
--filepath-in-to blender_api_2_64_0.json \
|
--api_out changes.rst
|
||||||
--filepath-out changes.rst
|
|
||||||
|
|
||||||
--------------
|
# Save the latest API dump in this folder, renaming it with its revision.
|
||||||
|
# This way the next person updating it doesn't need to build an old Blender only for that
|
||||||
API dump index format:
|
|
||||||
|
|
||||||
{[version_main, version_sub]: "<version>/api_dump.json", ...
|
|
||||||
}
|
|
||||||
|
|
||||||
API dump format:
|
|
||||||
|
|
||||||
[
|
|
||||||
[version_main, vserion_sub, version_path],
|
|
||||||
{"module.name":
|
|
||||||
{"parent.class":
|
|
||||||
{"basic_type", "member_name":
|
|
||||||
["Name", type, range, length, default, descr, f_args, f_arg_types, f_ret_types]}, ...
|
|
||||||
}, ...
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import json
|
# format
|
||||||
import os
|
'''
|
||||||
|
{"module.name":
|
||||||
|
{"parent.class":
|
||||||
|
{"basic_type", "member_name":
|
||||||
|
("Name", type, range, length, default, descr, f_args, f_arg_types, f_ret_types)}, ...
|
||||||
|
}, ...
|
||||||
|
}
|
||||||
|
'''
|
||||||
|
|
||||||
api_names = "basic_type" "name", "type", "range", "length", "default", "descr", "f_args", "f_arg_types", "f_ret_types"
|
api_names = "basic_type" "name", "type", "range", "length", "default", "descr", "f_args", "f_arg_types", "f_ret_types"
|
||||||
|
|
||||||
API_BASIC_TYPE = 0
|
API_BASIC_TYPE = 0
|
||||||
API_F_ARGS = 7
|
API_F_ARGS = 7
|
||||||
|
|
||||||
|
|
||||||
def api_version():
|
def api_dunp_fname():
|
||||||
try:
|
|
||||||
import bpy
|
import bpy
|
||||||
except:
|
return "blender_api_%s.py" % "_".join([str(i) for i in bpy.app.version])
|
||||||
return None, None
|
|
||||||
version = tuple(bpy.app.version[:2])
|
|
||||||
version_key = "%d.%d" % (version[0], version[1])
|
|
||||||
return version, version_key
|
|
||||||
|
|
||||||
|
|
||||||
def api_version_previous_in_index(index, version):
|
def api_dump():
|
||||||
print("Searching for previous version to %s in %r" % (version, index))
|
|
||||||
version_prev = (version[0], version[1])
|
|
||||||
while True:
|
|
||||||
version_prev = (version_prev[0], version_prev[1] - 1)
|
|
||||||
if version_prev[1] < 0:
|
|
||||||
version_prev = (version_prev[0] - 1, 99)
|
|
||||||
if version_prev[0] < 0:
|
|
||||||
return None, None
|
|
||||||
version_prev_key = "%d.%d" % (version_prev[0], version_prev[1])
|
|
||||||
print("Checking for previous version %s" % (version_prev,))
|
|
||||||
if version_prev_key in index:
|
|
||||||
print("Found previous version %s: %r" % (version_prev, index[version_prev_key]))
|
|
||||||
return version_prev, version_prev_key
|
|
||||||
|
|
||||||
|
|
||||||
class JSONEncoderAPIDump(json.JSONEncoder):
|
|
||||||
def default(self, o):
|
|
||||||
if o is ...:
|
|
||||||
return "..."
|
|
||||||
if isinstance(o, set):
|
|
||||||
return tuple(o)
|
|
||||||
return json.JSONEncoder.default(self, o)
|
|
||||||
|
|
||||||
|
|
||||||
def api_dump(args):
|
|
||||||
import rna_info
|
|
||||||
import inspect
|
|
||||||
|
|
||||||
version, version_key = api_version()
|
|
||||||
if version is None:
|
|
||||||
raise(ValueError("API dumps can only be generated from within Blender."))
|
|
||||||
|
|
||||||
dump = {}
|
dump = {}
|
||||||
dump_module = dump["bpy.types"] = {}
|
dump_module = dump["bpy.types"] = {}
|
||||||
|
|
||||||
|
import rna_info
|
||||||
|
import inspect
|
||||||
|
|
||||||
struct = rna_info.BuildRNAInfo()[0]
|
struct = rna_info.BuildRNAInfo()[0]
|
||||||
for struct_id, struct_info in sorted(struct.items()):
|
for struct_id, struct_info in sorted(struct.items()):
|
||||||
|
|
||||||
@@ -208,24 +155,17 @@ def api_dump(args):
|
|||||||
)
|
)
|
||||||
del funcs
|
del funcs
|
||||||
|
|
||||||
filepath_out = args.filepath_out
|
import pprint
|
||||||
with open(filepath_out, 'w', encoding='utf-8') as file_handle:
|
|
||||||
json.dump((version, dump), file_handle, cls=JSONEncoderAPIDump)
|
|
||||||
|
|
||||||
indexpath = args.indexpath
|
filename = api_dunp_fname()
|
||||||
if os.path.exists(indexpath):
|
filehandle = open(filename, 'w', encoding='utf-8')
|
||||||
with open(indexpath, 'r', encoding='utf-8') as file_handle:
|
tot = filehandle.write(pprint.pformat(dump, width=1))
|
||||||
index = json.load(file_handle)
|
filehandle.close()
|
||||||
else:
|
print("%s, %d bytes written" % (filename, tot))
|
||||||
index = {}
|
|
||||||
index[version_key] = filepath_out
|
|
||||||
with open(indexpath, 'w', encoding='utf-8') as file_handle:
|
|
||||||
json.dump(index, file_handle)
|
|
||||||
|
|
||||||
print("API version %s dumped into %r, and index %r has been updated" % (version_key, filepath_out, indexpath))
|
|
||||||
|
|
||||||
|
|
||||||
def compare_props(a, b, fuzz=0.75):
|
def compare_props(a, b, fuzz=0.75):
|
||||||
|
|
||||||
# must be same basic_type, function != property
|
# must be same basic_type, function != property
|
||||||
if a[0] != b[0]:
|
if a[0] != b[0]:
|
||||||
return False
|
return False
|
||||||
@@ -240,45 +180,15 @@ def compare_props(a, b, fuzz=0.75):
|
|||||||
return ((tot / totlen) >= fuzz)
|
return ((tot / totlen) >= fuzz)
|
||||||
|
|
||||||
|
|
||||||
def api_changelog(args):
|
def api_changelog(api_from, api_to, api_out):
|
||||||
indexpath = args.indexpath
|
|
||||||
filepath_in_from = args.filepath_in_from
|
|
||||||
filepath_in_to = args.filepath_in_to
|
|
||||||
filepath_out = args.filepath_out
|
|
||||||
|
|
||||||
rootpath = os.path.dirname(indexpath)
|
file_handle = open(api_from, 'r', encoding='utf-8')
|
||||||
|
dict_from = eval(file_handle.read())
|
||||||
|
file_handle.close()
|
||||||
|
|
||||||
version, version_key = api_version()
|
file_handle = open(api_to, 'r', encoding='utf-8')
|
||||||
if version is None and (filepath_in_from is None or filepath_in_to is None):
|
dict_to = eval(file_handle.read())
|
||||||
raise(ValueError("API dumps files must be given when ran outside of Blender."))
|
file_handle.close()
|
||||||
|
|
||||||
with open(indexpath, 'r', encoding='utf-8') as file_handle:
|
|
||||||
index = json.load(file_handle)
|
|
||||||
|
|
||||||
if filepath_in_to == None:
|
|
||||||
filepath_in_to = index.get(version_key, None)
|
|
||||||
if filepath_in_to == None:
|
|
||||||
raise(ValueError("Cannot find API dump file for Blender version " + str(version) + " in index file."))
|
|
||||||
|
|
||||||
print("Found to file: %r" % filepath_in_to)
|
|
||||||
|
|
||||||
if filepath_in_from == None:
|
|
||||||
version_from, version_from_key = api_version_previous_in_index(index, version)
|
|
||||||
if version_from is None:
|
|
||||||
raise(ValueError("No previous version of Blender could be found in the index."))
|
|
||||||
filepath_in_from = index.get(version_from_key, None)
|
|
||||||
if filepath_in_from is None:
|
|
||||||
raise(ValueError("Cannot find API dump file for previous Blender version " + str(version_from) + " in index file."))
|
|
||||||
|
|
||||||
print("Found from file: %r" % filepath_in_from)
|
|
||||||
|
|
||||||
|
|
||||||
with open(os.path.join(rootpath, filepath_in_from), 'r', encoding='utf-8') as file_handle:
|
|
||||||
_, dict_from = json.load(file_handle)
|
|
||||||
|
|
||||||
with open(os.path.join(rootpath, filepath_in_to), 'r', encoding='utf-8') as file_handle:
|
|
||||||
dump_version, dict_to = json.load(file_handle)
|
|
||||||
assert(tuple(dump_version) == version)
|
|
||||||
|
|
||||||
api_changes = []
|
api_changes = []
|
||||||
|
|
||||||
@@ -339,26 +249,12 @@ def api_changelog(args):
|
|||||||
|
|
||||||
# also document function argument changes
|
# also document function argument changes
|
||||||
|
|
||||||
with open(filepath_out, 'w', encoding='utf-8') as fout:
|
fout = open(api_out, 'w', encoding='utf-8')
|
||||||
fw = fout.write
|
fw = fout.write
|
||||||
# print(api_changes)
|
# print(api_changes)
|
||||||
|
|
||||||
# :class:`bpy_struct.id_data`
|
# :class:`bpy_struct.id_data`
|
||||||
|
|
||||||
# Write header.
|
|
||||||
fw(""
|
|
||||||
":tocdepth: 2\n"
|
|
||||||
"\n"
|
|
||||||
"Blender API Change Log\n"
|
|
||||||
"**********************\n"
|
|
||||||
"\n"
|
|
||||||
".. note, this document is auto generated by sphinx_changelog_gen.py\n"
|
|
||||||
"\n"
|
|
||||||
"\n"
|
|
||||||
"%s to %s\n"
|
|
||||||
"============\n"
|
|
||||||
"\n" % (version_from_key, version_key))
|
|
||||||
|
|
||||||
def write_title(title, title_char):
|
def write_title(title, title_char):
|
||||||
fw("%s\n%s\n\n" % (title, title_char * len(title)))
|
fw("%s\n%s\n\n" % (title, title_char * len(title)))
|
||||||
|
|
||||||
@@ -393,14 +289,22 @@ def api_changelog(args):
|
|||||||
fw("* :class:`%s.%s.%s` (%s), *was (%s)*\n" % (mod_id, class_name, func_id, args_new, args_old))
|
fw("* :class:`%s.%s.%s` (%s), *was (%s)*\n" % (mod_id, class_name, func_id, args_new, args_old))
|
||||||
fw("\n")
|
fw("\n")
|
||||||
|
|
||||||
print("Written: %r" % filepath_out)
|
fout.close()
|
||||||
|
|
||||||
|
print("Written: %r" % api_out)
|
||||||
|
|
||||||
|
|
||||||
def main(argv=None):
|
def main():
|
||||||
import sys
|
import sys
|
||||||
import argparse
|
import os
|
||||||
|
|
||||||
|
try:
|
||||||
|
import argparse
|
||||||
|
except ImportError:
|
||||||
|
print("Old Blender, just dumping")
|
||||||
|
api_dump()
|
||||||
|
return
|
||||||
|
|
||||||
if argv is None:
|
|
||||||
argv = sys.argv
|
argv = sys.argv
|
||||||
|
|
||||||
if "--" not in argv:
|
if "--" not in argv:
|
||||||
@@ -412,39 +316,42 @@ def main(argv=None):
|
|||||||
usage_text = "Run blender in background mode with this script: "
|
usage_text = "Run blender in background mode with this script: "
|
||||||
"blender --background --factory-startup --python %s -- [options]" % os.path.basename(__file__)
|
"blender --background --factory-startup --python %s -- [options]" % os.path.basename(__file__)
|
||||||
|
|
||||||
parser = argparse.ArgumentParser(description=usage_text,
|
epilog = "Run this before releases"
|
||||||
epilog=__doc__,
|
|
||||||
formatter_class=argparse.RawDescriptionHelpFormatter)
|
parser = argparse.ArgumentParser(description=usage_text, epilog=epilog)
|
||||||
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--indexpath", dest="indexpath", metavar='FILE', required=True,
|
"--dump", dest="dump", action='store_true',
|
||||||
help="Path of the JSON file containing the index of all available API dumps.")
|
help="When set the api will be dumped into blender_version.py")
|
||||||
|
|
||||||
parser_commands = parser.add_subparsers(required=True)
|
parser.add_argument(
|
||||||
|
"--api_from", dest="api_from", metavar='FILE',
|
||||||
|
help="File to compare from (previous version)")
|
||||||
|
parser.add_argument(
|
||||||
|
"--api_to", dest="api_to", metavar='FILE',
|
||||||
|
help="File to compare from (current)")
|
||||||
|
parser.add_argument(
|
||||||
|
"--api_out", dest="api_out", metavar='FILE',
|
||||||
|
help="Output sphinx changelog")
|
||||||
|
|
||||||
parser_dump = parser_commands.add_parser('dump', help="Dump the current Blender Python API into a JSON file.")
|
args = parser.parse_args(argv) # In this example we won't use the args
|
||||||
parser_dump.add_argument(
|
|
||||||
"--filepath-out", dest="filepath_out", metavar='FILE', required=True,
|
|
||||||
help="Path of the JSON file containing the dump of the API.")
|
|
||||||
parser_dump.set_defaults(func=api_dump)
|
|
||||||
|
|
||||||
parser_changelog = parser_commands.add_parser('changelog', help="Generate the RST changelog page based on two Blender Python API JSON dumps.")
|
if not argv:
|
||||||
|
print("No args given!")
|
||||||
|
parser.print_help()
|
||||||
|
return
|
||||||
|
|
||||||
parser_changelog.add_argument(
|
if args.dump:
|
||||||
"--filepath-in-from", dest="filepath_in_from", metavar='FILE', default=None,
|
api_dump()
|
||||||
help="JSON dump file to compare from (typically, previous version). "
|
else:
|
||||||
"If not given, will be automatically determined from current Blender version and index file.")
|
if args.api_from and args.api_to and args.api_out:
|
||||||
parser_changelog.add_argument(
|
api_changelog(args.api_from, args.api_to, args.api_out)
|
||||||
"--filepath-in-to", dest="filepath_in_to", metavar='FILE', default=None,
|
else:
|
||||||
help="JSON dump file to compare to (typically, current version). "
|
print("Error: --api_from/api_to/api_out args needed")
|
||||||
"If not given, will be automatically determined from current Blender version and index file.")
|
parser.print_help()
|
||||||
parser_changelog.add_argument(
|
return
|
||||||
"--filepath-out", dest="filepath_out", metavar='FILE', required=True,
|
|
||||||
help="Output sphinx changelog RST file.")
|
|
||||||
parser_changelog.set_defaults(func=api_changelog)
|
|
||||||
|
|
||||||
args = parser.parse_args(argv)
|
print("batch job finished, exiting")
|
||||||
|
|
||||||
args.func(args)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
@@ -141,26 +141,6 @@ def handle_args():
|
|||||||
required=False,
|
required=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
parser.add_argument(
|
|
||||||
"--api-changelog-generate",
|
|
||||||
dest="changelog",
|
|
||||||
default=False,
|
|
||||||
action='store_true',
|
|
||||||
help="Generate the API changelog RST file "
|
|
||||||
"(default=False, requires `--api-dump-index-path` parameter)",
|
|
||||||
required=False,
|
|
||||||
)
|
|
||||||
|
|
||||||
parser.add_argument(
|
|
||||||
"--api-dump-index-path",
|
|
||||||
dest="api_dump_index_path",
|
|
||||||
metavar='FILE',
|
|
||||||
default=None,
|
|
||||||
help="Path to the API dump index JSON file "
|
|
||||||
"(required when `--api-changelog-generate` is True)",
|
|
||||||
required=False,
|
|
||||||
)
|
|
||||||
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"-o", "--output",
|
"-o", "--output",
|
||||||
dest="output_dir",
|
dest="output_dir",
|
||||||
@@ -534,27 +514,6 @@ if ARGS.sphinx_build_pdf:
|
|||||||
sphinx_make_pdf_log = os.path.join(ARGS.output_dir, ".latex_make.log")
|
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")
|
SPHINX_MAKE_PDF_STDOUT = open(sphinx_make_pdf_log, "w", encoding="utf-8")
|
||||||
|
|
||||||
|
|
||||||
# --------------------------------CHANGELOG GENERATION--------------------------------------
|
|
||||||
|
|
||||||
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"))
|
|
||||||
|
|
||||||
def generate_changelog():
|
|
||||||
import importlib.util
|
|
||||||
spec = importlib.util.spec_from_file_location("sphinx_changelog_gen",
|
|
||||||
os.path.abspath(os.path.join(SCRIPT_DIR, "sphinx_changelog_gen.py")))
|
|
||||||
sphinx_changelog_gen = importlib.util.module_from_spec(spec)
|
|
||||||
spec.loader.exec_module(sphinx_changelog_gen)
|
|
||||||
|
|
||||||
sphinx_changelog_gen.main(("--", "--indexpath", API_DUMP_INDEX_FILEPATH, "dump", "--filepath-out", API_DUMP_FILEPATH))
|
|
||||||
|
|
||||||
sphinx_changelog_gen.main(("--", "--indexpath", API_DUMP_INDEX_FILEPATH, "changelog", "--filepath-out", API_CHANGELOG_FILEPATH))
|
|
||||||
|
|
||||||
|
|
||||||
# --------------------------------API DUMP--------------------------------------
|
# --------------------------------API DUMP--------------------------------------
|
||||||
|
|
||||||
# Lame, python won't give some access.
|
# Lame, python won't give some access.
|
||||||
@@ -2514,9 +2473,6 @@ def main():
|
|||||||
|
|
||||||
rna2sphinx(SPHINX_IN_TMP)
|
rna2sphinx(SPHINX_IN_TMP)
|
||||||
|
|
||||||
if ARGS.changelog:
|
|
||||||
generate_changelog()
|
|
||||||
|
|
||||||
if ARGS.full_rebuild:
|
if ARGS.full_rebuild:
|
||||||
# Only for full updates.
|
# Only for full updates.
|
||||||
shutil.rmtree(SPHINX_IN, True)
|
shutil.rmtree(SPHINX_IN, True)
|
||||||
|
|||||||
Reference in New Issue
Block a user