Skip to content

Commit 21e3036

Browse files
committed
Update helper functions to match new README
1 parent 726d737 commit 21e3036

File tree

1 file changed

+123
-89
lines changed

1 file changed

+123
-89
lines changed

sphinx_github_style/__init__.py

Lines changed: 123 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,15 @@
55
import subprocess
66
import pkg_resources
77
from pathlib import Path
8-
from typing import Dict, Any, Optional
98
from sphinx.application import Sphinx
109
from sphinx.errors import ExtensionError
10+
from typing import Dict, Any, Optional, Callable
1111

12-
__version__ = "0.0.1b8"
12+
13+
__version__ = "0.0.1b9"
1314
__author__ = 'Adam Korn <hello@dailykitten.net>'
1415

16+
1517
from .add_linkcode_class import add_linkcode_node_class
1618
from .meth_lexer import TDKMethLexer
1719
from .github_style import TDKStyle
@@ -23,114 +25,56 @@ def setup(app: Sphinx) -> Dict[str, Any]:
2325
pkg = pkg_resources.require(modname)[0]
2426
pkg_name = pkg.get_metadata('top_level.txt').strip()
2527

26-
app.connect("builder-inited", get_static_path)
27-
# app.connect('build-finished', save_generated_rst_files)
28+
app.connect("builder-inited", add_static_path)
2829

2930
app.add_config_value('pkg_name', pkg_name, 'html')
30-
app.add_config_value('linkcode_blob', 'master', True)
31+
app.add_config_value('linkcode_blob', 'head', True)
3132

3233
app.setup_extension('sphinx_github_style.add_linkcode_class')
3334
app.setup_extension('sphinx_github_style.github_style')
3435
app.setup_extension('sphinx_github_style.meth_lexer')
3536
app.setup_extension('sphinx.ext.linkcode')
3637

37-
app.config.html_context['github_version'] = get_linkcode_revision(app)
38+
html_context = getattr(app.config, 'html_context', {})
39+
html_context['github_version'] = get_linkcode_revision(app)
40+
setattr(app.config, 'html_context', html_context)
3841

3942
linkcode_url = get_linkcode_url(app)
4043
linkcode_func = getattr(app.config, 'linkcode_resolve', None)
4144

42-
if not linkcode_func or not callable(linkcode_func):
43-
print("Function `linkcode_resolve` is not given in conf.py; "
44-
"using default function from ``sphinx_github_style``")
45-
46-
def linkcode_resolve(domain, info):
47-
"""Returns a link to the source code on GitHub, with appropriate lines highlighted
48-
49-
:By:
50-
Adam Korn (https://github.com/tdkorn)
51-
:Adapted From:
52-
nlgranger/SeqTools (https://github.com/nlgranger/seqtools/blob/master/docs/conf.py)
53-
"""
54-
if domain != 'py' or not info['module']:
55-
return None
56-
57-
modname = info['module']
58-
fullname = info['fullname']
59-
60-
submod = sys.modules.get(modname)
61-
if submod is None:
62-
return None
63-
64-
obj = submod
65-
for part in fullname.split('.'):
66-
try:
67-
obj = getattr(obj, part)
68-
except Exception:
69-
return None
70-
71-
try:
72-
filepath = os.path.relpath(inspect.getsourcefile(obj), modpath)
73-
if filepath is None:
74-
return
75-
except Exception:
76-
return None
77-
78-
try:
79-
source, lineno = inspect.getsourcelines(obj)
80-
except OSError:
81-
print(f'failed to get source lines for {obj}')
82-
return None
83-
else:
84-
linestart, linestop = lineno, lineno + len(source) - 1
85-
86-
# Fix links with "../../../" or "..\\..\\..\\"
87-
filepath = '/'.join(filepath[filepath.find(pkg_name):].split('\\'))
88-
89-
# Example: https://github.com/TDKorn/my-magento/blob/docs/magento/models/model.py#L28-L59
90-
final_link = linkcode_url.format(
91-
filepath=filepath,
92-
linestart=linestart,
93-
linestop=linestop
94-
)
95-
print(f"Final Link for {fullname}: {final_link}")
96-
return final_link
97-
98-
linkcode_func = linkcode_resolve
45+
if not callable(linkcode_func):
46+
print(
47+
"Function `linkcode_resolve` not found in ``conf.py``; "
48+
"using default function from ``sphinx_github_style``"
49+
)
50+
linkcode_func = get_linkcode_resolve(
51+
linkcode_url, pkg_name, modpath
52+
)
9953

10054
app.config.linkcode_resolve = linkcode_func
10155
return {'version': sphinx.__display_version__, 'parallel_read_safe': True}
10256

10357

104-
def get_static_path(app):
58+
def add_static_path(app) -> None:
59+
"""Add the path for the ``_static`` folder"""
10560
app.config.html_static_path.append(
10661
str(Path(__file__).parent.joinpath("_static").absolute())
10762
)
10863

10964

