Using MkApi within Python

MkApi is a standalone library as well as a MkDocs plugin, so that you can use it within Python.

First, import MkApi:

import mkapi

[1] 2020-08-03 20:47:45 (84.0ms) python3 (248ms)

Node object

Define a simple class to show how MkApi works.

class A:
    """Class docstring.

    Note:
        Docstring of `__init__()` is deleted, if there is
        a class-level docstring.
    """

    def __init__(self):
        """Init docstring."""
        self.a: int = 1  #: Integer **attribute**.

    def to_str(self, x: int) -> str:
        """Converts `int` to `str`.

        Args:
            x: Input **value**.
        """
        return str(x)

[2] 2020-08-03 20:47:45 (3.00ms) python3 (251ms)

mkapi.get_node() generates a Node object that has tree structure.

node = mkapi.get_node(A)
type(node)

[3] 2020-08-03 20:47:45 (6.00ms) python3 (257ms)

mkapi.core.node.Node

Some attributes:

node.object.kind, node.object.name

[4] 2020-08-03 20:47:45 (4.00ms) python3 (261ms)

('class', 'A')
docstring = node.docstring
len(docstring.sections)

[5] 2020-08-03 20:47:45 (5.00ms) python3 (266ms)

1
section = docstring.sections[0]
section.name

[6] 2020-08-03 20:47:45 (3.00ms) python3 (269ms)

''
print(section.markdown)

[7] 2020-08-03 20:47:45 (4.00ms) python3 (273ms)

Class docstring.

!!! note "Note"
    Docstring of `__init__()` is deleted, if there is
    a class-level docstring.

The members attribute gives children, for example, bound methods of a class.

len(node.members)

[8] 2020-08-03 20:47:45 (3.00ms) python3 (276ms)

1
child = node.members[0]
type(child)

[9] 2020-08-03 20:47:45 (3.00ms) python3 (279ms)

mkapi.core.node.Node

Elements of Node.members are also Node objects, so this is a tree structure.

child.object.kind, child.object.name

[10] 2020-08-03 20:47:45 (5.00ms) python3 (284ms)

('method', 'to_str')
docstring = child.docstring
len(docstring.sections)

[11] 2020-08-03 20:47:45 (4.00ms) python3 (288ms)

2
section = docstring.sections[0]
section.name, section.markdown

[12] 2020-08-03 20:47:45 (4.00ms) python3 (292ms)

('', 'Converts `int` to `str`.')
section = docstring.sections[1]
section.name, section.markdown

[13] 2020-08-03 20:47:45 (3.00ms) python3 (295ms)

('Parameters', '')

The above Parameters section has an empty markdown, while its items represents an argument list:

item = section.items[0]
print(item)
print(item.type)
print(item.description)

[14] 2020-08-03 20:47:45 (4.00ms) python3 (299ms)

Item('x', 'int')
Type('int')
Inline('Input **value**.')

Node.get_markdown() creates a joint Markdown of this node.

markdown = node.get_markdown()
print(markdown)

[15] 2020-08-03 20:47:45 (4.00ms) python3 (303ms)

A

<!-- mkapi:sep -->

Class docstring.

!!! note "Note"
    Docstring of `__init__()` is deleted, if there is
    a class-level docstring.

<!-- mkapi:sep -->

__main__.A.to_str

<!-- mkapi:sep -->

Converts `int` to `str`.

<!-- mkapi:sep -->

Input **value**.

Where is Note or Parameters section heading, etc.? No problem. The Node.get_markdown() divides docstrings into two parts. One is a plain Markdown that will be converted into HTML by any Markdown converter, for example, MkDocs. The other is the outline structure of docstrings such as sections or arguments that will be processed by MkApi itself.

Converting Markdown

For simplicity, we use Python-Markdown library instead of MkDocs.

from markdown import Markdown

converter = Markdown(extensions=['admonition'])
html = converter.convert(markdown)
print(html)

[16] 2020-08-03 20:47:45 (7.00ms) python3 (310ms)

<p><a href="!A">A</a></p>
<!-- mkapi:sep -->

<p>Class docstring.</p>
<div class="admonition note">
<p class="admonition-title">Note</p>
<p>Docstring of <code>__init__()</code> is deleted, if there is
a class-level docstring.</p>
</div>
<!-- mkapi:sep -->

<p><a href="!__main__.A"><strong>main</strong>.A</a>.<a href="!__main__.A.to_str">to_str</a></p>
<!-- mkapi:sep -->

<p>Converts <code>int</code> to <code>str</code>.</p>
<!-- mkapi:sep -->

<p>Input <strong>value</strong>.</p>

Distributing HTML

Node.set_html() distributes HTML into docstring and members.

node.set_html(html)

[17] 2020-08-03 20:47:45 (3.00ms) python3 (313ms)

Take a look at what happened.

section = node.docstring.sections[0]
section.markdown, section.html

[18] 2020-08-03 20:47:45 (3.00ms) python3 (316ms)

('Class docstring.\n\n!!! note "Note"\n    Docstring of `__init__()` is deleted, if there is\n    a class-level docstring.',
 '<p>Class docstring.</p>\n<div class="admonition note">\n<p class="admonition-title">Note</p>\n<p>Docstring of <code>__init__()</code> is deleted, if there is\na class-level docstring.</p>\n</div>')
child = node.members[0]
section = child.docstring.sections[0]
section.markdown, section.html

[19] 2020-08-03 20:47:45 (5.00ms) python3 (321ms)

('Converts `int` to `str`.',
 '<p>Converts <code>int</code> to <code>str</code>.</p>')
section = child.docstring.sections[1]
item = section.items[0]
item.description.markdown, item.description.html  # A <p> tag is deleted.

[20] 2020-08-03 20:47:45 (4.00ms) python3 (325ms)

('Input **value**.', 'Input <strong>value</strong>.')

Constructing HTML

Finally, construct HTML calling Node.get_html() that internally uses Jinja library.

html = node.get_html()
print(html[:300].strip())

[21] 2020-08-03 20:47:45 (43.0ms) python3 (368ms)

<div class="mkapi-node" id="A">
<div class='mkapi-object-container'>
  <div class="mkapi-object class code top">
    <div class="mkapi-object-kind class top">class</div>
    <div class="mkapi-object-body class top"><code class="mkapi-object-name">A</code><code class="mkapi-object-parenthesis">(</cod

Jupyter allows us to see the rendered HTML.

from IPython.display import HTML

HTML(html)

[22] 2020-08-03 20:47:45 (3.00ms) python3 (371ms)

class
A()

Class docstring.

Note

Docstring of __init__() is deleted, if there is a class-level docstring.

method
to_str(x) → str

Converts int to str.

Parameters
  • x (int) Input value.

Summary

All you need to get the documentation of an object is described by the following function.

from markdown import Markdown

import mkapi

def get_html(obj) -> str:
    # Construct a node tree structure.
    node = mkapi.get_node(obj)

    # Create a joint Markdown from components of the node.
    markdown = node.get_markdown()

    # Convert it into HTML by any external converter.
    converter = Markdown()
    html = converter.convert(markdown)

    # Split and distribute the HTML into original components.
    node.set_html(html)

    # Render the node to create final HTML.
    return node.get_html()

[23] 2020-08-03 20:47:45 (4.00ms) python3 (375ms)