|
| 1 | +# this code is a copy/paste of |
| 2 | +# https://github.com/scikit-learn/scikit-learn/blob/ |
| 3 | +# b0b8a39d8bb80611398e4c57895420d5cb1dfe09/doc/sphinxext/github_link.py |
| 4 | + |
| 5 | +from operator import attrgetter |
| 6 | +import inspect |
| 7 | +import subprocess |
| 8 | +import os |
| 9 | +import sys |
| 10 | +from functools import partial |
| 11 | + |
| 12 | +REVISION_CMD = "git rev-parse --short HEAD" |
| 13 | + |
| 14 | + |
| 15 | +def _get_git_revision(): |
| 16 | + try: |
| 17 | + revision = subprocess.check_output(REVISION_CMD.split()).strip() |
| 18 | + except (subprocess.CalledProcessError, OSError): |
| 19 | + print("Failed to execute git to get revision") |
| 20 | + return None |
| 21 | + return revision.decode("utf-8") |
| 22 | + |
| 23 | + |
| 24 | +def _linkcode_resolve(domain, info, package, url_fmt, revision): |
| 25 | + """Determine a link to online source for a class/method/function |
| 26 | + This is called by sphinx.ext.linkcode |
| 27 | + An example with a long-untouched module that everyone has |
| 28 | + >>> _linkcode_resolve('py', {'module': 'tty', |
| 29 | + ... 'fullname': 'setraw'}, |
| 30 | + ... package='tty', |
| 31 | + ... url_fmt='http://hg.python.org/cpython/file/' |
| 32 | + ... '{revision}/Lib/{package}/{path}#L{lineno}', |
| 33 | + ... revision='xxxx') |
| 34 | + 'http://hg.python.org/cpython/file/xxxx/Lib/tty/tty.py#L18' |
| 35 | + """ |
| 36 | + |
| 37 | + if revision is None: |
| 38 | + return |
| 39 | + if domain not in ("py", "pyx"): |
| 40 | + return |
| 41 | + if not info.get("module") or not info.get("fullname"): |
| 42 | + return |
| 43 | + |
| 44 | + class_name = info["fullname"].split(".")[0] |
| 45 | + module = __import__(info["module"], fromlist=[class_name]) |
| 46 | + obj = attrgetter(info["fullname"])(module) |
| 47 | + |
| 48 | + # Unwrap the object to get the correct source |
| 49 | + # file in case that is wrapped by a decorator |
| 50 | + obj = inspect.unwrap(obj) |
| 51 | + |
| 52 | + try: |
| 53 | + fn = inspect.getsourcefile(obj) |
| 54 | + except Exception: |
| 55 | + fn = None |
| 56 | + if not fn: |
| 57 | + try: |
| 58 | + fn = inspect.getsourcefile(sys.modules[obj.__module__]) |
| 59 | + except Exception: |
| 60 | + fn = None |
| 61 | + if not fn: |
| 62 | + return |
| 63 | + |
| 64 | + fn = os.path.relpath(fn, start=os.path.dirname(__import__(package).__file__)) |
| 65 | + try: |
| 66 | + lineno = inspect.getsourcelines(obj)[1] |
| 67 | + except Exception: |
| 68 | + lineno = "" |
| 69 | + return url_fmt.format(revision=revision, package=package, path=fn, lineno=lineno) |
| 70 | + |
| 71 | + |
| 72 | +def make_linkcode_resolve(package, url_fmt): |
| 73 | + """Returns a linkcode_resolve function for the given URL format |
| 74 | + revision is a git commit reference (hash or name) |
| 75 | + package is the name of the root module of the package |
| 76 | + url_fmt is along the lines of ('https://github.com/USER/PROJECT/' |
| 77 | + 'blob/{revision}/{package}/' |
| 78 | + '{path}#L{lineno}') |
| 79 | + """ |
| 80 | + revision = _get_git_revision() |
| 81 | + return partial( |
| 82 | + _linkcode_resolve, revision=revision, package=package, url_fmt=url_fmt |
| 83 | + ) |
0 commit comments