1
- import os
2
- import fnmatch
3
1
from collections import OrderedDict , namedtuple
2
+ import fnmatch
3
+ import os
4
+ import pathlib
4
5
import re
5
6
6
- import anyascii
7
- from docutils .parsers .rst import convert_directive_function
8
7
from jinja2 import Environment , FileSystemLoader , TemplateNotFound
9
8
import sphinx
10
9
import sphinx .util
13
12
from sphinx .util .osutil import ensuredir
14
13
import sphinx .util .logging
15
14
16
- from ..settings import API_ROOT , TEMPLATE_DIR
15
+ from ..settings import TEMPLATE_DIR
17
16
18
17
LOGGER = sphinx .util .logging .getLogger (__name__ )
19
18
_OWN_PAGE_LEVELS = [
24
23
"function" ,
25
24
"method" ,
26
25
"property" ,
27
- "attribute" ,
28
26
"data" ,
27
+ "attribute" ,
29
28
]
30
29
31
30
Path = namedtuple ("Path" , ["absolute" , "relative" ])
@@ -38,14 +37,10 @@ class PythonMapperBase:
38
37
and map that onto this standard Python object.
39
38
Subclasses may also include language-specific attributes on this object.
40
39
41
- Arguments:
42
-
43
40
Args:
44
41
obj: JSON object representing this object
45
42
jinja_env: A template environment for rendering this object
46
43
47
- Required attributes:
48
-
49
44
Attributes:
50
45
id (str): A globally unique identifier for this object.
51
46
Generally a fully qualified name, including namespace.
@@ -55,25 +50,21 @@ class PythonMapperBase:
55
50
children (list): Children of this object
56
51
parameters (list): Parameters to this object
57
52
methods (list): Methods on this object
58
-
59
- Optional attributes:
60
-
61
53
"""
62
54
63
55
language = "base"
64
56
type = "base"
65
- # Create a page in the output for this object.
66
- top_level_object = False
67
57
_RENDER_LOG_LEVEL = "VERBOSE"
68
58
69
- def __init__ (self , obj , jinja_env , app , options = None ):
59
+ def __init__ (self , obj , jinja_env , app , url_root , options = None ):
70
60
self .app = app
71
61
self .obj = obj
72
62
self .options = options
73
63
self .jinja_env = jinja_env
74
- self .url_root = os . path . join ( "/" , API_ROOT )
64
+ self .url_root = url_root
75
65
76
66
self .name = None
67
+ self .qual_name = None
77
68
self .id = None
78
69
79
70
def __getstate__ (self ):
@@ -103,7 +94,7 @@ def rendered(self):
103
94
def get_context_data (self ):
104
95
own_page_level = self .app .config .autoapi_own_page_level
105
96
desired_page_level = _OWN_PAGE_LEVELS .index (own_page_level )
106
- own_page_types = set (_OWN_PAGE_LEVELS [:desired_page_level + 1 ])
97
+ own_page_types = set (_OWN_PAGE_LEVELS [: desired_page_level + 1 ])
107
98
108
99
return {
109
100
"autoapi_options" : self .app .config .autoapi_options ,
@@ -127,28 +118,19 @@ def short_name(self):
127
118
"""Shorten name property"""
128
119
return self .name .split ("." )[- 1 ]
129
120
130
- @property
131
- def pathname (self ):
132
- """Sluggified path for filenames
121
+ def output_dir (self , root ):
122
+ """The directory to render this object."""
123
+ module = self .id [: - (len ("." + self .qual_name ))]
124
+ parts = [root ] + module .split ("." )
125
+ return pathlib .PurePosixPath (* parts )
133
126
134
- Slugs to a filename using the follow steps
127
+ def output_filename (self ):
128
+ """The name of the file to render into, without a file suffix."""
129
+ filename = self .qual_name
130
+ if filename == "index" :
131
+ filename = ".index"
135
132
136
- * Decode unicode to approximate ascii
137
- * Remove existing hyphens
138
- * Substitute hyphens for non-word characters
139
- * Break up the string as paths
140
- """
141
- slug = self .name
142
- slug = anyascii .anyascii (slug )
143
- slug = slug .replace ("-" , "" )
144
- slug = re .sub (r"[^\w\.]+" , "-" , slug ).strip ("-" )
145
- return os .path .join (* slug .split ("." ))
146
-
147
- def include_dir (self , root ):
148
- """Return directory of file"""
149
- parts = [root ]
150
- parts .extend (self .pathname .split (os .path .sep ))
151
- return "/" .join (parts )
133
+ return filename
152
134
153
135
@property
154
136
def include_path (self ):
@@ -157,9 +139,7 @@ def include_path(self):
157
139
This is used in ``toctree`` directives, as Sphinx always expects Unix
158
140
path separators
159
141
"""
160
- parts = [self .include_dir (root = self .url_root )]
161
- parts .append ("index" )
162
- return "/" .join (parts )
142
+ return str (self .output_dir (self .url_root ) / self .output_filename ())
163
143
164
144
@property
165
145
def display (self ):
@@ -185,7 +165,7 @@ class SphinxMapperBase:
185
165
app: Sphinx application instance
186
166
"""
187
167
188
- def __init__ (self , app , template_dir = None , url_root = None ):
168
+ def __init__ (self , app , template_dir = None , dir_root = None , url_root = None ):
189
169
self .app = app
190
170
191
171
template_paths = [TEMPLATE_DIR ]
@@ -209,8 +189,9 @@ def _wrapped_prepare(value):
209
189
210
190
own_page_level = self .app .config .autoapi_own_page_level
211
191
desired_page_level = _OWN_PAGE_LEVELS .index (own_page_level )
212
- self .own_page_types = set (_OWN_PAGE_LEVELS [:desired_page_level + 1 ])
192
+ self .own_page_types = set (_OWN_PAGE_LEVELS [: desired_page_level + 1 ])
213
193
194
+ self .dir_root = dir_root
214
195
self .url_root = url_root
215
196
216
197
# Mapping of {filepath -> raw data}
@@ -298,14 +279,17 @@ def add_object(self, obj):
298
279
Args:
299
280
obj: Instance of a AutoAPI object
300
281
"""
301
- if obj .type in self .own_page_types :
282
+ display = obj .display
283
+ if display and obj .type in self .own_page_types :
302
284
self .objects_to_render [obj .id ] = obj
303
285
304
286
self .all_objects [obj .id ] = obj
305
287
child_stack = list (obj .children )
306
288
while child_stack :
307
289
child = child_stack .pop ()
308
290
self .all_objects [child .id ] = child
291
+ if display and child .type in self .own_page_types :
292
+ self .objects_to_render [child .id ] = child
309
293
child_stack .extend (getattr (child , "children" , ()))
310
294
311
295
def map (self , options = None ):
@@ -327,81 +311,32 @@ def create_class(self, data, options=None, **kwargs):
327
311
"""
328
312
raise NotImplementedError
329
313
330
- def output_child_rst (self , obj , obj_parent , detail_dir , source_suffix ):
331
-
332
- if not obj .display :
333
- return
334
-
335
- # Skip nested cases like functions in functions or clases in clases
336
- if obj .type == obj_parent .type :
337
- return
338
-
339
- obj_child_page_level = _OWN_PAGE_LEVELS .index (obj .type )
340
- desired_page_level = _OWN_PAGE_LEVELS .index (self .app .config .autoapi_own_page_level )
341
- is_own_page = obj_child_page_level <= desired_page_level
342
- if not is_own_page :
343
- return
344
-
345
- obj_child_rst = obj .render (
346
- is_own_page = is_own_page ,
347
- )
348
- if not obj_child_rst :
349
- return
350
-
351
- function_page_level = _OWN_PAGE_LEVELS .index ("function" )
352
- is_level_beyond_function = function_page_level < desired_page_level
353
- if obj .type in ["exception" , "class" ]:
354
- if not is_level_beyond_function :
355
- outfile = f"{ obj .short_name } { source_suffix } "
356
- path = os .path .join (detail_dir , outfile )
357
- else :
358
- outdir = os .path .join (detail_dir , obj .short_name )
359
- ensuredir (outdir )
360
- path = os .path .join (outdir , f"index{ source_suffix } " )
361
- else :
362
- is_parent_in_detail_dir = detail_dir .endswith (obj_parent .short_name )
363
- outdir = detail_dir if is_parent_in_detail_dir else os .path .join (detail_dir , obj_parent .short_name )
364
- ensuredir (outdir )
365
- path = os .path .join (outdir , f"{ obj .short_name } { source_suffix } " )
366
-
367
- with open (path , "wb+" ) as obj_child_detail_file :
368
- obj_child_detail_file .write (obj_child_rst .encode ("utf-8" ))
369
-
370
- for obj_child in obj .children :
371
- child_detail_dir = os .path .join (detail_dir , obj .name )
372
- self .output_child_rst (obj_child , obj , child_detail_dir , source_suffix )
373
-
374
- def output_rst (self , root , source_suffix ):
314
+ def output_rst (self , source_suffix ):
375
315
for _ , obj in status_iterator (
376
316
self .objects_to_render .items (),
377
317
colorize ("bold" , "[AutoAPI] " ) + "Rendering Data... " ,
378
318
length = len (self .objects_to_render ),
379
319
verbosity = 1 ,
380
320
stringify_func = (lambda x : x [0 ]),
381
321
):
382
- if not obj .display :
383
- continue
384
-
385
322
rst = obj .render (is_own_page = True )
386
323
if not rst :
387
324
continue
388
325
389
- detail_dir = obj .include_dir (root = root )
390
- ensuredir (detail_dir )
391
- path = os .path .join (detail_dir , f"index{ source_suffix } " )
326
+ output_dir = obj .output_dir (self .dir_root )
327
+ ensuredir (output_dir )
328
+ output_path = output_dir / obj .output_filename ()
329
+ path = f"{ output_path } { source_suffix } "
392
330
with open (path , "wb+" ) as detail_file :
393
331
detail_file .write (rst .encode ("utf-8" ))
394
-
395
- for child in obj .children :
396
- self .output_child_rst (child , obj , detail_dir , source_suffix )
397
332
398
333
if self .app .config .autoapi_add_toctree_entry :
399
- self ._output_top_rst (root )
334
+ self ._output_top_rst ()
400
335
401
- def _output_top_rst (self , root ):
336
+ def _output_top_rst (self ):
402
337
# Render Top Index
403
- top_level_index = os .path .join (root , "index.rst" )
404
- pages = self .objects_to_render .values ()
338
+ top_level_index = os .path .join (self . dir_root , "index.rst" )
339
+ pages = [ obj for obj in self .objects_to_render .values () if obj . display ]
405
340
with open (top_level_index , "wb" ) as top_level_file :
406
341
content = self .jinja_env .get_template ("index.rst" )
407
342
top_level_file .write (content .render (pages = pages ).encode ("utf-8" ))
0 commit comments