import logging
import os
import pathlib
import sys
from datetime import datetime
from dataclasses import is_dataclass
from importlib import import_module
from typing import Any, Dict
import sphinx
from docutils import nodes
from jinja2.filters import FILTERS
from sphinx.ext import autodoc
from sphinx.ext.autosummary import generate
from sphinx.util.inspect import safe_getattr
DEFAULT_API_GROUP = "Others"
logger = logging.getLogger(__name__)
sys.path.insert(0, os.path.abspath("."))
from custom_directives import (
DownloadAndPreprocessEcosystemDocs,
update_context,
LinkcheckSummarizer,
parse_navbar_config,
setup_context,
pregenerate_example_rsts,
generate_versions_json,
)
assert not os.path.exists("../../python/ray/_raylet.so"), (
"_raylet.so should not be imported for the purpose for doc build, "
"please rename the file to _raylet.so.bak and try again."
)
sys.path.insert(0, os.path.abspath("../../python/"))
default_role = "code"
sys.path.append(os.path.abspath("./_ext"))
extensions = [
"callouts",
"queryparamrefs",
"sphinx.ext.autodoc",
"sphinx.ext.viewcode",
"sphinx.ext.napoleon",
"sphinx_click.ext",
"sphinx-jsonschema",
"sphinxemoji.sphinxemoji",
"sphinx_copybutton",
"sphinx_sitemap",
"myst_nb",
"sphinx.ext.doctest",
"sphinx.ext.coverage",
"sphinx.ext.autosummary",
"sphinxcontrib.autodoc_pydantic",
"sphinxcontrib.redoc",
"sphinx_remove_toctrees",
"sphinx_design",
"sphinx.ext.intersphinx",
"sphinx_docsearch",
]
docsearch_app_id = "LBHF0PABBL"
docsearch_api_key = "6c42f30d9669d8e42f6fc92f44028596"
docsearch_index_name = "docs-ray"
remove_from_toctrees = [
"cluster/running-applications/job-submission/doc/*",
"ray-observability/reference/doc/*",
"ray-core/api/doc/*",
"data/api/doc/*",
"train/api/doc/*",
"tune/api/doc/*",
"serve/api/doc/*",
"rllib/package_ref/algorithm/*",
"rllib/package_ref/policy/*",
"rllib/package_ref/models/*",
"rllib/package_ref/catalogs/*",
"rllib/package_ref/rl_modules/*",
"rllib/package_ref/learner/*",
"rllib/package_ref/evaluation/*",
"rllib/package_ref/replay-buffers/*",
"rllib/package_ref/utils/*",
]
myst_enable_extensions = [
"dollarmath",
"amsmath",
"deflist",
"html_admonition",
"html_image",
"colon_fence",
"smartquotes",
"replacements",
]
myst_heading_anchors = 3
nb_render_key_pairs = {
"html": [
("img", ["src", "alt"]),
]
}
nb_output_folder = "_build/jupyter_execute"
nitpicky = True
nitpick_ignore_regex = [
("py:obj", "ray.actor.T"),
("py:class", ".*"),
("py:obj", "ray\\.data\\.datasource\\.datasink\\.WriteReturnType"),
]
nb_execution_mode = os.getenv("RUN_NOTEBOOKS", "off")
nb_mime_priority_overrides = [
("html", "application/vnd.jupyter.widget-view+json", 10),
("html", "application/javascript", 20),
("html", "text/html", 30),
("html", "image/svg+xml", 40),
("html", "image/png", 50),
("html", "image/jpeg", 60),
("html", "text/markdown", 70),
("html", "text/latex", 80),
("html", "text/plain", 90),
]
html_extra_path = ["robots.txt"]
html_baseurl = "https://docs.ray.io/en/latest"
copybutton_prompt_text = r">>> |\.\.\. |\$ |In \[\d*\]: | {2,5}\.\.\.: | {5,8}: "
copybutton_prompt_is_regexp = True
copybutton_selector = "div:not(.no-copybutton) > div.highlight > pre"
sphinx_tabs_disable_tab_closing = True
import packaging.version as packaging_version
Version = packaging_version.Version
class MockVersion(Version):
def __init__(self, version: str):
if isinstance(version, (str, bytes)):
super().__init__(version)
else:
super().__init__("0")
packaging_version.Version = MockVersion
templates_path = ["_templates"]
master_doc = "index"
project = "Ray"
copyright = str(datetime.now().year) + ", The Ray Team"
author = "The Ray Team"
from setup import find_version
release = find_version("ray", "_version.py")
language = "en"
autogen_files = [
"data/api/_autogen.rst",
]
exclude_patterns = [
"templates/*",
"cluster/running-applications/doc/ray.*",
"data/api/ray.data.*.rst",
"ray-overview/examples/**/README.md",
"train/examples/**/README.md",
] + autogen_files
build_one_lib = os.getenv("DOC_LIB")
all_toc_libs = [
f.path.strip("./") for f in os.scandir(".") if f.is_dir() and "ray-" in f.path
]
all_toc_libs += [
"cluster",
"tune",
"data",
"train",
"rllib",
"serve",
"llm",
"workflows",
]
if build_one_lib and build_one_lib in all_toc_libs:
all_toc_libs.remove(build_one_lib)
exclude_patterns += all_toc_libs
todo_include_todos = False
linkcheck_anchors = False
if os.environ.get("LINKCHECK_ALL"):
linkcheck_ignore = [
r"^((?!http).)*$",
"http://ala2017.it.nuigalway.ie/papers/ALA2017_Gupta.pdf",
"https://mvnrepository.com/artifact/*",
"https://github.com/serverlessworkflow/specification/blob/main/comparisons/comparison-cadence.md",
"https://www.oracle.com/java/technologies/javase-jdk15-downloads.html",
"https://speakerdeck.com/*",
r"https://huggingface.co/*",
r"https://www.meetup.com/*",
r"https://www.pettingzoo.ml/*",
r"http://localhost[:/].*",
r"^http:/$",
"https://www.datanami.com/2018/02/01/rays-new-library-targets-high-speed-reinforcement-learning/",
"https://www.researchgate.net/publication/222573328_Stochastic_Gradient_Boosting",
"https://www.datanami.com/2019/11/05/why-every-python-developer-will-love-ray/",
"https://dev.mysql.com/doc/connector-python/en/",
"https://lczero.org/",
"https://www.uber.com/blog/elastic-xgboost-ray/",
"https://archive.vn/*",
"https://archive.is/*",
"https://medium.com/*",
"https://towardsdatascience.com/*",
]
else:
linkcheck_ignore = [
r"^(?!https://(raw\.githubusercontent|github)\.com/ray-project/).*$"
]
def render_svg_logo(path):
with open(pathlib.Path(__file__).parent / path, "r") as f:
content = f.read()
return content
html_theme = "pydata_sphinx_theme"
html_theme_options = {
"use_edit_page_button": True,
"announcement": """Try Ray with $100 credit — <a target="_blank" href="https://console.anyscale.com/register/ha?render_flow=ray&utm_source=ray_docs&utm_medium=docs&utm_campaign=banner">Start now</a><button type="button" id="close-banner" aria-label="Close banner">×</button>""",
"logo": {
"svg": render_svg_logo("_static/img/ray_logo.svg"),
},
"navbar_start": ["navbar-ray-logo"],
"navbar_end": [
"theme-switcher",
"version-switcher",
"navbar-icon-links",
],
"navbar_center": ["navbar-links"],
"navbar_align": "left",
"secondary_sidebar_items": [
"page-toc",
"edit-on-github",
],
"content_footer_items": [
"csat",
],
"navigation_depth": 4,
"pygment_light_style": "stata-dark",
"pygment_dark_style": "stata-dark",
"switcher": {
"json_url": "https://docs.ray.io/en/master/_static/versions.json",
"version_match": os.getenv("READTHEDOCS_VERSION", "master"),
},
}
html_context = {
"github_user": "ray-project",
"github_repo": "ray",
"github_version": "master",
"doc_path": "doc/source/",
}
html_sidebars = {
"**": [
(
"main-sidebar-readthedocs"
if os.getenv("READTHEDOCS") == "True"
else "main-sidebar"
)
],
"ray-overview/examples": [],
}
html_title = f"Ray {release}"
autodoc_typehints_format = "short"
html_favicon = "_static/favicon.ico"
html_static_path = ["_static"]
htmlhelp_basename = "Raydoc"
latex_elements = {
}
latex_documents = [
(master_doc, "Ray.tex", "Ray Documentation", author, "manual"),
]
man_pages = [(master_doc, "ray", "Ray Documentation", [author], 1)]
texinfo_documents = [
(
master_doc,
"Ray",
"Ray Documentation",
author,
"Ray",
"Ray provides a simple, universal API for building distributed applications.",
"Miscellaneous",
),
]
autodoc_member_order = "bysource"
autodoc_typehints = "signature"
def filter_out_undoc_class_members(member_name, class_name, module_name):
module = import_module(module_name)
cls = getattr(module, class_name)
if getattr(cls, member_name).__doc__:
return f"~{class_name}.{member_name}"
else:
return ""
def has_public_constructor(class_name, module_name):
cls = getattr(import_module(module_name), class_name)
return _is_public_api(cls)
def get_api_groups(method_names, class_name, module_name):
api_groups = set()
cls = getattr(import_module(module_name), class_name)
for method_name in method_names:
method = getattr(cls, method_name)
if _is_public_api(method):
api_groups.add(
safe_getattr(method, "_annotated_api_group", DEFAULT_API_GROUP)
)
return sorted(api_groups)
def select_api_group(method_names, class_name, module_name, api_group):
cls = getattr(import_module(module_name), class_name)
return [
method_name
for method_name in method_names
if _is_public_api(getattr(cls, method_name))
and _is_api_group(getattr(cls, method_name), api_group)
]
def _is_public_api(obj):
api_type = safe_getattr(obj, "_annotated_type", None)
if not api_type:
return False
return api_type.value == "PublicAPI"
def _is_api_group(obj, group):
return safe_getattr(obj, "_annotated_api_group", DEFAULT_API_GROUP) == group
FILTERS["filter_out_undoc_class_members"] = filter_out_undoc_class_members
FILTERS["get_api_groups"] = get_api_groups
FILTERS["select_api_group"] = select_api_group
FILTERS["has_public_constructor"] = has_public_constructor
def add_custom_assets(
app: sphinx.application.Sphinx,
pagename: str,
templatename: str,
context: Dict[str, Any],
doctree: nodes.Node,
):
"""Add custom per-page assets.
See documentation on Sphinx Core Events for more information:
https://www.sphinx-doc.org/en/master/extdev/appapi.html#sphinx-core-events
"""
if pagename == "index":
app.add_css_file("css/index.css")
app.add_js_file("js/index.js")
return "index.html"
if pagename == "ray-overview/examples":
app.add_css_file("css/examples.css")
app.add_js_file("js/examples.js")
return "ray-overview/examples.html"
if pagename in [
"data/examples",
"train/examples",
"serve/examples",
]:
return "examples.html"
if pagename == "train/train":
app.add_css_file("css/ray-train.css")
elif pagename == "ray-overview/ray-libraries":
app.add_css_file("css/ray-libraries.css")
elif pagename == "ray-overview/use-cases":
app.add_css_file("css/use_cases.css")
def _autogen_apis(app: sphinx.application.Sphinx):
"""
Auto-generate public API documentation.
"""
generate.generate_autosummary_docs(
[os.path.join(app.srcdir, file) for file in autogen_files],
app=app,
)
def process_signature(app, what, name, obj, options, signature, return_annotation):
if what == "class" and is_dataclass(obj):
return signature.replace("<factory>", "..."), return_annotation
def setup(app):
if os.getenv("READTHEDOCS") == "True":
generate_versions_json()
pregenerate_example_rsts(app)
import doctest
doctest.register_optionflag("MOCK")
app.connect("html-page-context", update_context)
app.add_config_value("navbar_content_path", "navbar.yml", "env")
app.connect("config-inited", parse_navbar_config)
app.connect("html-page-context", setup_context)
app.connect("html-page-context", add_custom_assets)
app.add_js_file("js/termynal.js", defer="defer")
app.add_css_file("css/termynal.css")
app.add_js_file("js/custom.js", defer="defer")
app.add_css_file("css/custom.css", priority=800)
app.add_js_file("js/csat.js", defer="defer")
app.add_css_file("css/csat.css")
app.add_js_file("js/assistant.js", defer="defer")
app.add_css_file("css/assistant.css")
app.add_js_file("js/dismissable-banner.js", defer="defer")
app.add_css_file("css/dismissable-banner.css")
base_path = pathlib.Path(__file__).parent
github_docs = DownloadAndPreprocessEcosystemDocs(base_path)
app.connect("builder-inited", github_docs.write_new_docs)
app.connect("build-finished", github_docs.write_original_docs)
linkcheck_summarizer = LinkcheckSummarizer()
app.connect("builder-inited", linkcheck_summarizer.add_handler_to_linkcheck)
app.connect("build-finished", linkcheck_summarizer.summarize)
app.connect("builder-inited", _autogen_apis)
app.connect("autodoc-process-signature", process_signature)
class DuplicateObjectFilter(logging.Filter):
def filter(self, record):
if (
"duplicate object description of ray.actor.ActorMethod.bind"
in record.getMessage()
):
return False
return True
logging.getLogger("sphinx").addFilter(DuplicateObjectFilter())
redoc = [
{
"name": "Ray Jobs API",
"page": "cluster/running-applications/job-submission/api",
"spec": "cluster/running-applications/job-submission/openapi.yml",
"embed": True,
},
]
redoc_uri = "https://cdn.redoc.ly/redoc/latest/bundles/redoc.standalone.js"
autosummary_filename_map = {
"ray.serve.deployment": "ray.serve.deployment_decorator",
"ray.serve.Deployment": "ray.serve.Deployment",
}
autodoc_mock_imports = [
"aiohttp",
"async_timeout",
"backoff",
"cachetools",
"composer",
"cupy",
"dask",
"datasets",
"fastapi",
"filelock",
"fsspec",
"google",
"grpc",
"gymnasium",
"horovod",
"huggingface",
"httpx",
"joblib",
"lightgbm",
"lightgbm_ray",
"nevergrad",
"numpy",
"pandas",
"pyarrow",
"pytorch_lightning",
"scipy",
"setproctitle",
"skimage",
"sklearn",
"starlette",
"tensorflow",
"torch",
"torchvision",
"transformers",
"tree",
"typer",
"uvicorn",
"wandb",
"watchfiles",
"openai",
"xgboost",
"xgboost_ray",
"psutil",
"colorama",
"grpc",
"vllm",
"ray._raylet",
"ray.core.generated",
"ray.serve.generated",
]
for mock_target in autodoc_mock_imports:
if mock_target in sys.modules:
logger.info(
f"Potentially problematic mock target ({mock_target}) found; "
"autodoc_mock_imports cannot mock modules that have already "
"been loaded into sys.modules when the sphinx build starts."
)
class MockedClassDocumenter(autodoc.ClassDocumenter):
"""Remove note about base class when a class is derived from object."""
def add_line(self, line: str, source: str, *lineno: int) -> None:
if line == " Bases: :py:class:`object`":
return
super().add_line(line, source, *lineno)
autodoc.ClassDocumenter = MockedClassDocumenter
intersphinx_mapping = {
"aiohttp": ("https://docs.aiohttp.org/en/stable/", None),
"composer": ("https://docs.mosaicml.com/en/latest/", None),
"dask": ("https://docs.dask.org/en/stable/", None),
"datasets": ("https://huggingface.co/docs/datasets/main/en/", None),
"distributed": ("https://distributed.dask.org/en/stable/", None),
"grpc": ("https://grpc.github.io/grpc/python/", None),
"gymnasium": ("https://gymnasium.farama.org/", None),
"horovod": ("https://horovod.readthedocs.io/en/stable/", None),
"lightgbm": ("https://lightgbm.readthedocs.io/en/latest/", None),
"mars": ("https://mars-project.readthedocs.io/en/latest/", None),
"modin": ("https://modin.readthedocs.io/en/stable/", None),
"nevergrad": ("https://facebookresearch.github.io/nevergrad/", None),
"numpy": ("https://numpy.org/doc/stable/", None),
"pandas": ("https://pandas.pydata.org/pandas-docs/stable/", None),
"pyarrow": ("https://arrow.apache.org/docs", None),
"pydantic": ("https://docs.pydantic.dev/latest/", None),
"pymongoarrow": ("https://mongo-arrow.readthedocs.io/en/latest/", None),
"pyspark": ("https://spark.apache.org/docs/latest/api/python/", None),
"python": ("https://docs.python.org/3", None),
"pytorch_lightning": ("https://lightning.ai/docs/pytorch/stable/", None),
"scipy": ("https://docs.scipy.org/doc/scipy/", None),
"sklearn": ("https://scikit-learn.org/stable/", None),
"tensorflow": (
"https://www.tensorflow.org/api_docs/python",
"https://raw.githubusercontent.com/GPflow/tensorflow-intersphinx/master/tf2_py_objects.inv",
),
"torch": ("https://pytorch.org/docs/stable/", None),
"torchvision": ("https://pytorch.org/vision/stable/", None),
"transformers": ("https://huggingface.co/docs/transformers/main/en/", None),
}
assert (
"ray" not in sys.modules
), "If ray is already imported, we will not render documentation correctly!"
os.environ["RAY_TRAIN_V2_ENABLED"] = "1"