Beispiel #1
0
    def write_doc(self, docname: str, doctree: nodes.Node) -> None:
        logger.debug(f"[nbtutorial]: Called on {docname}")

        # Determine if the document represents a tutorial.
        nodes = list(doctree.traverse(condition=nbtutorial))

        if len(nodes) == 0:
            return

        # Find any solutions that it may constain.
        solutions = list(doctree.traverse(condition=nbsolution))
        self._process_solutions(docname, solutions)

        destination = StringOutput(encoding="utf-8")
        self.docwriter.write(doctree, destination)

        base, fname = os.path.split(docname)
        basedir = os.path.join(self.outdir, base)

        if not os.path.exists(basedir):
            os.makedirs(basedir)

        Path(basedir, "__init__.py").touch()
        outfile = os.path.join(basedir, fname + ".ipynb")

        with open(outfile, "w") as f:
            f.write(self.docwriter.output)
Beispiel #2
0
    def write_doc(self, docname: str, doctree: Node) -> None:
        logger.info('')
        n = 0

        # reference nodes
        for refnode in doctree.traverse(nodes.reference):
            if 'refuri' not in refnode:
                continue
            uri = refnode['refuri']
            lineno = get_node_line(refnode)
            self.wqueue.put((uri, docname, lineno), False)
            n += 1

        # image nodes
        for imgnode in doctree.traverse(nodes.image):
            uri = imgnode['candidates'].get('?')
            if uri and '://' in uri:
                lineno = get_node_line(imgnode)
                self.wqueue.put((uri, docname, lineno), False)
                n += 1

        done = 0
        while done < n:
            self.process_result(self.rqueue.get())
            done += 1

        if self.broken:
            self.app.statuscode = 1
Beispiel #3
0
def register_sections_as_label(app: Sphinx, document: Node) -> None:
    domain = cast(StandardDomain, app.env.get_domain('std'))
    for node in document.traverse(nodes.section):
        if (app.config.autosectionlabel_maxdepth and
                get_node_depth(node) >= app.config.autosectionlabel_maxdepth):
            continue
        labelid = node['ids'][0]
        docname = app.env.docname
        title = cast(nodes.title, node[0])
        ref_name = getattr(title, 'rawsource', title.astext())
        if app.config.autosectionlabel_prefix_document:
            name = nodes.fully_normalize_name(docname + ':' + ref_name)
        else:
            name = nodes.fully_normalize_name(ref_name)
        sectname = clean_astext(title)

        if name in domain.labels:
            logger.warning(__('duplicate label %s, other instance in %s'),
                           name,
                           app.env.doc2path(domain.labels[name][0]),
                           location=node,
                           type='autosectionlabel',
                           subtype=docname)

        domain.anonlabels[name] = docname, labelid
        domain.labels[name] = docname, labelid, sectname
Beispiel #4
0
    def select_doc_nodes(self, doctree: nodes.Node) -> List[nodes.Node]:
        """Select the nodes for which entries in the TOC are desired

        This is a separate method so that it might be overriden by
        subclasses wanting to add other types of nodes to the TOC.
        """
        return doctree.traverse(addnodes.desc)
Beispiel #5
0
def doctree_read(app: Sphinx, doctree: Node) -> None:
    """
    Go through the entire document looking for C signature nodes to create
    cross references to the actual source listings.

    Args:
        app (Sphinx):
            The sphinx app currently doing the processing.

        doctree (Node):
            The root node of the document to walk through. This will be
            modified in place, by modifiying signature nodes of C constructs.
    """
    c_nodes = (n for n in doctree.traverse(addnodes.desc)
               if n.get("domain") == "c")
    for node in c_nodes:
        signature_nodes = (n for n in node
                           if isinstance(n, addnodes.desc_signature))

        # I really dislike negative flags. Anyway it's ok to link to source
        # listing from anywhere, but it's undesirable to link from the source
        # listing back to any documentation location. So we leverage the
        # :noindex: option and assume that only the entries which allowed the
        # indices are the consolidated locations of documentation.
        use_back_refs = not node.get("noindex", False)

        for signature in signature_nodes:
            fullname = signature.get("fullname")
            if use_back_refs:
                _add_pending_back_reference(app, signature, fullname)
            _add_pending_source_cross_reference(app, signature, fullname)
