Skip to content

Commit

Permalink
Fix private subpackages causing orphan pages
Browse files Browse the repository at this point in the history
Fixes #446
  • Loading branch information
AWhetter committed May 23, 2024
1 parent d45f2ed commit eecb6e8
Show file tree
Hide file tree
Showing 7 changed files with 148 additions and 36 deletions.
72 changes: 38 additions & 34 deletions autoapi/_mapper.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import collections
import copy
import fnmatch
import itertools
import operator
import os
import re
Expand All @@ -27,7 +28,6 @@
PythonAttribute,
PythonData,
PythonException,
TopLevelPythonPythonMapper,
)
from .settings import OWN_PAGE_LEVELS, TEMPLATE_DIR

Expand Down Expand Up @@ -333,25 +333,6 @@ def find_files(patterns, dirs, ignore):
yield filename
seen.add(norm_name)

def add_object(self, obj):
"""Add object to local and app environment storage
Args:
obj: Instance of a AutoAPI object
"""
display = obj.display
if display and obj.type in self.own_page_types:
self.objects_to_render[obj.id] = obj

self.all_objects[obj.id] = obj
child_stack = list(obj.children)
while child_stack:
child = child_stack.pop()
self.all_objects[child.id] = child
if display and child.type in self.own_page_types:
self.objects_to_render[child.id] = child
child_stack.extend(getattr(child, "children", ()))

def output_rst(self, source_suffix):
for _, obj in status_iterator(
self.objects_to_render.items(),
Expand Down Expand Up @@ -499,27 +480,50 @@ def map(self, options=None):
stringify_func=(lambda x: x[0]),
):
for obj in self.create_class(data, options=options):
self.add_object(obj)

top_level_objects = {
obj.id: obj
for obj in self.all_objects.values()
if isinstance(obj, TopLevelPythonPythonMapper)
}
parents = {obj.name: obj for obj in top_level_objects.values()}
for obj in top_level_objects.values():
self.all_objects[obj.id] = obj

self._create_module_hierarchy()
self._render_selection()

self.app.env.autoapi_objects = self.objects_to_render
self.app.env.autoapi_all_objects = self.all_objects

def _create_module_hierarchy(self) -> None:
"""Populate the sub{module,package}s attributes of all top level objects."""
for obj in self.all_objects.values():
parent_name = obj.name.rsplit(".", 1)[0]
if parent_name in parents and parent_name != obj.name:
parent = parents[parent_name]
if parent_name in self.all_objects and parent_name != obj.name:
parent = self.all_objects[parent_name]
attr = f"sub{obj.type}s"
getattr(parent, attr).append(obj)

for obj in top_level_objects.values():
for obj in self.all_objects.values():
obj.submodules.sort()
obj.subpackages.sort()

self.app.env.autoapi_objects = self.objects_to_render
self.app.env.autoapi_all_objects = self.all_objects
def _render_selection(self):
"""Propagate display values to children."""
for obj in sorted(self.all_objects.values(), key=lambda obj: len(obj.id)):
if obj.display:
assert obj.type in self.own_page_types
self.objects_to_render[obj.id] = obj
else:
for module in itertools.chain(obj.subpackages, obj.submodules):
module.obj["hide"] = True

def _inner(parent):
for child in parent.children:
self.all_objects[child.id] = child
if not parent.display:
child.obj["hide"] = True

if child.display and child.type in self.own_page_types:
self.objects_to_render[child.id] = child

_inner(child)

for obj in list(self.all_objects.values()):
_inner(obj)

def create_class(self, data, options=None):
"""Create a class from the passed in data
Expand Down
3 changes: 1 addition & 2 deletions autoapi/_objects.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,6 @@ def __init__(
"""Whether this object was imported from another module."""
self.inherited: bool = obj.get("inherited", False)
"""Whether this was inherited from an ancestor of the parent class."""
self._hide = obj.get("hide", False)

# For later
self._class_content = class_content
Expand Down Expand Up @@ -234,7 +233,7 @@ def _should_skip(self) -> bool:
)

return (
self._hide
self.obj.get("hide", False)
or skip_undoc_member
or skip_private_member
or skip_special_member
Expand Down
1 change: 1 addition & 0 deletions docs/changes/446.bugfix
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix private subpackages causing orphan pages
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
"""This is a docstring."""

from .submodule import function as aliased_function
from .submodule import not_in_all_function

__all__ = (
"aliased_function",
"function",
)


def function(foo, bar):
"""A module level function"""
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
"""Example module
This is a description
"""

DATA = 42


def function(foo, bar):
"""A module level function"""


def _private_function():
"""A function that shouldn't get rendered."""


def not_in_all_function():
"""A function that doesn't exist in __all__ when imported."""


class Class(object):
"""This is a class."""

class_var = 42
"""Class var docstring"""

class NestedClass(object):
"""A nested class just to test things out"""

@classmethod
def a_classmethod():
"""A class method"""
return True

def method_okay(self, foo=None, bar=None):
"""This method should parse okay"""
return True


class MyException(Exception):
"""This is an exception."""
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
"""This is a docstring."""

from .submodule import function as aliased_function
from .submodule import not_in_all_function

__all__ = (
"aliased_function",
"function",
)


def function(foo, bar):
"""A module level function"""
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
"""Example module
This is a description
"""

DATA = 42


def function(foo, bar):
"""A module level function"""


def _private_function():
"""A function that shouldn't get rendered."""


def not_in_all_function():
"""A function that doesn't exist in __all__ when imported."""


class Class(object):
"""This is a class."""

class_var = 42
"""Class var docstring"""

class NestedClass(object):
"""A nested class just to test things out"""

@classmethod
def a_classmethod():
"""A class method"""
return True

def method_okay(self, foo=None, bar=None):
"""This method should parse okay"""
return True


class MyException(Exception):
"""This is an exception."""

0 comments on commit eecb6e8

Please sign in to comment.