1"""Adapted from 2sphinx.transforms.post_transforms.ReferencesResolver.resolve_anyref 3 4If 'py' is one of the domains and `py:class` is defined, 5the Python domain will be processed before the 'std' domain. 6 7License for Sphinx 8================== 9 10Copyright (c) 2007-2019 by the Sphinx team (see AUTHORS file). 11All rights reserved. 12 13Redistribution and use in source and binary forms, with or without 14modification, are permitted provided that the following conditions are 15met: 16 17* Redistributions of source code must retain the above copyright 18 notice, this list of conditions and the following disclaimer. 19 20* Redistributions in binary form must reproduce the above copyright 21 notice, this list of conditions and the following disclaimer in the 22 documentation and/or other materials provided with the distribution. 23 24THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 25"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 26LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 27A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 28HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 29SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 30LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 31DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 32THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 33(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 34OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 35""" 36from contextlib import suppress 37 38from docutils import nodes 39from sphinx.transforms.post_transforms import ReferencesResolver 40 41 42class CustomReferencesResolver(ReferencesResolver): 43 def resolve_anyref(self, refdoc, node, contnode): 44 """Resolve reference generated by the "any" role.""" 45 stddomain = self.env.get_domain("std") 46 target = node["reftarget"] 47 48 # process 'py' domain first for python classes 49 if "py:class" in node: 50 with suppress(KeyError): 51 py_domain = self.env.domains["py"] 52 py_ref = py_domain.resolve_any_xref( 53 self.env, refdoc, self.app.builder, target, node, contnode 54 ) 55 if py_ref: 56 return self.create_node(py_ref[0]) 57 58 # resolve :term: 59 term_ref = stddomain.resolve_xref( 60 self.env, refdoc, self.app.builder, "term", target, node, contnode 61 ) 62 if term_ref: 63 # replace literal nodes with inline nodes 64 if not isinstance(term_ref[0], nodes.inline): 65 inline_node = nodes.inline( 66 rawsource=term_ref[0].rawsource, classes=term_ref[0].get("classes") 67 ) 68 if term_ref[0]: 69 inline_node.append(term_ref[0][0]) 70 term_ref[0] = inline_node 71 return self.create_node(("std:term", term_ref)) 72 73 # next, do the standard domain 74 std_ref = stddomain.resolve_any_xref( 75 self.env, refdoc, self.app.builder, target, node, contnode 76 ) 77 if std_ref: 78 return self.create_node(std_ref[0]) 79 80 for domain in self.env.domains.values(): 81 try: 82 ref = domain.resolve_any_xref( 83 self.env, refdoc, self.app.builder, target, node, contnode 84 ) 85 if ref: 86 return self.create_node(ref[0]) 87 except NotImplementedError: 88 # the domain doesn't yet support the new interface 89 # we have to manually collect possible references (SLOW) 90 for role in domain.roles: 91 res = domain.resolve_xref( 92 self.env, refdoc, self.app.builder, role, target, node, contnode 93 ) 94 if res and isinstance(res[0], nodes.Element): 95 result = ("%s:%s" % (domain.name, role), res) 96 return self.create_node(result) 97 98 # no results considered to be <code> 99 contnode["classes"] = [] 100 return contnode 101 102 def create_node(self, result): 103 res_role, newnode = result 104 # Override "any" class with the actual role type to get the styling 105 # approximately correct. 106 res_domain = res_role.split(":")[0] 107 if ( 108 len(newnode) > 0 109 and isinstance(newnode[0], nodes.Element) 110 and newnode[0].get("classes") 111 ): 112 newnode[0]["classes"].append(res_domain) 113 newnode[0]["classes"].append(res_role.replace(":", "-")) 114 return newnode 115 116 117def setup(app): 118 if hasattr(app.registry, "get_post_transforms") and callable( 119 app.registry.get_post_transforms 120 ): 121 post_transforms = app.registry.get_post_transforms() 122 else: 123 # Support sphinx 1.6.* 124 post_transforms = app.post_transforms 125 126 for i, transform_class in enumerate(post_transforms): 127 if transform_class == ReferencesResolver: 128 post_transforms[i] = CustomReferencesResolver 129 break 130 else: 131 raise RuntimeError("ReferencesResolver not found") 132