15
15
import sys
16
16
import os
17
17
import shutil
18
+ import csv
18
19
import subprocess
19
20
import argparse
20
21
import webbrowser
22
+ import docutils
23
+ import docutils .parsers .rst
21
24
22
25
23
26
DOC_PATH = os .path .dirname (os .path .abspath (__file__ ))
24
27
SOURCE_PATH = os .path .join (DOC_PATH , 'source' )
25
28
BUILD_PATH = os .path .join (DOC_PATH , 'build' )
26
- BUILD_DIRS = [ 'doctrees' , 'html' , 'latex' , 'plots' , '_static' , '_templates' ]
29
+ REDIRECTS_FILE = os . path . join ( DOC_PATH , 'redirects.csv' )
27
30
28
31
29
32
class DocBuilder :
@@ -50,7 +53,7 @@ def __init__(self, num_jobs=0, include_api=True, single_doc=None,
50
53
if single_doc and single_doc .endswith ('.rst' ):
51
54
self .single_doc_html = os .path .splitext (single_doc )[0 ] + '.html'
52
55
elif single_doc :
53
- self .single_doc_html = 'api/generated /pandas.{}.html' .format (
56
+ self .single_doc_html = 'reference/api /pandas.{}.html' .format (
54
57
single_doc )
55
58
56
59
def _process_single_doc (self , single_doc ):
@@ -60,7 +63,7 @@ def _process_single_doc(self, single_doc):
60
63
61
64
For example, categorial.rst or pandas.DataFrame.head. For the latter,
62
65
return the corresponding file path
63
- (e.g. generated /pandas.DataFrame.head.rst).
66
+ (e.g. reference/api /pandas.DataFrame.head.rst).
64
67
"""
65
68
base_name , extension = os .path .splitext (single_doc )
66
69
if extension in ('.rst' , '.ipynb' ):
@@ -118,8 +121,6 @@ def _sphinx_build(self, kind):
118
121
raise ValueError ('kind must be html or latex, '
119
122
'not {}' .format (kind ))
120
123
121
- self .clean ()
122
-
123
124
cmd = ['sphinx-build' , '-b' , kind ]
124
125
if self .num_jobs :
125
126
cmd += ['-j' , str (self .num_jobs )]
@@ -139,6 +140,77 @@ def _open_browser(self, single_doc_html):
139
140
single_doc_html )
140
141
webbrowser .open (url , new = 2 )
141
142
143
+ def _get_page_title (self , page ):
144
+ """
145
+ Open the rst file `page` and extract its title.
146
+ """
147
+ fname = os .path .join (SOURCE_PATH , '{}.rst' .format (page ))
148
+ option_parser = docutils .frontend .OptionParser (
149
+ components = (docutils .parsers .rst .Parser ,))
150
+ doc = docutils .utils .new_document (
151
+ '<doc>' ,
152
+ option_parser .get_default_values ())
153
+ with open (fname ) as f :
154
+ data = f .read ()
155
+
156
+ parser = docutils .parsers .rst .Parser ()
157
+ # do not generate any warning when parsing the rst
158
+ with open (os .devnull , 'a' ) as f :
159
+ doc .reporter .stream = f
160
+ parser .parse (data , doc )
161
+
162
+ section = next (node for node in doc .children
163
+ if isinstance (node , docutils .nodes .section ))
164
+ title = next (node for node in section .children
165
+ if isinstance (node , docutils .nodes .title ))
166
+
167
+ return title .astext ()
168
+
169
+ def _add_redirects (self ):
170
+ """
171
+ Create in the build directory an html file with a redirect,
172
+ for every row in REDIRECTS_FILE.
173
+ """
174
+ html = '''
175
+ <html>
176
+ <head>
177
+ <meta http-equiv="refresh" content="0;URL={url}"/>
178
+ </head>
179
+ <body>
180
+ <p>
181
+ The page has been moved to <a href="{url}">{title}</a>
182
+ </p>
183
+ </body>
184
+ <html>
185
+ '''
186
+ with open (REDIRECTS_FILE ) as mapping_fd :
187
+ reader = csv .reader (mapping_fd )
188
+ for row in reader :
189
+ if not row or row [0 ].strip ().startswith ('#' ):
190
+ continue
191
+
192
+ path = os .path .join (BUILD_PATH ,
193
+ 'html' ,
194
+ * row [0 ].split ('/' )) + '.html'
195
+
196
+ try :
197
+ title = self ._get_page_title (row [1 ])
198
+ except Exception :
199
+ # the file can be an ipynb and not an rst, or docutils
200
+ # may not be able to read the rst because it has some
201
+ # sphinx specific stuff
202
+ title = 'this page'
203
+
204
+ if os .path .exists (path ):
205
+ raise RuntimeError ((
206
+ 'Redirection would overwrite an existing file: '
207
+ '{}' ).format (path ))
208
+
209
+ with open (path , 'w' ) as moved_page_fd :
210
+ moved_page_fd .write (
211
+ html .format (url = '{}.html' .format (row [1 ]),
212
+ title = title ))
213
+
142
214
def html (self ):
143
215
"""
144
216
Build HTML documentation.
@@ -150,6 +222,8 @@ def html(self):
150
222
151
223
if self .single_doc_html is not None :
152
224
self ._open_browser (self .single_doc_html )
225
+ else :
226
+ self ._add_redirects ()
153
227
return ret_code
154
228
155
229
def latex (self , force = False ):
@@ -184,7 +258,7 @@ def clean():
184
258
Clean documentation generated files.
185
259
"""
186
260
shutil .rmtree (BUILD_PATH , ignore_errors = True )
187
- shutil .rmtree (os .path .join (SOURCE_PATH , 'api ' , 'generated ' ),
261
+ shutil .rmtree (os .path .join (SOURCE_PATH , 'reference ' , 'api ' ),
188
262
ignore_errors = True )
189
263
190
264
def zip_html (self ):
0 commit comments