@@ -34,20 +34,53 @@ def merge_mapping(app, env, docnames, env_other):
34
34
35
35
36
36
def missing_reference_handler (app , env , node , contnode ):
37
+ """
38
+ Handler to be connect to the sphinx 'missing-reference' event. The handler a
39
+ resolves reference (node) and returns a new node when sphinx could not
40
+ originally resolve the reference.
41
+
42
+ see `missing-reference in sphinx documentation
43
+ <https://www.sphinx-doc.org/en/master/extdev/appapi.html#event-missing-reference>`_
44
+
45
+ :param app: The Sphinx application object
46
+ :param env: The build environment (``app.builder.env`)
47
+ :param node: The ``pending_xref`` node to be resolved. Its attributes reftype,
48
+ reftarget, modname and classname attributes determine the type and
49
+ target of the reference.
50
+ :param contnode: The node that carries the text and formatting inside the
51
+ future reference and should be a child of the returned
52
+ reference node.
53
+ """
54
+ # a good example of how a missing reference handle works look to
55
+ # https://github.com/sphinx-doc/sphinx/issues/1572#issuecomment-68590981
56
+ #
57
+ # Important attributes of the "node":
58
+ #
59
+ # example role: :ref:`title <target>`
60
+ #
61
+ # 'reftype' - role name (in the example above 'ref' is the reftype)
62
+ # 'reftarget' - target of the role, as given in the role content
63
+ # (in the example 'target' is the reftarget
64
+ # 'refexplicit' - the explicit title of the role
65
+ # (in the example 'title' is the refexplicit)
66
+ # 'refdoc' - document in which the role appeared
67
+ # 'refdomain' - domain of the role, in our case emtpy
37
68
38
69
if not hasattr (env , 'class_name_mapping' ):
39
70
env .class_name_mapping = {}
40
71
mapping = env .class_name_mapping
41
72
42
73
reftype = node ['reftype' ]
43
74
reftarget = node ['reftarget' ]
75
+ refexplicit = node .get ('refexplicit' ) # default: None
76
+ refdoc = node .get ('refdoc' , env .docname )
44
77
if reftype in ('obj' , 'class' , 'exc' , 'meth' ):
45
- reftarget = node ['reftarget' ]
46
78
suffix = ''
47
79
if reftarget not in mapping :
48
80
if '.' in reftarget :
49
81
front , suffix = reftarget .rsplit ('.' , 1 )
50
82
else :
83
+ front = None
51
84
suffix = reftarget
52
85
53
86
if suffix .startswith ('_' ) and not suffix .startswith ('__' ):
@@ -56,7 +89,7 @@ def missing_reference_handler(app, env, node, contnode):
56
89
# nitpick warning.
57
90
return node [0 ].deepcopy ()
58
91
59
- if reftype in ('obj' , 'meth' ) and '.' in reftarget :
92
+ if reftype in ('obj' , 'meth' ) and front is not None :
60
93
if front in mapping :
61
94
reftarget = front
62
95
suffix = '.' + suffix
@@ -73,10 +106,10 @@ def missing_reference_handler(app, env, node, contnode):
73
106
if (reftarget not in mapping and
74
107
prefix in inventory ):
75
108
76
- if 'py:class' in inventory [prefix ] and reftarget in inventory [prefix ]['py:class' ]:
109
+ if 'py:class' in inventory [prefix ] and \
110
+ reftarget in inventory [prefix ]['py:class' ]:
77
111
newtarget = inventory [prefix ]['py:class' ][reftarget ][2 ]
78
- if not node ['refexplicit' ] and \
79
- '~' not in node .rawsource :
112
+ if not refexplicit and '~' not in node .rawsource :
80
113
contnode = literal (text = reftarget )
81
114
newnode = reference ('' , '' , internal = True )
82
115
newnode ['reftitle' ] = reftarget
@@ -87,11 +120,10 @@ def missing_reference_handler(app, env, node, contnode):
87
120
88
121
if reftarget in mapping :
89
122
newtarget = mapping [reftarget ] + suffix
90
- if not node [ ' refexplicit' ] and '~' not in node .rawsource :
123
+ if not refexplicit and '~' not in node .rawsource :
91
124
contnode = literal (text = newtarget )
92
- newnode = env .domains ['py' ].resolve_xref (
93
- env , node ['refdoc' ], app .builder , 'class' , newtarget ,
94
- node , contnode )
125
+ newnode = env .domains ['py' ].resolve_xref (env , refdoc , app .builder , 'class' ,
126
+ newtarget , node , contnode )
95
127
if newnode is not None :
96
128
newnode ['reftitle' ] = reftarget
97
129
return newnode
0 commit comments