1
1
#!/usr/bin/env python
2
2
import re
3
3
import sys
4
+ from collections import defaultdict
5
+ from functools import cache
6
+ from operator import call
4
7
from pathlib import Path
5
8
9
+ from sphinx .ext .intersphinx import fetch_inventory
10
+
6
11
CHANGELOG = Path (__file__ ).parent .parent / 'Changelog'
7
12
8
13
# Match release lines like "5.2.0 (Monday 11 December 2023)"
9
14
RELEASE_REGEX = re .compile (r"""((?:\d+)\.(?:\d+)\.(?:\d+)) \(\w+ \d{1,2} \w+ \d{4}\)$""" )
10
15
11
16
17
+ class MockConfig :
18
+ intersphinx_timeout : int | None = None
19
+ tls_verify = False
20
+ tls_cacerts : str | dict [str , str ] | None = None
21
+ user_agent : str = ''
22
+
23
+
24
+ @call
25
+ class MockApp :
26
+ srcdir = ''
27
+ config = MockConfig ()
28
+
29
+
30
+ fetch_inv = cache (fetch_inventory )
31
+
32
+
33
+ def get_intersphinx (obj ):
34
+ module = obj .split ('.' , 1 )[0 ]
35
+
36
+ registry = defaultdict (lambda : 'https://docs.python.org/3' )
37
+ registry .update (
38
+ numpy = 'https://numpy.org/doc/stable' ,
39
+ )
40
+
41
+ base_url = registry [module ]
42
+
43
+ inventory = fetch_inv (MockApp , '' , f'{ base_url } /objects.inv' )
44
+ # Check py: first, then whatever
45
+ for objclass in sorted (inventory , key = lambda x : not x .startswith ('py:' )):
46
+ if obj in inventory [objclass ]:
47
+ return f'{ base_url } /{ inventory [objclass ][obj ][2 ]} '
48
+ raise ValueError ("Couldn't lookup {obj}" )
49
+
50
+
12
51
def main ():
13
52
version = sys .argv [1 ]
14
53
output = sys .argv [2 ]
@@ -46,7 +85,7 @@ def main():
46
85
release_notes = re .sub (r'\n +' , ' ' , release_notes )
47
86
48
87
# Replace pr/<number> with #<number> for GitHub
49
- release_notes = re .sub (r'\( pr/(\d+)\) ' , r'( #\1) ' , release_notes )
88
+ release_notes = re .sub (r'pr/(\d+)' , r'#\1' , release_notes )
50
89
51
90
# Replace :mod:`package.X` with [package.X](...)
52
91
release_notes = re .sub (
@@ -76,6 +115,14 @@ def main():
76
115
r'[\3](https://nipy.org/nibabel/reference/\1.html#\1.\2.\3)' ,
77
116
release_notes ,
78
117
)
118
+ # Replace :<any>:`<ref>` with intersphinx lookup
119
+ for ref in re .findall (r'(:[^:]*:`~?\w[\w.]+\w`)' , release_notes ):
120
+ objclass , tilde , module , obj = re .match (r':([^:]*):`(~?)([\w.]+)\.(\w+)`' , ref ).groups ()
121
+ url = get_intersphinx (f'{ module } .{ obj } ' )
122
+ mdlink = f'[{ "" if tilde else module } { obj } ]({ url } )'
123
+ release_notes = release_notes .replace (ref , mdlink )
124
+ # Replace RST links with Markdown links
125
+ release_notes = re .sub (r'`([^<`]*) <([^>]*)>`_+' , r'[\1](\2)' , release_notes )
79
126
80
127
def python_doc (match ):
81
128
module = match .group (1 )
@@ -84,10 +131,9 @@ def python_doc(match):
84
131
85
132
release_notes = re .sub (r':meth:`~([\w.]+)\.(\w+)`' , python_doc , release_notes )
86
133
87
- output .write ('## Release notes\n \n ' )
88
- output .write (release_notes )
89
-
90
- output .close ()
134
+ with output :
135
+ output .write ('## Release notes\n \n ' )
136
+ output .write (release_notes )
91
137
92
138
93
139
if __name__ == '__main__' :
0 commit comments