Beispiel #6
0
def doctree_read(app: Sphinx, document: Node) -> None:
    domain = cast(StandardDomain, app.env.get_domain("std"))

    # Traverse extension nodes
    for node in document.traverse():
        docname, labelid, sectname = "", "", ""

        if is_extension_node(node):
            name = node.get("names", [])[0]
            labelid = document.nameids[name]
            docname = app.env.docname

            # If solution node
            if is_linked_node(node):
                sectname = "Solution to "
            else:
                # If other node, simply add :math: to title
                # to allow for easy parsing in ref_node
                for item in node[0]:
                    if isinstance(item, nodes.math):
                        sectname += f":math:`{item.astext()}` "
                        continue
                    sectname += f"{item.astext()} "

                # Lastly, remove parans from title
                _r, _l = sectname.rfind(")"), sectname.find("(") + 1
                sectname = sectname[_l:_r].strip()

            # Now update domain.anonlabels and domain.labels
            # to include the newly updated sectname
            domain.anonlabels[name] = docname, labelid
            domain.labels[name] = docname, labelid, sectname
def register_sections_as_label(app: Sphinx, document: Node) -> None:
    docname = app.env.docname
    print(docname)

    for pattern in app.config.autosectionlabel_skip_docs:
        if fnmatch(docname, pattern):
            return None

    domain = cast(StandardDomain, app.env.get_domain("std"))
    for node in document.traverse(nodes.section):
        if (app.config.autosectionlabel_maxdepth and
                get_node_depth(node) >= app.config.autosectionlabel_maxdepth):
            continue
        labelid = node["ids"][0]

        title = cast(nodes.title, node[0])
        ref_name = getattr(title, "rawsource", title.astext())
        if app.config.autosectionlabel_prefix_document:
            name = nodes.fully_normalize_name(docname + ":" + ref_name)
        else:
            name = nodes.fully_normalize_name(ref_name)
        sectname = clean_astext(title)

        if name in domain.labels:
            logger.warning(
                __("duplicate label %s, other instance in %s"),
                name,
                app.env.doc2path(domain.labels[name][0]),
                location=node,
                type="autosectionlabel",
                subtype=docname,
            )

        domain.anonlabels[name] = docname, labelid
        domain.labels[name] = docname, labelid, sectname
Beispiel #8
0
 def post_process_images(self, doctree: Node) -> None:
     """Pick the best candidate for all image URIs."""
     images = ImageAdapter(self.env)
     for node in doctree.traverse(nodes.image):
         if '?' in node['candidates']:
             # don't rewrite nonlocal image URIs
             continue
         if '*' not in node['candidates']:
             for imgtype in self.supported_image_types:
                 candidate = node['candidates'].get(imgtype, None)
                 if candidate:
                     break
             else:
                 mimetypes = sorted(node['candidates'])
                 image_uri = images.get_original_image_uri(node['uri'])
                 if mimetypes:
                     logger.warning(__('a suitable image for %s builder not found: '
                                       '%s (%s)'),
                                    self.name, mimetypes, image_uri, location=node)
                 else:
                     logger.warning(__('a suitable image for %s builder not found: %s'),
                                    self.name, image_uri, location=node)
                 continue
             node['uri'] = candidate
         else:
             candidate = node['uri']
         if candidate not in self.env.images:
             # non-existing URI; let it alone
             continue
         self.images[candidate] = self.env.images[candidate][1]
Beispiel #9
0
def get_rst_title(rst_doc: Node) -> Optional[Any]:
    """ Given some RST, extract what docutils thinks is the title """

    for title in rst_doc.traverse(nodes.title):
        return title.astext()

    return None
Beispiel #10
0
def register_tabs_as_label(app: Sphinx, document: nodes.Node) -> None:
    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:
            logger.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()
Beispiel #11
0
    def post_process_images(self, doctree: Node) -> None:
        for node in doctree.traverse(RevealjsNode):
            elm = getattr(node, 'revealit_el', None)

            if elm:
                for img in elm.images.values():
                    if img not in self.env.images:
                        # non-existing URI; let it alone
                        continue
                    self.images[img] = self.env.images[img][1]

        super().post_process_images(doctree)
Beispiel #12
0
 def process_doc(self, env: BuildEnvironment, docname: str, document: Node) -> None:
     """Process a document after it is read by the environment."""
     entries = self.entries.setdefault(env.docname, [])
     for node in document.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)