110-
def get_linkcode_url(app: Sphinx) -> str:
111-
"""Template for linking to highlighted GitHub source code
112-
113-
Formatted into a final link by :meth:`~.linkcode_resolve`
114-
"""
115-
if (url := app.config._raw_config.get("linkcode_url")) is None:
116-
raise ExtensionError("Config value ``linkcode_url`` is missing")
117-
url = f"{url.rstrip('/')}/blob/{get_linkcode_revision(app)}/"
118-
url += "{filepath}#L{linestart}-L{linestop}"
119-
return url
120-
121-
122-
def get_linkcode_revision(app: Sphinx):
65+
def get_linkcode_revision(app: Sphinx) -> str:
12366
"""Get the blob to link to on GitHub
12467
125-
.. admonition:: Linkcode Blobs
68+
.. note::
12669
127-
The generated links will use the conf.py value of ``linkcode_blob``
70+
The generated links depend on the ``conf.py`` value of ``linkcode_blob``,
71+
which can be any of ``"head"``, ``"last_tag"``, or ``"{blob}"``
12872
129-
* ``"head"`` - most recent commit hash; if this commit is tagged, uses the tag instead
130-
* ``"last_tag" - the most recently tagged commit
131-
* "{blob}" - any blob (ex. ``"master"``, ``"v2.1.0b0"``)
73+
* ``head`` (default): links to the most recent commit hash; if this commit is tagged, uses the tag instead
74+
* ``last_tag``: links to the most recently tagged commit; if no tags exist, uses ``head``
75+
* ``blob``: links to any blob you want, for example ``"master"`` or ``"v2.0.1"``
13276
"""
133-
blob = getattr(app.config, "linkcode_blob", "master")
77+
blob = getattr(app.config, "linkcode_blob", "head")
13478
if blob == "head":
13579
return get_head()
13680
if blob == 'last_tag':
@@ -142,7 +86,7 @@ def get_linkcode_revision(app: Sphinx):
14286
def get_head(errors: bool = False) -> Optional[str]:
14387
"""Gets the most recent commit hash or tag
14488
145-
:raises subprocess.CalledProcessError: if the commit can't be found and ``errors`` is set to ``True``
89+
:raises subprocess.CalledProcessError: if the commit can't be found and ``errors`` is ``True``
14690
"""
14791
cmd = "git log -n1 --pretty=%H"
14892
try:
@@ -159,18 +103,108 @@ def get_head(errors: bool = False) -> Optional[str]:
159103
return head
160104

161105
except subprocess.CalledProcessError as e:
162-
# Raise error
163106
if errors:
164-
raise RuntimeError from e
107+
raise e
165108
else:
166-
return None
109+
return print("Failed to get head") # so no head?
167110

168111

169-
def get_last_tag():
170-
"""Get the most recent commit tag"""
112+
def get_last_tag() -> str:
113+
"""Get the most recent commit tag
114+
115+
:raises RuntimeError: if there are no tagged commits
116+
"""
171117
try:
172118
cmd = "git describe --tags --abbrev=0"
173119
return subprocess.check_output(cmd.split(" ")).strip().decode('utf-8')
174120

175121
except subprocess.CalledProcessError as e:
176122
raise RuntimeError("No tags exist for the repo...(?)") from e
123+
124+
125+
def get_linkcode_url(app: Sphinx) -> str:
126+
"""Get the template URL for linking to highlighted GitHub source code
127+
128+
Formatted into the final link by ``linkcode_resolve()``
129+
"""
130+
context = getattr(app.config, 'html_context', None)
131+
url = app.config._raw_config.get("linkcode_url", None)
132+
133+
if url is None:
134+
if context is None or not all(context.get(key) for key in ("github_user", "github_repo")):
135+
raise ExtensionError(
136+
"sphinx-github-style: config value ``linkcode_url`` is missing")
137+
else:
138+
print(
139+
"sphinx-github-style: config value ``linkcode_url`` is missing. "
140+
"Creating link from ``html_context`` values..."
141+
)
142+
blob = context['github_version'] # Added by setup() above
143+
url = f"https://github.com/{context['github_user']}/{context['github_repo']}/{blob}/"
144+
145+
else:
146+
# URL should be "https://github.com/user/repo"
147+
url = url.strip("/") + f"/blob/{get_linkcode_revision(app)}/"
148+
149+
url += "{filepath}#L{linestart}-L{linestop}"
150+
return url
151+
152+
153+
def get_linkcode_resolve(linkcode_url: str, pkg_name: str, modpath: str) -> Callable:
154+
"""Defines and returns a ``linkcode_resolve`` function for your package
155+
156+
Used by default if ``linkcode_resolve`` isn't defined in ``conf.py``
157+
"""
158+
def linkcode_resolve(domain, info):
159+
"""Returns a link to the source code on GitHub, with appropriate lines highlighted
160+
161+
:By:
162+
Adam Korn (https://github.com/tdkorn)
163+
:Adapted From:
164+
nlgranger/SeqTools (https://github.com/nlgranger/seqtools/blob/master/docs/conf.py)
165+
"""
166+
if domain != 'py' or not info['module']:
167+
return None
168+
169+
modname = info['module']
170+
fullname = info['fullname']
171+
172+
submod = sys.modules.get(modname)
173+
if submod is None:
174+
return None
175+
176+
obj = submod
177+
for part in fullname.split('.'):
178+
try:
179+
obj = getattr(obj, part)
180+
except Exception:
181+
return None
182+
183+
try:
184+
filepath = os.path.relpath(inspect.getsourcefile(obj), modpath)
185+
if filepath is None:
186+
return
187+
except Exception:
188+
return None
189+
190+
try:
191+
source, lineno = inspect.getsourcelines(obj)
192+
except OSError:
193+
print(f'failed to get source lines for {obj}')
194+
return None
195+
else:
196+
linestart, linestop = lineno, lineno + len(source) - 1
197+
198+
# Fix links with "../../../" or "..\\..\\..\\"
199+
filepath = '/'.join(filepath[filepath.find(pkg_name):].split('\\'))
200+
201+
# Example: https://github.com/TDKorn/my-magento/blob/docs/magento/models/model.py#L28-L59
202+
final_link = linkcode_url.format(
203+
filepath=filepath,
204+
linestart=linestart,
205+
linestop=linestop
206+
)
207+
print(f"Final Link for {fullname}: {final_link}")
208+
return final_link
209+
210+
return linkcode_resolve

0 commit comments

Comments
 (0)