def process(self, doctree: nodes.document, docname: str) -> None: todos = sum(self.domain.todos.values(), []) # type: List[todo_node] document = new_document('') for node in doctree.traverse(todolist): if not self.config.todo_include_todos: node.parent.remove(node) continue if node.get('ids'): content = [nodes.target()] # type: List[Element] else: content = [] for todo in todos: # Create a copy of the todo node new_todo = todo.deepcopy() new_todo['ids'].clear() # (Recursively) resolve references in the todo content # # Note: To resolve references, it is needed to wrap it with document node document += new_todo self.env.resolve_references(document, todo['docname'], self.builder) document.remove(new_todo) content.append(new_todo) todo_ref = self.create_todo_reference(todo, docname) content.append(todo_ref) node.replace_self(content)
def write_doc(self, docname: str, doctree: document) -> None: for node in doctree.traverse(nodes.reference): # add ``target=_blank`` attributes to external links if node.get('internal') is None and 'refuri' in node: node['target'] = '_blank' super().write_doc(docname, doctree)
def process_images(self, doctree: nodes.document, src: str) -> None: """Handle any images contained in the document. This ensures that the actual image files referenced by the document are copied to the ``resources`` folder. It also ensures that the reference to the image within the document is rewritten to work with the resources folder. Parameters ---------- doctree: The doctree to process src: The path to the file containing the document being processed. """ docpath = pathlib.Path(src) for image in list(doctree.traverse(condition=nodes.image)): source = pathlib.Path(self.app.srcdir, image["uri"]) destination = pathlib.Path( "resources", docpath.with_suffix(""), source.name ) refuri = relative_uri(src, str(destination)) logger.debug("[tutorial]: image src: %s", source) logger.debug("[tutorial]: image dest: %s", destination) logger.debug("[tutorial]: image ref: %s", refuri) self.resources[str(destination)] = ("copy", source) image["uri"] = refuri
def process_todos(app: Sphinx, doctree: nodes.document) -> None: warnings.warn('process_todos() is deprecated.', RemovedInSphinx40Warning, stacklevel=2) # collect all todos in the environment # this is not done in the directive itself because it some transformations # must have already been run, e.g. substitutions env = app.builder.env if not hasattr(env, 'todo_all_todos'): env.todo_all_todos = [] # type: ignore for node in doctree.traverse(todo_node): app.events.emit('todo-defined', node) newnode = node.deepcopy() newnode['ids'] = [] env.todo_all_todos.append({ # type: ignore 'docname': env.docname, 'source': node.source or env.doc2path(env.docname), 'lineno': node.line, 'todo': newnode, 'target': node['ids'][0], }) if env.config.todo_emit_warnings: label = cast(nodes.Element, node[1]) logger.warning(__("TODO entry found: %s"), label.astext(), location=node)
def inline_all_toctrees(builder: "Builder", docnameset: Set[str], docname: str, tree: nodes.document, colorfunc: Callable, traversed: List[str]) -> nodes.document: """Inline all toctrees in the *tree*. Record all docnames in *docnameset*, and output docnames with *colorfunc*. """ tree = cast(nodes.document, tree.deepcopy()) for toctreenode in tree.traverse(addnodes.toctree): newnodes = [] includefiles = map(str, toctreenode['includefiles']) for includefile in includefiles: if includefile not in traversed: try: traversed.append(includefile) logger.info(colorfunc(includefile) + " ", nonl=True) subtree = inline_all_toctrees( builder, docnameset, includefile, builder.env.get_doctree(includefile), colorfunc, traversed) docnameset.add(includefile) except Exception: logger.warning( __('toctree contains ref to nonexisting file %r'), includefile, location=docname) else: sof = addnodes.start_of_file(docname=includefile) sof.children = subtree.children for sectionnode in sof.traverse(nodes.section): if 'docname' not in sectionnode: sectionnode['docname'] = includefile newnodes.append(sof) toctreenode.parent.replace(toctreenode, newnodes) return tree
def get_and_resolve_doctree(self, docname: str, builder: "Builder", doctree: nodes.document = None, prune_toctrees: bool = True, includehidden: bool = False) -> nodes.document: """Read the doctree from the pickle, resolve cross-references and toctrees and return it. """ if doctree is None: doctree = self.get_doctree(docname) # resolve all pending cross-references self.apply_post_transforms(doctree, docname) # now, resolve all toctree nodes for toctreenode in doctree.traverse(addnodes.toctree): result = TocTree(self).resolve(docname, builder, toctreenode, prune=prune_toctrees, includehidden=includehidden) if result is None: toctreenode.replace_self([]) else: toctreenode.replace_self(result) return doctree
def process(self, doctree: nodes.document, docname: str) -> None: channels = sum(self.domain.channels.values(), []) # type: List[todo_node] document = new_document('') for node in doctree.traverse(asyncapi_overview): table = self.create_full_table(node, channels, docname) node.replace_self(table)
def process_doc(self, app: Sphinx, doctree: nodes.document) -> None: """Process and rewrite image URIs.""" docname = app.env.docname for node in doctree.traverse(nodes.image): # Map the mimetype to the corresponding image. The writer may # choose the best image from these candidates. The special key * is # set if there is only single candidate to be used by a writer. # The special key ? is set for nonlocal URIs. candidates = {} # type: Dict[str, str] node['candidates'] = candidates imguri = node['uri'] if imguri.startswith('data:'): candidates['?'] = imguri continue elif imguri.find('://') != -1: candidates['?'] = imguri continue if imguri.endswith(os.extsep + '*'): # Update `node['uri']` to a relative path from srcdir # from a relative path from current document. rel_imgpath, full_imgpath = app.env.relfn2path(imguri, docname) node['uri'] = rel_imgpath if app.config.language: # Search language-specific figures at first i18n_imguri = get_image_filename_for_language( imguri, app.env) _, full_i18n_imgpath = app.env.relfn2path( i18n_imguri, docname) self.collect_candidates(app.env, full_i18n_imgpath, candidates, node) self.collect_candidates(app.env, full_imgpath, candidates, node) else: if app.config.language: # substitute imguri by figure_language_filename # (ex. foo.png -> foo.en.png) imguri = search_image_for_language(imguri, app.env) # Update `node['uri']` to a relative path from srcdir # from a relative path from current document. node['uri'], _ = app.env.relfn2path(imguri, docname) candidates['*'] = node['uri'] # map image paths to unique image names (so that they can be put # into a single directory) for imgpath in candidates.values(): app.env.dependencies[docname].add(imgpath) if not os.access(path.join(app.srcdir, imgpath), os.R_OK): logger.warning(__('image file not readable: %s') % imgpath, location=node, type='image', subtype='not_readable') continue app.env.images.add_file(docname, imgpath)
def process_todo_nodes(app: Sphinx, doctree: nodes.document, fromdocname: str) -> None: """Replace all todolist nodes with a list of the collected todos. Augment each todo with a backlink to the original location. """ warnings.warn('process_todo_nodes() is deprecated.', RemovedInSphinx40Warning, stacklevel=2) domain = cast(TodoDomain, app.env.get_domain('todo')) todos = sum(domain.todos.values(), []) # type: List[todo_node] for node in doctree.traverse(todolist): if node.get('ids'): content = [nodes.target()] # type: List[Element] else: content = [] if not app.config['todo_include_todos']: node.replace_self(content) continue for todo_info in todos: para = nodes.paragraph(classes=['todo-source']) if app.config['todo_link_only']: description = _('<<original entry>>') else: description = ( _('(The <<original entry>> is located in %s, line %d.)') % (todo_info.source, todo_info.line)) desc1 = description[:description.find('<<')] desc2 = description[description.find('>>') + 2:] para += nodes.Text(desc1, desc1) # Create a reference innernode = nodes.emphasis(_('original entry'), _('original entry')) try: para += make_refnode(app.builder, fromdocname, todo_info['docname'], todo_info['ids'][0], innernode) except NoUri: # ignore if no URI can be determined, e.g. for LaTeX output pass para += nodes.Text(desc2, desc2) todo_entry = todo_info.deepcopy() todo_entry['ids'].clear() # (Recursively) resolve references in the todo content app.env.resolve_references(todo_entry, todo_info['docname'], app.builder) # type: ignore # NOQA # Insert into the todolist content.append(todo_entry) content.append(para) node.replace_self(content)
def get_rst_excerpt(rst_doc: document, paragraphs: int = 1) -> str: """ Given rst, parse and return a portion """ texts = [] for count, p in enumerate(rst_doc.traverse(paragraph)): texts.append(p.astext()) if count + 1 == paragraphs: break return ' '.join(texts)
def process_fillin(app: Sphinx, doctree: nodes.document, fromdocname: str) -> None: if app.builder.name == "handouts": count = 0 for node in doctree.traverse(fillin): node.children = [] # Remove all text nodes node.index = count count += 1
def process_doc(self, env: BuildEnvironment, docname: str, document: nodes.document) -> None: todos = self.todos.setdefault(docname, []) for todo in document.traverse(todo_node): env.app.emit('todo-defined', todo) todos.append(todo) if env.config.todo_emit_warnings: logger.warning(__("TODO entry found: %s"), todo[1].astext(), location=todo)
def process_auto_behave_nodes(app: Sphinx, doc_tree: document, from_doc_name: str): root = os.path.dirname(os.path.abspath(__file__)) template_string = open(os.path.join(root, 'templates/behave.html'), 'r').read() template = Template(template_string) for node in doc_tree.traverse(AutoBehave): output = process_modules(node.rawsource, template) node.replace_self(nodes.raw('', output, format='html'))
def footnote_spot(tree: nodes.document) -> Tuple[Element, int]: """Find or create a spot to place footnotes. The function returns the tuple (parent, index).""" # The code uses the following heuristic: # a) place them after the last existing footnote # b) place them after an (empty) Footnotes rubric # c) create an empty Footnotes rubric at the end of the document fns = tree.traverse(nodes.footnote) if fns: fn = fns[-1] return fn.parent, fn.parent.index(fn) + 1 for node in tree.traverse(nodes.rubric): if len(node) == 1 and node.astext() == FOOTNOTES_RUBRIC_NAME: return node.parent, node.parent.index(node) + 1 doc = tree.traverse(nodes.document)[0] rub = nodes.rubric() rub.append(nodes.Text(FOOTNOTES_RUBRIC_NAME)) doc.append(rub) return doc, doc.index(rub) + 1
def parse(self, inputstring: str, document: nodes.document) -> None: """Parse source text. :param inputstring: The source string to parse :param document: The root docutils node to add AST elements to """ self.setup_parse(inputstring, document) # check for exorbitantly long lines if hasattr(document.settings, "line_length_limit"): for i, line in enumerate(inputstring.split("\n")): if len(line) > document.settings.line_length_limit: error = document.reporter.error( f"Line {i+1} exceeds the line-length-limit:" f" {document.settings.line_length_limit}." ) document.append(error) return # create parsing configuration from the global config try: config = create_myst_config(document.settings, DOCUTILS_EXCLUDED_ARGS) except Exception as exc: error = document.reporter.error(f"Global myst configuration invalid: {exc}") document.append(error) config = MdParserConfig() # update the global config with the file-level config try: topmatter = read_topmatter(inputstring) except TopmatterReadError: pass # this will be reported during the render else: if topmatter: warning = lambda wtype, msg: create_warning( # noqa: E731 document, msg, line=1, append_to=document, subtype=wtype ) config = merge_file_level(config, topmatter, warning) # parse content parser = create_md_parser(config, DocutilsRenderer) parser.options["document"] = document parser.render(inputstring) # post-processing # replace raw nodes if raw is not allowed if not getattr(document.settings, "raw_enabled", True): for node in document.traverse(nodes.raw): warning = document.reporter.warning("Raw content disabled.") node.parent.replace(node, warning) self.finish_parse()
def process_doc(self, app: Sphinx, doctree: nodes.document) -> None: docname = app.env.docname entries = app.env.indexentries[docname] = [] for node in doctree.traverse(addnodes.index): try: for entry in node['entries']: split_index_msg(entry[0], entry[1]) except ValueError as exc: logger.warning(str(exc), location=node) node.parent.remove(node) else: for entry in node['entries']: entries.append(entry)
def process_doc(self, app: Sphinx, doctree: nodes.document) -> None: """Process downloadable file paths. """ for node in doctree.traverse(addnodes.download_reference): targetname = node['reftarget'] if '://' in targetname: node['refuri'] = targetname else: rel_filename, filename = app.env.relfn2path(targetname, app.env.docname) app.env.dependencies[app.env.docname].add(rel_filename) if not os.access(filename, os.R_OK): logger.warning(__('download file not readable: %s') % filename, location=node, type='download', subtype='not_readable') continue node['filename'] = app.env.dlfiles.add_file(app.env.docname, rel_filename)
def register_tabs_as_label(app: Sphinx, document: nodes.document) -> None: """Registers tabs as anchors in TOC.""" docname = app.env.docname labels = app.env.domaindata['std']['labels'] anonlabels = app.env.domaindata['std']['anonlabels'] for node in document.traverse(nxt_tab_head): if node.label_id in labels: logging.getLogger(__name__).warning( __('duplicate label %s, other instance in %s'), node.label_id, app.env.doc2path(labels[node.label_id][0]), location=node) anonlabels[node.label_id] = docname, node.label_id labels[node.label_id] = docname, node.label_id, node.astext()
def process_doc(self, app: Sphinx, doctree: nodes.document) -> None: if not hasattr(app.env, 'rjs_css_classes'): app.env.rjs_css_classes = set() for node in doctree.traverse(): if hasattr(node, 'attributes') and node.attributes.get('classes'): app.env.rjs_css_classes.update(node.attributes['classes']) if getattr( node, 'tagname', None) == 'raw' and node.attributes.get('format') == 'html': app.env.rjs_css_classes.update( CSSPurge.classes_from_html(node.rawsource)) elm = getattr(node, 'revealit_el', None) if isinstance(elm, RjsElement): app.env.rjs_css_classes.update(elm.classes) pass
def process_ifconfig_nodes(app: Sphinx, doctree: nodes.document, docname: str) -> None: ns = {confval.name: confval.value for confval in app.config} ns.update(app.config.__dict__.copy()) ns['builder'] = app.builder.name for node in doctree.traverse(ifconfig): try: res = eval(node['expr'], ns) except Exception as err: # handle exceptions in a clean fashion from traceback import format_exception_only msg = ''.join(format_exception_only(err.__class__, err)) newnode = doctree.reporter.error('Exception occurred in ' 'ifconfig expression: \n%s' % msg, base_node=node) node.replace_self(newnode) else: if not res: node.replace_self([]) else: node.replace_self(node.children)
def process_doc(self, app: Sphinx, doctree: nodes.document) -> None: """Add a title node to the document (just copy the first section title), and store that title in the environment. """ titlenode = nodes.title() longtitlenode = titlenode # explicit title set with title directive; use this only for # the <title> tag in HTML output if 'title' in doctree: longtitlenode = nodes.title() longtitlenode += nodes.Text(doctree['title']) # look for first section title and use that as the title for node in doctree.traverse(nodes.section): visitor = SphinxContentsFilter(doctree) node[0].walkabout(visitor) titlenode += visitor.get_entry_text() break else: # document has no title titlenode += nodes.Text(doctree.get('title', '<no title>')) app.env.titles[app.env.docname] = titlenode app.env.longtitles[app.env.docname] = longtitlenode
def process(self, doctree: nodes.document, docname: str) -> None: todos: List[todo_node] = sum(self.domain.todos.values(), []) for node in list(doctree.traverse(todolist)): if not self.config.todo_include_todos: node.parent.remove(node) continue if node.get('ids'): content: List[Element] = [nodes.target()] else: content = [] for todo in todos: # Create a copy of the todo node new_todo = todo.deepcopy() new_todo['ids'].clear() self.resolve_reference(new_todo, docname) content.append(new_todo) todo_ref = self.create_todo_reference(todo, docname) content.append(todo_ref) node.replace_self(content)
def process_doc(self, env: BuildEnvironment, docname: str, document: nodes.document) -> None: def math_node(node: Node) -> bool: return isinstance(node, (nodes.math, nodes.math_block)) self.data['has_equations'][docname] = any(document.traverse(math_node))
def insert_toctrees(app: Sphinx, doctree: nodes.document) -> None: """Create the toctree nodes and add it to the document. Adapted from `sphinx/directives/other.py::TocTree` """ # check for existing toctrees and raise warning for node in doctree.traverse(toctree_node): create_warning( app, doctree, "toctree", "toctree directive not expected with external-toc", line=node.line, ) toc_placeholders: List[TableOfContentsNode] = list( doctree.traverse(TableOfContentsNode) ) site_map: SiteMap = app.env.external_site_map doc_item: Optional[Document] = site_map.get(app.env.docname) if doc_item is None or not doc_item.subtrees: if toc_placeholders: create_warning( app, doctree, "tableofcontents", "tableofcontents directive in document with no descendants", ) for node in toc_placeholders: node.replace_self([]) return # TODO allow for more than one tableofcontents, i.e. per part? for node in toc_placeholders[1:]: create_warning( app, doctree, "tableofcontents", "more than one tableofcontents directive in document", line=node.line, ) node.replace_self([]) # initial variables all_docnames = app.env.found_docs.copy() all_docnames.remove(app.env.docname) # remove current document excluded = Matcher(app.config.exclude_patterns) node_list: List[nodes.Element] = [] for toctree in doc_item.subtrees: subnode = toctree_node() subnode["parent"] = app.env.docname subnode.source = doctree.source subnode["entries"] = [] subnode["includefiles"] = [] subnode["maxdepth"] = toctree.maxdepth subnode["caption"] = toctree.caption # TODO this wasn't in the original code, # but alabaster theme intermittently raised `KeyError('rawcaption')` subnode["rawcaption"] = toctree.caption or "" subnode["glob"] = any(isinstance(entry, GlobItem) for entry in toctree.items) subnode["hidden"] = False if toc_placeholders else toctree.hidden subnode["includehidden"] = False subnode["numbered"] = ( 0 if toctree.numbered is False else (999 if toctree.numbered is True else int(toctree.numbered)) ) subnode["titlesonly"] = toctree.titlesonly wrappernode = nodes.compound(classes=["toctree-wrapper"]) wrappernode.append(subnode) for entry in toctree.items: if isinstance(entry, UrlItem): subnode["entries"].append((entry.title, entry.url)) elif isinstance(entry, FileItem): child_doc_item = site_map[entry] docname = str(entry) title = child_doc_item.title docname = remove_suffix(docname, app.config.source_suffix) if docname not in app.env.found_docs: if excluded(app.env.doc2path(docname, None)): message = f"toctree contains reference to excluded document {docname!r}" else: message = f"toctree contains reference to nonexisting document {docname!r}" create_warning(app, doctree, "ref", message, append_to=node_list) app.env.note_reread() else: subnode["entries"].append((title, docname)) subnode["includefiles"].append(docname) elif isinstance(entry, GlobItem): patname = str(entry) docnames = sorted(patfilter(all_docnames, patname)) for docname in docnames: all_docnames.remove(docname) # don't include it again subnode["entries"].append((None, docname)) subnode["includefiles"].append(docname) if not docnames: message = ( f"toctree glob pattern '{entry}' didn't match any documents" ) create_warning(app, doctree, "glob", message, append_to=node_list) # reversing entries can be useful when globbing if toctree.reversed: subnode["entries"] = list(reversed(subnode["entries"])) subnode["includefiles"] = list(reversed(subnode["includefiles"])) node_list.append(wrappernode) if toc_placeholders: toc_placeholders[0].replace_self(node_list) else: # note here the toctree cannot not just be appended to the end of the doc, # since `TocTreeCollector.process_doc` expects it in a section # TODO check if there is this is always ok doctree.children[-1].extend(node_list)
def doc_tree_read(_app, doc_tree: document) -> None: for toc_tree in doc_tree.traverse(addnodes.toctree): for entry in toc_tree['entries']: if entry[1] in unused_docs: toc_tree['entries'].remove(entry)
def add_visible_links(self, tree: nodes.document, show_urls: str = 'inline') -> None: """Add visible link targets for external links""" def make_footnote_ref(doc: nodes.document, label: str) -> nodes.footnote_reference: """Create a footnote_reference node with children""" footnote_ref = nodes.footnote_reference('[#]_') footnote_ref.append(nodes.Text(label)) doc.note_autofootnote_ref(footnote_ref) return footnote_ref def make_footnote(doc: nodes.document, label: str, uri: str) -> nodes.footnote: """Create a footnote node with children""" footnote = nodes.footnote(uri) para = nodes.paragraph() para.append(nodes.Text(uri)) footnote.append(para) footnote.insert(0, nodes.label('', label)) doc.note_autofootnote(footnote) return footnote def footnote_spot(tree: nodes.document) -> Tuple[Element, int]: """Find or create a spot to place footnotes. The function returns the tuple (parent, index).""" # The code uses the following heuristic: # a) place them after the last existing footnote # b) place them after an (empty) Footnotes rubric # c) create an empty Footnotes rubric at the end of the document fns = tree.traverse(nodes.footnote) if fns: fn = fns[-1] return fn.parent, fn.parent.index(fn) + 1 for node in tree.traverse(nodes.rubric): if len(node) == 1 and node.astext() == FOOTNOTES_RUBRIC_NAME: return node.parent, node.parent.index(node) + 1 doc = tree.traverse(nodes.document)[0] rub = nodes.rubric() rub.append(nodes.Text(FOOTNOTES_RUBRIC_NAME)) doc.append(rub) return doc, doc.index(rub) + 1 if show_urls == 'no': return if show_urls == 'footnote': doc = tree.traverse(nodes.document)[0] fn_spot, fn_idx = footnote_spot(tree) nr = 1 for node in tree.traverse(nodes.reference): uri = node.get('refuri', '') if (uri.startswith('http:') or uri.startswith('https:') or uri.startswith('ftp:')) and uri not in node.astext(): idx = node.parent.index(node) + 1 if show_urls == 'inline': uri = self.link_target_template % {'uri': uri} link = nodes.inline(uri, uri) link['classes'].append(self.css_link_target_class) node.parent.insert(idx, link) elif show_urls == 'footnote': label = FOOTNOTE_LABEL_TEMPLATE % nr nr += 1 footnote_ref = make_footnote_ref(doc, label) node.parent.insert(idx, footnote_ref) footnote = make_footnote(doc, label, uri) fn_spot.insert(fn_idx, footnote) footnote_ref['refid'] = footnote['ids'][0] footnote.add_backref(footnote_ref['ids'][0]) fn_idx += 1
def process_solutions(self, doctree: nodes.document, src: str) -> None: """Handle any solutions contained in the document. This ensures that a ``*.py`` file is created in the ``resources`` directory containing the actual solution. It then also rewrites the given doctree to output a pair of code cells in the resulting notebook. The first is a prompt for the user to input their solution and the second contains a :magic:`ipython:load` declaration to give the user the option to load in the solution if they wish to see it. Parameters ---------- doctree: The doctree to process src: The path to the file containing the document being processed """ docpath = pathlib.Path(src) logger.debug("[tutorial]: processing solutions for: %s", docpath) basename = f"{docpath.stem}-soln" for idx, soln in enumerate(doctree.traverse(condition=solution)): name = f"{basename}-{idx+1:02d}.py" destination = pathlib.Path("resources", docpath.with_suffix(""), name) refuri = relative_uri(src, str(destination)) # Convert the solution to a valid Python document that can be executed. document = new_document("<solution>") document += soln # Rather than go through the trouble of maintaining 2 document translators, # one for notebooks and another for Python files. Let's just use the notebook # translator and do some post-processing on the result - much easier. translator = NotebookTranslator(document) document.walkabout(translator) notebook = translator.asnotebook() blocks = [] for cell in notebook.cells: source = cell.source # Comment out the lines containing markdown. if cell.cell_type == "markdown": source = textwrap.indent(source, "# ") blocks.append(source) self.resources[str(destination)] = ("create", "\n".join(blocks)) # TODO: Expose config options for these # TODO: Translations? your_soln = nodes.literal_block( "", "# Write your solution here...\n", language="python" ) load_soln = nodes.literal_block( "", f"# Execute this cell to load the example solution\n%load {refuri}\n", language="python", ) # Replace the actual solution with the 2 cells defined above. soln.children = [your_soln, load_soln]
def event_html_page_context(app: Sphinx, pagename: str, templatename: str, context: dict, doctree: document): """Called when the HTML builder has created a context dictionary to render a template with. Conditionally adding disqus.js to <head /> if the directive is used in a page. :param app: Sphinx application object. :param pagename: Name of the page being rendered (without .html or any file extension). :param templatename: Page name with .html. :param context: Jinja2 HTML context. :param doctree: Tree of docutils nodes. """ assert app or pagename or templatename # Unused, for linting. if "script_files" in context and doctree and any(hasattr(n, "disqus_shortname") for n in doctree.traverse()): # Clone list to prevent leaking into other pages and add disqus.js to this page. context["script_files"] = context["script_files"][:] + ["_static/disqus.js"]
def process_doc(self, env: BuildEnvironment, docname: str, document: nodes.document) -> None: channels = self.channels.setdefault(docname, []) for channel in document.traverse(asyncapi_node): env.app.emit('asyncapi-channels-defined', channel) channels.append(channel)