8000 ENH: add runtests option to find untyped module attributes (#53) · numpy/numpy-stubs@773130b · GitHub
[go: up one dir, main page]

Skip to content
This repository was archived by the owner on Jun 10, 2020. It is now read-only.

Commit 773130b

Browse files
authored
ENH: add runtests option to find untyped module attributes (#53)
* ENH: add runtests option to find untyped module attributes Now you can run something like ``` ./runtests.py --find-missing numpy ``` to get a list of everything that still needs annotations added. * ENH: Add the ability to blacklist functions we don't want stubs for Various things have accidentally made it into public namespaces, and we don't want to include stubs for those (since the stubs should promote best practices). * MAINT: run black on runtests.py
1 parent d816031 commit 773130b

File tree

1 file changed

+111
-2
lines changed

1 file changed

+111
-2
lines changed

runtests.py

Lines changed: 111 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
#!/usr/bin/env python
2+
import argparse
3+
import ast
4+
import importlib
25
import os
36
import subprocess
47
import sys
@@ -7,14 +10,120 @@
710

811
STUBS_ROOT = os.path.dirname(os.path.abspath(__file__))
912

13+
# Technically "public" functions (they don't start with an underscore)
14+
# that we don't want to include.
15+
BLACKLIST = {
16+
"numpy": {
17+
# Stdlib modules in the namespace by accident
18+
"absolute_import",
19+
"warnings",
20+
# Accidentally public
21+
"add_docstring",
22+
"add_newdoc",
23+
"add_newdoc_ufunc",
24+
# Builtins
25+
"bool",
26+
"complex",
27+
"float",
28+
"int",
29+
"long",
30+
"object",
31+
"str",
32+
"unicode",
33+
# Should use numpy_financial instead
34+
"fv",
35+
"ipmt",
36+
"irr",
37+
"mirr",
38+
"nper",
39+
"npv",
40+
"pmt",
41+
"ppmt",
42+
"pv",
43+
"rate",
44+
}
45+
}
1046

11-
def main():
47+
48+
class FindAttributes(ast.NodeVisitor):
49+
"""Find top-level attributes/functions/classes in the stubs.
50+
51+
Do this by walking the stubs ast. See e.g.
52+
53+
https://greentreesnakes.readthedocs.io/en/latest/index.html
54+
55+
for more information on working with Python's ast.
56+
57+
"""
58+
59+
def __init__(self):
60+
self.attributes = set()
61+
62+
def visit_FunctionDef(self, node):
63+
if node.name == "__getattr__":
64+
# Not really a module member.
65+
return
66+
self.attributes.add(node.name)
67+
# Do not call self.generic_visit; we are only interested in
68+
# top-level functions.
69+
return
70+
71+
def visit_ClassDef(self, node):
72+
if not node.name.startswith("_"):
73+
self.attributes.add(node.name)
74+
return
75+
76+
def visit_AnnAssign(self, node):
77+
self.attributes.add(node.target.id)
78+
79+
80+
def find_missing(module_name):
81+
module_path = os.path.join(
82+
STUBS_ROOT,
83+
module_name.replace("numpy", "numpy-stubs").replace(".", os.sep),
84+
"__init__.pyi",
85+
)
86+
87+
module = importlib.import_module(module_name)
88+
module_attributes = {
89+
attribute for attribute in dir(module) if not attribute.startswith("_")
90+
}
91+
92+
if os.path.isfile(module_path):
93+
with open(module_path) as f:
94+
tree = ast.parse(f.read())
95+
ast_visitor = FindAttributes()
96+
ast_visitor.visit(tree)
97+
stubs_attributes = ast_visitor.attributes
98+
else:
99+
# No stubs for this module yet.
100+
stubs_attributes = set()
101+
102+
blacklist = BLACKLIST.get(module_name, set())
103+
104+
missing = module_attributes - stubs_attributes - blacklist
105+
print("\n".join(sorted(missing)))
106+
107+
108+
def run_pytest(argv):
12109
subprocess.run(
13110
[sys.executable, "-m", "pip", "install", STUBS_ROOT],
14111
capture_output=True,
15112
check=True,
16113
)
17-
sys.exit(pytest.main([STUBS_ROOT] + sys.argv[1:]))
114+
return pytest.main([STUBS_ROOT] + argv)
115+
116+
117+
def main():
118+
parser = argparse.ArgumentParser()
119+
parser.add_argument("--find-missing")
120+
args, remaining_argv = parser.parse_known_args()
121+
122+
if args.find_missing is not None:
123+
find_missing(args.find_missing)
124+
sys.exit(0)
125+
126+
sys.exit(run_pytest(remaining_argv))
18127

19128

20129
if __name__ == "__main__":

0 commit comments

Comments
 (0)
0