Beispiel #13
0
def _run_doctests_on_graphtik_document(app: Sphinx, doctree: nodes.Node):
    """Callback of `doctree-resolved`` event. """
    try:
        docname = app.env.docname
        from ._graphtikbuilder import get_graphtik_builder

        if _should_work(app) and any(doctree.traverse(graphtik_node)):
            log.info(__("Graphtik-ing document %r..."), docname)
            graphtik_builder = get_graphtik_builder(app)
            graphtik_builder.test_doc(docname, doctree)
    except Exception as ex:
        log.error("General failure of Graphtik-sphinx extension: %s", ex, exc_info=True)
        raise
Beispiel #14
0
 def fix_refuris(self, tree: Node) -> None:
     # fix refuris with double anchor
     fname = self.config.master_doc + self.out_suffix
     for refnode in tree.traverse(nodes.reference):
         if 'refuri' not in refnode:
             continue
         refuri = refnode['refuri']
         hashindex = refuri.find('#')
         if hashindex < 0:
             continue
         hashindex = refuri.find('#', hashindex + 1)
         if hashindex >= 0:
             refnode['refuri'] = fname + refuri[hashindex:]
Beispiel #15
0
def add_uids(doctree: Node, condition: Any) -> Iterator[Node]:
    """Add a unique id to every node in the `doctree` which matches the
    condition and yield the nodes.

    :param doctree:
        A :class:`docutils.nodes.document` instance.

    :param condition:
        A callable which returns either ``True`` or ``False`` for a given node.
    """
    for node in doctree.traverse(condition):
        node.uid = uuid4().hex
        yield node
Beispiel #16
0
def doctree_read(app: Sphinx, doctree: Node) -> None:
    env = app.builder.env

    resolve_target = getattr(env.config, 'linkcode_resolve', None)
    if not callable(env.config.linkcode_resolve):
        raise LinkcodeError(
            "Function `linkcode_resolve` is not given in conf.py")

    domain_keys = {
        'py': ['module', 'fullname'],
        'c': ['names'],
        'cpp': ['names'],
        'js': ['object', 'fullname'],
    }

    for objnode in list(doctree.traverse(addnodes.desc)):
        domain = objnode.get('domain')
        uris: Set[str] = set()
        for signode in objnode:
            if not isinstance(signode, addnodes.desc_signature):
                continue

            # Convert signode to a specified format
            info = {}
            for key in domain_keys.get(domain, []):
                value = signode.get(key)
                if not value:
                    value = ''
                info[key] = value
            if not info:
                continue

            # Call user code to resolve the link
            uri = resolve_target(domain, info)
            if not uri:
                # no source
                continue

            if uri in uris or not uri:
                # only one link per name, please
                continue
            uris.add(uri)

            inline = nodes.inline('', _('[source]'), classes=['viewcode-link'])
            onlynode = addnodes.only(expr='html')
            onlynode += nodes.reference('',
                                        '',
                                        inline,
                                        internal=False,
                                        refuri=uri)
            signode += onlynode
Beispiel #17
0
def doctree_read(app: Sphinx, document: Node) -> None:
    """
    Read the doctree and apply updates to sphinx-exercise nodes
    """

    domain = cast(StandardDomain, app.env.get_domain("std"))

    # Traverse sphinx-exercise nodes
    for node in document.traverse():
        if is_extension_node(node):
            name = node.get("names", [])[0]
            label = document.nameids[name]
            docname = app.env.docname
            section_name = node.attributes.get("title")
            domain.anonlabels[name] = docname, label
            domain.labels[name] = docname, label, section_name
Beispiel #18
0
def process_only_nodes(document: Node, tags: "Tags") -> None:
    """Filter ``only`` nodes which does not match *tags*."""
    for node in document.traverse(addnodes.only):
        try:
            ret = tags.eval_condition(node['expr'])
        except Exception as err:
            logger.warning(__('exception while evaluating only directive expression: %s'), err,
                           location=node)
            node.replace_self(node.children or nodes.comment())
        else:
            if ret:
                node.replace_self(node.children or nodes.comment())
            else:
                # A comment on the comment() nodes being inserted: replacing by [] would
                # result in a "Losing ids" exception if there is a target node before
                # the only node, so we make sure docutils can transfer the id to
                # something, even if it's just a comment and will lose the id anyway...
                node.replace_self(nodes.comment())
