Created
August 30, 2017 09:31
-
-
Save OddBloke/5492041a96a4a2a3a335a492f49cab32 to your computer and use it in GitHub Desktop.
A simple typed_ast script to find nested classes
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Foo: | |
class Bar: | |
class Baz: ... | |
class Lolz: ... | |
def wibble(self): | |
class InMethod: | |
class InInMethod: ... | |
class Eggs: ... | |
def func(): | |
class InFunc: | |
class InInFunc: ... | |
class Second: | |
class InSecond: ... | |
if True: | |
class InIf: | |
class InInIf: ... | |
else: | |
class InElse: | |
class InInElse: ... | |
class Container: | |
if True: | |
class InClassIf: ... |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import sys | |
from typing import Any, List, NamedTuple, Union | |
from typed_ast.ast3 import ( | |
AST, AsyncFunctionDef, ClassDef, FunctionDef, Module, NodeVisitor, | |
iter_fields, parse) | |
scope_creators = (AsyncFunctionDef, FunctionDef, Module) | |
ScopeCreator = Union[AsyncFunctionDef, FunctionDef, Module] | |
class ClassStack(NamedTuple): | |
"""A stack of nested ClassDefs built on top of a root AST.""" | |
root: ScopeCreator | |
classes: List[ClassDef] | |
def push(self, cls: ClassDef) -> None: | |
self.classes.append(cls) | |
def pop(self) -> ClassDef: | |
return self.classes.pop() | |
def __str__(self) -> str: | |
first_char = ' ' if isinstance(self.root, Module) else '*' | |
return '{}{}'.format(first_char, | |
' -> '.join(cls.name for cls in self.classes)) | |
class NestedClassFinder(NodeVisitor): | |
def __init__(self) -> None: | |
self.class_stacks: List[ClassStack] = [] | |
self.output_lines: List[str] = [] | |
def generic_visit(self, node: AST) -> None: | |
if isinstance(node, scope_creators): | |
self.class_stacks.append(ClassStack(node, [])) | |
super().generic_visit(node) | |
if isinstance(node, scope_creators): | |
self.class_stacks.pop() | |
def visit_ClassDef(self, node: ClassDef) -> None: | |
current_stack = self.class_stacks[-1] | |
current_stack.push(node) | |
if len(current_stack.classes) > 1: | |
self.output_lines.append(str(current_stack)) | |
self.generic_visit(node) | |
current_stack.pop() | |
def __str__(self) -> str: | |
return '\n'.join(self.output_lines) | |
if __name__ == '__main__': | |
filename = sys.argv[1] | |
ncf = NestedClassFinder() | |
ncf.visit(parse(open(filename).read())) | |
if len(ncf.output_lines): | |
print('--- {} ---'.format(filename)) | |
print(ncf) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--- nested.py --- | |
Foo -> Bar | |
Foo -> Bar -> Baz | |
Foo -> Lolz | |
*InMethod -> InInMethod | |
Foo -> Eggs | |
*InFunc -> InInFunc | |
Second -> InSecond | |
InIf -> InInIf | |
InElse -> InInElse | |
Container -> InClassIf |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
$ for x in .../cpython/Lib/**.py; do python3 find_nested_classes.py "$x"; done | |
--- /home/daniel/personal_dev/cpython/Lib/argparse.py --- | |
HelpFormatter -> _Section | |
_SubParsersAction -> _ChoicesPseudoAction | |
--- /home/daniel/personal_dev/cpython/Lib/imaplib.py --- | |
IMAP4 -> error | |
IMAP4 -> abort | |
IMAP4 -> readonly | |
--- /home/daniel/personal_dev/cpython/Lib/profile.py --- | |
Profile -> fake_code | |
Profile -> fake_frame | |
--- /home/daniel/personal_dev/cpython/Lib/weakref.py --- | |
finalize -> _Info |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment