#!/usr/bin/env python3 # type: ignore # Ignore all mypy errors in this file # SPDX-FileCopyrightText: Copyright (c) 2024-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 # # Licensed 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. import logging import os import re import subprocess from contextlib import contextmanager from functools import partial # Get the directory of the current file dynamo_docs_abspath = os.path.dirname(os.path.abspath(__file__)) dynamo_abspath = os.path.dirname(dynamo_docs_abspath) repo_url = "https://github.com/ai-dynamo/dynamo/blob/main/" # Regex patterns http_patn = r"^https?://" http_reg = re.compile(http_patn) tag_patn = "/(?:blob|tree)/main" dynamo_repo_patn = rf"{http_patn}github.com/ai-dynamo/dynamo" dynamo_github_url_reg = re.compile( rf"{dynamo_repo_patn}/([^/#]+)(?:{tag_patn})?/*([^#]*)\s*(?=#|$)" ) # relpath_patn = r"]\s*\(\s*([^)]+)\)" # Hyperlink in a .md file, excluding embedded images. hyperlink_reg = re.compile(r"((?". Examples: https://github.com/triton-inference-server/server/blob/main/docs/protocol#restricted-protocols https://github.com/triton-inference-server/server/blob/main/docs/protocol/extension_shared_memory.md https://github.com/triton-inference-server/server/blob/main/docs/user_guide/model_configuration.md#dynamic-batcher Keep URL in the following cases: https://github.com/triton-inference-server/server/tree/r24.02 https://github.com/triton-inference-server/server/blob/main/build.py https://github.com/triton-inference-server/server/blob/main/qa https://github.com/triton-inference-server/server/blob/main/CONTRIBUTING.md """ m = dynamo_github_url_reg.match(url) # Do not replace URL if it is not a Triton GitHub file. if not m: return url target_repo_name = m.group(1) target_relpath_from_target_repo = os.path.normpath(m.groups("")[1]) section = url[len(m.group(0)) :] valid_hashtag = section not in ["", "#"] and section.startswith("#") if target_repo_name == "dynamo": target_path = os.path.join(dynamo_abspath, target_relpath_from_target_repo) else: target_path = os.path.join( dynamo_docs_abspath, target_repo_name, target_relpath_from_target_repo ) # Return URL if it points to a path outside server/docs. if os.path.commonpath([dynamo_docs_abspath, target_path]) != dynamo_docs_abspath: return url if ( os.path.isfile(target_path) and os.path.splitext(target_path)[1] == ".md" and not is_excluded(target_path) ): pass elif ( os.path.isdir(target_path) and os.path.isfile(os.path.join(target_path, "README.md")) and valid_hashtag and not is_excluded(os.path.join(target_path, "README.md")) ): target_path = os.path.join(target_path, "README.md") else: return url # The "target_path" must be a file at this line. relpath = os.path.relpath(target_path, start=os.path.dirname(src_doc_path)) return re.sub(dynamo_github_url_reg, relpath, url, count=1) def replace_relpath_with_url(relpath, src_doc_path): """ This function replaces relative paths with Triton Inference Server GitHub URLs in following cases. 1. Relative path is a file that is not ".md" type inside the current repo. 2. Relative path is a directory but not (has "README.md" and ends with "#
"). 3. Relative path does not exist (shows 404 page). Examples: ../examples/model_repository ../examples/model_repository/inception_graphdef/config.pbtxt Keep relpath in the following cases: build.md build.md#building-with-docker #building-with-docker ../getting_started/quickstart.md ../protocol#restricted-protocols """ target_path = relpath.rsplit("#")[0] section = relpath[len(target_path) :] valid_hashtag = section not in ["", "#"] if relpath.startswith("#"): target_path = os.path.basename(src_doc_path) target_path = os.path.join(os.path.dirname(src_doc_path), target_path) target_path = os.path.normpath(target_path) # Assert target path is under the current repo directory. assert os.path.commonpath([dynamo_abspath, target_path]) == dynamo_abspath target_path_from_src_repo = os.path.relpath(target_path, start=dynamo_abspath) # For example, target_path of "../protocol#restricted-protocols" should be "/server/docs/protocol/README.md" if ( os.path.isdir(target_path) and valid_hashtag and os.path.isfile(os.path.join(target_path, "README.md")) ): relpath = os.path.join(relpath.rsplit("#")[0], "README.md") + section target_path = os.path.join(target_path, "README.md") if ( os.path.isfile(target_path) and os.path.splitext(target_path)[1] == ".md" and os.path.commonpath([dynamo_docs_abspath, target_path]) == dynamo_docs_abspath and not is_excluded(target_path) ): return relpath else: return repo_url + target_path_from_src_repo + section def replace_hyperlink(m, src_doc_path): """ TODO: Support of HTML tags for future docs. Markdown allows , e.g. ]+>. Whether we want to find and replace the link depends on if they link to internal .md files or allows relative paths. I haven't seen one such case in our doc so should be safe for now. """ hyperlink_str = m.group(2) match = http_reg.match(hyperlink_str) if match: # Hyperlink is a URL. res = replace_url_with_relpath(hyperlink_str, src_doc_path) else: # Hyperlink is a relative path. res = replace_relpath_with_url(hyperlink_str, src_doc_path) return m.group(1) + res + m.group(3) def preprocess_docs(exclude_paths=[]): # Find all ".md" files inside the current repo. if exclude_paths: cmd = ( ["find", dynamo_docs_abspath, "-type", "d", "\\("] + " -o ".join([f"-path './{dir}'" for dir in exclude_paths]).split(" ") + ["\\)", "-prune", "-o", "-type", "f", "-name", "'*.md'", "-print"] ) else: cmd = ["find", dynamo_docs_abspath, "-name", "'*.md'"] cmd = " ".join(cmd) result = subprocess.run(cmd, check=True, capture_output=True, text=True, shell=True) docs_list = list(filter(None, result.stdout.split("\n"))) # Read, preprocess and write back to each document file. for doc_abspath in docs_list: if is_excluded(doc_abspath): continue content = None with open(doc_abspath, "r") as f: content = f.read() content = hyperlink_reg.sub( partial(replace_hyperlink, src_doc_path=doc_abspath), content, ) with open(doc_abspath, "w") as f: f.write(content) @contextmanager def change_directory(path): """ Context manager for changing the current working directory """ original_directory = os.getcwd() try: os.chdir(path) yield finally: os.chdir(original_directory) def main(): with change_directory(dynamo_docs_abspath): run_command("make clean") preprocess_docs() run_command("make html") if __name__ == "__main__": main()