Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -730,7 +730,7 @@ jobs:
path: warp/bin/

- name: Run doctest
run: uv run --extra docs build_docs.py --doctest --no-html
run: uv run --extra docs build_docs.py --doctest --no-html --warnings-as-errors

build-docs:
if: github.event_name != 'schedule' || github.repository == 'NVIDIA/warp'
Expand Down Expand Up @@ -758,7 +758,7 @@ jobs:
path: warp/bin/
- name: Build Sphinx documentation
run: |
uv run --extra docs build_docs.py
uv run --extra docs build_docs.py --warnings-as-errors
- name: Upload artifacts
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
id: build-docs-output
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/sphinx.yml
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ jobs:
- name: Build Sphinx documentation
if: steps.dv.outputs.metadata_only != 'true'
run: |
DOC_VERSION=${{ steps.dv.outputs.version }} uv run --extra docs build_docs.py
DOC_VERSION=${{ steps.dv.outputs.version }} uv run --extra docs build_docs.py --warnings-as-errors
- name: Upload artifacts
if: steps.dv.outputs.metadata_only != 'true'
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
Expand Down
6 changes: 3 additions & 3 deletions .gitlab-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ doctest:
- df -h
- !reference [.snippets, move-linux-x86_64-binaries]
script:
- uv run --extra docs build_docs.py --doctest --no-html
- uv run --extra docs build_docs.py --doctest --no-html --warnings-as-errors

# The only purpose of this job is to make sure documentation can be built on Windows.
# The output does not get published anywhere, but the website can be viewed in the
Expand All @@ -242,7 +242,7 @@ windows-x86_64 docs:
script:
- |
$env:PATH = "$env:CI_PROJECT_DIR\_uv;$env:PATH"
uv run --extra docs build_docs.py
uv run --extra docs build_docs.py --warnings-as-errors
- mv docs/_build/html/ ./public/
after_script:
- echo "View the website at https://$CI_PROJECT_ROOT_NAMESPACE.$CI_PAGES_DOMAIN/-/$CI_PROJECT_NAME/-/jobs/$CI_JOB_ID/artifacts/public/index.html"
Expand Down Expand Up @@ -1045,7 +1045,7 @@ publish wheels to github release:
- !reference [.snippets, move-linux-x86_64-binaries]
- !reference [.snippets, section-end-install-deps]
script:
- uv run --extra docs build_docs.py
- uv run --extra docs build_docs.py --warnings-as-errors
- mv docs/_build/html/ ./public/

# Merge requests: Build documentation and save as an artifact
Expand Down
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,11 @@

### Documentation

- Make `build_docs.py` lenient by default: warnings are no longer treated as errors unless the new
`--warnings-as-errors` flag is passed (CI passes it to keep strict checks). Local documentation builds
also skip external intersphinx resolution when the inventories are unreachable (or when
`WARP_DOCS_OFFLINE=1` is set), so an offline build no longer aborts with no output.

## [1.14.0] - 2026-06-01

### Added
Expand Down
37 changes: 31 additions & 6 deletions build_docs.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,16 @@
default=False,
help="Run doctest tests of code blocks",
)
parser.add_argument(
"--warnings-as-errors",
action=argparse.BooleanOptionalAction,
default=False,
help=(
"Treat Sphinx warnings as errors (passes -W). Off by default so local "
"builds stay lenient (e.g. unreachable intersphinx inventories when "
"offline do not abort the build). CI/CD opts in to enforce strictness."
),
)
parser.add_argument("--verbose", "-v", action="store_true", help="Enable verbose logging")

args = parser.parse_args()
Expand Down Expand Up @@ -81,7 +91,7 @@ def format_file_with_ruff(file_path):
) from err


def build_sphinx_docs(source_dir, output_dir, builder="html"):
def build_sphinx_docs(source_dir, output_dir, builder="html", warnings_as_errors=False):
"""Build Sphinx documentation programmatically."""
logger.info(f"Building {builder} documentation: {source_dir} -> {output_dir}")
try:
Expand All @@ -92,9 +102,24 @@ def build_sphinx_docs(source_dir, output_dir, builder="html"):
logger.debug(f"Cleaning previous output directory: {output_dir}")
shutil.rmtree(output_dir)

# sphinx-build -W -b html source_dir output_dir
logger.debug(f"Running sphinx-build -W -j auto -b {builder} {source_dir} {output_dir}")
result = build_main(["-W", "-j", "auto", "-b", builder, source_dir, output_dir])
# sphinx-build [-W] -j auto -b <builder> source_dir output_dir
sphinx_args = ["-j", "auto", "-b", builder, source_dir, output_dir]
if warnings_as_errors:
sphinx_args.insert(0, "-W")
logger.debug(f"Running sphinx-build {' '.join(sphinx_args)}")
previous_strict_env = os.environ.get("WARP_DOCS_WARNINGS_AS_ERRORS")
try:
if warnings_as_errors:
os.environ["WARP_DOCS_WARNINGS_AS_ERRORS"] = "1"
else:
os.environ.pop("WARP_DOCS_WARNINGS_AS_ERRORS", None)

result = build_main(sphinx_args)
finally:
if previous_strict_env is None:
os.environ.pop("WARP_DOCS_WARNINGS_AS_ERRORS", None)
else:
os.environ["WARP_DOCS_WARNINGS_AS_ERRORS"] = previous_strict_env
if result != 0:
raise RuntimeError(f"Sphinx build failed with exit code {result}")

