#!/usr/bin/env python3
#
# repos_and_users.py
"""
Roles and nodes for referencing GitHub repositories and organizations.
"""
#
# Copyright © 2020-2021 Dominic Davis-Foster <dominic@davis-foster.co.uk>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
# DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
# OR OTHER DEALINGS IN THE SOFTWARE.
#
# Parts of the docstrings based on https://docutils.sourceforge.io/docs/howto/rst-roles.html
#
# stdlib
from typing import Any, Dict, List, Tuple, Union
# 3rd party
from apeye.url import URL
from docutils import nodes
from docutils.nodes import system_message
from docutils.parsers.rst.states import Inliner
from sphinx.util.nodes import split_explicit_title
from sphinx.writers.html5 import HTML5Translator
from sphinx.writers.latex import LaTeXTranslator
# this package
from sphinx_toolbox.utils import GITHUB_COM, make_github_url
__all__ = (
"GitHubObjectLinkNode",
"repository_role",
"user_role",
"visit_github_object_link_node",
"depart_github_object_link_node",
)
[docs]class GitHubObjectLinkNode(nodes.reference):
"""
Docutils Node to represent a link to a GitHub repository.
:param repo_name: The full name of the repository, in the form ``owner/name``.
:param refuri: The URL of the issue / pull request on GitHub.
.. clearpage::
"""
name: str
url: str
def __init__(
self,
name: str,
refuri: Union[str, URL],
**kwargs,
):
self.name = str(name)
self.url = str(refuri)
super().__init__(self.name, self.name, refuri=self.url)
def copy(self) -> "GitHubObjectLinkNode": # pragma: no cover
"""
Return a copy of the :class:`sphinx_toolbox.github.repos.GitHubObjectLinkNode`.
"""
# This was required to stop some breakage, but it doesn't seem to run during the tests.
obj = self.__class__(self.name, self.url)
obj.document = self.document
obj.source = self.source
obj.line = self.line
return obj
[docs]def repository_role(
name: str,
rawtext: str,
text: str,
lineno: int,
inliner: Inliner,
options: Dict[str, Any] = {},
content: List[str] = []
) -> Tuple[List[nodes.reference], List[system_message]]:
"""
Adds a link to the given repository on GitHub.
:param name: The local name of the interpreted role, the role name actually used in the document.
:param rawtext: A string containing the entire interpreted text input, including the role and markup.
:param text: The interpreted text content.
:param lineno: The line number where the interpreted text begins.
:param inliner: The :class:`docutils.parsers.rst.states.Inliner` object that called :func:`~.repository_role`.
It contains the several attributes useful for error reporting and document tree access.
:param options: A dictionary of directive options for customization (from the ``role`` directive),
to be interpreted by the function.
Used for additional attributes for the generated elements and other functionality.
:param content: A list of strings, the directive content for customization (from the ``role`` directive).
To be interpreted by the function.
:return: A list containing the created node, and a list containing any messages generated during the function.
"""
has_t, text, repo_name = split_explicit_title(text)
repo_name = nodes.unescape(repo_name)
repository_parts = nodes.unescape(repo_name).split('/')
if len(repository_parts) != 2:
return [], [inliner.document.reporter.warning(f"Invalid repository '{repo_name}'.")]
# refnode: nodes.reference
if has_t:
refnode = nodes.reference(
text,
text,
refuri=str(make_github_url(*repository_parts)),
)
else:
refnode = GitHubObjectLinkNode(
name=repo_name,
refuri=make_github_url(*repository_parts),
)
return [refnode], []
[docs]def user_role(
name: str,
rawtext: str,
text: str,
lineno: int,
inliner: Inliner,
options: Dict[str, Any] = {},
content: List[str] = []
) -> Tuple[List[nodes.reference], List[system_message]]:
"""
Adds a link to the given user / organization on GitHub.
:param name: The local name of the interpreted role, the role name actually used in the document.
:param rawtext: A string containing the entire interpreted text input, including the role and markup.
:param text: The interpreted text content.
:param lineno: The line number where the interpreted text begins.
:param inliner: The :class:`docutils.parsers.rst.states.Inliner` object that called :func:`~.user_role`.
It contains the several attributes useful for error reporting and document tree access.
:param options: A dictionary of directive options for customization (from the ``role`` directive),
to be interpreted by the function.
Used for additional attributes for the generated elements and other functionality.
:param content: A list of strings, the directive content for customization (from the ``role`` directive).
To be interpreted by the function.
:return: A list containing the created node, and a list containing any messages generated during the function.
.. clearpage::
"""
has_t, text, username = split_explicit_title(text)
username = nodes.unescape(username)
messages: List[system_message] = []
if has_t:
refnode = nodes.reference(
text,
text,
refuri=str(GITHUB_COM / username),
)
else:
refnode = GitHubObjectLinkNode(
name=f"@{username}",
refuri=GITHUB_COM / username,
)
return [refnode], messages
[docs]def visit_github_object_link_node(translator: HTML5Translator, node: GitHubObjectLinkNode) -> None:
"""
Visit a :class:`~.GitHubObjectLinkNode`.
:param translator:
:param node: The node being visited.
"""
translator.body.append(f'<b class="github-object">')
translator.visit_reference(node)
[docs]def depart_github_object_link_node(translator: HTML5Translator, node: GitHubObjectLinkNode) -> None:
"""
Depart an :class:`~.GitHubObjectLinkNode`.
:param translator:
:param node: The node being visited.
"""
translator.depart_reference(node)
translator.body.append("</b>")
def _visit_github_object_link_node_latex(translator: LaTeXTranslator, node: GitHubObjectLinkNode) -> None:
"""
Visit a :class:`~.GitHubObjectLinkNode`.
:param translator:
:param node: The node being visited.
"""
node.children = node.children[:1]
translator.visit_reference(node)
def _depart_github_object_link_node_latex(translator: LaTeXTranslator, node: GitHubObjectLinkNode) -> None:
"""
Depart an :class:`~.GitHubObjectLinkNode`.
:param translator:
:param node: The node being visited.
"""
translator.depart_reference(node)