tabby/crates/ctranslate2-bindings/ctranslate2/docs/generate.py

185 lines
6.2 KiB
Python

import inspect
import numbers
import os
import sys
import ctranslate2
def document_class(cls, output_dir, class_path, base_path=None, children_paths=None):
with open(os.path.join(output_dir, "%s.rst" % class_path), "w") as doc:
class_name = class_path.split(".")[-1]
doc.write("%s\n" % class_name)
doc.write("=" * len(class_name))
doc.write("\n\n")
doc.write(".. autoclass:: %s\n" % class_path)
doc.write(" :members:\n")
doc.write(" :undoc-members:\n")
doc.write(" :inherited-members:\n")
if base_path:
doc.write("\n **Inherits from:** :class:`%s`\n" % base_path)
if children_paths:
doc.write("\n **Extended by:**\n\n")
for path in children_paths:
doc.write(" - :class:`%s`\n" % path)
def _list_members(category, predicate):
names = [
name
for name, _ in inspect.getmembers(cls, predicate=predicate)
if not name.startswith("_")
]
if names:
doc.write("\n **%s:**\n\n" % category)
for name in names:
doc.write(" - :obj:`~%s.%s`\n" % (class_path, name))
_list_members("Attributes", lambda member: isinstance(member, property))
_list_members("Methods", lambda member: inspect.isroutine(member))
def document_function(output_dir, function_path):
with open(os.path.join(output_dir, "%s.rst" % function_path), "w") as doc:
function_name = function_path.split(".")[-1]
doc.write("%s\n" % function_name)
doc.write("=" * len(function_name))
doc.write("\n\n")
doc.write(".. autofunction:: %s\n" % function_path)
def module_is_public(module):
return (
module.__name__.startswith("ctranslate2")
and hasattr(module, "__file__")
and module.__file__.endswith("__init__.py")
)
def get_module_map(module, module_path):
"""Map true modules to exported name"""
if not module_is_public(module):
return {}
m = {}
for symbol_name in dir(module):
if symbol_name.startswith("_"):
continue
symbol = getattr(module, symbol_name)
symbol_path = "%s.%s" % (module_path, symbol_name)
m[symbol] = symbol_path
if inspect.ismodule(symbol):
m.update(get_module_map(symbol, symbol_path))
return m
def get_first_public_parent(cls):
base = cls.__bases__[0]
while base.__name__.startswith("_"): # Skip private parent classes.
base = base.__bases__[0]
if base is not object and base.__bases__[0] is tuple: # For namedtuples.
base = tuple
return base
def annotate_classes(classes):
annotations = []
child_classes = {}
for cls, path in classes:
parent = get_first_public_parent(cls)
if parent not in child_classes:
child_classes[parent] = [cls]
else:
child_classes[parent].append(cls)
annotations.append(dict(cls=cls, path=path, parent=parent))
for annotation in annotations:
annotation["children"] = child_classes.get(annotation["cls"])
return annotations
def document_module(module, module_path, module_map, output_dir):
if not module_is_public(module):
return []
modules = [module_path]
submodules = []
classes = []
functions = []
constants = []
for symbol_name in dir(module):
if symbol_name.startswith("_"):
continue
symbol = getattr(module, symbol_name)
symbol_path = "%s.%s" % (module_path, symbol_name)
if inspect.isclass(symbol):
classes.append((symbol, symbol_path))
elif (
inspect.isfunction(symbol)
or inspect.ismethod(symbol)
or inspect.isroutine(symbol)
):
functions.append(symbol_path)
elif inspect.ismodule(symbol):
submodules.append((symbol_path, symbol))
elif isinstance(symbol, (numbers.Number, str)):
constants.append(symbol_path)
with open(os.path.join(output_dir, "%s.rst" % module_path), "w") as doc:
doc.write("%s\n" % module_path)
doc.write("=" * (len(module_path)))
doc.write("\n\n")
doc.write(".. automodule:: %s\n\n" % module_path)
doc.write(".. toctree::\n\n")
if classes:
for class_info in annotate_classes(classes):
base = class_info["parent"]
base_path = module_map.get(
base, "%s.%s" % (base.__module__, base.__name__)
)
children_paths = class_info["children"]
if children_paths:
children_paths = [
module_map.get(
child, "%s.%s" % (child.__module__, child.__name__)
)
for child in children_paths
]
class_path = class_info["path"]
doc.write(" %s\n" % class_path)
document_class(
class_info["cls"],
output_dir,
class_path,
base_path=base_path,
children_paths=children_paths,
)
if functions:
for function_path in functions:
doc.write(" %s\n" % function_path)
document_function(output_dir, function_path)
if constants:
for constant_path in constants:
doc.write("* %s\n" % constant_path)
if submodules:
for submodule_path, submodule in submodules:
modules.extend(
document_module(submodule, submodule_path, module_map, output_dir)
)
return modules
output_dir = sys.argv[1]
os.makedirs(output_dir)
module_map = get_module_map(ctranslate2, "ctranslate2")
module_paths = document_module(ctranslate2, "ctranslate2", module_map, output_dir)
with open(os.path.join(output_dir, "overview.rst"), "w") as overview:
overview.write("Python\n")
overview.write("======\n\n")
overview.write(".. toctree::\n\n")
for module_path in module_paths:
overview.write(" %s\n" % module_path)