| 13 | |
| 14 | |
| 15 | class Visitor(ast.NodeVisitor): |
| 16 | def __init__(self, filepath, output, existing_allowed): |
| 17 | self.filepath = filepath |
| 18 | self.context = list(filepath.with_suffix("").relative_to(lib).parts) |
| 19 | self.output = output |
| 20 | self.existing_allowed = existing_allowed |
| 21 | |
| 22 | def _is_already_allowed(self, parts): |
| 23 | # Skip outputting a path if it's already allowed before. |
| 24 | candidates = ['.'.join(parts[:s]) for s in range(1, len(parts))] |
| 25 | for allow in self.existing_allowed: |
| 26 | if any(allow.fullmatch(path) for path in candidates): |
| 27 | return True |
| 28 | return False |
| 29 | |
| 30 | def visit_FunctionDef(self, node): |
| 31 | # delete_parameter adds a private sentinel value that leaks |
| 32 | # we do not want that sentinel value in the type hints but it breaks typing |
| 33 | # Does not apply to variadic arguments (args/kwargs) |
| 34 | for dec in node.decorator_list: |
| 35 | if "delete_parameter" in ast.unparse(dec): |
| 36 | deprecated_arg = dec.args[1].value |
| 37 | if ( |
| 38 | node.args.vararg is not None |
| 39 | and node.args.vararg.arg == deprecated_arg |
| 40 | ): |
| 41 | continue |
| 42 | if ( |
| 43 | node.args.kwarg is not None |
| 44 | and node.args.kwarg.arg == deprecated_arg |
| 45 | ): |
| 46 | continue |
| 47 | |
| 48 | parents = [] |
| 49 | if hasattr(node, "parent"): |
| 50 | parent = node.parent |
| 51 | while hasattr(parent, "parent") and not isinstance( |
| 52 | parent, ast.Module |
| 53 | ): |
| 54 | parents.insert(0, parent.name) |
| 55 | parent = parent.parent |
| 56 | parts = [*self.context, *parents, node.name] |
| 57 | if not self._is_already_allowed(parts): |
| 58 | self.output.write("\\.".join(parts) + "\n") |
| 59 | break |
| 60 | |
| 61 | def visit_ClassDef(self, node): |
| 62 | for dec in node.decorator_list: |
| 63 | if "define_aliases" in ast.unparse(dec): |
| 64 | parents = [] |
| 65 | if hasattr(node, "parent"): |
| 66 | parent = node.parent |
| 67 | while hasattr(parent, "parent") and not isinstance( |
| 68 | parent, ast.Module |
| 69 | ): |
| 70 | parents.insert(0, parent.name) |
| 71 | parent = parent.parent |
| 72 | aliases = ast.literal_eval(dec.args[0]) |
no outgoing calls
no test coverage detected
searching dependent graphs…