diff --git a/docs/_static/custom.js b/docs/_static/custom.js index 235a14ea0..0f1ea7fc9 100644 --- a/docs/_static/custom.js +++ b/docs/_static/custom.js @@ -27,5 +27,13 @@ $(document).ready(function () { activeLink = $('.sphinxsidebar a[href="#' + currentSection.attr('id') + '"]'); activeLink.parent().addClass('active'); } + + const tables = document.querySelectorAll('.py-attribute-table[data-move-to-id]'); + tables.forEach(table => { + let element = document.getElementById(table.getAttribute('data-move-to-id')); + let parent = element.parentNode; + // insert ourselves after the element + parent.insertBefore(table, element.nextSibling); + }); }); }); diff --git a/docs/_static/style.css b/docs/_static/style.css index 43966db83..d371518c5 100644 --- a/docs/_static/style.css +++ b/docs/_static/style.css @@ -278,6 +278,12 @@ div.attention, div.warning, div.caution, div.seealso { border: 1px solid #fbe091; } +dl.field-list > dd { + margin-top: 3px; + margin-bottom: 10px; + margin-left: 20px; +} + /* no disgusting background in the FAQ */ div.topic { background-color: transparent; @@ -352,6 +358,25 @@ div.helpful > p.admonition-title:after { list-style: 'ยป' !important; } +/* attribute tables */ +.py-attribute-table { + display: flex; + flex-direction: row; + justify-content: space-between; + margin: 0 2em; + padding-top: 16px; +} + +.py-attribute-table-column > span { + font-weight: bold; +} + +div.body .py-attribute-table-column > ul { + list-style: none; + margin: 4px 0px; + padding-left: 12px; +} + pre { background-color: #f5f5f5; border: 1px solid #C6C9CB; diff --git a/docs/api.rst b/docs/api.rst index 695f81ef1..516be6908 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -30,48 +30,116 @@ There are two main ways to query version information about the library. For guar A string representation of the version. e.g. ``'1.0.0rc1'``. This is based off of :pep:`440`. +Clients +-------- + Client -------- +~~~~~~~ + +.. attributetable:: Client .. autoclass:: Client :members: +AutoShardedClient +~~~~~~~~~~~~~~~~~~ + +.. attributetable:: AutoShardedClient + .. autoclass:: AutoShardedClient :members: +Application Info +------------------ + +AppInfo +~~~~~~~~ + +.. attributetable:: AppInfo + .. autoclass:: AppInfo() :members: +Team +~~~~~ + +.. attributetable:: Team + .. autoclass:: Team() :members: +TeamMember +~~~~~~~~~~~ + +.. attributetable:: TeamMember + .. autoclass:: TeamMember() :members: -Voice ------- +Voice Related +--------------- + +VoiceClient +~~~~~~~~~~~~ + +.. attributetable:: VoiceClient .. autoclass:: VoiceClient() :members: +VoiceProtocol +~~~~~~~~~~~~~~~ + +.. attributetable:: VoiceProtocol + .. autoclass:: VoiceProtocol :members: +AudioSource +~~~~~~~~~~~~ + +.. attributetable:: AudioSource + .. autoclass:: AudioSource :members: +PCMAudio +~~~~~~~~~ + +.. attributetable:: PCMAudio + .. autoclass:: PCMAudio :members: +FFmpegAudio +~~~~~~~~~~~~ + +.. attributetable:: FFmpegAudio + .. autoclass:: FFmpegAudio :members: +FFmpegPCMAudio +~~~~~~~~~~~~~~~ + +.. attributetable:: FFmpegPCMAudio + .. autoclass:: FFmpegPCMAudio :members: +FFmpegOpusAudio +~~~~~~~~~~~~~~~~ + +.. attributetable:: FFmpegOpusAudio + .. autoclass:: FFmpegOpusAudio :members: +PCMVolumeTransformer +~~~~~~~~~~~~~~~~~~~~~ + +.. attributetable:: PCMVolumeTransformer + .. autoclass:: PCMVolumeTransformer :members: @@ -2149,9 +2217,19 @@ Working with :meth:`Guild.audit_logs` is a complicated process with a lot of mac involved. The library attempts to make it easy to use and friendly. In order to accomplish this goal, it must make use of a couple of data classes that aid in this goal. +AuditLogEntry +~~~~~~~~~~~~~~~ + +.. attributetable:: AuditLogEntry + .. autoclass:: AuditLogEntry :members: +AuditLogChanges +~~~~~~~~~~~~~~~~~ + +.. attributetable:: AuditLogChanges + .. class:: AuditLogChanges An audit log change set. @@ -2196,6 +2274,11 @@ this goal, it must make use of a couple of data classes that aid in this goal. | ``None`` | No attributes are set. | +----------------------------------------+--------------------------------------------------+ +AuditLogDiff +~~~~~~~~~~~~~ + +.. attributetable:: AuditLogDiff + .. class:: AuditLogDiff Represents an audit log "change" object. A change object has dynamic @@ -2541,6 +2624,8 @@ Webhook Support discord.py offers support for creating, editing, and executing webhooks through the :class:`Webhook` class. +.. attributetable:: Webhook + .. autoclass:: Webhook :members: @@ -2575,18 +2660,43 @@ They are mainly there for usage with :func:`py:isinstance` and :func:`py:issubcl This library has a module related to abstract base classes, some of which are actually from the :doc:`abc ` standard module, others which are not. +Snowflake +~~~~~~~~~~ + +.. attributetable:: discord.abc.Snowflake + .. autoclass:: discord.abc.Snowflake :members: +User +~~~~~ + +.. attributetable:: discord.abc.User + .. autoclass:: discord.abc.User :members: +PrivateChannel +~~~~~~~~~~~~~~~ + +.. attributetable:: discord.abc.PrivateChannel + .. autoclass:: discord.abc.PrivateChannel :members: +GuildChannel +~~~~~~~~~~~~~ + +.. attributetable:: discord.abc.GuildChannel + .. autoclass:: discord.abc.GuildChannel :members: +Messageable +~~~~~~~~~~~~ + +.. attributetable:: discord.abc.Messageable + .. autoclass:: discord.abc.Messageable :members: :exclude-members: history, typing @@ -2597,6 +2707,11 @@ module, others which are not. .. automethod:: discord.abc.Messageable.typing :async-with: +Connectable +~~~~~~~~~~~~ + +.. attributetable:: discord.abc.Connectable + .. autoclass:: discord.abc.Connectable .. _discord_api_models: @@ -2629,6 +2744,8 @@ the user of the library. ClientUser ~~~~~~~~~~~~ +.. attributetable:: ClientUser + .. autoclass:: ClientUser() :members: :inherited-members: @@ -2636,12 +2753,16 @@ ClientUser Relationship ~~~~~~~~~~~~~~ +.. attributetable:: Relationship + .. autoclass:: Relationship() :members: User ~~~~~ +.. attributetable:: User + .. autoclass:: User() :members: :inherited-members: @@ -2656,18 +2777,24 @@ User Attachment ~~~~~~~~~~~ +.. attributetable:: Attachment + .. autoclass:: Attachment() :members: Asset ~~~~~ +.. attributetable:: Asset + .. autoclass:: Asset() :members: Message ~~~~~~~ +.. attributetable:: Message + .. autoclass:: Message() :members: @@ -2681,6 +2808,8 @@ DeletedReferencedMessage Reaction ~~~~~~~~~ +.. attributetable:: Reaction + .. autoclass:: Reaction() :members: :exclude-members: users @@ -2691,18 +2820,24 @@ Reaction CallMessage ~~~~~~~~~~~~ +.. attributetable:: CallMessage + .. autoclass:: CallMessage() :members: GroupCall ~~~~~~~~~~ +.. attributetable:: GroupCall + .. autoclass:: GroupCall() :members: Guild ~~~~~~ +.. attributetable:: Guild + .. autoclass:: Guild() :members: :exclude-members: audit_logs @@ -2722,6 +2857,8 @@ Integration Member ~~~~~~ +.. attributetable:: Member + .. autoclass:: Member() :members: :inherited-members: @@ -2736,30 +2873,40 @@ Member Spotify ~~~~~~~~ +.. attributetable:: Spotify + .. autoclass:: Spotify() :members: VoiceState ~~~~~~~~~~~ +.. attributetable:: VoiceState + .. autoclass:: VoiceState() :members: Emoji ~~~~~ +.. attributetable:: Emoji + .. autoclass:: Emoji() :members: PartialEmoji ~~~~~~~~~~~~~~~~~~~~~~ +.. attributetable:: PartialEmoji + .. autoclass:: PartialEmoji() :members: Role ~~~~~ +.. attributetable:: Role + .. autoclass:: Role() :members: @@ -2772,6 +2919,8 @@ RoleTags TextChannel ~~~~~~~~~~~~ +.. attributetable:: TextChannel + .. autoclass:: TextChannel() :members: :inherited-members: @@ -2786,6 +2935,8 @@ TextChannel VoiceChannel ~~~~~~~~~~~~~ +.. attributetable:: VoiceChannel + .. autoclass:: VoiceChannel() :members: :inherited-members: @@ -2793,6 +2944,8 @@ VoiceChannel CategoryChannel ~~~~~~~~~~~~~~~~~ +.. attributetable:: CategoryChannel + .. autoclass:: CategoryChannel() :members: :inherited-members: @@ -2800,6 +2953,8 @@ CategoryChannel DMChannel ~~~~~~~~~ +.. attributetable:: DMChannel + .. autoclass:: DMChannel() :members: :inherited-members: @@ -2814,6 +2969,8 @@ DMChannel GroupChannel ~~~~~~~~~~~~ +.. attributetable:: GroupChannel + .. autoclass:: GroupChannel() :members: :inherited-members: @@ -2828,18 +2985,24 @@ GroupChannel PartialInviteGuild ~~~~~~~~~~~~~~~~~~~ +.. attributetable:: PartialInviteGuild + .. autoclass:: PartialInviteGuild() :members: PartialInviteChannel ~~~~~~~~~~~~~~~~~~~~~ +.. attributetable:: PartialInviteChannel + .. autoclass:: PartialInviteChannel() :members: Invite ~~~~~~~ +.. attributetable:: Invite + .. autoclass:: Invite() :members: @@ -2852,12 +3015,16 @@ Template WidgetChannel ~~~~~~~~~~~~~~~ +.. attributetable:: WidgetChannel + .. autoclass:: WidgetChannel() :members: WidgetMember ~~~~~~~~~~~~~ +.. attributetable:: WidgetMember + .. autoclass:: WidgetMember() :members: :inherited-members: @@ -2865,6 +3032,8 @@ WidgetMember Widget ~~~~~~~ +.. attributetable:: Widget + .. autoclass:: Widget() :members: @@ -2877,36 +3046,48 @@ Sticker RawMessageDeleteEvent ~~~~~~~~~~~~~~~~~~~~~~~ +.. attributetable:: RawMessageDeleteEvent + .. autoclass:: RawMessageDeleteEvent() :members: RawBulkMessageDeleteEvent ~~~~~~~~~~~~~~~~~~~~~~~~~~ +.. attributetable:: RawBulkMessageDeleteEvent + .. autoclass:: RawBulkMessageDeleteEvent() :members: RawMessageUpdateEvent ~~~~~~~~~~~~~~~~~~~~~~ +.. attributetable:: RawMessageUpdateEvent + .. autoclass:: RawMessageUpdateEvent() :members: RawReactionActionEvent ~~~~~~~~~~~~~~~~~~~~~~~ +.. attributetable:: RawReactionActionEvent + .. autoclass:: RawReactionActionEvent() :members: RawReactionClearEvent ~~~~~~~~~~~~~~~~~~~~~~ +.. attributetable:: RawReactionClearEvent + .. autoclass:: RawReactionClearEvent() :members: RawReactionClearEmojiEvent ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.. attributetable:: RawReactionClearEmojiEvent + .. autoclass:: RawReactionClearEmojiEvent() :members: @@ -2937,12 +3118,16 @@ Object Embed ~~~~~~ +.. attributetable:: Embed + .. autoclass:: Embed :members: AllowedMentions ~~~~~~~~~~~~~~~~~ +.. attributetable:: AllowedMentions + .. autoclass:: AllowedMentions :members: @@ -2973,54 +3158,72 @@ MemberCacheFlags File ~~~~~ +.. attributetable:: File + .. autoclass:: File :members: Colour ~~~~~~ +.. attributetable:: Colour + .. autoclass:: Colour :members: BaseActivity ~~~~~~~~~~~~~~ +.. attributetable:: BaseActivity + .. autoclass:: BaseActivity :members: Activity ~~~~~~~~~ +.. attributetable:: Activity + .. autoclass:: Activity :members: Game ~~~~~ +.. attributetable:: Game + .. autoclass:: Game :members: Streaming ~~~~~~~~~~~ +.. attributetable:: Streaming + .. autoclass:: Streaming :members: CustomActivity ~~~~~~~~~~~~~~~ +.. attributetable:: CustomActivity + .. autoclass:: CustomActivity :members: Permissions ~~~~~~~~~~~~ +.. attributetable:: Permissions + .. autoclass:: Permissions :members: PermissionOverwrite ~~~~~~~~~~~~~~~~~~~~ +.. attributetable:: PermissionOverwrite + .. autoclass:: PermissionOverwrite :members: diff --git a/docs/conf.py b/docs/conf.py index d87049ac3..feedd553d 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -38,7 +38,8 @@ extensions = [ 'sphinx.ext.napoleon', 'sphinxcontrib_trio', 'details', - 'exception_hierarchy' + 'exception_hierarchy', + 'attributetable', ] autodoc_member_order = 'bysource' diff --git a/docs/extensions/attributetable.py b/docs/extensions/attributetable.py new file mode 100644 index 000000000..234f944fa --- /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('
' % 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('
') + +def depart_attributetablecolumn_node(self, node): + self.body.append('') + +def depart_attributetabletitle_node(self, node): + self.body.append('') + +_name_parser_regex = re.compile(r'(?P[\w.]+\.)?(?P\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: + + + + 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)