diff --git a/docs/conf.py b/docs/conf.py index ebd887f4a5a..1a537f261c7 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -16,6 +16,7 @@ from pathlib import Path import logging import sys +import inspect import sphinx.application import sphinx.errors @@ -58,8 +59,8 @@ "sphinx.ext.graphviz", "sphinx.ext.todo", "sphinx.ext.coverage", + "sphinx.ext.linkcode", "sphinx.ext.ifconfig", - "sphinx.ext.viewcode", "sphinx.ext.extlinks", "sphinx-prompt", "sphinx_copybutton", @@ -725,6 +726,68 @@ def filter(self, record: logging.LogRecord) -> bool: return True +def linkcode_resolve(domain, info): + """ + Determine the URL corresponding to Python object + """ + if domain != "py": + return None + + import flytekit + + 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: + obj = getattr(obj, part) + except AttributeError: + return None + + if inspect.isfunction(obj): + obj = inspect.unwrap(obj) + try: + fn = inspect.getsourcefile(obj) + except TypeError: + fn = None + if not fn or fn.endswith("__init__.py"): + try: + fn = inspect.getsourcefile(sys.modules[obj.__module__]) + except (TypeError, AttributeError, KeyError): + fn = None + if not fn: + return None + + try: + source, lineno = inspect.getsourcelines(obj) + except (OSError, TypeError): + lineno = None + + linespec = f"#L{lineno:d}-L{lineno + len(source) - 1:d}" if lineno else "" + + startdir = Path(flytekit.__file__).parent.parent + try: + fn = os.path.relpath(fn, start=startdir).replace(os.path.sep, "/") + except ValueError: + return None + + if not fn.startswith("flytekit/"): + return None + + if flytekit.__version__ == "dev": + tag = "master" + else: + tag = f"v{flytekit.__version__}" + + out = f"https://github.com/flyteorg/flytekit/blob/{tag}/{fn}{linespec}" + return out + + def setup(app: sphinx.application.Sphinx) -> None: """Setup root logger for Sphinx""" logger = logging.getLogger("sphinx")