diff options
| author | Josh Wilson <person142@users.noreply.github.com> | 2020-06-17 20:52:26 -0700 |
|---|---|---|
| committer | Josh Wilson <person142@users.noreply.github.com> | 2020-06-17 21:08:23 -0700 |
| commit | a08586a0a6e86148c85d09ffba167c686d52a8b0 (patch) | |
| tree | 60125ff90bff229289e0c6f5fcc14370b6e77456 /tools | |
| parent | 8245b392a344a1ae0db6e569ab68b368ad8883c1 (diff) | |
| download | numpy-a08586a0a6e86148c85d09ffba167c686d52a8b0.tar.gz | |
ENH: add tool to find functions missing types
Closes https://github.com/numpy/numpy/issues/16625.
This is ported from the numpy-stubs here:
https://github.com/numpy/numpy-stubs/blob/master/runtests.py#L94
You run
```
./tools/functions_missing_types.py <module>
```
and it will give you a list of things in that module that don't have
type annotations.
Diffstat (limited to 'tools')
| -rwxr-xr-x | tools/functions_missing_types.py | 129 |
1 files changed, 129 insertions, 0 deletions
diff --git a/tools/functions_missing_types.py b/tools/functions_missing_types.py new file mode 100755 index 000000000..a3f57ddec --- /dev/null +++ b/tools/functions_missing_types.py @@ -0,0 +1,129 @@ +#!/usr/bin/env python +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", + # Accidentally public, deprecated, or shouldn't be used + "Tester", + "add_docstring", + "add_newdoc", + "add_newdoc_ufunc", + "core", + "fastCopyAndTranspose", + "get_array_wrap", + "int_asbuffer", + "oldnumeric", + "safe_eval", + "set_numeric_ops", + "test", + # Builtins + "bool", + "complex", + "float", + "int", + "long", + "object", + "str", + "unicode", + # Should use numpy_financial instead + "fv", + "ipmt", + "irr", + "mirr", + "nper", + "npv", + "pmt", + "ppmt", + "pv", + "rate", + # More standard names should be preferred + "alltrue", # all + "sometrue", # any + } +} + + +class FindAttributes(ast.NodeVisitor): + """Find top-level attributes/functions/classes a stubs file. + + 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() |