Beispiel #19
0
    def test_doc(self, docname: str, doctree: Node) -> None:
        # Get all applicable nodes
        doc_nodes = list(doctree.traverse(self.condition))

        if not doc_nodes:
            return

        print()
        print(f"{self.name} testing: {docname}")
        print()

        self.total_tries += 1

        try:
            with tempfile.TemporaryDirectory(
            ) as tempdir, contextlib.ExitStack() as stack:
                ctx = {"cwd": tempdir}

                for node in doc_nodes:  # type: Element
                    filename = self.get_filename_for_node(node, docname)
                    line_number = self.get_line_number(node)

                    if "consoletest-literalinclude" in node:
                        print()
                        print("Copying", node["source"], node["filepath"])
                        print()
                        shutil.copyfile(
                            node["source"],
                            os.path.join(ctx["cwd"], *node["filepath"]),
                        )
                    elif "consoletest_commands" in node:
                        for command in node["consoletest_commands"]:
                            print()
                            print("Running", command)
                            print()
                            stack.enter_context(command)
                            command.run(ctx)
                print()
                print("No more tempdir")
                print()
        except:
            self.total_failures += 1
Beispiel #20
0
 def write_doc(self, docname: str, doctree: Node) -> None:
     # work around multiple string % tuple issues in docutils;
     # replace tuples in attribute values with lists
     doctree = doctree.deepcopy()
     for node in doctree.traverse(nodes.Element):
         for att, value in node.attributes.items():
             if isinstance(value, tuple):
                 node.attributes[att] = list(value)
             value = node.attributes[att]
             if isinstance(value, list):
                 for i, val in enumerate(value):
                     if isinstance(val, tuple):
                         value[i] = list(val)
     destination = StringOutput(encoding='utf-8')
     self.writer.write(doctree, destination)
     outfilename = path.join(self.outdir, os_path(docname) + self.out_suffix)
     ensuredir(path.dirname(outfilename))
     try:
         with open(outfilename, 'w', encoding='utf-8') as f:
             f.write(self.writer.output)
     except OSError as err:
         logger.warning(__("error writing file %s: %s"), outfilename, err)
Beispiel #21
0
def process_attributetable(app: Sphinx, doctree: nodes.Node,
                           fromdocname: str) -> None:
    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,
                                      sorted(subitems, key=lambda c: c.label)))

        table['python-class'] = fullname

        if not table:
            node.replace_self([])
        else:
            node.replace_self([table])
Beispiel #22
0
    def post_process_images(self, doctree: Node) -> None:
        """Pick the best candidate for an image and link down-scaled images to
        their high res version.
        """
        Builder.post_process_images(self, doctree)

        if self.config.html_scaled_image_link and self.html_scaled_image_link:
            for node in doctree.traverse(nodes.image):
                scale_keys = ('scale', 'width', 'height')
                if not any((key in node) for key in scale_keys) or \
                   isinstance(node.parent, nodes.reference):
                    # docutils does unfortunately not preserve the
                    # ``target`` attribute on images, so we need to check
                    # the parent node here.
                    continue
                uri = node['uri']
                reference = nodes.reference('', '', internal=True)
                if uri in self.images:
                    reference['refuri'] = posixpath.join(self.imgpath,
                                                         self.images[uri])
                else:
                    reference['refuri'] = uri
                node.replace_self(reference)
                reference.append(node)
