Skip to content

Commit

Permalink
Add GitHub link roles in documentation (#361)
Browse files Browse the repository at this point in the history
## Description

<!-- Provide a brief description of the PR's purpose here. -->

Adds two Sphinx roles to make it easier to link to GitHub PRs and issues
in the documentation. Examples are:

```
:pr:`123` -> Links to PR 123
:issue:`123` -> Links to issue 123
```

References:
- https://doughellmann.com/posts/defining-custom-roles-in-sphinx/
-
https://www.sphinx-doc.org/en/master/development/tutorials/helloworld.html

## TODO

<!-- Notable points that this PR has either accomplished or will
accomplish. -->

- [x] Create extension
- [x] Replace links in history
- [x] Replace links in rest of code

## Questions

<!-- Any concerns or points of confusion? -->

## Status

- [x] I have read the guidelines in

[CONTRIBUTING.md](https://github.com/icaros-usc/pyribs/blob/master/CONTRIBUTING.md)
- [x] I have formatted my code using `yapf`
- [x] I have tested my code by running `pytest`
- [x] I have linted my code with `pylint`
- [x] I have added a one-line description of my change to the changelog
in
      `HISTORY.md`
- [x] This PR is ready to go
  • Loading branch information
btjanaka authored Sep 8, 2023
1 parent ae7d004 commit 563ea2c
Show file tree
Hide file tree
Showing 7 changed files with 239 additions and 137 deletions.
263 changes: 135 additions & 128 deletions HISTORY.md

Large diffs are not rendered by default.

92 changes: 92 additions & 0 deletions docs/_ext/github_links.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
"""Extension for adding links to GitHub issues and PRs.
Two new Sphinx roles are provided -- :issue: and :pr:. For instance, you can use
:issue:`123` and :pr:`123` to refer to issue 123 and PR 123, respectively.
The link to the GitHub repo must be configured with the github_repo_url config
in conf.py.
References:
- https://doughellmann.com/posts/defining-custom-roles-in-sphinx/
- https://www.sphinx-doc.org/en/master/development/tutorials/helloworld.html
"""
from docutils import nodes, utils
from docutils.parsers.rst.roles import set_classes


def make_link_node(rawtext, app, link_type, slug, options):
"""Creates a link to a GitHub resource.
Args:
rawtext: Text being replaced with link node.
app: Sphinx application context
link_type: Link type ("issue", "pull")
slug: ID of the thing to link to
options: Options dictionary passed to role func.
"""
try:
base = app.config.github_repo_url
if not base:
raise AttributeError
except AttributeError as e:
raise ValueError(
"github_repo_url configuration value is not set") from e

slash = '/' if base[-1] != '/' else ''
ref = f"{base}{slash}{link_type}/{slug}/"
set_classes(options)
node = nodes.reference(rawtext,
f"#{utils.unescape(slug)}",
refuri=ref,
**options)
return node


def github_link_role(
name,
rawtext,
text,
lineno,
inliner,
options=None,
content=(), # pylint: disable = unused-argument
):
"""Link to a GitHub issue or pull request.
Returns 2-part tuple containing list of nodes to insert into the document
and a list of system messages. Both are allowed to be empty.
Args:
name: The role name used in the document.
rawtext: The entire markup snippet, with role.
text: The text marked with the role.
lineno: The line number where rawtext appears in the input.
inliner: The inliner instance that called us.
options: Directive options for customization.
content: The directive content for customization.
"""
options = options or {}

try:
issue_num = int(text)
if issue_num <= 0:
raise ValueError
except ValueError:
msg = inliner.reporter.error(
"GitHub issue number must be a number greater than or equal to 1; "
f"'{text}' is invalid.",
line=lineno)
prb = inliner.problematic(rawtext, rawtext, msg)
return [prb], [msg]

app = inliner.document.settings.env.app
link_type = {"issue": "issue", "pr": "pull"}[name]
node = make_link_node(rawtext, app, link_type, str(issue_num), options)
return [node], []


def setup(app):
"""Install the plugin."""
app.add_role("issue", github_link_role)
app.add_role("pr", github_link_role)
app.add_config_value("github_repo_url", None, "env")
8 changes: 6 additions & 2 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@

import ribs

sys.path.insert(0, os.path.abspath(".."))
sys.path.insert(0, os.path.abspath("..")) # Detect ribs.
sys.path.append(os.path.abspath("./_ext")) # Detect extensions.

DEV_MODE = os.environ.get("DOCS_MODE", "regular") == "dev"
READTHEDOCS_VERSION = os.environ.get("READTHEDOCS_VERSION", "stable")
Expand Down Expand Up @@ -54,6 +55,7 @@
"sphinx_toolbox.more_autodoc.autonamedtuple",
"sphinx_autodoc_typehints",
"sphinx_codeautolink",
"github_links",
]

# Napoleon
Expand Down Expand Up @@ -119,6 +121,8 @@
# If true, `todo` and `todoList` produce output, else they produce nothing.
todo_include_todos = False

github_repo_url = "https://github.com/icaros-usc/pyribs/"

# -- Options for HTML output -------------------------------------------

html_show_sourcelink = True
Expand All @@ -137,7 +141,7 @@
"nav_title": "pyribs",
"base_url": (f"https://docs.pyribs.org/{READTHEDOCS_LANGUAGE}/"
f"{READTHEDOCS_VERSION}/"),
"repo_url": "https://github.com/icaros-usc/pyribs/",
"repo_url": github_repo_url,
"repo_name": "pyribs",
"google_analytics_account": None,
"html_minify": not DEV_MODE,
Expand Down
4 changes: 2 additions & 2 deletions ribs/archives/_archive_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -284,8 +284,8 @@ def best_elite(self):
replaced with an elite with a lower objective value. This can happen
because in non-elitist archives, new solutions only need to exceed
the *threshold* of the cell they are being inserted into, not the
*objective* of the elite currently in the cell. See `#314
<https://github.com/icaros-usc/pyribs/pull/314>`_ for more info.
*objective* of the elite currently in the cell. See :pr:`314` for
more info.
"""
return self._best_elite

Expand Down
4 changes: 2 additions & 2 deletions ribs/archives/_archive_stats.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ class ArchiveStats:
#: replaced with an elite with a lower objective value. This can happen
#: because in non-elitist archives, new solutions only need to exceed
#: the *threshold* of the cell they are being inserted into, not the
#: *objective* of the elite currently in the cell. See `#314
#: <https://github.com/icaros-usc/pyribs/pull/314>`_ for more info.
#: *objective* of the elite currently in the cell. See :pr:`314` for
#: more info.
obj_max: np.floating

#: Mean objective value of the elites in the archive. None if there are no
Expand Down
2 changes: 1 addition & 1 deletion ribs/archives/_cvt_archive.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ class CVTArchive(ArchiveBase):
the ``learning_rate`` and ``threshold_min`` parameters.
.. note:: For more information on our choice of k-D tree implementation, see
`#38 <https://github.com/icaros-usc/pyribs/issues/38>`_.
:pr:`38`.
Args:
solution_dim (int): Dimension of the solution space.
Expand Down
3 changes: 1 addition & 2 deletions ribs/visualize/_cvt_archive_heatmap.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,8 +113,7 @@ def cvt_archive_heatmap(archive,
bounds. Passing `clip=True` will clip the heatmap such that these
"outer edge" polygons are within the archive bounds. An arbitrary
polygon can also be passed in to clip the heatmap to a custom shape.
See `#356 <https://github.com/icaros-usc/pyribs/pull/356>`_ for more
info.
See :pr:`356` for more info.
plot_centroids (bool): Whether to plot the cluster centroids.
plot_samples (bool): Whether to plot the samples used when generating
the clusters.
Expand Down

0 comments on commit 563ea2c

Please sign in to comment.