Exemplo n.º 1
0
 def _resolve_doc_xref(self, env: "BuildEnvironment", fromdocname: str,
                       builder: "Builder", typ: str, target: str,
                       node: pending_xref, contnode: Element) -> Element:
     # directly reference to document by source name; can be absolute or relative
     refdoc = node.get('refdoc', fromdocname)
     docname = docname_join(refdoc, node['reftarget'])
     if docname not in env.all_docs:
         return None
     else:
         if node['refexplicit']:
             # reference with explicit title
             caption = node.astext()
         else:
             caption = clean_astext(env.titles[docname])
         innernode = nodes.inline(caption, caption, classes=['doc'])
         return make_refnode(builder, fromdocname, docname, None, innernode)
Exemplo n.º 2
0
    def _resolve_ref_nested(self,
                            node: pending_xref,
                            fromdocname: str,
                            target=None) -> Optional[Element]:
        """This is the same as ``sphinx.domains.std._resolve_ref_xref``,
        but allows for nested syntax, rather than converting the inner node to raw text.
        """
        stddomain = cast(StandardDomain, self.env.get_domain("std"))
        target = target or node["reftarget"].lower()

        if node["refexplicit"]:
            # reference to anonymous label; the reference uses
            # the supplied link caption
            docname, labelid = stddomain.anonlabels.get(target, ("", ""))
            sectname = node.astext()
            innernode = nodes.inline(sectname, "")
            innernode.extend(node[0].children)
        else:
            # reference to named label; the final node will
            # contain the section name after the label
            docname, labelid, sectname = stddomain.labels.get(
                target, ("", "", ""))
            innernode = nodes.inline(sectname, sectname)

        if not docname:
            return None

        return make_refnode(self.app.builder, fromdocname, docname, labelid,
                            innernode)
Exemplo n.º 3
0
    def resolve_any_xref(self, env: BuildEnvironment, fromdocname: str, builder: Builder,
                         target: str, node: pending_xref, contnode: Element
                         ) -> List[Tuple[str, Element]]:
        modname = node.get('py:module')
        clsname = node.get('py:class')
        results = []  # type: List[Tuple[str, Element]]

        # always search in "refspecific" mode with the :any: role
        matches = self.find_obj(env, modname, clsname, target, None, 1)
        for name, obj in matches:
            if obj[2] == 'module':
                results.append(('py:mod',
                                self._make_module_refnode(builder, fromdocname,
                                                          name, contnode)))
            else:
                results.append(('py:' + self.role_for_objtype(obj[2]),
                                make_refnode(builder, fromdocname, obj[0], obj[1],
                                             contnode, name)))
        return results
Exemplo n.º 4
0
 def _resolve_anchor(self, node: pending_xref,
                     fromdocname: str) -> Optional[Element]:
     """Resolve doc with anchor."""
     target = node["reftarget"]  # type: str
     if not ("#" in target and hasattr(self.env, "myst_anchors")):
         return None
     # the link may be a heading anchor; we need to first get the relative path
     rel_path, anchor = target.rsplit("#", 1)
     rel_path = os.path.normpath(rel_path)
     if rel_path == ".":
         # anchor in the same doc as the node
         doc_path = self.env.doc2path(node.get("refdoc", fromdocname),
                                      base=False)
     else:
         # anchor in a different doc from the node
         doc_path = os.path.normpath(
             os.path.join(node.get("refdoc", fromdocname), "..", rel_path))
     return self._resolve_ref_nested(node, fromdocname,
                                     doc_path + "#" + anchor)