Beispiel #23
0
    def test_doc(self, docname: str, doctree: nodes.Node) -> None:
        """
        HACK: Method overridden to annotate all TestCode instances with their nodes,

        so as to store back on them the value of `:graphvar:` in the doctest-runner globals,
        after they have been executed.
        """
        groups: Dict[str, extdoctest.TestGroup] = {}
        add_to_all_groups = []
        TestRunner = extdoctest.SphinxDocTestRunner
        self.setup_runner = TestRunner(verbose=False, optionflags=self.opt)
        self.test_runner = TestRunner(verbose=False, optionflags=self.opt)
        self.cleanup_runner = TestRunner(verbose=False, optionflags=self.opt)

        self.test_runner._fakeout = self.setup_runner._fakeout  # type: ignore
        self.cleanup_runner._fakeout = self.setup_runner._fakeout  # type: ignore

        if self.config.doctest_test_doctest_blocks:

            def condition(node: nodes.Node) -> bool:
                return (isinstance(node, (nodes.literal_block, nodes.comment))
                        and "testnodetype" in node) or isinstance(
                            node, nodes.doctest_block)

        else:

            def condition(node: nodes.Node) -> bool:
                return (isinstance(node, (nodes.literal_block, nodes.comment))
                        and "testnodetype" in node)

        for node in doctree.traverse(condition):  # type: Element
            if self.skipped(node):
                continue

            source = node["test"] if "test" in node else node.astext()
            filename = self.get_filename_for_node(node, docname)
            line_number = self.get_line_number(node)
            if not source and not self.run_empty_code:
                log.warning(
                    __("no code/output in %s block"),
                    node.get("testnodetype", "doctest"),
                    location=(filename, line_number),
                )
            code = extdoctest.TestCode(
                source,
                type=node.get("testnodetype", "doctest"),
                filename=filename,
                lineno=line_number,
                options=node.get("options"),
            )
            # HACK: annotate the TestCode with the node
            # to store back plottable from doctest-runner globals.
            code.node = node

            node_groups = node.get("groups", ["default"])
            if "*" in node_groups:
                add_to_all_groups.append(code)
                continue
            for groupname in node_groups:
                if groupname not in groups:
                    groups[groupname] = extdoctest.TestGroup(groupname)
                groups[groupname].add_code(code)
        for code in add_to_all_groups:
            for group in groups.values():
                group.add_code(code)
        if self.config.doctest_global_setup:
            code = extdoctest.TestCode(self.config.doctest_global_setup,
                                       "testsetup",
                                       filename=None,
                                       lineno=0)
            for group in groups.values():
                group.add_code(code, prepend=True)
        if self.config.doctest_global_cleanup:
            code = extdoctest.TestCode(
                self.config.doctest_global_cleanup,
                "testcleanup",
                filename=None,
                lineno=0,
            )
            for group in groups.values():
                group.add_code(code)
        if not groups:
            return

        self._out("\nDocument: %s\n----------%s\n" %
                  (docname, "-" * len(docname)))
        for group in groups.values():
            self.test_group(group)
        # Separately count results from setup code
        res_f, res_t = self.setup_runner.summarize(self._out, verbose=False)
        self.setup_failures += res_f
        self.setup_tries += res_t
        if self.test_runner.tries:
            res_f, res_t = self.test_runner.summarize(self._out, verbose=True)
            self.total_failures += res_f
            self.total_tries += res_t
        if self.cleanup_runner.tries:
            res_f, res_t = self.cleanup_runner.summarize(self._out,
                                                         verbose=True)
            self.cleanup_failures += res_f
            self.cleanup_tries += res_t
Beispiel #24
0
def doctree_read(app: Sphinx, doctree: Node) -> None:
    env = app.builder.env
    if not hasattr(env, '_viewcode_modules'):
        env._viewcode_modules = {}  # type: ignore

    def has_tag(modname: str, fullname: str, docname: str,
                refname: str) -> bool:
        entry = env._viewcode_modules.get(modname, None)  # type: ignore
        if entry is False:
            return False

        code_tags = app.emit_firstresult('viewcode-find-source', modname)
        if code_tags is None:
            try:
                analyzer = ModuleAnalyzer.for_module(modname)
                analyzer.find_tags()
            except Exception:
                env._viewcode_modules[modname] = False  # type: ignore
                return False

            code = analyzer.code
            tags = analyzer.tags
        else:
            code, tags = code_tags

        if entry is None or entry[0] != code:
            entry = code, tags, {}, refname
            env._viewcode_modules[modname] = entry  # type: ignore
        _, tags, used, _ = entry
        if fullname in tags:
            used[fullname] = docname
            return True

        return False

    for objnode in doctree.traverse(addnodes.desc):
        if objnode.get('domain') != 'py':
            continue
        names: Set[str] = set()
        for signode in objnode:
            if not isinstance(signode, addnodes.desc_signature):
                continue
            modname = signode.get('module')
            fullname = signode.get('fullname')
            refname = modname
            if env.config.viewcode_follow_imported_members:
                new_modname = app.emit_firstresult(
                    'viewcode-follow-imported',
                    modname,
                    fullname,
                )
                if not new_modname:
                    new_modname = _get_full_modname(app, modname, fullname)
                modname = new_modname
            if not modname:
                continue
            fullname = signode.get('fullname')
            if not has_tag(modname, fullname, env.docname, refname):
                continue
            if fullname in names:
                # only one link per name, please
                continue
            names.add(fullname)
            pagename = posixpath.join(OUTPUT_DIRNAME,
                                      modname.replace('.', '/'))
            signode += viewcode_anchor(reftarget=pagename,
                                       refid=fullname,
                                       refdoc=env.docname)
