11
11
DEFAULT_SUBTREES_KEY = "subtrees"
12
12
DEFAULT_ITEMS_KEY = "items"
13
13
FILE_FORMAT_KEY = "format"
14
+ ROOT_KEY = "root"
14
15
FILE_KEY = "file"
15
16
GLOB_KEY = "glob"
16
17
URL_KEY = "url"
@@ -97,7 +98,7 @@ def parse_toc_data(data: Dict[str, Any]) -> SiteMap:
97
98
defaults : Dict [str , Any ] = {** file_format .toc_defaults , ** data .get ("defaults" , {})}
98
99
99
100
doc_item , docs_list = _parse_doc_item (
100
- data , defaults , "/" , depth = 0 , file_key = "root" , file_format = file_format
101
+ data , defaults , "/" , depth = 0 , is_root = True , file_format = file_format
101
102
)
102
103
103
104
site_map = SiteMap (
@@ -118,11 +119,12 @@ def _parse_doc_item(
118
119
* ,
119
120
depth : int ,
120
121
file_format : FileFormat ,
121
- file_key : str = FILE_KEY ,
122
- ) -> Tuple [Document , Sequence [Dict [str , Any ]]]:
122
+ is_root : bool = False ,
123
+ ) -> Tuple [Document , Sequence [Tuple [ str , Dict [str , Any ] ]]]:
123
124
"""Parse a single doc item."""
125
+ file_key = ROOT_KEY if is_root else FILE_KEY
124
126
if file_key not in data :
125
- raise MalformedError (f"'{ file_key } ' key not found: '{ path } '" )
127
+ raise MalformedError (f"'{ file_key } ' key not found @ '{ path } '" )
126
128
127
129
subtrees_key = file_format .get_subtrees_key (depth )
128
130
items_key = file_format .get_items_key (depth )
@@ -142,20 +144,23 @@ def _parse_doc_item(
142
144
if not allowed_keys .issuperset (data .keys ()):
143
145
unknown_keys = set (data .keys ()).difference (allowed_keys )
144
146
raise MalformedError (
145
- f"Unknown keys found: { unknown_keys !r} , allowed: { allowed_keys !r} : '{ path } '"
147
+ f"Unknown keys found: { unknown_keys !r} , allowed: { allowed_keys !r} @ '{ path } '"
146
148
)
147
149
150
+ shorthand_used = False
148
151
if items_key in data :
149
152
# this is a shorthand for defining a single subtree
150
153
if subtrees_key in data :
151
154
raise MalformedError (
152
- f"Both '{ subtrees_key } ' and '{ items_key } ' found: '{ path } '"
155
+ f"Both '{ subtrees_key } ' and '{ items_key } ' found @ '{ path } '"
153
156
)
154
157
subtrees_data = [{items_key : data [items_key ], ** data .get ("options" , {})}]
158
+ shorthand_used = True
155
159
elif subtrees_key in data :
156
160
subtrees_data = data [subtrees_key ]
157
161
if not (isinstance (subtrees_data , Sequence ) and subtrees_data ):
158
- raise MalformedError (f"'{ subtrees_key } ' not a non-empty list: '{ path } '" )
162
+ raise MalformedError (f"'{ subtrees_key } ' not a non-empty list @ '{ path } '" )
163
+ path = f"{ path } { subtrees_key } /"
159
164
else :
160
165
subtrees_data = []
161
166
@@ -164,46 +169,46 @@ def _parse_doc_item(
164
169
toctrees = []
165
170
for toc_idx , toc_data in enumerate (subtrees_data ):
166
171
172
+ toc_path = path if shorthand_used else f"{ path } { toc_idx } /"
173
+
167
174
if not (isinstance (toc_data , Mapping ) and items_key in toc_data ):
168
175
raise MalformedError (
169
- f"subtree not a mapping containing '{ items_key } ' key: ' { path } { toc_idx } '"
176
+ f"item not a mapping containing '{ items_key } ' key @ ' { toc_path } '"
170
177
)
171
178
172
179
items_data = toc_data [items_key ]
173
180
174
181
if not (isinstance (items_data , Sequence ) and items_data ):
175
- raise MalformedError (
176
- f"'{ items_key } ' not a non-empty list: '{ path } { toc_idx } '"
177
- )
182
+ raise MalformedError (f"'{ items_key } ' not a non-empty list @ '{ toc_path } '" )
178
183
179
184
# generate items list
180
185
items : List [Union [GlobItem , FileItem , UrlItem ]] = []
181
186
for item_idx , item_data in enumerate (items_data ):
182
187
183
188
if not isinstance (item_data , Mapping ):
184
189
raise MalformedError (
185
- f"' { items_key } ' item not a mapping type: ' { path } { toc_idx } /{ item_idx } '"
190
+ f"item not a mapping type @ ' { toc_path } { items_key } /{ item_idx } '"
186
191
)
187
192
188
193
link_keys = _known_link_keys .intersection (item_data )
189
194
190
195
# validation checks
191
196
if not link_keys :
192
197
raise MalformedError (
193
- f"' { items_key } ' item does not contain one of "
194
- f"{ _known_link_keys !r} : ' { path } { toc_idx } /{ item_idx } '"
198
+ f"item does not contain one of "
199
+ f"{ _known_link_keys !r} @ ' { toc_path } { items_key } /{ item_idx } '"
195
200
)
196
201
if not len (link_keys ) == 1 :
197
202
raise MalformedError (
198
- f"' { items_key } ' item contains incompatible keys "
199
- f"{ link_keys !r} : { path } { toc_idx } /{ item_idx } "
203
+ f"item contains incompatible keys "
204
+ f"{ link_keys !r} @ ' { toc_path } { items_key } /{ item_idx } ' "
200
205
)
201
206
for item_key in (GLOB_KEY , URL_KEY ):
202
207
for other_key in (subtrees_key , items_key ):
203
208
if link_keys == {item_key } and other_key in item_data :
204
209
raise MalformedError (
205
- f"' { items_key } ' item contains incompatible keys "
206
- f"'{ item_key } ' and '{ other_key } ': { path } { toc_idx } /{ item_idx } "
210
+ f"item contains incompatible keys "
211
+ f"'{ item_key } ' and '{ other_key } ' @ ' { toc_path } { items_key } /{ item_idx } ' "
207
212
)
208
213
209
214
if link_keys == {FILE_KEY }:
@@ -222,7 +227,7 @@ def _parse_doc_item(
222
227
try :
223
228
toc_item = TocTree (items = items , ** keywords )
224
229
except TypeError as exc :
225
- raise MalformedError (f"toctree validation: { path } { toc_idx } " ) from exc
230
+ raise MalformedError (f"toctree validation @ ' { toc_path } ' " ) from exc
226
231
toctrees .append (toc_item )
227
232
228
233
try :
@@ -232,21 +237,27 @@ def _parse_doc_item(
232
237
except TypeError as exc :
233
238
raise MalformedError (f"doc validation: { path } " ) from exc
234
239
235
- docs_data = [
236
- item_data
237
- for toc_data in subtrees_data
238
- for item_data in toc_data [items_key ]
240
+ # list of docs that need to be parsed recursively (and path)
241
+ docs_to_be_parsed_list = [
242
+ (
243
+ f"{ path } /{ items_key } /{ ii } /"
244
+ if shorthand_used
245
+ else f"{ path } { ti } /{ items_key } /{ ii } /" ,
246
+ item_data ,
247
+ )
248
+ for ti , toc_data in enumerate (subtrees_data )
249
+ for ii , item_data in enumerate (toc_data [items_key ])
239
250
if FILE_KEY in item_data
240
251
]
241
252
242
253
return (
243
254
doc_item ,
244
- docs_data ,
255
+ docs_to_be_parsed_list ,
245
256
)
246
257
247
258
248
259
def _parse_docs_list (
249
- docs_list : Sequence [Dict [str , Any ]],
260
+ docs_list : Sequence [Tuple [ str , Dict [str , Any ] ]],
250
261
site_map : SiteMap ,
251
262
defaults : Dict [str , Any ],
252
263
path : str ,
@@ -255,11 +266,10 @@ def _parse_docs_list(
255
266
file_format : FileFormat ,
256
267
):
257
268
"""Parse a list of docs."""
258
- for doc_data in docs_list :
259
- docname = doc_data ["file" ]
269
+ for child_path , doc_data in docs_list :
270
+ docname = doc_data [FILE_KEY ]
260
271
if docname in site_map :
261
- raise MalformedError (f"document file used multiple times: { docname } " )
262
- child_path = f"{ path } { docname } /"
272
+ raise MalformedError (f"document file used multiple times: '{ docname } '" )
263
273
child_item , child_docs_list = _parse_doc_item (
264
274
doc_data , defaults , child_path , depth = depth , file_format = file_format
265
275
)
@@ -280,13 +290,13 @@ def create_toc_dict(site_map: SiteMap, *, skip_defaults: bool = True) -> Dict[st
280
290
try :
281
291
file_format = FILE_FORMATS [site_map .file_format or "default" ]
282
292
except KeyError :
283
- raise KeyError (f"File format not recognised: '{ site_map .file_format } '" )
293
+ raise KeyError (f"File format not recognised @ '{ site_map .file_format } '" )
284
294
data = _docitem_to_dict (
285
295
site_map .root ,
286
296
site_map ,
287
297
depth = 0 ,
288
298
skip_defaults = skip_defaults ,
289
- file_key = "root" ,
299
+ is_root = True ,
290
300
file_format = file_format ,
291
301
)
292
302
if site_map .meta :
@@ -304,14 +314,15 @@ def _docitem_to_dict(
304
314
depth : int ,
305
315
file_format : FileFormat ,
306
316
skip_defaults : bool = True ,
307
- file_key : str = FILE_KEY ,
317
+ is_root : bool = False ,
308
318
parsed_docnames : Optional [Set [str ]] = None ,
309
319
) -> Dict [str , Any ]:
310
320
"""
311
321
312
322
:param skip_defaults: do not add key/values for values that are already the default
313
323
314
324
"""
325
+ file_key = ROOT_KEY if is_root else FILE_KEY
315
326
subtrees_key = file_format .get_subtrees_key (depth )
316
327
items_key = file_format .get_items_key (depth )
317
328
0 commit comments