blob: 99a17343fb61fdaa0f13422fa0325b95aeb88a4b [file] [log] [blame]
csabella246ff3b2017-07-03 21:31:25 -04001"""Parse a Python module and describe its classes and functions.
Sjoerd Mullender8cb4b1f1995-07-28 09:30:01 +00002
Guido van Rossum0a6f9542002-12-03 08:14:35 +00003Parse enough of a Python file to recognize imports and class and
csabella246ff3b2017-07-03 21:31:25 -04004function definitions, and to find out the superclasses of a class.
Sjoerd Mullender8cb4b1f1995-07-28 09:30:01 +00005
6The interface consists of a single function:
csabella246ff3b2017-07-03 21:31:25 -04007 readmodule_ex(module, path=None)
Guido van Rossum0a6f9542002-12-03 08:14:35 +00008where module is the name of a Python module, and path is an optional
9list of directories where the module is to be searched. If present,
csabella246ff3b2017-07-03 21:31:25 -040010path is prepended to the system search path sys.path. The return value
11is a dictionary. The keys of the dictionary are the names of the
12classes and functions defined in the module (including classes that are
13defined via the from XXX import YYY construct). The values are
14instances of classes Class and Function. One special key/value pair is
15present for packages: the key '__path__' has a list as its value which
16contains the package search path.
Sjoerd Mullender8cb4b1f1995-07-28 09:30:01 +000017
csabella246ff3b2017-07-03 21:31:25 -040018Classes and Functions have a common superclass: _Object. Every instance
19has the following attributes:
20 module -- name of the module;
21 name -- name of the object;
22 file -- file in which the object is defined;
23 lineno -- line in the file where the object's definition starts;
24 parent -- parent of this object, if any;
25 children -- nested objects contained in this object.
26The 'children' attribute is a dictionary mapping names to objects.
27
28Instances of Function describe functions with the attributes from _Object.
29
30Instances of Class describe classes with the attributes from _Object,
31plus the following:
32 super -- list of super classes (Class instances if possible);
33 methods -- mapping of method names to beginning line numbers.
Sjoerd Mullender8cb4b1f1995-07-28 09:30:01 +000034If the name of a super class is not recognized, the corresponding
35entry in the list of super classes is not a class instance but a
36string giving the name of the super class. Since import statements
37are recognized and imported modules are scanned as well, this
38shouldn't happen often.
Guido van Rossum4b8c6ea2000-02-04 15:39:30 +000039"""
Sjoerd Mullender8cb4b1f1995-07-28 09:30:01 +000040
Brett Cannonee78a2b2012-05-12 17:43:17 -040041import io
Sjoerd Mullender8cb4b1f1995-07-28 09:30:01 +000042import sys
Eric Snow6029e082014-01-25 15:32:46 -070043import importlib.util
Christian Heimes81ee3ef2008-05-04 22:42:01 +000044import tokenize
45from token import NAME, DEDENT, OP
Sjoerd Mullender8cb4b1f1995-07-28 09:30:01 +000046
Guido van Rossum0ed7aa12002-12-02 14:54:20 +000047__all__ = ["readmodule", "readmodule_ex", "Class", "Function"]
Skip Montanaroc62c81e2001-02-12 02:00:42 +000048
csabella246ff3b2017-07-03 21:31:25 -040049_modules = {} # Initialize cache of modules we've seen.
Sjoerd Mullender8cb4b1f1995-07-28 09:30:01 +000050
csabella246ff3b2017-07-03 21:31:25 -040051
52class _Object:
Xtreak0d702272019-06-03 04:42:33 +053053 "Information about Python class or function."
csabella246ff3b2017-07-03 21:31:25 -040054 def __init__(self, module, name, file, lineno, parent):
Tim Peters2344fae2001-01-15 00:50:52 +000055 self.module = module
56 self.name = name
Tim Peters2344fae2001-01-15 00:50:52 +000057 self.file = file
58 self.lineno = lineno
csabella246ff3b2017-07-03 21:31:25 -040059 self.parent = parent
60 self.children = {}
61
62 def _addchild(self, name, obj):
63 self.children[name] = obj
64
65
66class Function(_Object):
67 "Information about a Python function, including methods."
68 def __init__(self, module, name, file, lineno, parent=None):
69 _Object.__init__(self, module, name, file, lineno, parent)
70
71
72class Class(_Object):
73 "Information about a Python class."
74 def __init__(self, module, name, super, file, lineno, parent=None):
75 _Object.__init__(self, module, name, file, lineno, parent)
76 self.super = [] if super is None else super
77 self.methods = {}
Sjoerd Mullender8cb4b1f1995-07-28 09:30:01 +000078
Tim Peters2344fae2001-01-15 00:50:52 +000079 def _addmethod(self, name, lineno):
80 self.methods[name] = lineno
Sjoerd Mullender8cb4b1f1995-07-28 09:30:01 +000081
csabella246ff3b2017-07-03 21:31:25 -040082
83def _nest_function(ob, func_name, lineno):
84 "Return a Function after nesting within ob."
85 newfunc = Function(ob.module, func_name, ob.file, lineno, ob)
86 ob._addchild(func_name, newfunc)
87 if isinstance(ob, Class):
88 ob._addmethod(func_name, lineno)
89 return newfunc
90
91def _nest_class(ob, class_name, lineno, super=None):
92 "Return a Class after nesting within ob."
93 newclass = Class(ob.module, class_name, super, ob.file, lineno, ob)
94 ob._addchild(class_name, newclass)
95 return newclass
Guido van Rossuma3b4a331999-06-10 14:39:39 +000096
Christian Heimes81ee3ef2008-05-04 22:42:01 +000097def readmodule(module, path=None):
csabella246ff3b2017-07-03 21:31:25 -040098 """Return Class objects for the top-level classes in module.
Guido van Rossuma3b4a331999-06-10 14:39:39 +000099
csabella246ff3b2017-07-03 21:31:25 -0400100 This is the original interface, before Functions were added.
101 """
Guido van Rossuma3b4a331999-06-10 14:39:39 +0000102
Tim Peters2344fae2001-01-15 00:50:52 +0000103 res = {}
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000104 for key, value in _readmodule(module, path or []).items():
Guido van Rossum0ed7aa12002-12-02 14:54:20 +0000105 if isinstance(value, Class):
Tim Peters2344fae2001-01-15 00:50:52 +0000106 res[key] = value
107 return res
Guido van Rossuma3b4a331999-06-10 14:39:39 +0000108
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000109def readmodule_ex(module, path=None):
csabella246ff3b2017-07-03 21:31:25 -0400110 """Return a dictionary with all functions and classes in module.
Sjoerd Mullender8cb4b1f1995-07-28 09:30:01 +0000111
csabella246ff3b2017-07-03 21:31:25 -0400112 Search for module in PATH + sys.path.
113 If possible, include imported superclasses.
114 Do this by reading source, without importing (and executing) it.
115 """
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000116 return _readmodule(module, path or [])
Sjoerd Mullender8cb4b1f1995-07-28 09:30:01 +0000117
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000118def _readmodule(module, path, inpackage=None):
csabella246ff3b2017-07-03 21:31:25 -0400119 """Do the hard work for readmodule[_ex].
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000120
csabella246ff3b2017-07-03 21:31:25 -0400121 If inpackage is given, it must be the dotted name of the package in
Guido van Rossum0ed7aa12002-12-02 14:54:20 +0000122 which we are searching for a submodule, and then PATH must be the
123 package search path; otherwise, we are searching for a top-level
csabella246ff3b2017-07-03 21:31:25 -0400124 module, and path is combined with sys.path.
125 """
126 # Compute the full module name (prepending inpackage if set).
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000127 if inpackage is not None:
Guido van Rossum0ed7aa12002-12-02 14:54:20 +0000128 fullmodule = "%s.%s" % (inpackage, module)
129 else:
130 fullmodule = module
131
csabella246ff3b2017-07-03 21:31:25 -0400132 # Check in the cache.
Guido van Rossum0ed7aa12002-12-02 14:54:20 +0000133 if fullmodule in _modules:
134 return _modules[fullmodule]
135
csabella246ff3b2017-07-03 21:31:25 -0400136 # Initialize the dict for this module's contents.
137 tree = {}
Guido van Rossum3d548711999-06-09 15:49:09 +0000138
csabella246ff3b2017-07-03 21:31:25 -0400139 # Check if it is a built-in module; we don't do much for these.
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000140 if module in sys.builtin_module_names and inpackage is None:
csabella246ff3b2017-07-03 21:31:25 -0400141 _modules[module] = tree
142 return tree
Sjoerd Mullender8cb4b1f1995-07-28 09:30:01 +0000143
csabella246ff3b2017-07-03 21:31:25 -0400144 # Check for a dotted module name.
Guido van Rossum0ed7aa12002-12-02 14:54:20 +0000145 i = module.rfind('.')
146 if i >= 0:
147 package = module[:i]
148 submodule = module[i+1:]
Guido van Rossum0a6f9542002-12-03 08:14:35 +0000149 parent = _readmodule(package, path, inpackage)
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000150 if inpackage is not None:
Guido van Rossum0ed7aa12002-12-02 14:54:20 +0000151 package = "%s.%s" % (inpackage, package)
Petri Lehtinen8d886042012-05-18 21:51:11 +0300152 if not '__path__' in parent:
153 raise ImportError('No package named {}'.format(package))
Guido van Rossum0a6f9542002-12-03 08:14:35 +0000154 return _readmodule(submodule, parent['__path__'], package)
Guido van Rossum0ed7aa12002-12-02 14:54:20 +0000155
csabella246ff3b2017-07-03 21:31:25 -0400156 # Search the path for the module.
Tim Peters2344fae2001-01-15 00:50:52 +0000157 f = None
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000158 if inpackage is not None:
Brett Cannonee78a2b2012-05-12 17:43:17 -0400159 search_path = path
Guido van Rossum0ed7aa12002-12-02 14:54:20 +0000160 else:
Brett Cannonee78a2b2012-05-12 17:43:17 -0400161 search_path = path + sys.path
Eric Snow6029e082014-01-25 15:32:46 -0700162 spec = importlib.util._find_spec_from_path(fullmodule, search_path)
Brett Cannon50865892019-03-22 15:16:50 -0700163 if spec is None:
164 raise ModuleNotFoundError(f"no module named {fullmodule!r}", name=fullmodule)
csabella246ff3b2017-07-03 21:31:25 -0400165 _modules[fullmodule] = tree
166 # Is module a package?
Victor Stinner5c13aa12016-03-17 09:06:41 +0100167 if spec.submodule_search_locations is not None:
csabella246ff3b2017-07-03 21:31:25 -0400168 tree['__path__'] = spec.submodule_search_locations
Brett Cannonee78a2b2012-05-12 17:43:17 -0400169 try:
Eric Snow02b9f9d2014-01-06 20:42:59 -0700170 source = spec.loader.get_source(fullmodule)
Brett Cannonee78a2b2012-05-12 17:43:17 -0400171 except (AttributeError, ImportError):
csabella246ff3b2017-07-03 21:31:25 -0400172 # If module is not Python source, we cannot do anything.
173 return tree
Brett Cannon50865892019-03-22 15:16:50 -0700174 else:
175 if source is None:
176 return tree
Sjoerd Mullender8cb4b1f1995-07-28 09:30:01 +0000177
Victor Stinner5c13aa12016-03-17 09:06:41 +0100178 fname = spec.loader.get_filename(fullmodule)
csabella246ff3b2017-07-03 21:31:25 -0400179 return _create_tree(fullmodule, path, fname, source, tree, inpackage)
Victor Stinner5c13aa12016-03-17 09:06:41 +0100180
csabella246ff3b2017-07-03 21:31:25 -0400181
182def _create_tree(fullmodule, path, fname, source, tree, inpackage):
183 """Return the tree for a particular module.
184
185 fullmodule (full module name), inpackage+module, becomes o.module.
186 path is passed to recursive calls of _readmodule.
187 fname becomes o.file.
188 source is tokenized. Imports cause recursive calls to _readmodule.
189 tree is {} or {'__path__': <submodule search locations>}.
190 inpackage, None or string, is passed to recursive calls of _readmodule.
191
192 The effect of recursive calls is mutation of global _modules.
193 """
Brett Cannonee78a2b2012-05-12 17:43:17 -0400194 f = io.StringIO(source)
195
csabella246ff3b2017-07-03 21:31:25 -0400196 stack = [] # Initialize stack of (class, indent) pairs.
Guido van Rossumad380551999-06-07 15:25:18 +0000197
Guido van Rossum040d7ca2002-08-23 01:36:01 +0000198 g = tokenize.generate_tokens(f.readline)
199 try:
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000200 for tokentype, token, start, _end, _line in g:
Guido van Rossum0a6f9542002-12-03 08:14:35 +0000201 if tokentype == DEDENT:
Guido van Rossum040d7ca2002-08-23 01:36:01 +0000202 lineno, thisindent = start
csabella246ff3b2017-07-03 21:31:25 -0400203 # Close previous nested classes and defs.
Guido van Rossum0a6f9542002-12-03 08:14:35 +0000204 while stack and stack[-1][1] >= thisindent:
205 del stack[-1]
206 elif token == 'def':
207 lineno, thisindent = start
csabella246ff3b2017-07-03 21:31:25 -0400208 # Close previous nested classes and defs.
Guido van Rossum0a6f9542002-12-03 08:14:35 +0000209 while stack and stack[-1][1] >= thisindent:
210 del stack[-1]
csabella246ff3b2017-07-03 21:31:25 -0400211 tokentype, func_name, start = next(g)[0:3]
Guido van Rossum040d7ca2002-08-23 01:36:01 +0000212 if tokentype != NAME:
csabella246ff3b2017-07-03 21:31:25 -0400213 continue # Skip def with syntax error.
214 cur_func = None
Guido van Rossum0a6f9542002-12-03 08:14:35 +0000215 if stack:
csabella246ff3b2017-07-03 21:31:25 -0400216 cur_obj = stack[-1][0]
217 cur_func = _nest_function(cur_obj, func_name, lineno)
Guido van Rossum040d7ca2002-08-23 01:36:01 +0000218 else:
csabella246ff3b2017-07-03 21:31:25 -0400219 # It is just a function.
220 cur_func = Function(fullmodule, func_name, fname, lineno)
221 tree[func_name] = cur_func
222 stack.append((cur_func, thisindent))
Guido van Rossum040d7ca2002-08-23 01:36:01 +0000223 elif token == 'class':
224 lineno, thisindent = start
csabella246ff3b2017-07-03 21:31:25 -0400225 # Close previous nested classes and defs.
Guido van Rossum0a6f9542002-12-03 08:14:35 +0000226 while stack and stack[-1][1] >= thisindent:
227 del stack[-1]
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000228 tokentype, class_name, start = next(g)[0:3]
Guido van Rossum040d7ca2002-08-23 01:36:01 +0000229 if tokentype != NAME:
csabella246ff3b2017-07-03 21:31:25 -0400230 continue # Skip class with syntax error.
231 # Parse what follows the class name.
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000232 tokentype, token, start = next(g)[0:3]
Guido van Rossum040d7ca2002-08-23 01:36:01 +0000233 inherit = None
234 if token == '(':
csabella246ff3b2017-07-03 21:31:25 -0400235 names = [] # Initialize list of superclasses.
Guido van Rossum040d7ca2002-08-23 01:36:01 +0000236 level = 1
csabella246ff3b2017-07-03 21:31:25 -0400237 super = [] # Tokens making up current superclass.
Guido van Rossum040d7ca2002-08-23 01:36:01 +0000238 while True:
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000239 tokentype, token, start = next(g)[0:3]
Guido van Rossum040d7ca2002-08-23 01:36:01 +0000240 if token in (')', ',') and level == 1:
241 n = "".join(super)
csabella246ff3b2017-07-03 21:31:25 -0400242 if n in tree:
243 # We know this super class.
244 n = tree[n]
Guido van Rossum040d7ca2002-08-23 01:36:01 +0000245 else:
246 c = n.split('.')
247 if len(c) > 1:
csabella246ff3b2017-07-03 21:31:25 -0400248 # Super class form is module.class:
249 # look in module for class.
Guido van Rossum040d7ca2002-08-23 01:36:01 +0000250 m = c[-2]
251 c = c[-1]
252 if m in _modules:
253 d = _modules[m]
254 if c in d:
255 n = d[c]
256 names.append(n)
Guido van Rossum0a6f9542002-12-03 08:14:35 +0000257 super = []
Guido van Rossum040d7ca2002-08-23 01:36:01 +0000258 if token == '(':
259 level += 1
260 elif token == ')':
261 level -= 1
262 if level == 0:
263 break
264 elif token == ',' and level == 1:
265 pass
csabella246ff3b2017-07-03 21:31:25 -0400266 # Only use NAME and OP (== dot) tokens for type name.
Thomas Wouters4d70c3d2006-06-08 14:42:34 +0000267 elif tokentype in (NAME, OP) and level == 1:
Guido van Rossum040d7ca2002-08-23 01:36:01 +0000268 super.append(token)
csabella246ff3b2017-07-03 21:31:25 -0400269 # Expressions in the base list are not supported.
Guido van Rossum040d7ca2002-08-23 01:36:01 +0000270 inherit = names
csabella246ff3b2017-07-03 21:31:25 -0400271 if stack:
272 cur_obj = stack[-1][0]
273 cur_class = _nest_class(
274 cur_obj, class_name, lineno, inherit)
275 else:
276 cur_class = Class(fullmodule, class_name, inherit,
277 fname, lineno)
278 tree[class_name] = cur_class
Guido van Rossum0a6f9542002-12-03 08:14:35 +0000279 stack.append((cur_class, thisindent))
Guido van Rossum040d7ca2002-08-23 01:36:01 +0000280 elif token == 'import' and start[1] == 0:
281 modules = _getnamelist(g)
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000282 for mod, _mod2 in modules:
Guido van Rossum258cba82002-09-16 16:36:02 +0000283 try:
csabella246ff3b2017-07-03 21:31:25 -0400284 # Recursively read the imported module.
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000285 if inpackage is None:
Guido van Rossum0a6f9542002-12-03 08:14:35 +0000286 _readmodule(mod, path)
Guido van Rossum0ed7aa12002-12-02 14:54:20 +0000287 else:
288 try:
Guido van Rossum0a6f9542002-12-03 08:14:35 +0000289 _readmodule(mod, path, inpackage)
Guido van Rossum0ed7aa12002-12-02 14:54:20 +0000290 except ImportError:
Guido van Rossum0a6f9542002-12-03 08:14:35 +0000291 _readmodule(mod, [])
Guido van Rossum258cba82002-09-16 16:36:02 +0000292 except:
293 # If we can't find or parse the imported module,
294 # too bad -- don't die here.
295 pass
Guido van Rossum040d7ca2002-08-23 01:36:01 +0000296 elif token == 'from' and start[1] == 0:
297 mod, token = _getname(g)
298 if not mod or token != "import":
299 continue
300 names = _getnamelist(g)
Tim Peters2344fae2001-01-15 00:50:52 +0000301 try:
csabella246ff3b2017-07-03 21:31:25 -0400302 # Recursively read the imported module.
Guido van Rossum0a6f9542002-12-03 08:14:35 +0000303 d = _readmodule(mod, path, inpackage)
Tim Peters2344fae2001-01-15 00:50:52 +0000304 except:
Guido van Rossum258cba82002-09-16 16:36:02 +0000305 # If we can't find or parse the imported module,
306 # too bad -- don't die here.
Guido van Rossum040d7ca2002-08-23 01:36:01 +0000307 continue
csabella246ff3b2017-07-03 21:31:25 -0400308 # Add any classes that were defined in the imported module
309 # to our name space if they were mentioned in the list.
Guido van Rossum040d7ca2002-08-23 01:36:01 +0000310 for n, n2 in names:
311 if n in d:
csabella246ff3b2017-07-03 21:31:25 -0400312 tree[n2 or n] = d[n]
Guido van Rossum040d7ca2002-08-23 01:36:01 +0000313 elif n == '*':
csabella246ff3b2017-07-03 21:31:25 -0400314 # Don't add names that start with _.
Guido van Rossum040d7ca2002-08-23 01:36:01 +0000315 for n in d:
Guido van Rossum0a6f9542002-12-03 08:14:35 +0000316 if n[0] != '_':
csabella246ff3b2017-07-03 21:31:25 -0400317 tree[n] = d[n]
Guido van Rossum040d7ca2002-08-23 01:36:01 +0000318 except StopIteration:
319 pass
Guido van Rossumad380551999-06-07 15:25:18 +0000320
Guido van Rossum040d7ca2002-08-23 01:36:01 +0000321 f.close()
csabella246ff3b2017-07-03 21:31:25 -0400322 return tree
323
Guido van Rossumdf9f7a31999-06-08 12:53:21 +0000324
Guido van Rossum040d7ca2002-08-23 01:36:01 +0000325def _getnamelist(g):
csabella246ff3b2017-07-03 21:31:25 -0400326 """Return list of (dotted-name, as-name or None) tuples for token source g.
327
328 An as-name is the name that follows 'as' in an as clause.
329 """
Guido van Rossum040d7ca2002-08-23 01:36:01 +0000330 names = []
331 while True:
332 name, token = _getname(g)
333 if not name:
334 break
335 if token == 'as':
336 name2, token = _getname(g)
337 else:
338 name2 = None
339 names.append((name, name2))
340 while token != "," and "\n" not in token:
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000341 token = next(g)[1]
Guido van Rossum040d7ca2002-08-23 01:36:01 +0000342 if token != ",":
343 break
344 return names
345
csabella246ff3b2017-07-03 21:31:25 -0400346
Guido van Rossum040d7ca2002-08-23 01:36:01 +0000347def _getname(g):
csabella246ff3b2017-07-03 21:31:25 -0400348 "Return (dotted-name or None, next-token) tuple for token source g."
Guido van Rossum040d7ca2002-08-23 01:36:01 +0000349 parts = []
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000350 tokentype, token = next(g)[0:2]
Guido van Rossum040d7ca2002-08-23 01:36:01 +0000351 if tokentype != NAME and token != '*':
352 return (None, token)
353 parts.append(token)
354 while True:
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000355 tokentype, token = next(g)[0:2]
Guido van Rossum040d7ca2002-08-23 01:36:01 +0000356 if token != '.':
357 break
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000358 tokentype, token = next(g)[0:2]
Guido van Rossum040d7ca2002-08-23 01:36:01 +0000359 if tokentype != NAME:
360 break
361 parts.append(token)
362 return (".".join(parts), token)
Guido van Rossum0a6f9542002-12-03 08:14:35 +0000363
csabella246ff3b2017-07-03 21:31:25 -0400364
Guido van Rossum0a6f9542002-12-03 08:14:35 +0000365def _main():
csabella246ff3b2017-07-03 21:31:25 -0400366 "Print module output (default this file) for quick visual check."
Guido van Rossum0a6f9542002-12-03 08:14:35 +0000367 import os
csabella246ff3b2017-07-03 21:31:25 -0400368 try:
369 mod = sys.argv[1]
370 except:
371 mod = __file__
Guido van Rossum0a6f9542002-12-03 08:14:35 +0000372 if os.path.exists(mod):
373 path = [os.path.dirname(mod)]
374 mod = os.path.basename(mod)
375 if mod.lower().endswith(".py"):
376 mod = mod[:-3]
377 else:
378 path = []
csabella246ff3b2017-07-03 21:31:25 -0400379 tree = readmodule_ex(mod, path)
380 lineno_key = lambda a: getattr(a, 'lineno', 0)
381 objs = sorted(tree.values(), key=lineno_key, reverse=True)
382 indent_level = 2
383 while objs:
384 obj = objs.pop()
385 if isinstance(obj, list):
386 # Value is a __path__ key.
387 continue
388 if not hasattr(obj, 'indent'):
389 obj.indent = 0
390
391 if isinstance(obj, _Object):
392 new_objs = sorted(obj.children.values(),
393 key=lineno_key, reverse=True)
394 for ob in new_objs:
395 ob.indent = obj.indent + indent_level
396 objs.extend(new_objs)
Guido van Rossum0a6f9542002-12-03 08:14:35 +0000397 if isinstance(obj, Class):
csabella246ff3b2017-07-03 21:31:25 -0400398 print("{}class {} {} {}"
399 .format(' ' * obj.indent, obj.name, obj.super, obj.lineno))
Guido van Rossum0a6f9542002-12-03 08:14:35 +0000400 elif isinstance(obj, Function):
csabella246ff3b2017-07-03 21:31:25 -0400401 print("{}def {} {}".format(' ' * obj.indent, obj.name, obj.lineno))
Guido van Rossum0a6f9542002-12-03 08:14:35 +0000402
403if __name__ == "__main__":
404 _main()