| # Licensed to the Apache Software Foundation (ASF) under one | |
| # or more contributor license agreements. See the NOTICE file | |
| # distributed with this work for additional information | |
| # regarding copyright ownership. The ASF licenses this file | |
| # to you under the Apache License, Version 2.0 (the | |
| # "License"); you may not use this file except in compliance | |
| # with the License. You may obtain a copy of the License at | |
| # | |
| # http://www.apache.org/licenses/LICENSE-2.0 | |
| # | |
| # Unless required by applicable law or agreed to in writing, | |
| # software distributed under the License is distributed on an | |
| # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |
| # KIND, either express or implied. See the License for the | |
| # specific language governing permissions and limitations | |
| # under the License. | |
| # ruff: noqa: E402, E501, F401 | |
| # | |
| # documentation build configuration file, created by | |
| # sphinx-quickstart on Thu Jul 23 19:40:08 2015. | |
| # | |
| # This file is execfile()d with the current directory set to its | |
| # containing dir. | |
| # | |
| # Note that not all possible configuration values are present in this | |
| # autogenerated file. | |
| # | |
| # All configuration values have a default; values that are commented out | |
| # serve to show the default. | |
| import gc | |
| import inspect | |
| import os | |
| import re | |
| import sys | |
| from functools import partial | |
| from hashlib import md5 | |
| from importlib import import_module | |
| from pathlib import Path | |
| from textwrap import dedent, indent | |
| from unittest.mock import patch | |
| # If extensions (or modules to document with autodoc) are in another directory, | |
| # add these directories to sys.path here. If the directory is relative to the | |
| # documentation root, use os.path.abspath to make it absolute, like shown here. | |
| curr_path = Path(__file__).expanduser().absolute().parent | |
| if curr_path.name == "_staging": | |
| # Can't use curr_path.parent, because sphinx_gallery requires a relative path. | |
| tvm_path = Path(os.pardir, os.pardir) | |
| else: | |
| tvm_path = Path(os.pardir) | |
| sys.path.insert(0, str(tvm_path.resolve() / "docs")) | |
| # -- General configuration ------------------------------------------------ | |
| # General information about the project. | |
| project = "tvm" | |
| author = "Apache Software Foundation" | |
| copyright = f"2020 - 2026, {author}" | |
| github_doc_root = "https://github.com/apache/tvm/tree/main/docs/" | |
| os.environ["TVM_BUILD_DOC"] = "1" | |
| # Version information. | |
| import tvm | |
| from tvm import te, topi | |
| # The version is derived from the Git tag by setuptools_scm at build time and exposed | |
| # as tvm.__version__ (see [tool.setuptools_scm] in pyproject.toml). | |
| version = tvm.__version__ | |
| release = version | |
| def monkey_patch(module_name, func_name): | |
| """Helper function for monkey-patching library functions. | |
| Used to modify a few sphinx-gallery behaviors to make the "Open in Colab" button work correctly. | |
| Should be called as a decorator with arguments. Note this behaves differently from unittest's | |
| @mock.patch, as our monkey_patch decorator should be placed on the new version of the function. | |
| """ | |
| module = import_module(module_name) | |
| original_func = getattr(module, func_name) | |
| def decorator(function): | |
| updated_func = partial(function, real_func=original_func) | |
| setattr(module, func_name, updated_func) | |
| return updated_func | |
| return decorator | |
| CURRENT_FILE_CONF = None | |
| @monkey_patch("sphinx_gallery.py_source_parser", "split_code_and_text_blocks") | |
| def split_code_and_text_blocks(source_file, return_node, real_func): | |
| """Monkey-patch split_code_and_text_blocks to expose sphinx-gallery's file-level config. | |
| It's kinda gross, but we need access to file_conf to detect the requires_cuda flag. | |
| """ | |
| global CURRENT_FILE_CONF | |
| file_conf, blocks, node = real_func(source_file, return_node) | |
| CURRENT_FILE_CONF = file_conf | |
| return (file_conf, blocks, node) | |
| # This header replaces the default sphinx-gallery one in sphinx_gallery/gen_rst.py. | |
| # Colab button has been temporarily disabled due to prebuilt packages unavailability. | |
| COLAB_HTML_HEADER = """ | |
| .. DO NOT EDIT. THIS FILE WAS AUTOMATICALLY GENERATED BY | |
| .. TVM'S MONKEY-PATCHED VERSION OF SPHINX-GALLERY. TO MAKE | |
| .. CHANGES, EDIT THE SOURCE PYTHON FILE: | |
| .. "{python_file}" | |
| .. only:: html | |
| .. note:: | |
| :class: sphx-glr-download-link-note | |
| You can click :ref:`here <sphx_glr_download_{ref_name}>` to run the Jupyter notebook locally. | |
| .. rst-class:: sphx-glr-example-title | |
| .. _sphx_glr_{ref_name}: | |
| """ | |
| # Google Colab allows opening .ipynb files on GitHub by appending a GitHub path to this base URL. | |
| COLAB_URL_BASE = "https://colab.research.google.com/github" | |
| # The GitHub path where the site is automatically deployed by tvm-bot. | |
| IPYTHON_GITHUB_BASE = "apache/tvm-site/blob/asf-site/docs/_downloads/" | |
| # The SVG image of the "Open in Colab" button. | |
| BUTTON = ( | |
| "https://raw.githubusercontent.com/tlc-pack/web-data/main/images/utilities/colab_button.svg" | |
| ) | |
| @monkey_patch("sphinx_gallery.gen_rst", "save_rst_example") | |
| def save_rst_example( | |
| example_rst, example_file, time_elapsed, memory_used, gallery_conf, language, real_func | |
| ): | |
| """Monkey-patch save_rst_example to customize the tutorial header. | |
| Note: Colab button has been temporarily disabled. The colab_url and button_svg | |
| are still generated but not used in the header template. | |
| """ | |
| # The url is the md5 hash of the notebook path. | |
| example_fname = os.path.relpath(example_file, gallery_conf["src_dir"]) | |
| ref_fname = example_fname.replace(os.path.sep, "_") | |
| notebook_path = example_fname[:-2] + "ipynb" | |
| digest = md5(notebook_path.encode()).hexdigest() | |
| # Fixed documentation versions must link to different (earlier) .ipynb notebooks. | |
| # Note: colab_url is generated but not currently used in the header template. | |
| colab_url = f"{COLAB_URL_BASE}/{IPYTHON_GITHUB_BASE}" | |
| if "dev" not in version: | |
| colab_url += version + "/" | |
| colab_url += digest + "/" + os.path.basename(notebook_path) | |
| new_header = COLAB_HTML_HEADER.format( | |
| python_file=example_fname, ref_name=ref_fname, colab_url=colab_url, button_svg=BUTTON | |
| ) | |
| with patch("sphinx_gallery.gen_rst.EXAMPLE_HEADER", new_header): | |
| real_func( | |
| example_rst, example_file, time_elapsed, memory_used, gallery_conf, language=language | |
| ) | |
| INCLUDE_DIRECTIVE_RE = re.compile(r"^([ \t]*)\.\. include::\s*(.+)\n", flags=re.M) | |
| COMMENT_DIRECTIVE_RE = re.compile(r"^\.\.(?: .*)?\n(?:(?: .*)?\n)*", flags=re.M) | |
| ADMONITION_DIRECTIVE_RE = re.compile(r"^\.\. admonition:: *(.*)\n((?:(?: .*)?\n)*)\n", flags=re.M) | |
| @monkey_patch("sphinx_gallery.notebook", "rst2md") | |
| def rst2md(text, gallery_conf, target_dir, heading_levels, real_func): | |
| """Monkey-patch rst2md to support comments and some include directives. | |
| Currently, only include directives without any parameters are supported. Also, note that in | |
| reStructuredText any unrecognized explicit markup block is treated as a comment (see | |
| https://docutils.sourceforge.io/docs/ref/rst/restructuredtext.html#comments). | |
| For callouts, we only replace generic "admonition" directives. All others should be replaced by | |
| sphinx-gallery's rst2md. Note that the "alert" and "alert-info" tags are support in most IPython | |
| notebooks, but they render kinda funky on Colab. | |
| """ | |
| def load_include(match): | |
| full_path = os.path.join(target_dir, match.group(2)) | |
| with open(full_path) as f: | |
| lines = f.read() | |
| indented = indent(lines, match.group(1)) + "\n" | |
| return indented | |
| text = re.sub(INCLUDE_DIRECTIVE_RE, load_include, text) | |
| # Replace generic, titled admonitions with indented text. Other admonitions (e.g. .. note::) | |
| # will be handled by sphinx-gallery's | |
| def rewrite_generic_admonition(match): | |
| title, text = match.groups() | |
| stripped_text = dedent(text).strip() | |
| return f'<div class="alert alert-info"><h4>{title}</h4><p>{stripped_text}</p></div>' | |
| text = re.sub(ADMONITION_DIRECTIVE_RE, rewrite_generic_admonition, text) | |
| # Call the real function, and then strip any remaining directives (i.e. comments) | |
| text = real_func(text, gallery_conf, target_dir, heading_levels) | |
| text = re.sub(COMMENT_DIRECTIVE_RE, "", text) | |
| return text | |
| INSTALL_TVM_DEV = """\ | |
| %%shell | |
| # Installs the latest dev build of TVM from PyPI. If you wish to build | |
| # from source, see https://tvm.apache.org/docs/install/from_source.html | |
| pip install apache-tvm --pre""" | |
| INSTALL_TVM_FIXED = f"""\ | |
| %%shell | |
| # Installs TVM version {version} from PyPI. If you wish to build | |
| # from source, see https://tvm.apache.org/docs/install/from_source.html | |
| pip install apache-tvm=={version}""" | |
| INSTALL_TVM_CUDA_DEV = """\ | |
| %%markdown | |
| > **Note:** This tutorial requires a CUDA-enabled build of TVM. | |
| > Pre-built CUDA wheels are not currently available on PyPI. | |
| > Please build TVM from source with CUDA enabled before running this notebook: | |
| > https://tvm.apache.org/docs/install/from_source.html""" | |
| INSTALL_TVM_CUDA_FIXED = f"""\ | |
| %%markdown | |
| > **Note:** This tutorial requires a CUDA-enabled build of TVM (version {version}). | |
| > Pre-built CUDA wheels are not currently available on PyPI. | |
| > Please build TVM from source with CUDA enabled before running this notebook: | |
| > https://tvm.apache.org/docs/install/from_source.html""" | |
| @monkey_patch("sphinx_gallery.gen_rst", "jupyter_notebook") | |
| def jupyter_notebook(script_blocks, gallery_conf, target_dir, real_func): | |
| """Monkey-patch sphinx-gallery to add a TVM import block to each IPython notebook. | |
| If we had only one import block, we could skip the patching and just set first_notebook_cell. | |
| However, how we import TVM depends on if we are using a fixed or dev version, and whether we | |
| will use the GPU. | |
| Tutorials requiring a CUDA-enabled build of TVM should use the flag: | |
| # sphinx_gallery_requires_cuda = True | |
| """ | |
| requires_cuda = CURRENT_FILE_CONF.get("requires_cuda", False) | |
| fixed_version = "dev" not in version | |
| if fixed_version and requires_cuda: | |
| install_block = INSTALL_TVM_CUDA_FIXED | |
| elif fixed_version and not requires_cuda: | |
| install_block = INSTALL_TVM_FIXED | |
| elif not fixed_version and requires_cuda: | |
| install_block = INSTALL_TVM_CUDA_DEV | |
| else: | |
| install_block = INSTALL_TVM_DEV | |
| new_conf = {**gallery_conf, "first_notebook_cell": install_block} | |
| return real_func(script_blocks, new_conf, target_dir) | |
| # Add any Sphinx extension module names here, as strings. They can be | |
| # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones | |
| extensions = [ | |
| "sphinx.ext.autodoc", | |
| "sphinx.ext.autosummary", | |
| "sphinx.ext.intersphinx", | |
| "sphinx.ext.napoleon", | |
| "sphinx.ext.mathjax", | |
| "sphinx_gallery.gen_gallery", | |
| "autodocsumm", | |
| ] | |
| # Add any paths that contain templates here, relative to this directory. | |
| templates_path = ["_templates"] | |
| # The suffix(es) of source filenames. | |
| # You can specify multiple suffix as a list of string: | |
| # source_suffix = ['.rst', '.md'] | |
| source_suffix = [".rst", ".md"] | |
| # The encoding of source files. | |
| # source_encoding = 'utf-8-sig' | |
| # generate autosummary even if no references | |
| autosummary_generate = True | |
| # The main toctree document. | |
| main_doc = "index" | |
| # The language for content autogenerated by Sphinx. Refer to documentation | |
| # for a list of supported languages. | |
| # | |
| # This is also used if you do content translation via gettext catalogs. | |
| # Usually you set "language" from the command line for these cases. | |
| language = None | |
| # There are two options for replacing |today|: either, you set today to some | |
| # non-false value, then it is used: | |
| # today = '' | |
| # Else, today_fmt is used as the format for a strftime call. | |
| # today_fmt = '%B %d, %Y' | |
| # List of patterns, relative to source directory, that match files and | |
| # directories to ignore when looking for source files. | |
| exclude_patterns = ["_build", "_staging"] | |
| # The TIRx API pages autodoc modules (tvm_tirx.tirx, tvm_tirx.backend.cuda) that re-export | |
| # common IR types (PrimExpr, Op, ...) from several modules, which makes a handful | |
| # of autodoc'd cross references ambiguous. Silence that specific, benign category. | |
| suppress_warnings = ["ref.python"] | |
| # The reST default role (used for this markup: `text`) to use for all | |
| # documents. | |
| # default_role = None | |
| # If true, '()' will be appended to :func: etc. cross-reference text. | |
| # add_function_parentheses = True | |
| # If true, the current module name will be prepended to all description | |
| # unit titles (such as .. function::). | |
| # add_module_names = True | |
| # If true, sectionauthor and moduleauthor directives will be shown in the | |
| # output. They are ignored by default. | |
| # show_authors = False | |
| # The name of the Pygments (syntax highlighting) style to use. | |
| pygments_style = "sphinx" | |
| # A list of ignored prefixes for module index sorting. | |
| # modindex_common_prefix = [] | |
| # If true, keep warnings as "system message" paragraphs in the built documents. | |
| # keep_warnings = False | |
| # If true, `todo` and `todoList` produce output, else they produce nothing. | |
| todo_include_todos = False | |
| # -- Options for HTML output ---------------------------------------------- | |
| html_theme = "sphinx_book_theme" | |
| html_title = "Apache TVM" | |
| # Add any paths that contain custom static files (such as style sheets) here, | |
| # relative to this directory. They are copied after the builtin static files, | |
| # so a file named "default.css" will overwrite the builtin "default.css". | |
| html_static_path = ["_static"] | |
| html_logo = "_static/img/tvm-logo-small.png" | |
| html_favicon = "_static/img/tvm-logo-square.png" | |
| # The Apache trademark/copyright footer is rendered through sphinx-book-theme's | |
| # ``extra_footer`` hook (see footer_html below). This mirrors how the tvm-ffi docs | |
| # (3rdparty/tvm-tirx/3rdparty/tvm-ffi/docs/conf.py) preserve the ASF menu under the book theme. | |
| footer_dropdown = { | |
| "name": "ASF", | |
| "items": [ | |
| ("Apache Homepage", "https://apache.org/"), | |
| ("License", "https://www.apache.org/licenses/"), | |
| ("Sponsorship", "https://www.apache.org/foundation/sponsorship.html"), | |
| ("Security", "https://tvm.apache.org/docs/reference/security.html"), | |
| ("Thanks", "https://www.apache.org/foundation/thanks.html"), | |
| ("Events", "https://www.apache.org/events/current-event"), | |
| ], | |
| } | |
| footer_note = " ".join( | |
| """ | |
| Copyright © 2026 The Apache Software Foundation. Apache TVM, Apache, the Apache feather, | |
| and the Apache TVM project logo are either trademarks or registered trademarks of | |
| the Apache Software Foundation.""".split("\n") | |
| ).strip() | |
| def footer_html() -> str: | |
| """Build the extra footer: ASF dropdown and the Apache trademark note. | |
| The copyright line is rendered natively by sphinx-book-theme (from the ``copyright`` | |
| config value), so it is intentionally not repeated here. | |
| """ | |
| dropdown_items = "" | |
| for item_name, item_url in footer_dropdown["items"]: | |
| dropdown_items += f'<li><a class="dropdown-item" href="{item_url}" target="_blank" style="font-size: 0.9em;">{item_name}</a></li>\n' | |
| return f""" | |
| <div class="footer-container" style="margin: 5px 0; font-size: 0.9em; color: #6c757d; text-align: right;"> | |
| <div class="footer-line1" style="display: flex; justify-content: flex-end; align-items: center; gap: 0.9em; margin-bottom: 3px; flex-wrap: wrap;"> | |
| <div class="footer-dropdown"> | |
| <div class="dropdown"> | |
| <button class="btn btn-link dropdown-toggle" type="button" id="footerDropdown" data-bs-toggle="dropdown" | |
| aria-expanded="false" style="font-size: 0.9em; color: #6c757d; text-decoration: none; padding: 0; border: none; background: none;"> | |
| {footer_dropdown["name"]} | |
| </button> | |
| <ul class="dropdown-menu dropdown-menu-end" aria-labelledby="footerDropdown" style="font-size: 0.9em;"> | |
| {dropdown_items} </ul> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="footer-line2" style="font-size: 0.9em; color: #6c757d; text-align: right;"> | |
| {footer_note} | |
| </div> | |
| </div> | |
| """ | |
| html_theme_options = { | |
| "repository_url": "https://github.com/apache/tvm", | |
| "repository_branch": "main", | |
| "path_to_docs": "docs/", | |
| "use_repository_button": True, | |
| "use_edit_page_button": True, | |
| "use_source_button": True, | |
| "use_issues_button": True, | |
| "show_toc_level": 2, | |
| "show_navbar_depth": 1, | |
| "icon_links": [ | |
| { | |
| "name": "TVM Homepage", | |
| "url": "https://tvm.apache.org/", | |
| "icon": "fa-solid fa-house", | |
| "type": "fontawesome", | |
| }, | |
| { | |
| "name": "Community", | |
| "url": "https://tvm.apache.org/community", | |
| "icon": "fa-solid fa-users", | |
| "type": "fontawesome", | |
| }, | |
| { | |
| "name": "Download", | |
| "url": "https://tvm.apache.org/download", | |
| "icon": "fa-solid fa-box-open", | |
| "type": "fontawesome", | |
| }, | |
| ], | |
| "extra_footer": footer_html(), | |
| } | |
| # Output file base name for HTML help builder. | |
| htmlhelp_basename = project + "doc" | |
| # -- Options for LaTeX output --------------------------------------------- | |
| latex_elements = {} | |
| # Grouping the document tree into LaTeX files. List of tuples | |
| # (source start file, target name, title, | |
| # author, documentclass [howto, manual, or own class]). | |
| latex_documents = [ | |
| (main_doc, f"{project}.tex", project, author, "manual"), | |
| ] | |
| intersphinx_mapping = { | |
| "python": (f"https://docs.python.org/{sys.version_info.major}", None), | |
| # "numpy": ("https://numpy.org/doc/stable", None), | |
| # "scipy": ("https://docs.scipy.org/doc/scipy", None), | |
| # "matplotlib": ("https://matplotlib.org/", None), | |
| } | |
| from sphinx_gallery.sorting import ExplicitOrder | |
| examples_dirs = [ | |
| tvm_path.joinpath("docs", "get_started", "tutorials"), | |
| tvm_path.joinpath("docs", "how_to", "tutorials"), | |
| tvm_path.joinpath("docs", "deep_dive", "relax", "tutorials"), | |
| tvm_path.joinpath("docs", "deep_dive", "tensor_ir", "tutorials"), | |
| ] | |
| gallery_dirs = [ | |
| "get_started/tutorials/", | |
| "how_to/tutorials/", | |
| "deep_dive/relax/tutorials/", | |
| "deep_dive/tensor_ir/tutorials/", | |
| ] | |
| # Explicitly define the order within a subsection. | |
| # The listed files are sorted according to the list. | |
| # The unlisted files are sorted by filenames. | |
| # The unlisted files always appear after listed files. | |
| within_subsection_order = {} | |
| class WithinSubsectionOrder: | |
| def __init__(self, src_dir): | |
| self.src_dir = src_dir.split("/")[-1] | |
| def __call__(self, filename): | |
| # If the order is provided, use the provided order | |
| if ( | |
| self.src_dir in within_subsection_order | |
| and filename in within_subsection_order[self.src_dir] | |
| ): | |
| index = within_subsection_order[self.src_dir].index(filename) | |
| assert index < 1e10 | |
| return f"\0{index:010d}" | |
| # Otherwise, sort by filename | |
| return filename | |
| # When running the tutorials on GPUs we are dependent on the Python garbage collector | |
| # collecting TVM packed function closures for any device memory to also be released. This | |
| # is not a good setup for machines with lots of CPU ram but constrained GPU ram, so force | |
| # a gc after each example. | |
| def force_gc(gallery_conf, fname): | |
| gc.collect() | |
| filename_pattern_default = ".*" | |
| sphinx_gallery_conf = { | |
| "backreferences_dir": "gen_modules/backreferences", | |
| "doc_module": ("tvm", "numpy"), | |
| "reference_url": { | |
| "tvm": None, | |
| # "matplotlib": "https://matplotlib.org/", | |
| # "numpy": "https://numpy.org/doc/stable", | |
| }, | |
| "examples_dirs": examples_dirs, | |
| "within_subsection_order": WithinSubsectionOrder, | |
| "gallery_dirs": gallery_dirs, | |
| "filename_pattern": os.environ.get("TVM_TUTORIAL_EXEC_PATTERN", filename_pattern_default), | |
| "download_all_examples": False, | |
| "min_reported_time": 60, | |
| "expected_failing_examples": [], | |
| "reset_modules": ("matplotlib", "seaborn", force_gc), | |
| "promote_jupyter_magic": True, | |
| # Drop the "Gallery generated by Sphinx-Gallery" signature line on generated pages. | |
| "show_signature": False, | |
| } | |
| autodoc_default_options = { | |
| "member-order": "bysource", | |
| } | |
| # Maps the original namespace to list of potential modules | |
| # that we can import alias from. | |
| tvm_alias_check_map = { | |
| "tvm.te": ["tvm_tirx.tirx"], | |
| "tvm_tirx.tirx": ["tvm_tirx.ir", "tvm_tirx.runtime"], | |
| } | |
| def update_alias_docstring(name, obj, lines): | |
| """Update the docstring of alias functions. | |
| This function checks if the obj is an alias of another documented object | |
| in a different module. | |
| If it is an alias, then it will append the alias information to the docstring. | |
| Parameters | |
| ---------- | |
| name : str | |
| The full name of the object in the doc. | |
| obj : object | |
| The original object. | |
| lines : list | |
| The docstring lines, need to be modified inplace. | |
| """ | |
| arr = name.rsplit(".", 1) | |
| if len(arr) != 2: | |
| return | |
| target_mod, target_name = arr | |
| if target_mod not in tvm_alias_check_map: | |
| return | |
| if not hasattr(obj, "__module__"): | |
| return | |
| obj_mod = obj.__module__ | |
| for amod in tvm_alias_check_map[target_mod]: | |
| if not obj_mod.startswith(amod): | |
| continue | |
| if hasattr(sys.modules[amod], target_name): | |
| obj_type = ":py:func" if callable(obj) else ":py:class" | |
| lines.append(f".. rubric:: Alias of {obj_type}:`{amod}.{target_name}`") | |
| tvm_class_name_rewrite_map = { | |
| "tvm_tirx.tirx": ["Var", "Call"], | |
| "tvm.relax": ["Var", "Call", "StringImm"], | |
| "tvm.relax.frontend.nn": ["Module"], | |
| } | |
| # When documenting modules under these prefixes, prefer types from the mapped module | |
| # to resolve ambiguous cross-references (e.g. Var exists in both tvm_tirx.tirx and tvm.relax). | |
| tvm_module_type_preference = { | |
| "tvm.s_tir": "tvm_tirx.tirx", | |
| } | |
| def distinguish_class_name(name: str, lines: list[str]): | |
| """Distinguish the docstring of type annotations. | |
| In the whole TVM, there are many classes with the same name but in different modules, | |
| e.g. ``tirx.Var``, ``relax.Var``. This function is used to distinguish them in the docstring, | |
| by adding the module name as prefix. | |
| To be specific, this function will check the current object name, and if it in the specific | |
| module with specific name, it will add the module name as prefix to the class name to prevent | |
| the confusion. Further, we only add the prefix to those standalone class name, but skip | |
| the pattern of `xx.Var`, `Var.xx` and `xx.Var.xx`. | |
| Parameters | |
| ---------- | |
| name : str | |
| The full name of the object in the doc. | |
| lines : list | |
| The docstring lines, need to be modified inplace. | |
| """ | |
| remap = {} | |
| for module_name in tvm_class_name_rewrite_map: | |
| if name.startswith(module_name): | |
| short_name = module_name[4:] if module_name.startswith("tvm.") else module_name | |
| for class_name in tvm_class_name_rewrite_map[module_name]: | |
| remap.update({class_name: f"{short_name}.{class_name}"}) | |
| for k, v in remap.items(): | |
| for i in range(len(lines)): | |
| lines[i] = re.sub(rf"(?<!\.)\b{k}\b(?!\.)", v, lines[i]) | |
| def process_docstring(app, what, name, obj, options, lines): | |
| """Sphinx callback to process docstring""" | |
| if callable(obj) or inspect.isclass(obj): | |
| update_alias_docstring(name, obj, lines) | |
| distinguish_class_name(name, lines) | |
| def strip_ipython_magic(app, docname, source): | |
| """Prevents IPython magic commands from being rendered in HTML files. | |
| TODO rework this function to remove IPython magic commands from include directives too. | |
| """ | |
| for i in range(len(source)): | |
| source[i] = re.sub(r"%%.*\n\s*", "", source[i]) | |
| def _patch_python_domain_find_obj(): | |
| """Patch PythonDomain.find_obj to resolve ambiguous cross-references. | |
| Sphinx's ``warn-missing-reference`` event is only fired for unresolved | |
| references. Ambiguous short names such as ``StringImm`` already have | |
| multiple matches at ``PythonDomain.find_obj`` time, so the disambiguation | |
| needs to happen here instead. | |
| """ | |
| from sphinx.domains.python import PythonDomain | |
| if getattr(PythonDomain.find_obj, "_tvm_patched", False): | |
| return | |
| _original_find_obj = PythonDomain.find_obj | |
| def _common_prefix_len(lhs: str, rhs: str) -> int: | |
| count = 0 | |
| for lpart, rpart in zip(lhs.split("."), rhs.split(".")): | |
| if lpart != rpart: | |
| break | |
| count += 1 | |
| return count | |
| def _dedup_find_obj(self, env, modname, classname, name, objtype, searchmode=0): | |
| matches = _original_find_obj(self, env, modname, classname, name, objtype, searchmode) | |
| if len(matches) <= 1: | |
| return matches | |
| short_name = name.rsplit(".", 1)[-1] | |
| # Prefer a single canonical (non-aliased) entry if Sphinx already found one. | |
| canonical_matches = [match for match in matches if not match[1].aliased] | |
| if len(canonical_matches) == 1: | |
| return canonical_matches | |
| # Use TVM's module context for the known short names we rewrite in docstrings. | |
| if modname: | |
| candidate_modules = sorted( | |
| ( | |
| module_name | |
| for module_name, class_names in tvm_class_name_rewrite_map.items() | |
| if short_name in class_names and modname.startswith(module_name) | |
| ), | |
| key=len, | |
| reverse=True, | |
| ) | |
| for module_name in candidate_modules: | |
| target_name = f"{module_name}.{short_name}" | |
| context_matches = [match for match in matches if match[0] == target_name] | |
| if len(context_matches) == 1: | |
| return context_matches | |
| # Fall back to the unique match that best shares the current module prefix. | |
| match_scores = {match[0]: _common_prefix_len(modname, match[0]) for match in matches} | |
| best_score = max(match_scores.values()) | |
| if best_score > 1: | |
| best_matches = [match for match in matches if match_scores[match[0]] == best_score] | |
| if len(best_matches) == 1: | |
| return best_matches | |
| # Check module type preference for cross-module resolution | |
| # (e.g. tvm.s_tir.analysis uses types from tvm_tirx.tirx). | |
| for prefix, preferred_mod in tvm_module_type_preference.items(): | |
| if modname.startswith(prefix): | |
| preferred = [m for m in matches if m[0].startswith(preferred_mod + ".")] | |
| if len(preferred) >= 1: | |
| return preferred[:1] | |
| return matches | |
| _dedup_find_obj._tvm_patched = True | |
| PythonDomain.find_obj = _dedup_find_obj | |
| _patch_python_domain_find_obj() | |
| def setup(app): | |
| app.connect("source-read", strip_ipython_magic) | |
| app.connect("autodoc-process-docstring", process_docstring) |