Exemplo n.º 1
0
def add_intersphinx_aliases_to_inv(app):
    """Workaround for data type mismatches between intersphinx document sources
    Implementation based on a workaround found here:
    https://github.com/sphinx-doc/sphinx/issues/5603
    The calling code need only define a dictionary that maps the source object
    as it appears in the Sphinx object tree, to the actual location for the
    object definition in an object.inv inventory file. The syntax looks like
    this:
    intersphinx_aliases = {
        'dt-domain': {
            'src-obj-name': 'target-obj-name'
        }
    }
    TIP: when debugging intersphinx mapping problems, it is helpful to use
    the sphobjinv tool:
        https://pypi.org/project/sphobjinv/
    Example use:
    sphobjinv suggest https://requests.readthedocs.io/en/master/ HTTPError -su
    """
    inventories = InventoryAdapter(app.builder.env)

    for domain, mapping in app.config.intersphinx_aliases.items():
        if domain not in inventories.main_inventory:
            raise NotImplementedError(
                "Domain {0} not found in Sphinx inventory".format(domain))
        for source, target in mapping.items():
            if source not in inventories.main_inventory[domain]:
                raise NotImplementedError(
                    "Source object {0} not found in Sphinx domain {1}".format(
                        source, domain))
            inventories.main_inventory[domain][target] = \
                inventories.main_inventory[domain][source]
Exemplo n.º 2
0
def add_intersphinx_aliases_to_inv(app):
    from sphinx.ext.intersphinx import InventoryAdapter
    inventories = InventoryAdapter(app.builder.env)

    for alias, target in app.config.intersphinx_aliases.items():
        alias_domain, alias_name = alias
        target_domain, target_name = target
        try:
            found = inventories.main_inventory[target_domain][target_name]
            try:
                inventories.main_inventory[alias_domain][alias_name] = found
            except KeyError:
                continue
        except KeyError:
            continue
Exemplo n.º 3
0
def add_intersphinx_aliases_to_inv(app):
    """Add type aliases for intersphinx.

    see https://github.com/sphinx-doc/sphinx/issues/5603
    """
    from sphinx.ext.intersphinx import InventoryAdapter

    inventories = InventoryAdapter(app.builder.env)

    for alias, target in app.config.intersphinx_aliases.items():
        alias_domain, alias_name = alias
        target_domain, target_name = target
        try:
            found = inventories.main_inventory[target_domain][target_name]
            try:
                inventories.main_inventory[alias_domain][alias_name] = found
            except KeyError:
                continue
        except KeyError:
            continue
Exemplo n.º 4
0
    def resolve_xref(self, env, fromdocname, builder, type, target, node,
                     contnode):

        objects = self.data['objects']
        expected_type = self._role_to_object_type[type]

        target = target.replace(' ', '-')
        if expected_type == 'keyword':
            targets = [f'keyword::{target}']
        elif expected_type == 'operator':
            targets = [f'operator::{target}']
        elif expected_type == 'statement':
            targets = [f'statement::{target}']
        elif expected_type in {'type', 'function', 'constraint'}:
            targets = [f'{expected_type}::{target}']
            if '::' not in target:
                targets.append(f'{expected_type}::std::{target}')
        else:
            targets = [target]

        docname = None
        obj_type = None
        obj_desc = None
        for target in targets:
            try:
                docname, obj_type, obj_desc = objects[target]
            except KeyError:
                continue

        if docname is None:
            if not node.get('eql-auto-link'):
                # if ref was not found, the :eql: xref may be being used
                # outside of the docs, so try resolving ref from intersphinx
                # inventories
                inventories = InventoryAdapter(env)

                for target in targets:
                    obj_type, name = target.split('::', 1)
                    if ':' not in name:
                        continue
                    docset_name, name = name.split(':', 1)

                    docset = inventories.named_inventory.get(docset_name)
                    if docset is None:
                        continue
                    refs = docset.get('eql:' + obj_type)
                    if refs is None:
                        continue
                    ref = refs.get(obj_type + '::' + name)
                    if ref is None:
                        continue

                    newnode = d_nodes.reference(
                        '',
                        '',
                        internal=False,
                        refuri=ref[2],
                    )
                    if node.get('refexplicit'):
                        newnode.append(d_nodes.Text(contnode.astext()))
                    else:
                        title = contnode.astext()
                        newnode.append(
                            contnode.__class__(title[len(docset_name) + 1:],
                                               title[len(docset_name) + 1:]))
                    return newnode

                raise shared.DomainError(
                    f'cannot resolve :eql:{type}: targeting {target!r}')
            else:
                return

        if obj_type != expected_type:
            raise shared.DomainError(
                f'cannot resolve :eql:{type}: targeting {target!r}: '
                f'the type of referred object {expected_type!r} '
                f'does not match the reftype')

        if node['reftype'] in self.desc_roles:
            node = d_nodes.Text(obj_desc)
        else:
            node = s_nodes_utils.make_refnode(builder, fromdocname, docname,
                                              target, contnode, None)
            node['eql-type'] = obj_type

        return node
