Skip to content

Commit eecb6e8

Browse files
committed
Fix private subpackages causing orphan pages
Fixes #446
1 parent d45f2ed commit eecb6e8

File tree

7 files changed

+148
-36
lines changed

7 files changed

+148
-36
lines changed

autoapi/_mapper.py

Lines changed: 38 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import collections
22
import copy
33
import fnmatch
4+
import itertools
45
import operator
56
import os
67
import re
@@ -27,7 +28,6 @@
2728
PythonAttribute,
2829
PythonData,
2930
PythonException,
30-
TopLevelPythonPythonMapper,
3131
)
3232
from .settings import OWN_PAGE_LEVELS, TEMPLATE_DIR
3333

@@ -333,25 +333,6 @@ def find_files(patterns, dirs, ignore):
333333
yield filename
334334
seen.add(norm_name)
335335

336-
def add_object(self, obj):
337-
"""Add object to local and app environment storage
338-
339-
Args:
340-
obj: Instance of a AutoAPI object
341-
"""
342-
display = obj.display
343-
if display and obj.type in self.own_page_types:
344-
self.objects_to_render[obj.id] = obj
345-
346-
self.all_objects[obj.id] = obj
347-
child_stack = list(obj.children)
348-
while child_stack:
349-
child = child_stack.pop()
350-
self.all_objects[child.id] = child
351-
if display and child.type in self.own_page_types:
352-
self.objects_to_render[child.id] = child
353-
child_stack.extend(getattr(child, "children", ()))
354-
355336
def output_rst(self, source_suffix):
356337
for _, obj in status_iterator(
357338
self.objects_to_render.items(),
@@ -499,27 +480,50 @@ def map(self, options=None):
499480
stringify_func=(lambda x: x[0]),
500481
):
501482
for obj in self.create_class(data, options=options):
502-
self.add_object(obj)
503-
504-
top_level_objects = {
505-
obj.id: obj
506-
for obj in self.all_objects.values()
507-
if isinstance(obj, TopLevelPythonPythonMapper)
508-
}
509-
parents = {obj.name: obj for obj in top_level_objects.values()}
510-
for obj in top_level_objects.values():
483+
self.all_objects[obj.id] = obj
484+
485+
self._create_module_hierarchy()
486+
self._render_selection()
487+
488+
self.app.env.autoapi_objects = self.objects_to_render
489+
self.app.env.autoapi_all_objects = self.all_objects
490+
491+
def _create_module_hierarchy(self) -> None:
492+
"""Populate the sub{module,package}s attributes of all top level objects."""
493+
for obj in self.all_objects.values():
511494
parent_name = obj.name.rsplit(".", 1)[0]
512-
if parent_name in parents and parent_name != obj.name:
513-
parent = parents[parent_name]
495+
if parent_name in self.all_objects and parent_name != obj.name:
496+
parent = self.all_objects[parent_name]
514497
attr = f"sub{obj.type}s"
515498
getattr(parent, attr).append(obj)
516499

517-
for obj in top_level_objects.values():
500+
for obj in self.all_objects.values():
518501
obj.submodules.sort()
519502
obj.subpackages.sort()
520503

521-
self.app.env.autoapi_objects = self.objects_to_render
522-
self.app.env.autoapi_all_objects = self.all_objects
504+
def _render_selection(self):
505+
"""Propagate display values to children."""
506+
for obj in sorted(self.all_objects.values(), key=lambda obj: len(obj.id)):
507+
if obj.display:
508+
assert obj.type in self.own_page_types
509+
self.objects_to_render[obj.id] = obj
510+
else:
511+
for module in itertools.chain(obj.subpackages, obj.submodules):
512+
module.obj["hide"] = True
513+
514+
def _inner(parent):
515+
for child in parent.children:
516+
self.all_objects[child.id] = child
517+
if not parent.display:
518+
child.obj["hide"] = True
519+
520+
if child.display and child.type in self.own_page_types:
521+
self.objects_to_render[child.id] = child
522+
523+
_inner(child)
524+
525+
for obj in list(self.all_objects.values()):
526+
_inner(obj)
523527

524528
def create_class(self, data, options=None):
525529
"""Create a class from the passed in data

autoapi/_objects.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,6 @@ def __init__(
7676
"""Whether this object was imported from another module."""
7777
self.inherited: bool = obj.get("inherited", False)
7878
"""Whether this was inherited from an ancestor of the parent class."""
79-
self._hide = obj.get("hide", False)
8079

8180
# For later
8281
self._class_content = class_content
@@ -234,7 +233,7 @@ def _should_skip(self) -> bool:
234233
)
235234

236235
return (
237-
self._hide
236+
self.obj.get("hide", False)
238237
or skip_undoc_member
239238
or skip_private_member
240239
or skip_special_member

docs/changes/446.bugfix

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix private subpackages causing orphan pages
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
"""This is a docstring."""
2+
3+
from .submodule import function as aliased_function
4+
from .submodule import not_in_all_function
5+
6+
__all__ = (
7+
"aliased_function",
8+
"function",
9+
)
10+
11+
12+
def function(foo, bar):
13+
"""A module level function"""
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
"""Example module
2+
3+
This is a description
4+
"""
5+
6+
DATA = 42
7+
8+
9+
def function(foo, bar):
10+
"""A module level function"""
11+
12+
13+
def _private_function():
14+
"""A function that shouldn't get rendered."""
15+
16+
17+
def not_in_all_function():
18+
"""A function that doesn't exist in __all__ when imported."""
19+
20+
21+
class Class(object):
22+
"""This is a class."""
23+
24+
class_var = 42
25+
"""Class var docstring"""
26+
27+
class NestedClass(object):
28+
"""A nested class just to test things out"""
29+
30+
@classmethod
31+
def a_classmethod():
32+
"""A class method"""
33+
return True
34+
35+
def method_okay(self, foo=None, bar=None):
36+
"""This method should parse okay"""
37+
return True
38+
39+
40+
class MyException(Exception):
41+
"""This is an exception."""
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
"""This is a docstring."""
2+
3+
from .submodule import function as aliased_function
4+
from .submodule import not_in_all_function
5+
6+
__all__ = (
7+
"aliased_function",
8+
"function",
9+
)
10+
11+
12+
def function(foo, bar):
13+
"""A module level function"""
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
"""Example module
2+
3+
This is a description
4+
"""
5+
6+
DATA = 42
7+
8+
9+
def function(foo, bar):
10+
"""A module level function"""
11+
12+
13+
def _private_function():
14+
"""A function that shouldn't get rendered."""
15+
16+
17+
def not_in_all_function():
18+
"""A function that doesn't exist in __all__ when imported."""
19+
20+
21+
class Class(object):
22+
"""This is a class."""
23+
24+
class_var = 42
25+
"""Class var docstring"""
26+
27+
class NestedClass(object):
28+
"""A nested class just to test things out"""
29+
30+
@classmethod
31+
def a_classmethod():
32+
"""A class method"""
33+
return True
34+
35+
def method_okay(self, foo=None, bar=None):
36+
"""This method should parse okay"""
37+
return True
38+
39+
40+
class MyException(Exception):
41+
"""This is an exception."""

0 commit comments

Comments
 (0)