Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs: pin version in links to external docs #236

Merged
merged 9 commits into from
Mar 6, 2024
Merged
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
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added

-
- docs: enable pin version in links to external docs ([#236](https://github.com/Lightning-AI/utilities/pull/236))


### Changed
Expand Down
7 changes: 6 additions & 1 deletion docs/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

import lightning_utilities
import pt_lightning_sphinx_theme
from lightning_utilities.docs import fetch_external_assets
from lightning_utilities.docs import adjust_linked_external_docs, fetch_external_assets

# -- Path setup --------------------------------------------------------------

Expand Down Expand Up @@ -46,6 +46,11 @@
# -- Project documents -------------------------------------------------------

fetch_external_assets(docs_folder=_PATH_HERE)
adjust_linked_external_docs(
source_link="https://numpy.org/doc/stable/",
target_link="https://numpy.org/doc/{numpy.__version__}/",
browse_folder=_PATH_HERE,
)


# export the READme
Expand Down
2 changes: 2 additions & 0 deletions requirements/_docs.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,5 @@ sphinx-autodoc-typehints >=1.0
sphinx-paramlinks >=0.5.1
sphinx-togglebutton >=0.2
sphinx-copybutton >=0.3

numpy # for validation of custom/local version adjustemnt
5 changes: 4 additions & 1 deletion src/lightning_utilities/docs/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
"""General tools for Docs."""

from lightning_utilities.docs.retriever import fetch_external_assets # noqa: F401
from lightning_utilities.docs.formatting import adjust_linked_external_docs
from lightning_utilities.docs.retriever import fetch_external_assets

__all__ = ["adjust_linked_external_docs", "fetch_external_assets"]
79 changes: 78 additions & 1 deletion src/lightning_utilities/docs/formatting.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
# Licensed under the Apache License, Version 2.0 (the "License");
# http://www.apache.org/licenses/LICENSE-2.0
#
import glob
import importlib
import inspect
import logging
import os
import re
import sys
from typing import Tuple
from typing import Iterable, Optional, Tuple, Union


def _transform_changelog(path_in: str, path_out: str) -> None:
Expand Down Expand Up @@ -63,3 +67,76 @@ def find_source() -> Tuple[str, int, int]:
branch = {"latest": "master", "stable": "master"}.get(branch, branch)
filename = "/".join([branch] + filename.split("/")[1:])
return f"https://github.com/{github_user}/{github_repo}/blob/{filename}"


def _update_link_based_imported_package(link: str, pkg_ver: str, version_digits: Optional[int]) -> str:
"""Adjust the linked external docs to be local.

Args:
link: the source link to be replaced
pkg_ver: the target link to be replaced, if ``{package.version}`` is included it will be replaced accordingly
version_digits: for semantic versioning, how many digits to be considered

"""
pkg_att = pkg_ver.split(".")
# load the package with all additional sub-modules
module = importlib.import_module(".".join(pkg_att[:-1]))
# load the attribute
ver = getattr(module, pkg_att[-1])
# drop any additional context after `+`
ver = ver.split("+")[0]
# crop the version to the number of digits
ver = ".".join(ver.split(".")[:version_digits])
# replace the version
return link.replace(f"{{{pkg_ver}}}", ver)


def adjust_linked_external_docs(
source_link: str,
target_link: str,
browse_folder: Union[str, Iterable[str]],
file_extensions: Iterable[str] = (".rst", ".py"),
version_digits: int = 2,
) -> None:
r"""Adjust the linked external docs to be local.

Args:
source_link: the link to be replaced
target_link: the link to be replaced, if ``{package.version}`` is included it will be replaced accordingly
browse_folder: the location of the browsable folder
file_extensions: what kind of files shall be scanned
version_digits: for semantic versioning, how many digits to be considered

Examples:
>>> adjust_linked_external_docs(
... "https://numpy.org/doc/stable/",
... "https://numpy.org/doc/{numpy.__version__}/",
... "docs/source",
... )

"""
list_files = []
if isinstance(browse_folder, str):
browse_folder = [browse_folder]
for folder in browse_folder:
for ext in file_extensions:
list_files += glob.glob(os.path.join(folder, "**", f"*{ext}"), recursive=True)
if not list_files:
logging.warning(f'No files were listed in folder "{browse_folder}" and pattern "{file_extensions}"')
return

# find the expression for package version in {} brackets if any, use re to find it
pkg_ver_all = re.findall(r"{([\w.]+)}", target_link)
for pkg_ver in pkg_ver_all:
target_link = _update_link_based_imported_package(target_link, pkg_ver, version_digits)

# replace the source link with target link
for fpath in set(list_files):
with open(fpath, encoding="UTF-8") as fopen:
body = fopen.read()
body_ = body.replace(source_link, target_link)
if body == body_:
continue
logging.debug(f'links adjusting in {fpath}: "{source_link}" -> "{target_link}"')
with open(fpath, "w", encoding="UTF-8") as fw:
fw.write(body_)
32 changes: 32 additions & 0 deletions tests/unittests/docs/test_formatting.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import os.path

import pytest
from lightning_utilities.docs import adjust_linked_external_docs


@pytest.mark.online()
def test_adjust_linked_external_docs(temp_docs):
# take config as it includes API references with `stable`
path_conf = os.path.join(temp_docs, "conf.py")

def _get_line_with_numpy(path_rst: str) -> str:
with open(path_rst, encoding="UTF-8") as fopen:
lines = fopen.readlines()
# find the first line with figure reference
return next(ln for ln in lines if ln.lstrip().startswith('"numpy":'))

# validate the initial expectations
line = _get_line_with_numpy(path_conf)
assert "https://numpy.org/doc/stable/" in line

adjust_linked_external_docs(
"https://numpy.org/doc/stable/", "https://numpy.org/doc/{numpy.__version__}/", temp_docs
)

import numpy as np

np_version = np.__version__.split(".")

# validate the final state of index page
line = _get_line_with_numpy(path_conf)
assert f"https://numpy.org/doc/{'.'.join(np_version[:2])}/" in line
4 changes: 2 additions & 2 deletions tests/unittests/docs/test_retriever.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ def _get_line_with_figure(path_rst: str) -> str:

# validate the initial expectations
line = _get_line_with_figure(path_index)
# that the image exists~
# that the image exists
assert "Lightning.gif" in line
# and it is sourced in S3
assert ".s3." in line
Expand All @@ -31,7 +31,7 @@ def _get_line_with_figure(path_rst: str) -> str:

# validate the final state of index page
line = _get_line_with_figure(path_index)
# that the image exists~
# that the image exists
assert os.path.join("fetched-s3-assets", "Lightning.gif") in line
# but it is not sourced from S3
assert ".s3." not in line
Expand Down
Loading