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