1 # Copyright (c) Microsoft Corporation. All rights reserved.
2 # Licensed under the MIT License.
9 class Visitor(ast.NodeVisitor):
11 self.symbols = {"classes": [], "methods": [], "functions": []}
13 def visit_Module(self, node):
14 self.visitChildren(node)
16 def visitChildren(self, node, namespace=""):
17 for child in node.body:
18 if isinstance(child, ast.FunctionDef):
19 self.visitDef(child, namespace)
20 if isinstance(child, ast.ClassDef):
21 self.visitClassDef(child, namespace)
23 if isinstance(child, ast.AsyncFunctionDef):
24 self.visitDef(child, namespace)
28 def visitDef(self, node, namespace=""):
29 end_position = self.getEndPosition(node)
30 symbol = "functions" if namespace == "" else "methods"
31 self.symbols[symbol].append(self.getDataObject(node, namespace))
33 def visitClassDef(self, node, namespace=""):
34 end_position = self.getEndPosition(node)
35 self.symbols['classes'].append(self.getDataObject(node, namespace))
37 if len(namespace) > 0:
38 namespace = "{0}::{1}".format(namespace, node.name)
41 self.visitChildren(node, namespace)
43 def getDataObject(self, node, namespace=""):
44 end_position = self.getEndPosition(node)
46 "namespace": namespace,
50 "line": node.lineno - 1,
51 "character": node.col_offset
54 "line": end_position[0],
55 "character": end_position[1]
60 def getEndPosition(self, node):
61 if not hasattr(node, 'body') or len(node.body) == 0:
62 return (node.lineno - 1, node.col_offset)
63 return self.getEndPosition(node.body[-1])
66 def provide_symbols(source):
67 """Provides a list of all symbols in provided code.
69 The list comprises of 3-item tuples that contain the starting line number,
70 ending line number and whether the statement is a single line.
73 tree = ast.parse(source)
76 sys.stdout.write(json.dumps(visitor.symbols))
80 if __name__ == "__main__":
81 if len(sys.argv) == 3:
82 contents = sys.argv[2]
84 with open(sys.argv[1], "r") as source:
85 contents = source.read()
88 default_encoding = sys.getdefaultencoding()
89 encoded_contents = contents.encode(default_encoding, 'surrogateescape')
90 contents = encoded_contents.decode(default_encoding, 'replace')
91 except (UnicodeError, LookupError):
93 if isinstance(contents, bytes):
94 contents = contents.decode('utf8')
95 provide_symbols(contents)