Skip to content

Commit

Permalink
WIP tests for single page rendering
Browse files Browse the repository at this point in the history
  • Loading branch information
AWhetter committed Jan 16, 2024
1 parent d7b2c08 commit a1050d7
Show file tree
Hide file tree
Showing 25 changed files with 697 additions and 190 deletions.
2 changes: 1 addition & 1 deletion autoapi/extension.py
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,7 @@ def setup(app):
app.add_config_value("autoapi_python_class_content", "class", "html")
app.add_config_value("autoapi_generate_api_docs", True, "html")
app.add_config_value("autoapi_prepare_jinja_env", None, "html")
app.add_config_value("autoapi_single_page_level", "module", "html")
app.add_config_value("autoapi_own_page_level", "module", "html")
app.add_autodocumenter(documenters.AutoapiFunctionDocumenter)
app.add_autodocumenter(documenters.AutoapiPropertyDocumenter)
app.add_autodocumenter(documenters.AutoapiDecoratorDocumenter)
Expand Down
79 changes: 29 additions & 50 deletions autoapi/mappers/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,20 @@
from sphinx.util.osutil import ensuredir
import sphinx.util.logging

from ..settings import API_ROOT, TEMPLATE_DIR, SINGLE_PAGE_LEVELS
from ..settings import API_ROOT, TEMPLATE_DIR

LOGGER = sphinx.util.logging.getLogger(__name__)
_OWN_PAGE_LEVELS = [
"package",
"module",
"exception",
"class",
"function",
"method",
"property",
"attribute",
"data",
]

Path = namedtuple("Path", ["absolute", "relative"])

Expand Down Expand Up @@ -91,10 +102,15 @@ def rendered(self):
return self.render()

def get_context_data(self):
own_page_level = self.app.config.autoapi_own_page_level
desired_page_level = _OWN_PAGE_LEVELS.index(own_page_level)
own_page_types = set(_OWN_PAGE_LEVELS[:desired_page_level+1])

return {
"autoapi_options": self.app.config.autoapi_options,
"include_summaries": self.app.config.autoapi_include_summaries,
"obj": self,
"own_page_types": own_page_types,
"sphinx_version": sphinx.version_info,
}

Expand Down Expand Up @@ -193,18 +209,20 @@ def _wrapped_prepare(value):
if self.app.config.autoapi_prepare_jinja_env:
self.app.config.autoapi_prepare_jinja_env(self.jinja_env)

own_page_level = self.app.config.autoapi_own_page_level
desired_page_level = _OWN_PAGE_LEVELS.index(own_page_level)
self.own_page_types = set(_OWN_PAGE_LEVELS[:desired_page_level+1])

self.url_root = url_root

# Mapping of {filepath -> raw data}
self.paths = OrderedDict()
# Mapping of {object id -> Python Object}
self.objects = OrderedDict()
self.objects_to_render = OrderedDict()
# Mapping of {object id -> Python Object}
self.all_objects = OrderedDict()
# Mapping of {namespace id -> Python Object}
self.namespaces = OrderedDict()
# Mapping of {namespace id -> Python Object}
self.top_level_objects = OrderedDict()

def load(self, patterns, dirs, ignore=None):
"""Load objects from the filesystem into the ``paths`` dictionary."""
Expand Down Expand Up @@ -282,7 +300,9 @@ def add_object(self, obj):
Args:
obj: Instance of a AutoAPI object
"""
self.objects[obj.id] = obj
if 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:
Expand All @@ -309,75 +329,34 @@ def create_class(self, data, options=None, **kwargs):
"""
raise NotImplementedError

def output_child_rst(self, obj, obj_parent, detail_dir, single_page_level, source_suffix):

if not obj.display:
return

obj_child_page_level = SINGLE_PAGE_LEVELS.index(obj.type)
desired_page_level = SINGLE_PAGE_LEVELS.index(single_page_level)
needs_single_page = obj_child_page_level <= desired_page_level
if not needs_single_page:
return

obj_child_rst = obj.render(
needs_single_page=needs_single_page,
)
if not obj_child_rst:
return

ensuredir(os.path.join(detail_dir, obj.short_name))
path = os.path.join(
detail_dir, obj.short_name, f"index{source_suffix}"
)

with open(path, "wb+") as obj_child_detail_file:
obj_child_detail_file.write(obj_child_rst.encode("utf-8"))

for obj_child in obj.children:
child_detail_dir = os.path.join(detail_dir, obj.name)
self.output_child_rst(obj_child, obj, child_detail_dir, single_page_level, source_suffix)


def output_rst(self, root, source_suffix):
# Evaluate which object types should render in a single page
single_page_level = self.app.config.autoapi_single_page_level
desired_page_level = SINGLE_PAGE_LEVELS.index(single_page_level)
single_page_objects = SINGLE_PAGE_LEVELS[:desired_page_level+1]

