Browse Source

Rework attributetable to look prettier

pull/6176/head
Rapptz 5 years ago
parent
commit
875a701edb
  1. 67
      docs/_static/style.css
  2. 88
      docs/extensions/attributetable.py

67
docs/_static/style.css

@ -97,6 +97,13 @@ Historically however, thanks to:
--rtd-ad-background: var(--grey-2); --rtd-ad-background: var(--grey-2);
--rtd-ad-main-text: var(--grey-6); --rtd-ad-main-text: var(--grey-6);
--rtd-ad-small-text: var(--grey-4); --rtd-ad-small-text: var(--grey-4);
--attribute-table-title: var(--grey-6);
--attribute-table-entry-border: var(--grey-3);
--attribute-table-entry-text: var(--grey-5);
--attribute-table-entry-hover-border: var(--blue);
--attribute-table-entry-hover-background: var(--grey-2);
--attribute-table-entry-hover-text: var(--blue);
--attribute-table-badge: var(--grey-7);
} }
:root[data-font="sans"] { :root[data-font="sans"] {
@ -151,6 +158,13 @@ Historically however, thanks to:
--rtd-ad-background: var(--grey-5); --rtd-ad-background: var(--grey-5);
--rtd-ad-main-text: var(--grey-2); --rtd-ad-main-text: var(--grey-2);
--rtd-ad-small-text: var(--grey-1); --rtd-ad-small-text: var(--grey-1);
--attribute-table-title: var(--grey-3);
--attribute-table-entry-border: var(--grey-5);
--attribute-table-entry-text: var(--grey-3);
--attribute-table-entry-hover-border: var(--blue);
--attribute-table-entry-hover-background: var(--grey-6);
--attribute-table-entry-hover-text: var(--blue);
--attribute-table-badge: var(--grey-4);
} }
img[src$="snake_dark.svg"] { img[src$="snake_dark.svg"] {
@ -744,19 +758,64 @@ div.helpful > p.admonition-title::after {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
flex-direction: row; flex-direction: row;
justify-content: space-between;
margin: 0 2em; margin: 0 2em;
padding-top: 16px; padding-top: 16px;
} }
.py-attribute-table-column {
flex: 1 1 auto;
}
.py-attribute-table-column:not(:first-child) {
margin-top: 1em;
}
.py-attribute-table-column > span { .py-attribute-table-column > span {
font-weight: bold; font-weight: bold;
color: var(--attribute-table-title);
} }
main .py-attribute-table-column > ul { main .py-attribute-table-column > ul {
list-style: none; list-style: none;
margin: 4px 0px; margin: 4px 0px;
padding-left: 12px; padding-left: 0;
font-size: 0.95em;
}
.py-attribute-table-entry {
margin: 0;
padding: 2px 0;
padding-left: 0.2em;
border-left: 2px solid var(--attribute-table-entry-border);
display: flex;
line-height: 1.2em;
}
.py-attribute-table-entry > a {
padding-left: 0.5em;
color: var(--attribute-table-entry-text);
flex-grow: 1;
}
.py-attribute-table-entry > a:hover {
color: var(--attribute-table-entry-hover-text);
text-decoration: none;
}
.py-attribute-table-entry:hover {
background-color: var(--attribute-table-entry-hover-background);
border-left: 2px solid var(--attribute-table-entry-hover-border);
text-decoration: none;
}
.py-attribute-table-badge {
flex-basis: 3em;
text-align: right;
font-size: 0.9em;
color: var(--attribute-table-badge);
-moz-user-select: none;
-webkit-user-select: none;
user-select: none;
} }
pre { pre {
@ -1076,6 +1135,10 @@ div.code-block-caption {
font-size: 1.5em; font-size: 1.5em;
} }
.py-attribute-table-column:not(:first-child) {
margin-top: unset;
}
main img { main img {
display: block; display: block;
margin-left: auto; margin-left: auto;

88
docs/extensions/attributetable.py

@ -3,7 +3,7 @@ from sphinx.locale import _
from docutils import nodes from docutils import nodes
from sphinx import addnodes from sphinx import addnodes
from collections import OrderedDict from collections import OrderedDict, namedtuple
import importlib import importlib
import inspect import inspect
import os import os
@ -21,6 +21,12 @@ class attributetabletitle(nodes.TextElement):
class attributetableplaceholder(nodes.General, nodes.Element): class attributetableplaceholder(nodes.General, nodes.Element):
pass pass
class attributetablebadge(nodes.TextElement):
pass
class attributetable_item(nodes.Part, nodes.Element):
pass
def visit_attributetable_node(self, node): def visit_attributetable_node(self, node):
self.body.append('<div class="py-attribute-table" data-move-to-id="%s">' % node['python-class']) self.body.append('<div class="py-attribute-table" data-move-to-id="%s">' % node['python-class'])
@ -30,6 +36,16 @@ def visit_attributetablecolumn_node(self, node):
def visit_attributetabletitle_node(self, node): def visit_attributetabletitle_node(self, node):
self.body.append(self.starttag(node, 'span')) self.body.append(self.starttag(node, 'span'))
def visit_attributetablebadge_node(self, node):
attributes = {
'class': 'py-attribute-table-badge',
'title': node['badge-type'],
}
self.body.append(self.starttag(node, 'span', **attributes))
def visit_attributetable_item_node(self, node):
self.body.append(self.starttag(node, 'li', CLASS='py-attribute-table-entry'))
def depart_attributetable_node(self, node): def depart_attributetable_node(self, node):
self.body.append('</div>') self.body.append('</div>')
@ -39,6 +55,12 @@ def depart_attributetablecolumn_node(self, node):
def depart_attributetabletitle_node(self, node): def depart_attributetabletitle_node(self, node):
self.body.append('</span>') self.body.append('</span>')
def depart_attributetablebadge_node(self, node):
self.body.append('</span>')
def depart_attributetable_item_node(self, node):
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):
@ -68,22 +90,20 @@ class PyAttributeTable(SphinxDirective):
<div class="py-attribute-table-column"> <div class="py-attribute-table-column">
<span>_('Attributes')</span> <span>_('Attributes')</span>
<ul> <ul>
<li><a href="..."></li> <li>
</ul> <a href="...">
</div> </li>
<div class="py-attribute-table-column">
<span>_('Coroutines')</span>
<ul>
<li><a href="..."></li>
</ul> </ul>
</div> </div>
<div class="py-attribute-table-column"> <div class="py-attribute-table-column">
<span>_('Methods')</span> <span>_('Methods')</span>
<ul> <ul>
<li><a href="..."></li> <li>
<a href="..."></a>
<span class="py-attribute-badge" title="decorator">D</span>
</li>
</ul> </ul>
</div> </div>
...
</div> </div>
However, since this requires the tree to be complete However, since this requires the tree to be complete
@ -120,6 +140,9 @@ def build_lookup_table(env):
return result return result
TableElement = namedtuple('TableElement', 'fullname label badge')
def process_attributetable(app, doctree, fromdocname): def process_attributetable(app, doctree, fromdocname):
env = app.builder.env env = app.builder.env
@ -131,7 +154,7 @@ def process_attributetable(app, doctree, fromdocname):
for label, subitems in groups.items(): for label, subitems in groups.items():
if not subitems: if not subitems:
continue continue
table.append(class_results_to_node(label, sorted(subitems))) table.append(class_results_to_node(label, sorted(subitems, key=lambda c: c.label)))
table['python-class'] = fullname table['python-class'] = fullname
@ -145,11 +168,8 @@ def get_class_results(lookup, modulename, name, fullname):
cls_dict = getattr(module, name).__dict__ cls_dict = getattr(module, name).__dict__
groups = OrderedDict([ groups = OrderedDict([
('Attributes', []), (_('Attributes'), []),
('Coroutines', []), (_('Methods'), []),
('Classmethods', []),
('Methods', []),
('Decorators', []),
]) ])
try: try:
@ -159,38 +179,50 @@ def get_class_results(lookup, modulename, name, fullname):
for attr in members: for attr in members:
attrlookup = '%s.%s' % (fullname, attr) attrlookup = '%s.%s' % (fullname, attr)
key = 'Attributes' key = _('Attributes')
badge = None
label = attr label = attr
value = cls_dict.get(attr) value = cls_dict.get(attr)
if value is not None: if value is not None:
doc = value.__doc__ or '' doc = value.__doc__ or ''
if inspect.iscoroutinefunction(value) or doc.startswith('|coro|'): if inspect.iscoroutinefunction(value) or doc.startswith('|coro|'):
key = 'Coroutines' key = _('Methods')
badge = attributetablebadge('async', 'async')
badge['badge-type'] = _('coroutine')
elif isinstance(value, classmethod): elif isinstance(value, classmethod):
key = 'Classmethods' key = _('Methods')
label = '%s.%s' % (name, attr)
badge = attributetablebadge('cls', 'cls')
badge['badge-type'] = _('classmethod')
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 = 'Decorators' badge = attributetablebadge('@', '@')
badge['badge-type'] = _('decorator')
key = _('Methods')
else: else:
key = 'Methods' key = _('Methods')
badge = attributetablebadge('def', 'def')
badge['badge-type'] = _('method')
groups[key].append((attrlookup, label)) groups[key].append(TableElement(fullname=attrlookup, label=label, badge=badge))
return groups return groups
def class_results_to_node(key, elements): def class_results_to_node(key, elements):
title = attributetabletitle(key, key) title = attributetabletitle(key, key)
ul = nodes.bullet_list('') ul = nodes.bullet_list('')
for fullname, label in elements: for element in elements:
ref = nodes.reference('', '', internal=True, ref = nodes.reference('', '', internal=True,
refuri='#' + fullname, refuri='#' + element.fullname,
anchorname='', anchorname='',
*[nodes.Text(label)]) *[nodes.Text(element.label)])
para = addnodes.compact_paragraph('', '', ref) para = addnodes.compact_paragraph('', '', ref)
item = nodes.list_item('', para) if element.badge is not None:
ul.append(item) ul.append(attributetable_item('', element.badge, para))
else:
ul.append(attributetable_item('', para))
return attributetablecolumn('', title, ul) return attributetablecolumn('', title, ul)
@ -199,5 +231,7 @@ def setup(app):
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))
app.add_node(attributetabletitle, html=(visit_attributetabletitle_node, depart_attributetabletitle_node)) app.add_node(attributetabletitle, html=(visit_attributetabletitle_node, depart_attributetabletitle_node))
app.add_node(attributetablebadge, html=(visit_attributetablebadge_node, depart_attributetablebadge_node))
app.add_node(attributetable_item, html=(visit_attributetable_item_node, depart_attributetable_item_node))
app.add_node(attributetableplaceholder) app.add_node(attributetableplaceholder)
app.connect('doctree-resolved', process_attributetable) app.connect('doctree-resolved', process_attributetable)

Loading…
Cancel
Save