11#!/usr/bin/env python
22import re
33import sys
4+ from collections import defaultdict
5+ from functools import cache
6+ from operator import call
47from pathlib import Path
58
9+ from sphinx .ext .intersphinx import fetch_inventory
10+
611CHANGELOG = Path (__file__ ).parent .parent / 'Changelog'
712
813# Match release lines like "5.2.0 (Monday 11 December 2023)"
914RELEASE_REGEX = re .compile (r"""((?:\d+)\.(?:\d+)\.(?:\d+)) \(\w+ \d{1,2} \w+ \d{4}\)$""" )
1015
1116
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+
1251def main ():
1352 version = sys .argv [1 ]
1453 output = sys .argv [2 ]
@@ -46,7 +85,7 @@ def main():
4685 release_notes = re .sub (r'\n +' , ' ' , release_notes )
4786
4887 # 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 )
5089
5190 # Replace :mod:`package.X` with [package.X](...)
5291 release_notes = re .sub (
@@ -76,6 +115,14 @@ def main():
76115 r'[\3](https://nipy.org/nibabel/reference/\1.html#\1.\2.\3)' ,
77116 release_notes ,
78117 )
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 )
79126
80127 def python_doc (match ):
81128 module = match .group (1 )
@@ -84,10 +131,9 @@ def python_doc(match):
84131
85132 release_notes = re .sub (r':meth:`~([\w.]+)\.(\w+)`' , python_doc , release_notes )
86133
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 )
91137
92138
93139if __name__ == '__main__' :
0 commit comments