Exemplo n.º 5
0
def missing_reference(app, env, node, contnode):
    """
    Override original ``missing_referece`` to add data into the node.

    We call the original intersphinx extension and add hoverxref CSS classes
    plus the ``data-url`` to the node returned from it.

    Sphinx intersphinx downloads all the ``objects.inv`` and load each of them
    into a "named inventory" and also updates the "main inventory". We check if
    reference is part of any of the "named invetories" the user defined in
    ``hoverxref_intersphinx`` and we add hoverxref to the node **only if** the
    reference is on those inventories.

    See https://github.com/sphinx-doc/sphinx/blob/4d90277c/sphinx/ext/intersphinx.py#L244-L250
    """
    if not app.config.hoverxref_intersphinx:
        # Do nothing if the user doesn't have hoverxref intersphinx enabled
        return

    # We need to grab all the attributes before calling
    # ``sphinx_missing_reference`` because it modifies the node in-place
    domain = node.get('refdomain')  # ``std`` if used on ``:ref:``
    target = node['reftarget']
    reftype = node['reftype']

    # By default we skip adding hoverxref to the node to avoid possible
    # problems. We want to be sure we have to add hoverxref on it
    skip_node = True
    inventory_name_matched = None

    if domain == 'std':
        # Using ``:ref:`` manually, we could write intersphinx like:
        # :ref:`datetime <python:datetime.datetime>`
        # and the node will have these attribues:
        #   refdomain: std
        #   reftype: ref
        #   reftarget: python:datetime.datetime
        #   refexplicit: True
        if ':' in target:
            inventory_name, _ = target.split(':', 1)
            if inventory_name in app.config.hoverxref_intersphinx:
                skip_node = False
                inventory_name_matched = inventory_name
    else:
        # Using intersphinx via ``sphinx.ext.autodoc`` generates links for docstrings like:
        # :py:class:`float`
        # and the node will have these attribues:
        #   refdomain: py
        #   reftype: class
        #   reftarget: float
        #   refexplicit: False
        inventories = InventoryAdapter(env)

        # TODO: credits to https://github.com/readthedocs/sphinx-hoverxref/pull/144
        # This chunk of code needs tests :)
        reftype_fallbacks = {
            'meth': 'method',
            'mod': 'module',
        }

        for inventory_name in app.config.hoverxref_intersphinx:
            inventory = inventories.named_inventory.get(inventory_name, {})
            inventory_member = (
                inventory.get(f'{domain}:{reftype}')
                or inventory.get(f'{domain}:{reftype_fallbacks.get(reftype)}'))
            if inventory_member and inventory_member.get(target) is not None:
                # The object **does** exist on the inventories defined by the
                # user: enable hoverxref on this node
                skip_node = False
                inventory_name_matched = inventory_name
                break

    newnode = sphinx_missing_reference(app, env, node, contnode)
    if newnode is not None and not skip_node:
        hoverxref_type = app.config.hoverxref_intersphinx_types.get(
            inventory_name_matched)
        if isinstance(hoverxref_type, dict):
            # Specific style for a particular reftype
            hoverxref_type = hoverxref_type.get(reftype)
        hoverxref_type = hoverxref_type or app.config.hoverxref_default_type

        classes = newnode.get('classes')
        classes.extend(['hoverxref', hoverxref_type])
        newnode.replace_attr('classes', classes)

    return newnode
def missing_reference(app: Sphinx, env: BuildEnvironment, node: Element,
                      contnode: TextElement) -> Optional[nodes.reference]:
    """Linking to Qt documentation."""
    target: str = node["reftarget"]
    inventories = InventoryAdapter(env)
    objtypes: Optional[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 target.startswith("PySide2"):
        head, tail = target.split(".", 1)
        target = "PyQt5." + tail
    if signal_pattern.match(target):
        uri = signal_slot_uri[app.config.qt_documentation]
        dispname = signal_name[app.config.qt_documentation]
        version = QT_VERSION
    elif slot_pattern.match(target):
        uri = signal_slot_uri[app.config.qt_documentation]
        dispname = slot_name[app.config.qt_documentation]
        version = QT_VERSION
    else:
        target_list = [target, "PyQt5." + target]
        target_list += [
            name + "." + target for name in
            inventories.named_inventory["PyQt5"]["sip:module"].keys()
        ]
        if node.get("reftype") in type_translate_dict:
            type_names = type_translate_dict[node.get("reftype")]
        else:
            type_names = [node.get("reftype")]
        for name in type_names:
            obj_type_name = "sip:{}".format(name)
            if obj_type_name not in inventories.named_inventory["PyQt5"]:
                return None
            for target_name in target_list:
                if target_name in inventories.main_inventory[obj_type_name]:
                    proj, version, uri, dispname = inventories.named_inventory[
                        "PyQt5"][obj_type_name][target_name]
                    uri = uri.replace("##", "#")
                    #  print(node)  # print nodes with unresolved references
                    break
            else:
                continue
            break
        else:
            return None
        if app.config.qt_documentation == "Qt5":
            html_name = uri.split("/")[-1]
            uri = "https://doc.qt.io/qt-5/" + html_name
        elif app.config.qt_documentation == "PySide2":
            if node.get("reftype") == "meth":
                split_tup = target_name.split(".")[1:]
                ref_name = ".".join(["PySide2", split_tup[0], "PySide2"] +
                                    split_tup)
                html_name = "/".join(split_tup[:-1]) + ".html#" + ref_name
            else:
                html_name = "/".join(target_name.split(".")[1:]) + ".html"
            uri = "https://doc.qt.io/qtforpython/PySide2/" + html_name

    # remove this line if you would like straight to pyqt documentation
    if version:
        reftitle = _("(in %s v%s)") % (app.config.qt_documentation, version)
    else:
        reftitle = _("(in %s)") % (app.config.qt_documentation, )
    newnode = nodes.reference("",
                              "",
                              internal=False,
                              refuri=uri,
                              reftitle=reftitle)
    if node.get("refexplicit"):
        # use whatever title was given
        newnode.append(contnode)
    else:
        # else use the given display name (used for :ref:)
        newnode.append(contnode.__class__(dispname, dispname))
    return newnode