Exemplo n.º 5
0
    def warn_missing_reference(self, refdoc: str, typ: str, target: str,
                               node: pending_xref,
                               domain: Optional[Domain]) -> None:
        warn = node.get('refwarn')
        if self.config.nitpicky:
            warn = True
            dtype = '%s:%s' % (domain.name, typ) if domain else typ
            if self.config.nitpick_ignore:
                if (dtype, target) in self.config.nitpick_ignore:
                    warn = False
                # for "std" types also try without domain name
                if (not domain or domain.name == 'std') and \
                   (typ, target) in self.config.nitpick_ignore:
                    warn = False
            if self.config.nitpick_ignore_regex:

                def matches_ignore(entry_type: str, entry_target: str) -> bool:
                    for ignore_type, ignore_target in self.config.nitpick_ignore_regex:
                        if re.fullmatch(ignore_type, entry_type) and \
                           re.fullmatch(ignore_target, entry_target):
                            return True
                    return False

                if matches_ignore(dtype, target):
                    warn = False
                # for "std" types also try without domain name
                if (not domain or domain.name == 'std') and \
                   matches_ignore(typ, target):
                    warn = False
        if not warn:
            return

        if self.app.emit_firstresult('warn-missing-reference', domain, node):
            return
        elif domain and typ in domain.dangling_warnings:
            msg = domain.dangling_warnings[typ] % {'target': target}
        elif node.get('refdomain', 'std') not in ('', 'std'):
            msg = (__('%s:%s reference target not found: %s') %
                   (node['refdomain'], typ, target))
        else:
            msg = __('%r reference target not found: %s') % (typ, target)
        logger.warning(msg, location=node, type='ref', subtype=typ)
Exemplo n.º 6
0
    def resolve_xref(self, env: BuildEnvironment, fromdocname: str, builder: Builder,
                     type: str, target: str, node: pending_xref, contnode: Element
                     ) -> Element:
        modname = node.get('py:module')
        clsname = node.get('py:class')
        searchmode = node.hasattr('refspecific') and 1 or 0
        matches = self.find_obj(env, modname, clsname, target,
                                type, searchmode)
        if not matches:
            return None
        elif len(matches) > 1:
            logger.warning(__('more than one target found for cross-reference %r: %s'),
                           target, ', '.join(match[0] for match in matches),
                           type='ref', subtype='python', location=node)
        name, obj = matches[0]

        if obj[1] == 'module':
            return self._make_module_refnode(builder, fromdocname, name, contnode)
        else:
            return make_refnode(builder, fromdocname, obj[0], name, contnode, name)
Exemplo n.º 7
0
 def _resolve_anchor(self, node: pending_xref,
                     fromdocname: str) -> Optional[Element]:
     """Resolve doc with anchor."""
     if self.env.config.myst_heading_anchors is None:
         # no target anchors will have been created, so we don't look for them
         return None
     target = node["reftarget"]  # type: str
     if "#" not in target:
         return None
     # the link may be a heading anchor; we need to first get the relative path
     rel_path, anchor = target.rsplit("#", 1)
     rel_path = os.path.normpath(rel_path)
     if rel_path == ".":
         # anchor in the same doc as the node
         doc_path = self.env.doc2path(node.get("refdoc", fromdocname),
                                      base=False)
     else:
         # anchor in a different doc from the node
         doc_path = os.path.normpath(
             os.path.join(node.get("refdoc", fromdocname), "..", rel_path))
     return self._resolve_ref_nested(node, fromdocname,
                                     doc_path + "#" + anchor)
Exemplo n.º 8
0
def builtin_resolver(app: Sphinx, env: BuildEnvironment,
                     node: pending_xref, contnode: Element) -> Element:
    """Do not emit nitpicky warnings for built-in types."""
    def istyping(s: str) -> bool:
        if s.startswith('typing.'):
            s = s.split('.', 1)[1]

        return s in typing.__all__  # type: ignore

    if node.get('refdomain') != 'py':
        return None
    elif node.get('reftype') == 'obj' and node.get('reftarget') == 'None':
        return contnode
    elif node.get('reftype') in ('class', 'exc'):
        reftarget = node.get('reftarget')
        if inspect.isclass(getattr(builtins, reftarget, None)):
            # built-in class
            return contnode
        elif istyping(reftarget):
            # typing class
            return contnode

    return None
Exemplo n.º 9
0
 def warn_missing_reference(self, refdoc: str, typ: str, target: str,
                            node: pending_xref, domain: Domain) -> None:
     warn = node.get('refwarn')
     if self.config.nitpicky:
         warn = True
         if self.config.nitpick_ignore:
             dtype = '%s:%s' % (domain.name, typ) if domain else typ
             if (dtype, target) in self.config.nitpick_ignore:
                 warn = False
             # for "std" types also try without domain name
             if (not domain or domain.name == 'std') and \
                (typ, target) in self.config.nitpick_ignore:
                 warn = False
     if not warn:
         return
     if domain and typ in domain.dangling_warnings:
         msg = domain.dangling_warnings[typ]
     elif node.get('refdomain', 'std') not in ('', 'std'):
         msg = (__('%s:%s reference target not found: %%(target)s') %
                (node['refdomain'], typ))
     else:
         msg = __('%r reference target not found: %%(target)s') % typ
     logger.warning(msg % {'target': target},
                    location=node, type='ref', subtype=typ)
