File: preprocess.py

package info (click to toggle)
kivy 2.3.1-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 35,316 kB
  • sloc: python: 80,678; ansic: 5,326; javascript: 780; objc: 725; lisp: 195; sh: 173; makefile: 150
file content (125 lines) | stat: -rw-r--r-- 4,456 bytes parent folder | download | duplicates (2)
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
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
'''
Extension for enhancing sphinx documentation generation for cython module
'''

import re
import types
import sys
from os.path import dirname, join
import sphinx
from sphinx.ext.autodoc import MethodDocumenter

class CythonMethodDocumenter(MethodDocumenter):
    # XXX I don't understand the impact of having a priority more than the
    # attribute or instance method but the thing is, if it's a cython module,
    # the attribute will be preferred over method.
    priority = 12

def is_cython_extension(what, obj):
    # try to check if the first line of the doc is a signature
    doc = obj.__doc__
    if not doc:
        return False
    doc = doc.split('\n')
    if not len(doc):
        return False
    doc = doc[0]

    # an identifier starts with a letter or underscore
    # followed by optional numbers or letters or underscores
    identifier_pattern = r"([a-zA-Z_][a-zA-Z0-9_]*)"
    params_pattern = r"\((.*)\)"

    # test for cython cpdef
    if what in ('attribute', 'method') and hasattr(obj, '__objclass__'):
        # match identifier.identifier(anything).
        return re.match(
                r"^" + identifier_pattern
                + r"\." + identifier_pattern + params_pattern,
                doc)
    # test for cython class
    if what == 'class' and hasattr(obj, '__pyx_vtable__'):
        # match identifier(anything)
        return re.match(r"^" + identifier_pattern + params_pattern, doc)

    # test for python method in cython class
    if what in ('method', 'function') and obj.__class__ == types.BuiltinFunctionType:
        # match identifier(anything) where
        return re.match(r"^" + identifier_pattern + params_pattern, doc)

def callback_docstring(app, what, name, obj, options, lines):
    if what == 'module':
        # remove empty lines
        while len(lines):
            line = lines[0].strip()
            if not line.startswith('.. _') and line != '':
                break
            lines.pop(0)

        # if we still have lines, remove the title
        if len(lines):
            lines.pop(0)

        # if the title is followed by a separator, remove it.
        if len(lines) and lines[0].startswith('=='):
            lines.pop(0)

    elif is_cython_extension(what, obj) and lines:
        if what == 'class':
            lines.pop(0)
        line = lines.pop(0)

        # trick to realign the first line to the second one.
        # FIXME: fail if we finishing with::
        line_with_text = [x for x in lines if len(x.strip())]
        if len(line_with_text) and line is not None and len(lines):
            l = len(line_with_text[0]) - len(line_with_text[0].lstrip())
        else:
            l = 0
        lines.insert(0, ' ' * l + line)

        # calculate the minimum space available
        min_space = 999
        for line in lines:
            if not line.strip():
                continue
            min_space = min(min_space, len(line) - len(line.lstrip()))

        # remove that kind of space now.
        if min_space > 0:
            spaces = ' ' * min_space
            for idx, line in enumerate(lines):
                if not line.strip():
                    continue
                if not line.startswith(spaces):
                    continue
                lines[idx] = line[min_space:]

def callback_signature(app, what, name, obj, options, signature,
                       return_annotation):
    # remove the first 'self' argument, because python autodoc don't
    # add it for python method class. So do the same for cython class.
    if is_cython_extension(what, obj):
        try:
            doc = obj.__doc__.split('\n').pop(0)
            doc = '(%s' % doc.split('(')[1]
            doc = doc.replace('(self, ', '(')
            doc = doc.replace('(self)', '( )')
            return doc, None
        except AttributeError:
            pass
        except IndexError:
            pass

def setup(app):
    import kivy
    sys.path += [join(dirname(kivy.__file__), 'extras')]
    from highlight import KivyLexer

    if sphinx.version_info[0] >= 3:
        app.add_lexer('kv', KivyLexer)
    else:
        app.add_lexer('kv', KivyLexer())
    app.add_autodocumenter(CythonMethodDocumenter)
    app.connect('autodoc-process-docstring', callback_docstring)
    app.connect('autodoc-process-signature', callback_signature)