Beispiel #25
0
def merge_doctrees(old: Node, new: Node, condition: Any) -> Iterator[Node]:
    """Merge the `old` doctree with the `new` one while looking at nodes
    matching the `condition`.

    Each node which replaces another one or has been added to the `new` doctree
    will be yielded.

    :param condition:
        A callable which returns either ``True`` or ``False`` for a given node.
    """
    old_iter = old.traverse(condition)
    new_iter = new.traverse(condition)
    old_nodes = []
    new_nodes = []
    ratios = {}
    seen = set()
    # compare the nodes each doctree in order
    for old_node, new_node in zip_longest(old_iter, new_iter):
        if old_node is None:
            new_nodes.append(new_node)
            continue
        if not getattr(old_node, 'uid', None):
            # maybe config.gettext_uuid has been changed.
            old_node.uid = uuid4().hex
        if new_node is None:
            old_nodes.append(old_node)
            continue
        ratio = get_ratio(old_node.rawsource, new_node.rawsource)
        if ratio == 0:
            new_node.uid = old_node.uid
            seen.add(new_node)
        else:
            ratios[old_node, new_node] = ratio
            old_nodes.append(old_node)
            new_nodes.append(new_node)
    # calculate the ratios for each unequal pair of nodes, should we stumble
    # on a pair which is equal we set the uid and add it to the seen ones
    for old_node, new_node in product(old_nodes, new_nodes):
        if new_node in seen or (old_node, new_node) in ratios:
            continue
        ratio = get_ratio(old_node.rawsource, new_node.rawsource)
        if ratio == 0:
            new_node.uid = old_node.uid
            seen.add(new_node)
        else:
            ratios[old_node, new_node] = ratio
    # choose the old node with the best ratio for each new node and set the uid
    # as long as the ratio is under a certain value, in which case we consider
    # them not changed but different
    ratios = sorted(ratios.items(), key=itemgetter(1))  # type: ignore
    for (old_node, new_node), ratio in ratios:
        if new_node in seen:
            continue
        else:
            seen.add(new_node)
        if ratio < VERSIONING_RATIO:
            new_node.uid = old_node.uid
        else:
            new_node.uid = uuid4().hex
            yield new_node
    # create new uuids for any new node we left out earlier, this happens
    # if one or more nodes are simply added.
    for new_node in set(new_nodes) - seen:
        new_node.uid = uuid4().hex
        yield new_node
Beispiel #26
0
def doctree_read(app: Sphinx, doctree: Node) -> None:
    env = app.builder.env
    if not hasattr(env, '_viewcode_modules'):
        env._viewcode_modules = {}  # type: ignore
    if app.builder.name == "singlehtml":
        return
    if app.builder.name.startswith("epub") and not env.config.viewcode_enable_epub:
        return

    def has_tag(modname: str, fullname: str, docname: str, refname: str) -> bool:
        entry = env._viewcode_modules.get(modname, None)  # type: ignore
        if entry is False:
            return False

        code_tags = app.emit_firstresult('viewcode-find-source', modname)
        if code_tags is None:
            try:
                analyzer = ModuleAnalyzer.for_module(modname)
                analyzer.find_tags()
            except Exception:
                env._viewcode_modules[modname] = False  # type: ignore
                return False

            code = analyzer.code
            tags = analyzer.tags
        else:
            code, tags = code_tags

        if entry is None or entry[0] != code:
            entry = code, tags, {}, refname
            env._viewcode_modules[modname] = entry  # type: ignore
        _, tags, used, _ = entry
        if fullname in tags:
            used[fullname] = docname
            return True

        return False

    for objnode in doctree.traverse(addnodes.desc):
        if objnode.get('domain') != 'py':
            continue
        names = set()  # type: Set[str]
        for signode in objnode:
            if not isinstance(signode, addnodes.desc_signature):
                continue
            modname = signode.get('module')
            fullname = signode.get('fullname')
            refname = modname
            if env.config.viewcode_follow_imported_members:
                new_modname = app.emit_firstresult(
                    'viewcode-follow-imported', modname, fullname,
                )
                if not new_modname:
                    new_modname = _get_full_modname(app, modname, fullname)
                modname = new_modname
            if not modname:
                continue
            fullname = signode.get('fullname')
            if not has_tag(modname, fullname, env.docname, refname):
                continue
            if fullname in names:
                # only one link per name, please
                continue
            names.add(fullname)
            pagename = '_modules/' + modname.replace('.', '/')
            inline = nodes.inline('', _('[source]'), classes=['viewcode-link'])
            onlynode = addnodes.only(expr='html')
            onlynode += addnodes.pending_xref('', inline, reftype='viewcode', refdomain='std',
                                              refexplicit=False, reftarget=pagename,
                                              refid=fullname, refdoc=env.docname)
            signode += onlynode
