6
6
import os
7
7
import argparse
8
8
import sys
9
+ import shutil
9
10
10
11
parser = argparse .ArgumentParser ()
11
12
parser .add_argument (
18
19
"top-level headings start with a single `#`, sub-headings start with `##`, etc. For example, "
19
20
"3 would start with `###`, sub-headings would be `####`, etc." ,
20
21
)
22
+ parser .add_argument (
23
+ "-m" ,
24
+ "--multi-file" ,
25
+ action = "store_true" ,
26
+ help = "Write to one markdown file per section, rather than one single large file" ,
27
+ )
21
28
parser .add_argument (
22
29
"-o" ,
23
30
"--output" ,
24
31
type = str ,
25
- help = "Specify output file; specifing a - or omitting outputs to stdout" ,
32
+ help = "Specify output file (or directory, when using -m); specifing a - or omitting outputs to "
33
+ "stdout, but is not accepted in multi-file (-m) mode." ,
26
34
)
27
35
parser .add_argument (
28
36
"-W" ,
34
42
args = parser .parse_args ()
35
43
36
44
out_file = False
37
- if args .output and args .output != '-' :
45
+ if args .multi_file and (not args .output or args .output == '-' ):
46
+ print ("-o must specify a directory when using -m" )
47
+ sys .exit (1 )
48
+
49
+ if not args .multi_file and args .output and args .output != '-' :
38
50
if not args .overwrite and os .path .exists (args .output ):
39
51
print (f"{ args .output } already exists; remove it first or use --overwrite/-W to overwrite" )
40
52
sys .exit (1 )
@@ -71,20 +83,23 @@ def read_snippet(markdown, depth=args.markdown_level):
71
83
return None
72
84
73
85
74
- section_list , sections = [], {}
86
+ section_list , section_names , section_snips , sections = [], [], [], {}
75
87
for name , bp in app .blueprints .items ():
76
88
s = []
77
89
section_list .append (s )
90
+ section_names .append (name )
78
91
sections [name ] = s
79
92
snip = read_snippet (f'{ name } .md' )
80
93
if snip :
81
94
s .append (snip )
82
95
else :
83
96
s .append (f"{ h1 } { name .title ()} \n \n " )
84
97
app .logger .warning (f"{ name } .md not found: adding stub '{ name .title ()} ' section" )
98
+ section_snips .append (snip )
85
99
86
100
# Last section is for anything with a not found category:
87
101
section_list .append ([])
102
+ section_names .append ('uncategorized' )
88
103
89
104
90
105
# Sort endpoints within a section by the number of URL parts, first, then alphabetically because we
@@ -114,7 +129,7 @@ def endpoint_sort_key(rule):
114
129
if ep .startswith ('legacy' ):
115
130
# We deliberately omit legacy endpoint documentation
116
131
if doc is not None :
117
- app .logger .warning (f"Legacy endpoint { ep } has docstring but it will be omitted" );
132
+ app .logger .warning (f"Legacy endpoint { ep } has docstring but it will be omitted" )
118
133
continue
119
134
if doc is None :
120
135
app .logger .warning (f"Endpoint { ep } has no docstring!" )
@@ -181,7 +196,9 @@ def endpoint_sort_key(rule):
181
196
argdoc = argdoc .replace ('\n ' , '\n ' )
182
197
183
198
if ':`' in argdoc :
184
- app .logger .warning (f"{ method } { url } ({ arg } ) still contains some rst crap we need to handle" )
199
+ app .logger .warning (
200
+ f"{ method } { url } ({ arg } ) still contains some rst crap we need to handle"
201
+ )
185
202
186
203
s .append (f" — { argdoc } \n \n " )
187
204
else :
@@ -196,30 +213,90 @@ def endpoint_sort_key(rule):
196
213
197
214
more = read_snippet (f'{ ep } .md' , depth = 3 )
198
215
if more :
216
+ s .append ("\n \n " )
199
217
s .append (more )
200
218
201
219
s .append ("\n \n \n " )
202
220
203
221
204
- out = open (args .output , 'w' ) if out_file else sys .stdout
222
+ out = open (args .output , 'w' ) if out_file else sys .stdout if not args . multi_file else None
205
223
206
224
if section_list [- 1 ]:
207
225
# We have some uncategorized entries, so load the .md for it
208
226
other = read_snippet ('uncategorized.md' )
209
- if other :
210
- section_list [- 1 ].insert (0 , other )
211
- else :
227
+ if not other :
212
228
app .logger .warning (
213
229
"Found uncategorized sections, but uncategorized.md not found; inserting stub"
214
230
)
215
- section_list [- 1 ].insert (0 , "# Uncategorized Endpoints\n \n " )
231
+ other = "# Uncategorized Endpoints\n \n "
232
+
233
+ section_list [- 1 ].insert (0 , other )
234
+ section_snips .append (other )
235
+
236
+ else :
237
+ section_list .pop ()
238
+ section_names .pop ()
239
+
240
+ if args .multi_file :
241
+ if not os .path .exists (args .output ):
242
+ os .makedirs (args .output )
243
+ if not args .overwrite and os .path .exists (args .output + '/index.md' ):
244
+ print (f"{ args .output } /index.md already exists; remove it first or use --overwrite/-W" )
245
+ sys .exit (1 )
246
+
247
+ api_readme_f = open (args .output + '/index.md' , 'w' )
248
+
249
+ section_order = range (0 , len (section_list ))
250
+ if args .multi_file :
251
+ # In multi-file mode we take the order for the index file section from the order it is listed in
252
+ # sidebar.md:
253
+ sidebar = read_snippet ('sidebar.md' )
254
+ shutil .copy (snippets + '/sidebar.md' , args .output + '/sidebar.md' )
255
+
256
+ def pos (i ):
257
+ x = sidebar .find (section_names [i ])
258
+ return x if x >= 0 else len (sidebar )
259
+
260
+ section_order = sorted (section_order , key = pos )
261
+
262
+ for i in section_order :
263
+ if args .multi_file :
264
+ filename = args .output + '/' + section_names [i ] + '.md'
265
+ if not args .overwrite and os .path .exists (filename ):
266
+ print (f"{ filename } already exists; remove it first or use --overwrite/-W to overwrite" )
267
+ sys .exit (1 )
268
+ if out is not None :
269
+ out .close ()
270
+ out = open (filename , 'w' )
271
+
272
+ if section_names [i ] + '.md' not in sidebar :
273
+ app .logger .warning (
274
+ f"{ section_names [i ]} .md not found in snippets/sidebar.md: "
275
+ "section will be missing from the sidebar!"
276
+ )
277
+
278
+ snip = section_snips [i ]
279
+ if snip .startswith (f'{ h1 } ' ):
280
+ preamble = snip .find (f'{ h2 } ' )
281
+ if preamble == - 1 :
282
+ preamble = snip
283
+ else :
284
+ preamble = snip [:preamble ]
285
+ print (
286
+ re .sub (fr'^{ h1 } (.*)' , fr'{ h1 } [\1]({ section_names [i ]} .md)' , preamble ),
287
+ file = api_readme_f ,
288
+ )
289
+ else :
290
+ app .logger .warning (
291
+ f"{ section_names [i ]} section didn't start with expected '# Title', "
292
+ f"cannot embed section link in { args .output } /index.md"
293
+ )
216
294
217
- for s in section_list :
218
- for x in s :
295
+ for x in section_list [i ]:
219
296
print (x , end = '' , file = out )
220
297
print ("\n \n " , file = out )
221
298
222
- if out_file :
299
+ if out is not None and out != sys . stdout :
223
300
out .close ()
224
301
225
302
app .logger .info ("API doc created successfully!" )
0 commit comments