5
5
import subprocess
6
6
import pkg_resources
7
7
from pathlib import Path
8
- from typing import Dict , Any , Optional
9
8
from sphinx .application import Sphinx
10
9
from sphinx .errors import ExtensionError
10
+ from typing import Dict , Any , Optional , Callable
11
11
12
- __version__ = "0.0.1b8"
12
+
13
+ __version__ = "0.0.1b9"
13
14
__author__ = 'Adam Korn <hello@dailykitten.net>'
14
15
16
+
15
17
from .add_linkcode_class import add_linkcode_node_class
16
18
from .meth_lexer import TDKMethLexer
17
19
from .github_style import TDKStyle
@@ -23,114 +25,56 @@ def setup(app: Sphinx) -> Dict[str, Any]:
23
25
pkg = pkg_resources .require (modname )[0 ]
24
26
pkg_name = pkg .get_metadata ('top_level.txt' ).strip ()
25
27
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 )
28
29
29
30
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 )
31
32
32
33
app .setup_extension ('sphinx_github_style.add_linkcode_class' )
33
34
app .setup_extension ('sphinx_github_style.github_style' )
34
35
app .setup_extension ('sphinx_github_style.meth_lexer' )
35
36
app .setup_extension ('sphinx.ext.linkcode' )
36
37
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 )
38
41
39
42
linkcode_url = get_linkcode_url (app )
40
43
linkcode_func = getattr (app .config , 'linkcode_resolve' , None )
41
44
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
+ )
99
53
100
54
app .config .linkcode_resolve = linkcode_func
101
55
return {'version' : sphinx .__display_version__ , 'parallel_read_safe' : True }
102
56
103
57
104
- def get_static_path (app ):
58
+ def add_static_path (app ) -> None :
59
+ """Add the path for the ``_static`` folder"""
105
60
app .config .html_static_path .append (
106
61
str (Path (__file__ ).parent .joinpath ("_static" ).absolute ())
107
62
)
108
63
109
64
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 :
123
66
"""Get the blob to link to on GitHub
124
67
125
- .. admonition:: Linkcode Blobs
68
+ .. note::
126
69
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}"``
128
72
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 "``
132
76
"""
133
- blob = getattr (app .config , "linkcode_blob" , "master " )
77
+ blob = getattr (app .config , "linkcode_blob" , "head " )
134
78
if blob == "head" :
135
79
return get_head ()
136
80
if blob == 'last_tag' :
@@ -142,7 +86,7 @@ def get_linkcode_revision(app: Sphinx):
142
86
def get_head (errors : bool = False ) -> Optional [str ]:
143
87
"""Gets the most recent commit hash or tag
144
88
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``
146
90
"""
147
91
cmd = "git log -n1 --pretty=%H"
148
92
try :
@@ -159,18 +103,108 @@ def get_head(errors: bool = False) -> Optional[str]:
159
103
return head
160
104
161
105
except subprocess .CalledProcessError as e :
162
- # Raise error
163
106
if errors :
164
- raise RuntimeError from e
107
+ raise e
165
108
else :
166
- return None
109
+ return print ( "Failed to get head" ) # so no head?
167
110
168
111
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
+ """
171
117
try :
172
118
cmd = "git describe --tags --abbrev=0"
173
119
return subprocess .check_output (cmd .split (" " )).strip ().decode ('utf-8' )
174
120
175
121
except subprocess .CalledProcessError as e :
176
122
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