File: smart_resolver.py

package info (click to toggle)
python-astropy-helpers 1.3-2~bpo8%2B1
  • links: PTS, VCS
  • area: main
  • in suites: jessie-backports
  • size: 756 kB
  • sloc: python: 5,929; ansic: 88; makefile: 11
file content (93 lines) | stat: -rw-r--r-- 3,712 bytes parent folder | download | duplicates (17)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
# Licensed under a 3-clause BSD style license - see LICENSE.rst
"""
The classes in the astropy docs are documented by their API location,
which is not necessarily where they are defined in the source.  This
causes a problem when certain automated features of the doc build,
such as the inheritance diagrams or the `Bases` list of a class
reference a class by its canonical location rather than its "user"
location.

In the `autodoc-process-docstring` event, a mapping from the actual
name to the API name is maintained.  Later, in the `missing-reference`
event, unresolved references are looked up in this dictionary and
corrected if possible.
"""

from docutils.nodes import literal, reference


def process_docstring(app, what, name, obj, options, lines):
    if isinstance(obj, type):
        env = app.env
        if not hasattr(env, 'class_name_mapping'):
            env.class_name_mapping = {}
        mapping = env.class_name_mapping
        mapping[obj.__module__ + '.' + obj.__name__] = name


def missing_reference_handler(app, env, node, contnode):
    if not hasattr(env, 'class_name_mapping'):
        env.class_name_mapping = {}
    mapping = env.class_name_mapping
    reftype = node['reftype']
    reftarget = node['reftarget']
    if reftype in ('obj', 'class', 'exc', 'meth'):
        reftarget = node['reftarget']
        suffix = ''
        if reftarget not in mapping:
            if '.' in reftarget:
                front, suffix = reftarget.rsplit('.', 1)
            else:
                suffix = reftarget

            if suffix.startswith('_') and not suffix.startswith('__'):
                # If this is a reference to a hidden class or method,
                # we can't link to it, but we don't want to have a
                # nitpick warning.
                return node[0].deepcopy()

            if reftype in ('obj', 'meth') and '.' in reftarget:
                if front in mapping:
                    reftarget = front
                    suffix = '.' + suffix

            if (reftype in ('class', ) and '.' in reftarget and
                    reftarget not in mapping):

                if '.' in front:
                    reftarget, _ = front.rsplit('.', 1)
                    suffix = '.' + suffix
                reftarget = reftarget + suffix
                prefix = reftarget.rsplit('.')[0]
                inventory = env.intersphinx_named_inventory
                if (reftarget not in mapping and
                        prefix in inventory):

                    if reftarget in inventory[prefix]['py:class']:
                        newtarget = inventory[prefix]['py:class'][reftarget][2]
                        if not node['refexplicit'] and \
                                '~' not in node.rawsource:
                            contnode = literal(text=reftarget)
                        newnode = reference('', '', internal=True)
                        newnode['reftitle'] = reftarget
                        newnode['refuri'] = newtarget
                        newnode.append(contnode)

                        return newnode

        if reftarget in mapping:
            newtarget = mapping[reftarget] + suffix
            if not node['refexplicit'] and '~' not in node.rawsource:
                contnode = literal(text=newtarget)
            newnode = env.domains['py'].resolve_xref(
                env, node['refdoc'], app.builder, 'class', newtarget,
                node, contnode)
            if newnode is not None:
                newnode['reftitle'] = reftarget
            return newnode


def setup(app):
    app.connect('autodoc-process-docstring', process_docstring)

    app.connect('missing-reference', missing_reference_handler)