99to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
1010copies of the Software, and to permit persons to whom the Software is
1111furnished to do so, subject to the following conditions:
12-
1312The above copyright notice and this permission notice shall be included in
1413all copies or substantial portions of the Software.
15-
1614THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1715IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1816FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
2119OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
2220THE SOFTWARE.
2321"""
22+ import re
2423
2524from docutils import nodes , utils
2625from sphinx .util .nodes import split_explicit_title
2726
28- __version__ = '0 .2.0'
29- __author__ = ' Steven Loria'
30- __license__ = ' MIT'
27+ __version__ = "1 .2.0"
28+ __author__ = " Steven Loria"
29+ __license__ = " MIT"
3130
3231
33- def user_role (name , rawtext , text , lineno ,
34- inliner , options = None , content = None ):
32+ def user_role (name , rawtext , text , lineno , inliner , options = None , content = None ):
3533 """Sphinx role for linking to a user profile. Defaults to linking to
36- GitHub profiles, but the profile URIS can be configured via the
34+ Github profiles, but the profile URIS can be configured via the
3735 ``issues_user_uri`` config value.
38-
39- Example: ::
40-
36+ Examples: ::
4137 :user:`sloria`
38+ Anchor text also works: ::
39+ :user:`Steven Loria <sloria>`
4240 """
4341 options = options or {}
4442 content = content or []
@@ -50,64 +48,171 @@ def user_role(name, rawtext, text, lineno,
5048 if config .issues_user_uri :
5149 ref = config .issues_user_uri .format (user = target )
5250 else :
53- ref = ' https://github.com/{0}' .format (target )
51+ ref = " https://github.com/{0}" .format (target )
5452 if has_explicit_title :
5553 text = title
5654 else :
57- text = ' @{0}' .format (target )
55+ text = " @{0}" .format (target )
5856
5957 link = nodes .reference (text = text , refuri = ref , ** options )
6058 return [link ], []
6159
6260
63- def _make_issue_node (issue_no , config , options = None ):
61+ def cve_role (name , rawtext , text , lineno , inliner , options = None , content = None ):
62+ """Sphinx role for linking to a CVE on https://cve.mitre.org.
63+ Examples: ::
64+ :cve:`CVE-2018-17175`
65+ """
6466 options = options or {}
65- if issue_no not in ('-' , '0' ):
66- if config .issues_uri :
67- ref = config .issues_uri .format (issue = issue_no )
68- elif config .issues_github_path :
69- ref = 'https://github.com/{0}/issues/{1}' .format (
70- config .issues_github_path , issue_no
67+ content = content or []
68+ has_explicit_title , title , target = split_explicit_title (text )
69+
70+ target = utils .unescape (target ).strip ()
71+ title = utils .unescape (title ).strip ()
72+ ref = "https://cve.mitre.org/cgi-bin/cvename.cgi?name={0}" .format (target )
73+ text = title if has_explicit_title else target
74+ link = nodes .reference (text = text , refuri = ref , ** options )
75+ return [link ], []
76+
77+
78+ class IssueRole (object ):
79+
80+ EXTERNAL_REPO_REGEX = re .compile (r"^(\w+)/(.+)([#@])([\w]+)$" )
81+
82+ def __init__ (
83+ self , uri_config_option , format_kwarg , github_uri_template , format_text = None
84+ ):
85+ self .uri_config_option = uri_config_option
86+ self .format_kwarg = format_kwarg
87+ self .github_uri_template = github_uri_template
88+ self .format_text = format_text or self .default_format_text
89+
90+ @staticmethod
91+ def default_format_text (issue_no ):
92+ return "#{0}" .format (issue_no )
93+
94+ def make_node (self , name , issue_no , config , options = None ):
95+ name_map = {"pr" : "pull" , "issue" : "issues" , "commit" : "commit" }
96+ options = options or {}
97+ repo_match = self .EXTERNAL_REPO_REGEX .match (issue_no )
98+ if repo_match : # External repo
99+ username , repo , symbol , issue = repo_match .groups ()
100+ if name not in name_map :
101+ raise ValueError (
102+ "External repo linking not supported for :{}:" .format (name )
103+ )
104+ path = name_map .get (name )
105+ ref = "https://github.com/{issues_github_path}/{path}/{n}" .format (
106+ issues_github_path = "{}/{}" .format (username , repo ), path = path , n = issue
71107 )
72- issue_text = '#{0}' .format (issue_no )
73- link = nodes .reference (text = issue_text , refuri = ref , ** options )
74- else :
75- link = None
76- return link
108+ formatted_issue = self .format_text (issue ).lstrip ("#" )
109+ text = "{username}/{repo}{symbol}{formatted_issue}" .format (** locals ())
110+ link = nodes .reference (text = text , refuri = ref , ** options )
111+ return link
112+
113+ if issue_no not in ("-" , "0" ):
114+ uri_template = getattr (config , self .uri_config_option , None )
115+ if uri_template :
116+ ref = uri_template .format (** {self .format_kwarg : issue_no })
117+ elif config .issues_github_path :
118+ ref = self .github_uri_template .format (
119+ issues_github_path = config .issues_github_path , n = issue_no
120+ )
121+ else :
122+ raise ValueError (
123+ "Neither {} nor issues_github_path "
124+ "is set" .format (self .uri_config_option )
125+ )
126+ issue_text = self .format_text (issue_no )
127+ link = nodes .reference (text = issue_text , refuri = ref , ** options )
128+ else :
129+ link = None
130+ return link
131+
132+ def __call__ (
133+ self , name , rawtext , text , lineno , inliner , options = None , content = None
134+ ):
135+ options = options or {}
136+ content = content or []
137+ issue_nos = [each .strip () for each in utils .unescape (text ).split ("," )]
138+ config = inliner .document .settings .env .app .config
139+ ret = []
140+ for i , issue_no in enumerate (issue_nos ):
141+ node = self .make_node (name , issue_no , config , options = options )
142+ ret .append (node )
143+ if i != len (issue_nos ) - 1 :
144+ sep = nodes .raw (text = ", " , format = "html" )
145+ ret .append (sep )
146+ return ret , []
147+
148+
149+ """Sphinx role for linking to an issue. Must have
150+ `issues_uri` or `issues_github_path` configured in ``conf.py``.
151+ Examples: ::
152+ :issue:`123`
153+ :issue:`42,45`
154+ :issue:`sloria/konch#123`
155+ """
156+ issue_role = IssueRole (
157+ uri_config_option = "issues_uri" ,
158+ format_kwarg = "issue" ,
159+ github_uri_template = "https://github.com/{issues_github_path}/issues/{n}" ,
160+ )
161+
162+ """Sphinx role for linking to a pull request. Must have
163+ `issues_pr_uri` or `issues_github_path` configured in ``conf.py``.
164+ Examples: ::
165+ :pr:`123`
166+ :pr:`42,45`
167+ :pr:`sloria/konch#43`
168+ """
169+ pr_role = IssueRole (
170+ uri_config_option = "issues_pr_uri" ,
171+ format_kwarg = "pr" ,
172+ github_uri_template = "https://github.com/{issues_github_path}/pull/{n}" ,
173+ )
77174
78175
79- def issue_role (name , rawtext , text , lineno ,
80- inliner , options = None , content = None ):
81- """Sphinx role for linking to an issue. Must have
82- `issues_uri` or `issues_github_path` configured in ``conf.py``.
176+ def format_commit_text (sha ):
177+ return sha [:7 ]
83178
84- Examples: ::
85179
86- :issue:`123`
87- :issue:`42,45`
88- """
89- options = options or {}
90- content = content or []
91- issue_nos = [each .strip () for each in utils .unescape (text ).split (',' )]
92- config = inliner .document .settings .env .app .config
93- ret = []
94- for i , issue_no in enumerate (issue_nos ):
95- node = _make_issue_node (issue_no , config , options = options )
96- ret .append (node )
97- if i != len (issue_nos ) - 1 :
98- sep = nodes .raw (text = ', ' , format = 'html' )
99- ret .append (sep )
100- return ret , []
180+ """Sphinx role for linking to a commit. Must have
181+ `issues_pr_uri` or `issues_github_path` configured in ``conf.py``.
182+ Examples: ::
183+ :commit:`123abc456def`
184+ :commit:`sloria/konch@123abc456def`
185+ """
186+ commit_role = IssueRole (
187+ uri_config_option = "issues_commit_uri" ,
188+ format_kwarg = "commit" ,
189+ github_uri_template = "https://github.com/{issues_github_path}/commit/{n}" ,
190+ format_text = format_commit_text ,
191+ )
101192
102193
103194def setup (app ):
104195 # Format template for issues URI
105196 # e.g. 'https://github.com/sloria/marshmallow/issues/{issue}
106- app .add_config_value ('issues_uri' , default = None , rebuild = 'html' )
107- # Shortcut for GitHub, e.g. 'sloria/marshmallow'
108- app .add_config_value ('issues_github_path' , default = None , rebuild = 'html' )
197+ app .add_config_value ("issues_uri" , default = None , rebuild = "html" )
198+ # Format template for PR URI
199+ # e.g. 'https://github.com/sloria/marshmallow/pull/{issue}
200+ app .add_config_value ("issues_pr_uri" , default = None , rebuild = "html" )
201+ # Format template for commit URI
202+ # e.g. 'https://github.com/sloria/marshmallow/commits/{commit}
203+ app .add_config_value ("issues_commit_uri" , default = None , rebuild = "html" )
204+ # Shortcut for Github, e.g. 'sloria/marshmallow'
205+ app .add_config_value ("issues_github_path" , default = None , rebuild = "html" )
109206 # Format template for user profile URI
110207 # e.g. 'https://github.com/{user}'
111- app .add_config_value ('issues_user_uri' , default = None , rebuild = 'html' )
112- app .add_role ('issue' , issue_role )
113- app .add_role ('user' , user_role )
208+ app .add_config_value ("issues_user_uri" , default = None , rebuild = "html" )
209+ app .add_role ("issue" , issue_role )
210+ app .add_role ("pr" , pr_role )
211+ app .add_role ("user" , user_role )
212+ app .add_role ("commit" , commit_role )
213+ app .add_role ("cve" , cve_role )
214+ return {
215+ "version" : __version__ ,
216+ "parallel_read_safe" : True ,
217+ "parallel_write_safe" : True ,
218+ }
0 commit comments