def fix_ids(self, tree: nodes.document) -> None: """Replace colons with hyphens in href and id attributes. Some readers crash because they interpret the part as a transport protocol specification. """ def update_node_id(node: Element) -> None: """Update IDs of given *node*.""" new_ids: List[str] = [] for node_id in node['ids']: new_id = self.fix_fragment('', node_id) if new_id not in new_ids: new_ids.append(new_id) node['ids'] = new_ids for reference in tree.findall(nodes.reference): if 'refuri' in reference: m = self.refuri_re.match(reference['refuri']) if m: reference['refuri'] = self.fix_fragment(m.group(1), m.group(2)) if 'refid' in reference: reference['refid'] = self.fix_fragment('', reference['refid']) for target in tree.findall(nodes.target): update_node_id(target) next_node: Node = target.next_node(ascend=True) if isinstance(next_node, nodes.Element): update_node_id(next_node) for desc_signature in tree.findall(addnodes.desc_signature): update_node_id(desc_signature)
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.findall(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 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 list(tree.findall(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.findall(nodes.section): if 'docname' not in sectionnode: sectionnode['docname'] = includefile newnodes.append(sof) toctreenode.parent.replace(toctreenode, newnodes) return tree
def process_doc(self, app: Sphinx, doctree: nodes.document) -> None: """Process and rewrite image URIs.""" docname = app.env.docname for node in doctree.findall(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: 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_doc(self, env: BuildEnvironment, docname: str, document: nodes.document) -> None: todos = self.todos.setdefault(docname, []) for todo in document.findall(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 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 = list(tree.findall(nodes.footnote)) if fns: fn = fns[-1] return fn.parent, fn.parent.index(fn) + 1 for node in tree.findall(nodes.rubric): if len(node) == 1 and node.astext() == FOOTNOTES_RUBRIC_NAME: return node.parent, node.parent.index(node) + 1 doc = next(tree.findall(nodes.document)) rub = nodes.rubric() rub.append(nodes.Text(FOOTNOTES_RUBRIC_NAME)) doc.append(rub) return doc, doc.index(rub) + 1
def process_doc(self, app: Sphinx, doctree: nodes.document) -> None: """Process downloadable file paths. """ for node in doctree.findall(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 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.findall(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.findall(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.findall(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.findall(math_node))
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 = list(tree.findall(nodes.footnote)) if fns: fn = fns[-1] return fn.parent, fn.parent.index(fn) + 1 for node in tree.findall(nodes.rubric): if len(node) == 1 and node.astext() == FOOTNOTES_RUBRIC_NAME: return node.parent, node.parent.index(node) + 1 doc = next(tree.findall(nodes.document)) 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 = next(tree.findall(nodes.document)) fn_spot, fn_idx = footnote_spot(tree) nr = 1 for node in list(tree.findall(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