"""Resolve missing references using aliased target names, domains, and/or types.Expanded from and with thanks for https://stackoverflow.com/a/62301461."""importrefromcollections.abcimportMappingfromtypingimportTYPE_CHECKING,Optionalfromdocutils.nodesimportTextfromsphinx.addnodesimportpending_xreffromsphinx.ext.intersphinximportmissing_referenceifTYPE_CHECKING:importsphinx.applicationclassReplacement:refdomain:Optional[str]reftype:Optional[str]reftarget:strtext:Optional[str]# Identifier charactersc="[^`<>]+"# Match any of:# - ":domain:type:`text <target>`"# - ":domain:type:`target`"# - ":type:`text <target>`"# - ":type:`target`"# - "text <target>"# - "target"_target_expr=re.compile(rf"(:((?P<rd>{c}):)?(?P<rt>{c}):)?(`?)(?P<t_or_t>{c})(<(?P<target>{c})>)?\5")def__init__(self,value:str)->None:match=self._target_expr.fullmatch(value)assertmatchisnotNoneself.refdomain,self.reftype,target_or_text,target=[match.group(k)forkin("rd","rt","t_or_t","target")]iftargetisNone:# Target only, no replacement textself.reftarget,self.text=target_or_text,Noneelse:# Both target and text replacementself.reftarget,self.text=target,target_or_text.rstrip()
[docs]defapply_alias(config:Mapping[str,str],node)->bool:"""Apply `config` to `node`."""try:# Identify an alias expression matching the "reftarget" attribute of `node`expr=next(filter(lambdae:re.match(e,node["reftarget"]),config))except(KeyError,StopIteration):# No such attribute, or no matching expression → nothing to doreturnFalse# Unpack information about the replacementreplace=Replacement(config[expr])# Resolve the ref by substituting the reftarget for the matching partnode["reftarget"]=re.sub(f"^{expr}",replace.reftarget,node["reftarget"])# Rewrite the rendered text, reftype, and refdomainifreplace.text:# Find the text node childtext_node=next(iter(node.traverse(lambdan:n.tagname=="#text")))# Remove the old text node, add new text node with custom texttext_node.parent.replace(text_node,Text(replace.text))# Force further processing to preserve this text nodenode["refexplicit"]=Trueifreplace.reftype:node["reftype"]=replace.reftypeifreplace.refdomain:node["refdomain"]=replace.refdomainreturnTrue
[docs]defresolve_internal_aliases(app:"sphinx.application.Sphinx",doctree):"""Handler for 'doctree-read' events."""config=app.config["reference_aliases"]fornodeindoctree.traverse(condition=pending_xref):apply_alias(config,node)
[docs]defresolve_intersphinx_aliases(app,env,node,contnode):"""Handler for 'missing-reference' (intersphinx) events."""ifapply_alias(app.config["reference_aliases"],node):# Delegate the rest of the work to intersphinxreturnmissing_reference(app,env,node,contnode)