File: functions_missing_types.py

package info (click to toggle)
numpy 1%3A2.2.4%2Bds-1
  • links: PTS, VCS
  • area: main
  • in suites: sid, trixie
  • size: 83,420 kB
  • sloc: python: 248,499; asm: 232,365; ansic: 216,874; cpp: 135,657; f90: 1,540; sh: 938; fortran: 558; makefile: 409; sed: 139; xml: 109; java: 92; perl: 79; cs: 54; javascript: 53; objc: 29; lex: 13; yacc: 9
file content (128 lines) | stat: -rwxr-xr-x 3,044 bytes parent folder | download
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
126
127
128
#!/usr/bin/env python
"""Find the functions in a module missing type annotations.

To use it run

./functions_missing_types.py <module>

and it will print out a list of functions in the module that don't
have types.

"""
import argparse
import ast
import importlib
import os

NUMPY_ROOT = os.path.dirname(os.path.join(
    os.path.abspath(__file__), "..",
))

# Technically "public" functions (they don't start with an underscore)
# that we don't want to include.
EXCLUDE_LIST = {
    "numpy": {
        # Stdlib modules in the namespace by accident
        "absolute_import",
        "division",
        "print_function",
        "warnings",
        "sys",
        "os",
        "math",
        # Accidentally public, deprecated, or shouldn't be used
        "Tester",
        "_core",
        "get_array_wrap",
        "int_asbuffer",
        "numarray",
        "oldnumeric",
        "safe_eval",
        "test",
        "typeDict",
        # Builtins
        "bool",
        "complex",
        "float",
        "int",
        "long",
        "object",
        "str",
        "unicode",
        # More standard names should be preferred
        "alltrue",  # all
        "sometrue",  # any
    }
}


class FindAttributes(ast.NodeVisitor):
    """Find top-level attributes/functions/classes in stubs files.

    Do this by walking the stubs ast. See e.g.

    https://greentreesnakes.readthedocs.io/en/latest/index.html

    for more information on working with Python's ast.

    """

    def __init__(self):
        self.attributes = set()

    def visit_FunctionDef(self, node):
        if node.name == "__getattr__":
            # Not really a module member.
            return
        self.attributes.add(node.name)
        # Do not call self.generic_visit; we are only interested in
        # top-level functions.
        return

    def visit_ClassDef(self, node):
        if not node.name.startswith("_"):
            self.attributes.add(node.name)
        return

    def visit_AnnAssign(self, node):
        self.attributes.add(node.target.id)


def find_missing(module_name):
    module_path = os.path.join(
        NUMPY_ROOT,
        module_name.replace(".", os.sep),
        "__init__.pyi",
    )

    module = importlib.import_module(module_name)
    module_attributes = {
        attribute for attribute in dir(module) if not attribute.startswith("_")
    }

    if os.path.isfile(module_path):
        with open(module_path) as f:
            tree = ast.parse(f.read())
        ast_visitor = FindAttributes()
        ast_visitor.visit(tree)
        stubs_attributes = ast_visitor.attributes
    else:
        # No stubs for this module yet.
        stubs_attributes = set()

    exclude_list = EXCLUDE_LIST.get(module_name, set())

    missing = module_attributes - stubs_attributes - exclude_list
    print("\n".join(sorted(missing)))


def main():
    parser = argparse.ArgumentParser()
    parser.add_argument("module")
    args = parser.parse_args()

    find_missing(args.module)


if __name__ == "__main__":
    main()