Exemplo n.º 10
0
def term_resolver(app: SphinxApplication, env: BuildEnvironment,
                  orphan: addnodes.pending_xref, textnode: nodes.inline):
    """Triggered when a xref cannot be resolved. For resolving
       missing :term:`glossary-term` references.

       * Create a new referene node with the class "term-missing"
       * Add a MissingTerm object lexicon domain data

       Params
       ------
       * app (sphinx.application.Sphinx): application object
       * env (app.builder.env): the build environment
       * orphan (nodes.pending_xref): node to be resolved
       * textnode (nodes.inline): the text node porition of orphan ref, used to
                                  create a new refnode if resolved

       Returns
       -------
       * if refdomain is "std" and reftype is "term":
           a new reference node that contains textnode
           with class "term-missing" and no href if not found
       * otherwise:
           None
    """

    if orphan.get("reftype") != "term" or orphan.get("refdomain") != "std":
        return

    domain = env.domains.get("lexicon")
    term = Term(orphan.astext())
    listing = domain.find_term(term)

    if not listing:
        domain.add_missing(term, env.docname, orphan.line)

    return domain.mkref(app.builder, term, listing, textnode)
Exemplo n.º 11
0
    def _resolve_ref_xref(self, env: "BuildEnvironment", fromdocname: str,
                          builder: "Builder", typ: str, target: str,
                          node: pending_xref, contnode: Element) -> Element:
        if node['refexplicit']:
            # reference to anonymous label; the reference uses
            # the supplied link caption
            docname, labelid = self.anonlabels.get(target, ('', ''))
            sectname = node.astext()
        else:
            # reference to named label; the final node will
            # contain the section name after the label
            docname, labelid, sectname = self.labels.get(target, ('', '', ''))
        if not docname:
            return None

        return self.build_reference_node(fromdocname, builder, docname,
                                         labelid, sectname, 'ref')
Exemplo n.º 12
0
    def resolve_xref(self, env: BuildEnvironment, fromdocname: str,
                     builder: Builder, type: str, target: str,
                     node: pending_xref, contnode: Element) -> Element:
        searchmode = 1 if node.hasattr('refspecific') else 0
        matches = self.find_obj(env, target, type, searchmode)

        if not matches:
            return None
        elif len(matches) > 1:
            logger.warning(
                __('more than one target found for cross-reference %r: %s'),
                target,
                ', '.join(match[0] for match in matches),
                type='ref',
                subtype='ff',
                location=node)

        name, obj = matches[0]
        return make_refnode(builder, fromdocname, obj[0], name, contnode, name)
Exemplo n.º 13
0
    def _resolve_option_xref(self, env: "BuildEnvironment", fromdocname: str,
                             builder: "Builder", typ: str, target: str,
                             node: pending_xref, contnode: Element) -> Element:
        progname = node.get('std:program')
        target = target.strip()
        docname, labelid = self.progoptions.get((progname, target), ('', ''))
        if not docname:
            commands = []
            while ws_re.search(target):
                subcommand, target = ws_re.split(target, 1)
                commands.append(subcommand)
                progname = "-".join(commands)

                docname, labelid = self.progoptions.get((progname, target),
                                                        ('', ''))
                if docname:
                    break
            else:
                return None

        return make_refnode(builder, fromdocname, docname, labelid, contnode)