for _, obj in status_iterator(
self.objects.items(),
self.objects_to_render.items(),
colorize("bold", "[AutoAPI] ") + "Rendering Data... ",
length=len(self.objects),
length=len(self.objects_to_render),
verbosity=1,
stringify_func=(lambda x: x[0]),
):
if not obj.display:
continue

rst = obj.render(single_page_objects=single_page_objects)
rst = obj.render(is_own_page=True)
if not rst:
continue

detail_dir = obj.include_dir(root=root)
ensuredir(detail_dir)
path = os.path.join(detail_dir, f"index{source_suffix}")

with open(path, "wb+") as detail_file:
detail_file.write(rst.encode("utf-8"))

for obj_child in obj.children:
self.output_child_rst(obj_child, obj, detail_dir=detail_dir,
single_page_level=single_page_level,
source_suffix=source_suffix)

if self.app.config.autoapi_add_toctree_entry:
self._output_top_rst(root)

def _output_top_rst(self, root):
# Render Top Index
top_level_index = os.path.join(root, "index.rst")
pages = self.objects.values()
pages = self.objects_to_render.values()
with open(top_level_index, "wb") as top_level_file:
content = self.jinja_env.get_template("index.rst")
top_level_file.write(content.render(pages=pages).encode("utf-8"))
10 changes: 6 additions & 4 deletions autoapi/mappers/python/mapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
PythonAttribute,
PythonData,
PythonException,
TopLevelPythonPythonMapper,
)

LOGGER = sphinx.util.logging.getLogger(__name__)
Expand Down Expand Up @@ -345,19 +346,20 @@ def map(self, options=None):

super().map(options)

parents = {obj.name: obj for obj in self.objects.values()}
for obj in self.objects.values():
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 self.objects_to_render.values():
parent_name = obj.name.rsplit(".", 1)[0]
if parent_name in parents and parent_name != obj.name:
parent = parents[parent_name]
attr = f"sub{obj.type}s"
getattr(parent, attr).append(obj)

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

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

def create_class(self, data, options=None, **kwargs):
Expand Down
1 change: 1 addition & 0 deletions autoapi/mappers/python/objects.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ class PythonPythonMapper(PythonMapperBase):
language = "python"
is_callable = False
member_order = 0
type: str

def __init__(self, obj, class_content="class", **kwargs) -> None:
super().__init__(obj, **kwargs)
Expand Down
12 changes: 0 additions & 12 deletions autoapi/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,3 @@
TEMPLATE_DIR = os.path.join(SITE_ROOT, "templates")

API_ROOT = "autoapi"

SINGLE_PAGE_LEVELS = [
"package",
"module",
"exception",
"class",
"function",
"method",
"property",
"attribute",
"data",
]
7 changes: 4 additions & 3 deletions autoapi/templates/python/class.rst
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{% if obj.display %}
{% if needs_single_page %}
{{ obj.short_name }}
{{ "=" * obj.short_name | length }}
{% if is_own_page %}
{{ obj.name }}
{{ "=" * obj.name | length }}
{% endif %}

.. py:{{ obj.type }}:: {{ obj.short_name }}{% if obj.args %}({{ obj.args }}){% endif %}
Expand Down Expand Up @@ -30,6 +30,7 @@
{% if obj.docstring %}
{{ obj.docstring|indent(3) }}
{% endif %}
{# TODO: Rendering of all children below this line must be conditional on own_page_types #}
{% if "inherited-members" in autoapi_options %}
{% set visible_classes = obj.classes|selectattr("display")|list %}
{% else %}
Expand Down
5 changes: 5 additions & 0 deletions autoapi/templates/python/data.rst
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
{% if obj.display %}
{% if is_own_page %}
{{ obj.name }}
{{ "=" * obj.name | length }}

{% endif %}
.. py:{{ obj.type }}:: {{ obj.name }}
{%- if obj.annotation is not none %}

Expand Down
8 changes: 4 additions & 4 deletions autoapi/templates/python/function.rst
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
{% if obj.display %}
{% if needs_single_page %}
{{ obj.short_name }}
{{ "=" * obj.short_name | length }}
{% endif %}
{% if is_own_page %}
{{ obj.name }}
{{ "=" * obj.name | length }}

{% endif %}
.. py:function:: {{ obj.short_name }}({{ obj.args }}){% if obj.return_annotation is not none %} -> {{ obj.return_annotation }}{% endif %}
{% for (args, return_annotation) in obj.overloads %}
Expand Down
8 changes: 4 additions & 4 deletions autoapi/templates/python/method.rst
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
{%- if obj.display %}
{% if needs_single_page %}
{{ obj.short_name }}
{{ "=" * obj.short_name | length }}
{% endif %}
{% if is_own_page %}
{{ obj.name }}
{{ "=" * obj.name | length }}

{% endif %}
.. py:method:: {{ obj.short_name }}({{ obj.args }}){% if obj.return_annotation is not none %} -> {{ obj.return_annotation }}{% endif %}
{% for (args, return_annotation) in obj.overloads %}
Expand Down
Loading

0 comments on commit a1050d7

Please sign in to comment.