示例#1
0
    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)
示例#2
0
    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)
示例#3
0
    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
示例#4
0
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)
示例#5
0
文件: nodes.py 项目: Kyeza/doctuts
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
示例#6
0
    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
示例#7
0
    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)
示例#8
0
    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)
示例#9
0
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)
示例#10
0
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)
示例#11
0
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
示例#12
0
    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)
示例#13
0
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'))
示例#14
0
        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
示例#15
0
    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)
示例#17
0
文件: asset.py 项目: zwq000/sphinx
 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)
示例#18
0
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()
示例#19
0
    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
示例#20
0
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)
示例#21
0
 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
示例#22
0
    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)
示例#23
0
    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))
示例#24
0
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)
示例#25
0
 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)
示例#26
0
    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
示例#27
0
    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]
示例#28
0
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"]
示例#29
0
 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)