Exemplo n.º 14
0
def _resolve_reference(env: BuildEnvironment, inv_name: Optional[str], inventory: Inventory,
                       honor_disabled_refs: bool,
                       node: pending_xref, contnode: TextElement) -> Optional[Element]:
    # disabling should only be done if no inventory is given
    honor_disabled_refs = honor_disabled_refs and inv_name is None

    if honor_disabled_refs and '*' in env.config.intersphinx_disabled_reftypes:
        return None

    typ = node['reftype']
    if typ == 'any':
        for domain_name, domain in env.domains.items():
            if honor_disabled_refs \
                    and (domain_name + ":*") in env.config.intersphinx_disabled_reftypes:
                continue
            objtypes = list(domain.object_types)
            res = _resolve_reference_in_domain(env, inv_name, inventory,
                                               honor_disabled_refs,
                                               domain, objtypes,
                                               node, contnode)
            if res is not None:
                return res
        return None
    else:
        domain_name = node.get('refdomain')
        if not domain_name:
            # only objects in domains are in the inventory
            return None
        if honor_disabled_refs \
                and (domain_name + ":*") in env.config.intersphinx_disabled_reftypes:
            return None
        domain = env.get_domain(domain_name)
        objtypes = domain.objtypes_for_role(typ)
        if not objtypes:
            return None
        return _resolve_reference_in_domain(env, inv_name, inventory,
                                            honor_disabled_refs,
                                            domain, objtypes,
                                            node, contnode)
Exemplo n.º 15
0
def missing_reference(app: Sphinx, env: BuildEnvironment, node: pending_xref,
                      contnode: TextElement) -> nodes.reference:
    """Attempt to resolve a missing reference via intersphinx references."""
    target = node['reftarget']
    inventories = InventoryAdapter(env)
    objtypes: List[str] = None
    if node['reftype'] == 'any':
        # we search anything!
        objtypes = [
            '%s:%s' % (domain.name, objtype)
            for domain in env.domains.values()
            for objtype in domain.object_types
        ]
        domain = None
    else:
        domain = node.get('refdomain')
        if not domain:
            # only objects in domains are in the inventory
            return None
        objtypes = env.get_domain(domain).objtypes_for_role(node['reftype'])
        if not objtypes:
            return None
        objtypes = ['%s:%s' % (domain, objtype) for objtype in objtypes]
    if 'std:cmdoption' in objtypes:
        # until Sphinx-1.6, cmdoptions are stored as std:option
        objtypes.append('std:option')
    if 'py:attribute' in objtypes:
        # Since Sphinx-2.1, properties are stored as py:method
        objtypes.append('py:method')

    to_try = [(inventories.main_inventory, target)]
    if domain:
        full_qualified_name = env.get_domain(domain).get_full_qualified_name(
            node)
        if full_qualified_name:
            to_try.append((inventories.main_inventory, full_qualified_name))
    in_set = None
    if ':' in target:
        # first part may be the foreign doc set name
        setname, newtarget = target.split(':', 1)
        if setname in inventories.named_inventory:
            in_set = setname
            to_try.append((inventories.named_inventory[setname], newtarget))
            if domain:
                node['reftarget'] = newtarget
                full_qualified_name = env.get_domain(
                    domain).get_full_qualified_name(node)
                if full_qualified_name:
                    to_try.append((inventories.named_inventory[setname],
                                   full_qualified_name))
    for inventory, target in to_try:
        for objtype in objtypes:
            if objtype not in inventory:
                # Continue if there's nothing of this kind in the inventory
                continue
            if target in inventory[objtype]:
                # Case sensitive match, use it
                proj, version, uri, dispname = inventory[objtype][target]
            elif objtype == 'std:term':
                # Check for potential case insensitive matches for terms only
                target_lower = target.lower()
                insensitive_matches = list(
                    filter(lambda k: k.lower() == target_lower,
                           inventory[objtype].keys()))
                if insensitive_matches:
                    proj, version, uri, dispname = inventory[objtype][
                        insensitive_matches[0]]
                else:
                    # No case insensitive match either, continue to the next candidate
                    continue
            else:
                # Could reach here if we're not a term but have a case insensitive match.
                # This is a fix for terms specifically, but potentially should apply to
                # other types.
                continue

            if '://' not in uri and node.get('refdoc'):
                # get correct path in case of subdirectories
                uri = path.join(relative_path(node['refdoc'], '.'), uri)
            if version:
                reftitle = _('(in %s v%s)') % (proj, version)
            else:
                reftitle = _('(in %s)') % (proj, )
            newnode = nodes.reference('',
                                      '',
                                      internal=False,
                                      refuri=uri,
                                      reftitle=reftitle)
            if node.get('refexplicit'):
                # use whatever title was given
                newnode.append(contnode)
            elif dispname == '-' or \
                    (domain == 'std' and node['reftype'] == 'keyword'):
                # use whatever title was given, but strip prefix
                title = contnode.astext()
                if in_set and title.startswith(in_set + ':'):
                    newnode.append(
                        contnode.__class__(title[len(in_set) + 1:],
                                           title[len(in_set) + 1:]))
                else:
                    newnode.append(contnode)
            else:
                # else use the given display name (used for :ref:)
                newnode.append(contnode.__class__(dispname, dispname))
            return newnode
    # at least get rid of the ':' in the target if no explicit title given
    if in_set is not None and not node.get('refexplicit', True):
        if len(contnode) and isinstance(contnode[0], nodes.Text):
            contnode[0] = nodes.Text(newtarget, contnode[0].rawsource)

    return None
