Browse Source

Fix async iterable not showing up attributetable

Co-authored-by: Danny <[email protected]>
pull/7587/head
James Hilton-Balfe 3 years ago
committed by GitHub
parent
commit
c3d175fbc3
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 121
      docs/extensions/attributetable.py

121
docs/extensions/attributetable.py

@ -1,78 +1,105 @@
from sphinx.util.docutils import SphinxDirective from __future__ import annotations
from sphinx.locale import _
from docutils import nodes
from sphinx import addnodes
from collections import OrderedDict, namedtuple
import importlib import importlib
import inspect import inspect
import os
import re import re
from typing import Dict, List, NamedTuple, Optional, Tuple, Sequence, TYPE_CHECKING
from docutils import nodes
from sphinx import addnodes
from sphinx.application import Sphinx
from sphinx.environment import BuildEnvironment
from sphinx.locale import _
from sphinx.util.docutils import SphinxDirective
from sphinx.util.typing import OptionSpec
if TYPE_CHECKING:
from .builder import DPYHTML5Translator
class attributetable(nodes.General, nodes.Element): class attributetable(nodes.General, nodes.Element):
pass pass
class attributetablecolumn(nodes.General, nodes.Element): class attributetablecolumn(nodes.General, nodes.Element):
pass pass
class attributetabletitle(nodes.TextElement): class attributetabletitle(nodes.TextElement):
pass pass
class attributetableplaceholder(nodes.General, nodes.Element): class attributetableplaceholder(nodes.General, nodes.Element):
pass pass
class attributetablebadge(nodes.TextElement): class attributetablebadge(nodes.TextElement):
pass pass
class attributetable_item(nodes.Part, nodes.Element): class attributetable_item(nodes.Part, nodes.Element):
pass pass
def visit_attributetable_node(self, node):
class_ = node["python-class"] def visit_attributetable_node(self: DPYHTML5Translator, node: attributetable) -> None:
class_ = node['python-class']
self.body.append(f'<div class="py-attribute-table" data-move-to-id="{class_}">') self.body.append(f'<div class="py-attribute-table" data-move-to-id="{class_}">')
def visit_attributetablecolumn_node(self, node):
def visit_attributetablecolumn_node(self: DPYHTML5Translator, node: attributetablecolumn) -> None:
self.body.append(self.starttag(node, 'div', CLASS='py-attribute-table-column')) self.body.append(self.starttag(node, 'div', CLASS='py-attribute-table-column'))
def visit_attributetabletitle_node(self, node):
def visit_attributetabletitle_node(self: DPYHTML5Translator, node: attributetabletitle) -> None:
self.body.append(self.starttag(node, 'span')) self.body.append(self.starttag(node, 'span'))
def visit_attributetablebadge_node(self, node):
def visit_attributetablebadge_node(self: DPYHTML5Translator, node: attributetablebadge) -> None:
attributes = { attributes = {
'class': 'py-attribute-table-badge', 'class': 'py-attribute-table-badge',
'title': node['badge-type'], 'title': node['badge-type'],
} }
self.body.append(self.starttag(node, 'span', **attributes)) self.body.append(self.starttag(node, 'span', **attributes))
def visit_attributetable_item_node(self, node):
def visit_attributetable_item_node(self: DPYHTML5Translator, node: attributetable_item) -> None:
self.body.append(self.starttag(node, 'li', CLASS='py-attribute-table-entry')) self.body.append(self.starttag(node, 'li', CLASS='py-attribute-table-entry'))
def depart_attributetable_node(self, node):
def depart_attributetable_node(self: DPYHTML5Translator, node: attributetable) -> None:
self.body.append('</div>') self.body.append('</div>')
def depart_attributetablecolumn_node(self, node):
def depart_attributetablecolumn_node(self: DPYHTML5Translator, node: attributetablecolumn) -> None:
self.body.append('</div>') self.body.append('</div>')
def depart_attributetabletitle_node(self, node):
def depart_attributetabletitle_node(self: DPYHTML5Translator, node: attributetabletitle) -> None:
self.body.append('</span>') self.body.append('</span>')
def depart_attributetablebadge_node(self, node):
def depart_attributetablebadge_node(self: DPYHTML5Translator, node: attributetablebadge) -> None:
self.body.append('</span>') self.body.append('</span>')
def depart_attributetable_item_node(self, node):
def depart_attributetable_item_node(self: DPYHTML5Translator, node: attributetable_item) -> None:
self.body.append('</li>') self.body.append('</li>')
_name_parser_regex = re.compile(r'(?P<module>[\w.]+\.)?(?P<name>\w+)') _name_parser_regex = re.compile(r'(?P<module>[\w.]+\.)?(?P<name>\w+)')
class PyAttributeTable(SphinxDirective): class PyAttributeTable(SphinxDirective):
has_content = False has_content = False
required_arguments = 1 required_arguments = 1
optional_arguments = 0 optional_arguments = 0
final_argument_whitespace = False final_argument_whitespace = False
option_spec = {} option_spec: OptionSpec = {}
def parse_name(self, content): def parse_name(self, content: str) -> Tuple[str, str]:
path, name = _name_parser_regex.match(content).groups() match = _name_parser_regex.match(content)
if match is None:
raise RuntimeError(f"content {content} somehow doesn't match regex in {self.env.docname}.")
path, name = match.groups()
if path: if path:
modulename = path.rstrip('.') modulename = path.rstrip('.')
else: else:
@ -80,11 +107,11 @@ class PyAttributeTable(SphinxDirective):
if not modulename: if not modulename:
modulename = self.env.ref_context.get('py:module') modulename = self.env.ref_context.get('py:module')
if modulename is None: if modulename is None:
raise RuntimeError('modulename somehow None for %s in %s.' % (content, self.env.docname)) raise RuntimeError(f'modulename somehow None for {content} in {self.env.docname}.')
return modulename, name return modulename, name
def run(self): def run(self) -> List[attributetableplaceholder]:
"""If you're curious on the HTML this is meant to generate: """If you're curious on the HTML this is meant to generate:
<div class="py-attribute-table"> <div class="py-attribute-table">
@ -120,17 +147,21 @@ class PyAttributeTable(SphinxDirective):
node['python-full-name'] = f'{modulename}.{name}' node['python-full-name'] = f'{modulename}.{name}'
return [node] return [node]
def build_lookup_table(env):
def build_lookup_table(env: BuildEnvironment) -> Dict[str, List[str]]:
# Given an environment, load up a lookup table of # Given an environment, load up a lookup table of
# full-class-name: objects # full-class-name: objects
result = {} result = {}
domain = env.domains['py'] domain = env.domains['py']
ignored = { ignored = {
'data', 'exception', 'module', 'class', 'data',
'exception',
'module',
'class',
} }
for (fullname, _, objtype, docname, _, _) in domain.get_objects(): for fullname, _, objtype, docname, _, _ in domain.get_objects():
if objtype in ignored: if objtype in ignored:
continue continue
@ -143,9 +174,13 @@ def build_lookup_table(env):
return result return result
TableElement = namedtuple('TableElement', 'fullname label badge') class TableElement(NamedTuple):
fullname: str
label: str
badge: Optional[attributetablebadge]
def process_attributetable(app, doctree, fromdocname):
def process_attributetable(app: Sphinx, doctree: nodes.Node, fromdocname: str) -> None:
env = app.builder.env env = app.builder.env
lookup = build_lookup_table(env) lookup = build_lookup_table(env)
@ -165,14 +200,17 @@ def process_attributetable(app, doctree, fromdocname):
else: else:
node.replace_self([table]) node.replace_self([table])
def get_class_results(lookup, modulename, name, fullname):
def get_class_results(
lookup: Dict[str, List[str]], modulename: str, name: str, fullname: str
) -> Dict[str, List[TableElement]]:
module = importlib.import_module(modulename) module = importlib.import_module(modulename)
cls = getattr(module, name) cls = getattr(module, name)
groups = OrderedDict([ groups: Dict[str, List[TableElement]] = {
(_('Attributes'), []), _('Attributes'): [],
(_('Methods'), []), _('Methods'): [],
]) }
try: try:
members = lookup[fullname] members = lookup[fullname]
@ -205,9 +243,13 @@ def get_class_results(lookup, modulename, name, fullname):
elif inspect.isfunction(value): elif inspect.isfunction(value):
if doc.startswith(('A decorator', 'A shortcut decorator')): if doc.startswith(('A decorator', 'A shortcut decorator')):
# finicky but surprisingly consistent # finicky but surprisingly consistent
key = _('Methods')
badge = attributetablebadge('@', '@') badge = attributetablebadge('@', '@')
badge['badge-type'] = _('decorator') badge['badge-type'] = _('decorator')
elif inspect.isasyncgenfunction(value):
key = _('Methods') key = _('Methods')
badge = attributetablebadge('async for', 'async for')
badge['badge-type'] = _('async iterable')
else: else:
key = _('Methods') key = _('Methods')
badge = attributetablebadge('def', 'def') badge = attributetablebadge('def', 'def')
@ -217,14 +259,14 @@ def get_class_results(lookup, modulename, name, fullname):
return groups return groups
def class_results_to_node(key, elements):
def class_results_to_node(key: str, elements: Sequence[TableElement]) -> attributetablecolumn:
title = attributetabletitle(key, key) title = attributetabletitle(key, key)
ul = nodes.bullet_list('') ul = nodes.bullet_list('')
for element in elements: for element in elements:
ref = nodes.reference('', '', internal=True, ref = nodes.reference(
refuri='#' + element.fullname, '', '', internal=True, refuri=f'#{element.fullname}', anchorname='', *[nodes.Text(element.label)]
anchorname='', )
*[nodes.Text(element.label)])
para = addnodes.compact_paragraph('', '', ref) para = addnodes.compact_paragraph('', '', ref)
if element.badge is not None: if element.badge is not None:
ul.append(attributetable_item('', element.badge, para)) ul.append(attributetable_item('', element.badge, para))
@ -233,7 +275,8 @@ def class_results_to_node(key, elements):
return attributetablecolumn('', title, ul) return attributetablecolumn('', title, ul)
def setup(app):
def setup(app: Sphinx) -> None:
app.add_directive('attributetable', PyAttributeTable) app.add_directive('attributetable', PyAttributeTable)
app.add_node(attributetable, html=(visit_attributetable_node, depart_attributetable_node)) app.add_node(attributetable, html=(visit_attributetable_node, depart_attributetable_node))
app.add_node(attributetablecolumn, html=(visit_attributetablecolumn_node, depart_attributetablecolumn_node)) app.add_node(attributetablecolumn, html=(visit_attributetablecolumn_node, depart_attributetablecolumn_node))

Loading…
Cancel
Save