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