Exemplo n.º 16
0
def missing_reference(app: Sphinx, env: BuildEnvironment, node: pending_xref,
                      contnode: TextElement) -> nodes.reference:
    """Attempt to resolve a missing reference via intersphinx references."""
    target = node['reftarget']
    inventories = InventoryAdapter(env)
    objtypes: List[str] = None
    if node['reftype'] == 'any':
        # we search anything!
        objtypes = [
            '%s:%s' % (domain.name, objtype)
            for domain in env.domains.values()
            for objtype in domain.object_types
        ]
        domain = None
    else:
        domain = node.get('refdomain')
        if not domain:
            # only objects in domains are in the inventory
            return None
        objtypes = env.get_domain(domain).objtypes_for_role(node['reftype'])
        if not objtypes:
            return None
        objtypes = ['%s:%s' % (domain, objtype) for objtype in objtypes]
    if 'std:cmdoption' in objtypes:
        # until Sphinx-1.6, cmdoptions are stored as std:option
        objtypes.append('std:option')
    if 'py:attribute' in objtypes:
        # Since Sphinx-2.1, properties are stored as py:method
        objtypes.append('py:method')

    # determine the contnode by pending_xref_condition
    content = find_pending_xref_condition(node, 'resolved')
    if content:
        # resolved condition found.
        contnodes = content.children
        contnode = content.children[0]  # type: ignore
    else:
        # not resolved. Use the given contnode
        contnodes = [contnode]

    to_try = [(inventories.main_inventory, target)]
    if domain:
        full_qualified_name = env.get_domain(domain).get_full_qualified_name(
            node)
        if full_qualified_name:
            to_try.append((inventories.main_inventory, full_qualified_name))
    in_set = None
    if ':' in target:
        # first part may be the foreign doc set name
        setname, newtarget = target.split(':', 1)
        if setname in inventories.named_inventory:
            in_set = setname
            to_try.append((inventories.named_inventory[setname], newtarget))
            if domain:
                node['reftarget'] = newtarget
                full_qualified_name = env.get_domain(
                    domain).get_full_qualified_name(node)
                if full_qualified_name:
                    to_try.append((inventories.named_inventory[setname],
                                   full_qualified_name))
    for inventory, target in to_try:
        for objtype in objtypes:
            if objtype not in inventory or target not in inventory[objtype]:
                continue
            proj, version, uri, dispname = inventory[objtype][target]
            if '://' not in uri and node.get('refdoc'):
                # get correct path in case of subdirectories
                uri = path.join(relative_path(node['refdoc'], '.'), uri)
            if version:
                reftitle = _('(in %s v%s)') % (proj, version)
            else:
                reftitle = _('(in %s)') % (proj, )
            newnode = nodes.reference('',
                                      '',
                                      internal=False,
                                      refuri=uri,
                                      reftitle=reftitle)
            if node.get('refexplicit'):
                # use whatever title was given
                newnode.extend(contnodes)
            elif dispname == '-' or \
                    (domain == 'std' and node['reftype'] == 'keyword'):
                # use whatever title was given, but strip prefix
                title = contnode.astext()
                if in_set and title.startswith(in_set + ':'):
                    newnode.append(
                        contnode.__class__(title[len(in_set) + 1:],
                                           title[len(in_set) + 1:]))
                else:
                    newnode.extend(contnodes)
            else:
                # else use the given display name (used for :ref:)
                newnode.append(contnode.__class__(dispname, dispname))
            return newnode
    # at least get rid of the ':' in the target if no explicit title given
    if in_set is not None and not node.get('refexplicit', True):
        if len(contnode) and isinstance(contnode[0], nodes.Text):
            contnode[0] = nodes.Text(newtarget, contnode[0].rawsource)

    return None