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]
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
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
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
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