Files
ray-project--ray/ci/ray_ci/doc/cmd_check_api_discrepancy.py
T
2026-07-13 13:17:40 +08:00

164 lines
5.9 KiB
Python

import sys
import click
from ci.ray_ci.doc.api import API
from ci.ray_ci.doc.autodoc import Autodoc
from ci.ray_ci.doc.module import Module
TEAM_API_CONFIGS = {
"data": {
"head_modules": {"ray.data", "ray.data.grouped_data"},
"head_doc_file": "doc/source/data/api/api.rst",
# List of APIs that are not following our API policy, and we will be fixing, or
# we cannot deprecate them although we want to
"white_list_apis": {
# not sure what to do
"ray.data.dataset.MaterializedDataset",
# special case where we cannot deprecate although we want to
"ray.data.random_access_dataset.RandomAccessDataset",
},
},
"serve": {
"head_modules": {"ray.serve"},
"head_doc_file": "doc/source/serve/api/index.md",
"white_list_apis": {
# private versions of request router APIs
"ray.serve._private.common.ReplicaID",
"ray.serve._private.request_router.common.PendingRequest",
"ray.serve._private.request_router.pow_2_router.PowerOfTwoChoicesRequestRouter",
"ray.serve._private.request_router.request_router.RequestRouter",
"ray.serve._private.request_router.replica_wrapper.RunningReplica",
"ray.serve._private.request_router.request_router.FIFOMixin",
"ray.serve._private.request_router.request_router.LocalityMixin",
"ray.serve._private.request_router.request_router.MultiplexMixin",
},
},
"core": {
"head_modules": {"ray"},
"head_doc_file": "doc/source/ray-core/api/index.rst",
"white_list_apis": {
# These APIs will be documented in near future
"ray.util.scheduling_strategies.DoesNotExist",
"ray.util.scheduling_strategies.Exists",
"ray.util.scheduling_strategies.NodeLabelSchedulingStrategy",
"ray.util.scheduling_strategies.In",
"ray.util.scheduling_strategies.NotIn",
# TODO(jjyao): document this API
"ray.ObjectRefGenerator",
# TODO(jjyao): document or deprecate these APIs
"ray.experimental.compiled_dag_ref.CompiledDAGFuture",
"ray.experimental.compiled_dag_ref.CompiledDAGRef",
"ray.cross_language.cpp_actor_class",
"ray.cross_language.cpp_function",
"ray.client_builder.ClientContext",
"ray.remote_function.RemoteFunction",
},
},
"train": {
"head_modules": {"ray.train"},
"head_doc_file": "doc/source/train/api/api.rst",
"white_list_apis": {
# NOTE: These APIs are documented in a separate file (deprecated.rst).
# These are deprecated APIs, so just white-listing them here for CI.
"ray.train.error.SessionMisuseError",
"ray.train.base_trainer.TrainingFailedError",
"ray.train.TrainingFailedError",
"ray.train.context.TrainContext",
"ray.train.context.get_context",
},
},
"tune": {
"head_modules": {"ray.tune"},
"head_doc_file": "doc/source/tune/api/api.rst",
"white_list_apis": {
# Already documented as ray.tune.search.ConcurrencyLimiter
"ray.tune.search.searcher.ConcurrencyLimiter",
# TODO(ml-team): deprecate these APIs
"ray.tune.utils.log.Verbosity",
},
},
"rllib": {
"head_modules": {"ray.rllib"},
"head_doc_file": "doc/source/rllib/package_ref/index.rst",
"white_list_apis": {},
},
}
def _check_team(ray_checkout_dir: str, team: str) -> bool:
# Load all APIs from the codebase
api_in_codes = {}
for module in TEAM_API_CONFIGS[team]["head_modules"]:
module = Module(module)
api_in_codes.update(
{api.get_canonical_name(): api for api in module.get_apis()}
)
# Load all APIs from the documentation
autodoc = Autodoc(f"{ray_checkout_dir}/{TEAM_API_CONFIGS[team]['head_doc_file']}")
api_in_docs = {api.get_canonical_name() for api in autodoc.get_apis()}
# Load the white list APIs
white_list_apis = TEAM_API_CONFIGS[team]["white_list_apis"]
# Policy 01: all public APIs should be documented
print(
f"--- Validating that public {team} APIs should be documented...",
file=sys.stderr,
)
good_apis, bad_apis = API.split_good_and_bad_apis(
api_in_codes, api_in_docs, white_list_apis
)
if good_apis:
print("Public APIs that are documented:", file=sys.stderr)
for api in good_apis:
print(f"\t{api}", file=sys.stderr)
if bad_apis:
print("Public APIs that are NOT documented:", file=sys.stderr)
for api in bad_apis:
print(f"\t{api}", file=sys.stderr)
if bad_apis:
print(
f"Some public {team} APIs are not documented. Please document them.",
file=sys.stderr,
)
return False
return True
@click.command()
@click.argument("ray_checkout_dir", required=True, type=str)
@click.argument(
"team", default="ALL", type=click.Choice(list(TEAM_API_CONFIGS.keys()) + ["ALL"])
)
def main(ray_checkout_dir: str, team: str) -> None:
"""
This script checks for annotated classes and functions in a module, and finds
discrepancies between the annotations and the documentation.
"""
if team != "ALL":
if not _check_team(ray_checkout_dir, team):
exit(1)
return
all_pass = True
# Needs to do core first, otherwise, the APIs in other teams may be covered by core.
# This is due to the side effect of "importlib" and walking through the modules.
if not _check_team(ray_checkout_dir, "core"):
all_pass = False
for team in TEAM_API_CONFIGS:
if team == "core":
continue
if not _check_team(ray_checkout_dir, team):
all_pass = False
if not all_pass:
exit(1)
if __name__ == "__main__":
main()