Expand Down Expand Up @@ -124,12 +149,12 @@ def build_sphinx_docs(source_dir, output_dir, builder="html"):
if args.html:
# Build HTML docs
html_output_dir = os.path.join(base_path, "docs", "_build", "html")
build_sphinx_docs(source_dir, html_output_dir, "html")
build_sphinx_docs(source_dir, html_output_dir, "html", warnings_as_errors=args.warnings_as_errors)

if args.doctest:
# Run doctest
logger.info("Running doctest...")
doctest_output_dir = os.path.join(base_path, "docs", "_build", "doctest")
build_sphinx_docs(source_dir, doctest_output_dir, "doctest")
build_sphinx_docs(source_dir, doctest_output_dir, "doctest", warnings_as_errors=args.warnings_as_errors)

logger.info("Documentation build completed successfully")
60 changes: 58 additions & 2 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

import docutils
import sphinx
import sphinx.util.logging
from sphinx import addnodes
from sphinx.environment.adapters.toctree import note_toctree
from sphinx.ext.autosummary import autosummary_toc
Expand Down Expand Up @@ -396,15 +397,70 @@ def render(self, template_name, context):

# -- sphinx.ext.intersphinx --------------------------------------------------

# Mapping to external documentation to enable cross-linking (e.g., :class:`numpy.ndarray`)
intersphinx_mapping = {
# Resolving intersphinx links requires fetching each project's remote inventory
# (objects.inv) over the network. That matters for CI/CD, where we want external
# cross-references (e.g. :class:`numpy.ndarray`) to resolve and stale inventory
# URLs to fail loudly, but a local build should not hard-fail just because the
# network is unavailable (offline machine, agent sandbox with no egress, flaky
# DNS). Under -W an unreachable inventory is promoted to a fatal error and
# aborts the whole build, producing no output.
#
# To keep local builds robust, skip intersphinx entirely when the inventories
# are unreachable (or when WARP_DOCS_OFFLINE=1 is set to force it off). With the
# mapping empty, external references are left unresolved: with nitpicky mode this
# emits (non-fatal) warnings rather than aborting, so the build still succeeds.
# CI runs with network access (and --warnings-as-errors) still asks Sphinx to
# fetch inventories, so moved or invalid inventory URLs are caught there.
_intersphinx_mapping = {
"jax": ("https://docs.jax.dev/en/latest", None),
"numpy": ("https://numpy.org/doc/stable", None),
"python": ("https://docs.python.org/3", None),
"pytorch": ("https://docs.pytorch.org/docs/stable", None),
}


def _inventories_reachable(mapping, timeout=2):
"""Check whether configured intersphinx inventories can be fetched.

Args:
mapping: Intersphinx mapping dictionary.
timeout: Per-inventory probe timeout in seconds.

Returns:
``True`` if every configured inventory returns a successful HTTP
response. ``False`` if any inventory cannot be fetched, including
connection-level failures and HTTP errors such as stale ``404`` URLs.
"""
import urllib.error # noqa: PLC0415
import urllib.request # noqa: PLC0415

for base, _ in mapping.values():
url = base.rstrip("/") + "/objects.inv"
try:
with urllib.request.urlopen(url, timeout=timeout):
continue
except (urllib.error.HTTPError, urllib.error.URLError, OSError):
return False
return bool(mapping)


_sphinx_logger = sphinx.util.logging.getLogger(__name__)
if os.environ.get("WARP_DOCS_OFFLINE") == "1":
_sphinx_logger.info("intersphinx: WARP_DOCS_OFFLINE=1 set; skipping external cross-reference resolution.")
intersphinx_mapping = {}
elif os.environ.get("WARP_DOCS_WARNINGS_AS_ERRORS") == "1":
intersphinx_mapping = _intersphinx_mapping
elif not _inventories_reachable(_intersphinx_mapping):
_sphinx_logger.info(
"intersphinx: external inventories unreachable; skipping external cross-reference resolution "
"for this build. Set WARP_DOCS_OFFLINE=1 to silence this probe, or build with network access "
"to resolve external links."
)
intersphinx_mapping = {}
Comment thread
shi-eric marked this conversation as resolved.
else:
intersphinx_mapping = _intersphinx_mapping


# -- sphinx.ext.linkcode -----------------------------------------------------


Expand Down
13 changes: 13 additions & 0 deletions docs/user_guide/contribution_guide.rst
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,19 @@ The ``--no-html`` flag can also be used to skip building the HTML documentation,
# Build the HTML documentation AND run the doctest tests
uv run --extra docs build_docs.py --doctest

By default, warnings are not treated as errors, so a local build still succeeds (and produces
output) even when, for example, external `intersphinx <https://www.sphinx-doc.org/en/master/usage/extensions/intersphinx.html>`__
inventories cannot be reached without network access. CI builds run with the ``--warnings-as-errors``
flag to enforce strictness, so pass it locally to reproduce the CI build and catch warnings before
opening a pull request:

.. code-block:: bash

uv run --extra docs build_docs.py --warnings-as-errors

If you are building offline and want to skip the external cross-reference resolution entirely
(rather than relying on the automatic reachability probe), set ``WARP_DOCS_OFFLINE=1``.

Running ``build_docs.py`` also regenerates both the stub file (``warp/__init__.pyi``) and the reStructuredText files for the
reference pages. After building the documentation, it is recommended to run a ``git status`` to
check if your changes have modified these files. If so, please commit the modified files to your branch.
Expand Down
Loading