1- import os
2- import fnmatch
31from collections import OrderedDict , namedtuple
2+ import fnmatch
3+ import os
4+ import pathlib
45import re
56
6- import anyascii
7- from docutils .parsers .rst import convert_directive_function
87from jinja2 import Environment , FileSystemLoader , TemplateNotFound
98import sphinx
109import sphinx .util
1312from sphinx .util .osutil import ensuredir
1413import sphinx .util .logging
1514
16- from ..settings import API_ROOT , TEMPLATE_DIR
15+ from ..settings import TEMPLATE_DIR
1716
1817LOGGER = sphinx .util .logging .getLogger (__name__ )
1918_OWN_PAGE_LEVELS = [
2423 "function" ,
2524 "method" ,
2625 "property" ,
27- "attribute" ,
2826 "data" ,
27+ "attribute" ,
2928]
3029
3130Path = namedtuple ("Path" , ["absolute" , "relative" ])
@@ -38,14 +37,10 @@ class PythonMapperBase:
3837 and map that onto this standard Python object.
3938 Subclasses may also include language-specific attributes on this object.
4039
41- Arguments:
42-
4340 Args:
4441 obj: JSON object representing this object
4542 jinja_env: A template environment for rendering this object
4643
47- Required attributes:
48-
4944 Attributes:
5045 id (str): A globally unique identifier for this object.
5146 Generally a fully qualified name, including namespace.
@@ -55,25 +50,21 @@ class PythonMapperBase:
5550 children (list): Children of this object
5651 parameters (list): Parameters to this object
5752 methods (list): Methods on this object
58-
59- Optional attributes:
60-
6153 """
6254
6355 language = "base"
6456 type = "base"
65- # Create a page in the output for this object.
66- top_level_object = False
6757 _RENDER_LOG_LEVEL = "VERBOSE"
6858
69- def __init__ (self , obj , jinja_env , app , options = None ):
59+ def __init__ (self , obj , jinja_env , app , url_root , options = None ):
7060 self .app = app
7161 self .obj = obj
7262 self .options = options
7363 self .jinja_env = jinja_env
74- self .url_root = os . path . join ( "/" , API_ROOT )
64+ self .url_root = url_root
7565
7666 self .name = None
67+ self .qual_name = None
7768 self .id = None
7869
7970 def __getstate__ (self ):
@@ -103,7 +94,7 @@ def rendered(self):
10394 def get_context_data (self ):
10495 own_page_level = self .app .config .autoapi_own_page_level
10596 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 ])
10798
10899 return {
109100 "autoapi_options" : self .app .config .autoapi_options ,
@@ -127,28 +118,19 @@ def short_name(self):
127118 """Shorten name property"""
128119 return self .name .split ("." )[- 1 ]
129120
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 )
133126
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"
135132
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
152134
153135 @property
154136 def include_path (self ):
@@ -157,9 +139,7 @@ def include_path(self):
157139 This is used in ``toctree`` directives, as Sphinx always expects Unix
158140 path separators
159141 """
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 ())
163143
164144 @property
165145 def display (self ):
@@ -185,7 +165,7 @@ class SphinxMapperBase:
185165 app: Sphinx application instance
186166 """
187167
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 ):
189169 self .app = app
190170
191171 template_paths = [TEMPLATE_DIR ]
@@ -209,8 +189,9 @@ def _wrapped_prepare(value):
209189
210190 own_page_level = self .app .config .autoapi_own_page_level
211191 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 ])
213193
194+ self .dir_root = dir_root
214195 self .url_root = url_root
215196
216197 # Mapping of {filepath -> raw data}
@@ -298,14 +279,17 @@ def add_object(self, obj):
298279 Args:
299280 obj: Instance of a AutoAPI object
300281 """
301- if obj .type in self .own_page_types :
282+ display = obj .display
283+ if display and obj .type in self .own_page_types :
302284 self .objects_to_render [obj .id ] = obj
303285
304286 self .all_objects [obj .id ] = obj
305287 child_stack = list (obj .children )
306288 while child_stack :
307289 child = child_stack .pop ()
308290 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
309293 child_stack .extend (getattr (child , "children" , ()))
310294
311295 def map (self , options = None ):
@@ -327,81 +311,32 @@ def create_class(self, data, options=None, **kwargs):
327311 """
328312 raise NotImplementedError
329313
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 ):
375315 for _ , obj in status_iterator (
376316 self .objects_to_render .items (),
377317 colorize ("bold" , "[AutoAPI] " ) + "Rendering Data... " ,
378318 length = len (self .objects_to_render ),
379319 verbosity = 1 ,
380320 stringify_func = (lambda x : x [0 ]),
381321 ):
382- if not obj .display :
383- continue
384-
385322 rst = obj .render (is_own_page = True )
386323 if not rst :
387324 continue
388325
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 } "
392330 with open (path , "wb+" ) as detail_file :
393331 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 )
397332
398333 if self .app .config .autoapi_add_toctree_entry :
399- self ._output_top_rst (root )
334+ self ._output_top_rst ()
400335
401- def _output_top_rst (self , root ):
336+ def _output_top_rst (self ):
402337 # 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 ]
405340 with open (top_level_index , "wb" ) as top_level_file :
406341 content = self .jinja_env .get_template ("index.rst" )
407342 top_level_file .write (content .render (pages = pages ).encode ("utf-8" ))
0 commit comments