aboutsummaryrefslogtreecommitdiff
path: root/docs/extensions/attributetable.py
diff options
context:
space:
mode:
authorRapptz <[email protected]>2020-05-25 11:48:16 -0400
committerRapptz <[email protected]>2020-12-18 21:18:47 -0500
commit9b88c631639167433bfd2c244b3be04d3a52a66d (patch)
tree9e71c65d119643bca4b5408f558a13ed3c732da5 /docs/extensions/attributetable.py
parentAdd support for Member.pending boolean attribute (diff)
downloaddiscord.py-9b88c631639167433bfd2c244b3be04d3a52a66d.tar.xz
discord.py-9b88c631639167433bfd2c244b3be04d3a52a66d.zip
Add attributetable and add some class-level sections.
The extensions have yet to receive this treatment and CSS needs work, but for now this is fine.
Diffstat (limited to 'docs/extensions/attributetable.py')
-rw-r--r--docs/extensions/attributetable.py199
1 files changed, 199 insertions, 0 deletions
diff --git a/docs/extensions/attributetable.py b/docs/extensions/attributetable.py
new file mode 100644
index 00000000..234f944f
--- /dev/null
+++ b/docs/extensions/attributetable.py
@@ -0,0 +1,199 @@
+from sphinx.util.docutils import SphinxDirective
+from sphinx.locale import _
+from docutils import nodes
+from sphinx import addnodes
+
+from collections import OrderedDict
+import importlib
+import inspect
+import os
+import re
+
+class attributetable(nodes.General, nodes.Element):
+ pass
+
+class attributetablecolumn(nodes.General, nodes.Element):
+ pass
+
+class attributetabletitle(nodes.TextElement):
+ pass
+
+class attributetableplaceholder(nodes.General, nodes.Element):
+ pass
+
+def visit_attributetable_node(self, node):
+ self.body.append('<div class="py-attribute-table" data-move-to-id="%s">' % node['python-class'])
+
+def visit_attributetablecolumn_node(self, node):
+ self.body.append(self.starttag(node, 'div', CLASS='py-attribute-table-column'))
+
+def visit_attributetabletitle_node(self, node):
+ self.body.append(self.starttag(node, 'span'))
+
+def depart_attributetable_node(self, node):
+ self.body.append('</div>')
+
+def depart_attributetablecolumn_node(self, node):
+ self.body.append('</div>')
+
+def depart_attributetabletitle_node(self, node):
+ self.body.append('</span>')
+
+_name_parser_regex = re.compile(r'(?P<module>[\w.]+\.)?(?P<name>\w+)')
+
+class PyAttributeTable(SphinxDirective):
+ has_content = False
+ required_arguments = 1
+ optional_arguments = 0
+ final_argument_whitespace = False
+ option_spec = {}
+
+ def parse_name(self, content):
+ path, name = _name_parser_regex.match(content).groups()
+ if path:
+ modulename = path.rstrip('.')
+ else:
+ modulename = self.env.temp_data.get('autodoc:module')
+ if not modulename:
+ modulename = self.env.ref_context.get('py:module')
+ if modulename is None:
+ raise RuntimeError('modulename somehow None for %s in %s.' % (content, self.env.docname))
+
+ return modulename, name
+
+ def run(self):
+ """If you're curious on the HTML this is meant to generate:
+
+ <div class="py-attribute-table">
+ <div class="py-attribute-table-column">
+ <span>_('Attributes')</span>
+ <ul>
+ <li><a href="..."></li>
+ </ul>
+ </div>
+ <div class="py-attribute-table-column">
+ <span>_('Coroutines')</span>
+ <ul>
+ <li><a href="..."></li>
+ </ul>
+ </div>
+ <div class="py-attribute-table-column">
+ <span>_('Methods')</span>
+ <ul>
+ <li><a href="..."></li>
+ </ul>
+ </div>
+ ...
+ </div>
+
+ However, since this requires the tree to be complete
+ and parsed, it'll need to be done at a different stage and then
+ replaced.
+ """
+ content = self.arguments[0].strip()
+ node = attributetableplaceholder('')
+ modulename, name = self.parse_name(content)
+ node['python-module'] = modulename
+ node['python-class'] = name
+ node['python-full-name'] = '%s.%s' % (modulename, name)
+ return [node]
+
+def build_lookup_table(env):
+ # Given an environment, load up a lookup table of
+ # full-class-name: objects
+ result = {}
+ domain = env.domains['py']
+
+ ignored = {
+ 'data', 'exception', 'module', 'class',
+ }
+ for (fullname, (docname, objtype)) in domain.objects.items():
+ if objtype in ignored:
+ continue
+
+ classname, _, child = fullname.rpartition('.')
+ try:
+ result[classname].append(child)
+ except KeyError:
+ result[classname] = [child]
+
+ return result
+
+def process_attributetable(app, doctree, fromdocname):
+ env = app.builder.env
+
+ lookup = build_lookup_table(env)
+ for node in doctree.traverse(attributetableplaceholder):
+ modulename, classname, fullname = node['python-module'], node['python-class'], node['python-full-name']
+ groups = get_class_results(lookup, modulename, classname, fullname)
+ table = attributetable('')
+ for label, subitems in groups.items():
+ if not subitems:
+ continue
+ table.append(class_results_to_node(label, subitems))
+
+ table['python-class'] = fullname
+
+ if not table:
+ node.replace_self([])
+ else:
+ node.replace_self([table])
+
+def get_class_results(lookup, modulename, name, fullname):
+ module = importlib.import_module(modulename)
+ cls_dict = getattr(module, name).__dict__
+
+ groups = OrderedDict([
+ ('Attributes', []),
+ ('Coroutines', []),
+ ('Methods', []),
+ ('Decorators', []),
+ ])
+
+ try:
+ members = lookup[fullname]
+ except KeyError:
+ return groups
+
+ for attr in members:
+ attrlookup = '%s.%s' % (fullname, attr)
+ key = 'Attributes'
+ label = attr
+
+ value = cls_dict.get(attr)
+ if value is not None:
+ doc = value.__doc__ or ''
+ if inspect.iscoroutinefunction(value) or doc.startswith('|coro|'):
+ key = 'Coroutines'
+ elif inspect.isfunction(value):
+ if doc.startswith(('A decorator', 'A shortcut decorator')):
+ # finicky but surprisingly consistent
+ key = 'Decorators'
+ else:
+ key = 'Methods'
+
+ groups[key].append((attrlookup, label))
+
+ return groups
+
+def class_results_to_node(key, elements):
+ title = attributetabletitle(key, key)
+ ul = nodes.bullet_list('')
+ for fullname, label in elements:
+ ref = nodes.reference('', '', internal=True,
+ refuri='#' + fullname,
+ anchorname='',
+ *[nodes.Text(label)])
+ para = addnodes.compact_paragraph('', '', ref)
+ item = nodes.list_item('', para)
+ ul.append(item)
+
+ return attributetablecolumn('', title, ul)
+
+def setup(app):
+ app.add_directive('attributetable', PyAttributeTable)
+ 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(attributetabletitle, html=(visit_attributetabletitle_node, depart_attributetabletitle_node))
+ app.add_node(attributetableplaceholder)
+ app.connect('doctree-resolved', process_attributetable)