Beispiel #27
0
    def test_doc(self, docname: str, doctree: Node) -> None:
        groups = {}  # type: Dict[str, TestGroup]
        add_to_all_groups = []
        self.setup_runner = SphinxDocTestRunner(verbose=False,
                                                optionflags=self.opt)
        self.test_runner = SphinxDocTestRunner(verbose=False,
                                               optionflags=self.opt)
        self.cleanup_runner = SphinxDocTestRunner(verbose=False,
                                                  optionflags=self.opt)

        self.test_runner._fakeout = self.setup_runner._fakeout  # type: ignore
        self.cleanup_runner._fakeout = self.setup_runner._fakeout  # type: ignore

        if self.config.doctest_test_doctest_blocks:

            def condition(node: Node) -> bool:
                return (isinstance(node, (nodes.literal_block, nodes.comment)) and
                        'testnodetype' in node) or \
                    isinstance(node, nodes.doctest_block)
        else:

            def condition(node: Node) -> bool:
                return isinstance(node, (nodes.literal_block, nodes.comment)) \
                    and 'testnodetype' in node

        for node in doctree.traverse(condition):  # type: Element
            if self.skipped(node):
                continue

            source = node['test'] if 'test' in node else node.astext()
            filename = self.get_filename_for_node(node, docname)
            line_number = self.get_line_number(node)
            if not source:
                logger.warning(__('no code/output in %s block at %s:%s'),
                               node.get('testnodetype', 'doctest'), filename,
                               line_number)
            code = TestCode(source,
                            type=node.get('testnodetype', 'doctest'),
                            filename=filename,
                            lineno=line_number,
                            options=node.get('options'))
            node_groups = node.get('groups', ['default'])
            if '*' in node_groups:
                add_to_all_groups.append(code)
                continue
            for groupname in node_groups:
                if groupname not in groups:
                    groups[groupname] = TestGroup(groupname)
                groups[groupname].add_code(code)
        for code in add_to_all_groups:
            for group in groups.values():
                group.add_code(code)
        if self.config.doctest_global_setup:
            code = TestCode(self.config.doctest_global_setup,
                            'testsetup',
                            filename=None,
                            lineno=0)
            for group in groups.values():
                group.add_code(code, prepend=True)
        if self.config.doctest_global_cleanup:
            code = TestCode(self.config.doctest_global_cleanup,
                            'testcleanup',
                            filename=None,
                            lineno=0)
            for group in groups.values():
                group.add_code(code)
        if not groups:
            return

        self._out('\nDocument: %s\n----------%s\n' %
                  (docname, '-' * len(docname)))
        for group in groups.values():
            self.test_group(group)
        # Separately count results from setup code
        res_f, res_t = self.setup_runner.summarize(self._out, verbose=False)
        self.setup_failures += res_f
        self.setup_tries += res_t
        if self.test_runner.tries:
            res_f, res_t = self.test_runner.summarize(self._out, verbose=True)
            self.total_failures += res_f
            self.total_tries += res_t
        if self.cleanup_runner.tries:
            res_f, res_t = self.cleanup_runner.summarize(self._out,
                                                         verbose=True)
            self.cleanup_failures += res_f
            self.cleanup_tries += res_t