From 13eefcd67aa34a75d2bad7329ce51448990a43f3 Mon Sep 17 00:00:00 2001 From: Tobias Raabe Date: Wed, 18 Oct 2023 16:52:33 +0200 Subject: [PATCH] Refer to source code on Github in API docs. --- docs/source/changes.md | 1 + docs/source/conf.py | 66 +++++++++++++++++++++++++++++++++++++++++- 2 files changed, 66 insertions(+), 1 deletion(-) diff --git a/docs/source/changes.md b/docs/source/changes.md index f3c9b85b..90122496 100644 --- a/docs/source/changes.md +++ b/docs/source/changes.md @@ -12,6 +12,7 @@ releases are available on [PyPI](https://pypi.org/project/pytask) and - {pull}`454` removes more `.svg`s and replaces them with animations. - {pull}`455` adds more explanation when {meth}`~pytask.PNode.load` fails during the execution. +- {pull}`456` refers to the source code on Github when clicking on a source link. ## 0.4.1 - 2023-10-11 diff --git a/docs/source/conf.py b/docs/source/conf.py index ee6e4b72..81d39883 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -6,9 +6,16 @@ """ from __future__ import annotations +import inspect +import os +import sys +import warnings from importlib.metadata import version +from pathlib import Path from typing import TYPE_CHECKING +import pytask + if TYPE_CHECKING: import sphinx @@ -36,9 +43,9 @@ "sphinx.ext.autodoc", "sphinx.ext.extlinks", "sphinx.ext.intersphinx", + "sphinx.ext.linkcode", "sphinx.ext.napoleon", "sphinxext.opengraph", - "sphinx.ext.viewcode", "sphinx_copybutton", "sphinx_click", "sphinx_toolbox.more_autodoc.autoprotocol", @@ -90,6 +97,63 @@ ogp_social_cards = {"image": "_static/images/pytask_w_text.png"} +# Linkcode, based on numpy doc/source/conf.py +def linkcode_resolve(domain: str, info: dict[str, str]) -> str: # noqa: C901, PLR0912 + """Determine the URL corresponding to Python object.""" + if domain != "py": + return None + + modname = info["module"] + fullname = info["fullname"] + + submod = sys.modules.get(modname) + if submod is None: + return None + + obj = submod + for part in fullname.split("."): + try: + with warnings.catch_warnings(): + # Accessing deprecated objects will generate noisy warnings + warnings.simplefilter("ignore", FutureWarning) + obj = getattr(obj, part) + except AttributeError: # noqa: PERF203 + return None + + try: + fn = inspect.getsourcefile(inspect.unwrap(obj)) + except TypeError: + try: # property + fn = inspect.getsourcefile(inspect.unwrap(obj.fget)) + except (AttributeError, TypeError): + fn = None + if not fn: + return None + + try: + source, lineno = inspect.getsourcelines(obj) + except TypeError: + try: # property + source, lineno = inspect.getsourcelines(obj.fget) + except (AttributeError, TypeError): + lineno = None + except OSError: + lineno = None + + linespec = f"#L{lineno}-L{lineno + len(source) - 1}" if lineno else "" + + fn = os.path.relpath(fn, start=Path(pytask.__file__).parent) + + if "+" in pytask.__version__: + return ( + f"https://github.com/pytask-dev/pytask/blob/main/src/pytask/{fn}{linespec}" + ) + return ( + f"https://github.com/pytask-dev/pytask/blob/" + f"v{pytask.__version__}/src/pytask/{fn}{linespec}" + ) + + # -- Options for HTML output ----------------------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for a list of