|
12 | 12 | # |
13 | 13 | import os |
14 | 14 | import sys |
| 15 | +import inspect |
| 16 | +import subprocess |
| 17 | +from os.path import relpath, dirname |
| 18 | + |
| 19 | +import gnss_lib_py |
| 20 | + |
15 | 21 | sys.path.insert(0, os.path.abspath('.')) |
16 | 22 | sys.path.insert(0, os.path.abspath('../')) |
17 | 23 | sys.path.insert(0, os.path.abspath('../../')) |
|
40 | 46 | extensions = [ |
41 | 47 | 'sphinx.ext.autodoc', |
42 | 48 | 'sphinx.ext.napoleon', |
| 49 | + 'sphinx.ext.linkcode', |
43 | 50 | 'nbsphinx', |
44 | 51 | 'nbsphinx_link', |
45 | 52 | 'IPython.sphinxext.ipython_console_highlighting', |
|
104 | 111 |
|
105 | 112 | # document __init__ methods |
106 | 113 | autoclass_content = 'both' |
| 114 | + |
| 115 | +# Function to find URLs for the source code on GitHub for built docs |
| 116 | + |
| 117 | +# The following code to find the head tag is taken from: |
| 118 | +# https://gist.github.com/nlgranger/55ff2e7ff10c280731348a16d569cb73 |
| 119 | + |
| 120 | +linkcode_revision = "main" |
| 121 | +try: |
| 122 | + # lock to commit number |
| 123 | + cmd = "git log -n1 --pretty=%H" |
| 124 | + head = subprocess.check_output(cmd.split()).strip().decode('utf-8') |
| 125 | + linkcode_revision = head |
| 126 | + |
| 127 | + # if we are on main's HEAD, use main as reference |
| 128 | + cmd = "git log --first-parent main -n1 --pretty=%H" |
| 129 | + main = subprocess.check_output(cmd.split()).strip().decode('utf-8') |
| 130 | + if head == main: |
| 131 | + linkcode_revision = "main" |
| 132 | + |
| 133 | + # if we have a tag, use tag as reference |
| 134 | + cmd = "git describe --exact-match --tags " + head |
| 135 | + tag = subprocess.check_output(cmd.split(" ")).strip().decode('utf-8') |
| 136 | + linkcode_revision = tag |
| 137 | + |
| 138 | +except subprocess.CalledProcessError: |
| 139 | + pass |
| 140 | + |
| 141 | +linkcode_url = "https://github.com/Stanford-NavLab/gnss_lib_py/blob/" \ |
| 142 | + + linkcode_revision + "/{filepath}#L{linestart}-L{linestop}" |
| 143 | + |
| 144 | + |
| 145 | +def linkcode_resolve(domain, info): |
| 146 | + """Return GitHub link to Python file for docs. |
| 147 | +
|
| 148 | + This function does not return a link for non-Python objects. |
| 149 | + For Python objects, `domain == 'py'`, `info['module']` contains the |
| 150 | + name of the module containing the method being documented, and |
| 151 | + `info['fullname']` contains the name of the method. |
| 152 | +
|
| 153 | + Notes |
| 154 | + ----- |
| 155 | + Based off the numpy implementation of linkcode_resolve: |
| 156 | + https://github.com/numpy/numpy/blob/2f375c0f9f19085684c9712d602d22a2b4cb4c8e/doc/source/conf.py#L443 |
| 157 | + Retrieved on 1 Jul, 2023. |
| 158 | + """ |
| 159 | + if domain != 'py': |
| 160 | + return None |
| 161 | + |
| 162 | + modname = info['module'] |
| 163 | + fullname = info['fullname'] |
| 164 | + submod = sys.modules.get(modname) |
| 165 | + if submod is None: |
| 166 | + return None |
| 167 | + # print('modname:', modname) |
| 168 | + # print('fullname:', fullname) |
| 169 | + # print(f"submod:{submod}") |
| 170 | + |
| 171 | + obj = submod |
| 172 | + for part in fullname.split('.'): |
| 173 | + try: |
| 174 | + obj = getattr(obj, part) |
| 175 | + except Exception: |
| 176 | + return None |
| 177 | + |
| 178 | + # strip decorators, which would resolve to the source of the decorator |
| 179 | + # possibly an upstream bug in getsourcefile, bpo-1764286 |
| 180 | + try: |
| 181 | + unwrap = inspect.unwrap |
| 182 | + except AttributeError: |
| 183 | + pass |
| 184 | + else: |
| 185 | + obj = unwrap(obj) |
| 186 | + filepath = None |
| 187 | + lineno = None |
| 188 | + |
| 189 | + if filepath is None: |
| 190 | + try: |
| 191 | + filepath = inspect.getsourcefile(obj) |
| 192 | + except Exception: |
| 193 | + filepath = None |
| 194 | + if not filepath: |
| 195 | + return None |
| 196 | + #NOTE: Re-export filtering turned off because |
| 197 | + # # Ignore re-exports as their source files are not within the gnss_lib_py repo |
| 198 | + # module = inspect.getmodule(obj) |
| 199 | + # if module is not None and not module.__name__.startswith("gnss_lib_py"): |
| 200 | + # return "no_module_not_gnss_lib_py" |
| 201 | + |
| 202 | + try: |
| 203 | + source, lineno = inspect.getsourcelines(obj) |
| 204 | + except Exception: |
| 205 | + lineno = "" |
| 206 | + # The following line of code first finds the relative path from |
| 207 | + # the location of gnss_lib_py.__init__.py and then adds gnss_lib_py |
| 208 | + # to the beginning to give the path of that file from the root folder |
| 209 | + # of gnss_lib_py and the tests directory adjacent to it |
| 210 | + |
| 211 | + filepath = relpath(filepath, dirname(gnss_lib_py.__file__)) |
| 212 | + filepath = os.path.join('gnss_lib_py', filepath) |
| 213 | + |
| 214 | + if lineno: |
| 215 | + linestart = lineno |
| 216 | + linestop = lineno + len(source) - 1 |
| 217 | + else: |
| 218 | + linestart = "" |
| 219 | + linestop = "" |
| 220 | + |
| 221 | + codelink = linkcode_url.format( |
| 222 | + filepath=filepath, linestart=linestart, linestop=linestop) |
| 223 | + return codelink |
0 commit comments