xref: /aosp_15_r20/prebuilts/build-tools/common/py3-stdlib/pyclbr.py (revision cda5da8d549138a6648c5ee6d7a49cf8f4a657be)
1*cda5da8dSAndroid Build Coastguard Worker"""Parse a Python module and describe its classes and functions.
2*cda5da8dSAndroid Build Coastguard Worker
3*cda5da8dSAndroid Build Coastguard WorkerParse enough of a Python file to recognize imports and class and
4*cda5da8dSAndroid Build Coastguard Workerfunction definitions, and to find out the superclasses of a class.
5*cda5da8dSAndroid Build Coastguard Worker
6*cda5da8dSAndroid Build Coastguard WorkerThe interface consists of a single function:
7*cda5da8dSAndroid Build Coastguard Worker    readmodule_ex(module, path=None)
8*cda5da8dSAndroid Build Coastguard Workerwhere module is the name of a Python module, and path is an optional
9*cda5da8dSAndroid Build Coastguard Workerlist of directories where the module is to be searched.  If present,
10*cda5da8dSAndroid Build Coastguard Workerpath is prepended to the system search path sys.path.  The return value
11*cda5da8dSAndroid Build Coastguard Workeris a dictionary.  The keys of the dictionary are the names of the
12*cda5da8dSAndroid Build Coastguard Workerclasses and functions defined in the module (including classes that are
13*cda5da8dSAndroid Build Coastguard Workerdefined via the from XXX import YYY construct).  The values are
14*cda5da8dSAndroid Build Coastguard Workerinstances of classes Class and Function.  One special key/value pair is
15*cda5da8dSAndroid Build Coastguard Workerpresent for packages: the key '__path__' has a list as its value which
16*cda5da8dSAndroid Build Coastguard Workercontains the package search path.
17*cda5da8dSAndroid Build Coastguard Worker
18*cda5da8dSAndroid Build Coastguard WorkerClasses and Functions have a common superclass: _Object.  Every instance
19*cda5da8dSAndroid Build Coastguard Workerhas the following attributes:
20*cda5da8dSAndroid Build Coastguard Worker    module  -- name of the module;
21*cda5da8dSAndroid Build Coastguard Worker    name    -- name of the object;
22*cda5da8dSAndroid Build Coastguard Worker    file    -- file in which the object is defined;
23*cda5da8dSAndroid Build Coastguard Worker    lineno  -- line in the file where the object's definition starts;
24*cda5da8dSAndroid Build Coastguard Worker    end_lineno -- line in the file where the object's definition ends;
25*cda5da8dSAndroid Build Coastguard Worker    parent  -- parent of this object, if any;
26*cda5da8dSAndroid Build Coastguard Worker    children -- nested objects contained in this object.
27*cda5da8dSAndroid Build Coastguard WorkerThe 'children' attribute is a dictionary mapping names to objects.
28*cda5da8dSAndroid Build Coastguard Worker
29*cda5da8dSAndroid Build Coastguard WorkerInstances of Function describe functions with the attributes from _Object,
30*cda5da8dSAndroid Build Coastguard Workerplus the following:
31*cda5da8dSAndroid Build Coastguard Worker    is_async -- if a function is defined with an 'async' prefix
32*cda5da8dSAndroid Build Coastguard Worker
33*cda5da8dSAndroid Build Coastguard WorkerInstances of Class describe classes with the attributes from _Object,
34*cda5da8dSAndroid Build Coastguard Workerplus the following:
35*cda5da8dSAndroid Build Coastguard Worker    super   -- list of super classes (Class instances if possible);
36*cda5da8dSAndroid Build Coastguard Worker    methods -- mapping of method names to beginning line numbers.
37*cda5da8dSAndroid Build Coastguard WorkerIf the name of a super class is not recognized, the corresponding
38*cda5da8dSAndroid Build Coastguard Workerentry in the list of super classes is not a class instance but a
39*cda5da8dSAndroid Build Coastguard Workerstring giving the name of the super class.  Since import statements
40*cda5da8dSAndroid Build Coastguard Workerare recognized and imported modules are scanned as well, this
41*cda5da8dSAndroid Build Coastguard Workershouldn't happen often.
42*cda5da8dSAndroid Build Coastguard Worker"""
43*cda5da8dSAndroid Build Coastguard Worker
44*cda5da8dSAndroid Build Coastguard Workerimport ast
45*cda5da8dSAndroid Build Coastguard Workerimport sys
46*cda5da8dSAndroid Build Coastguard Workerimport importlib.util
47*cda5da8dSAndroid Build Coastguard Worker
48*cda5da8dSAndroid Build Coastguard Worker__all__ = ["readmodule", "readmodule_ex", "Class", "Function"]
49*cda5da8dSAndroid Build Coastguard Worker
50*cda5da8dSAndroid Build Coastguard Worker_modules = {}  # Initialize cache of modules we've seen.
51*cda5da8dSAndroid Build Coastguard Worker
52*cda5da8dSAndroid Build Coastguard Worker
53*cda5da8dSAndroid Build Coastguard Workerclass _Object:
54*cda5da8dSAndroid Build Coastguard Worker    "Information about Python class or function."
55*cda5da8dSAndroid Build Coastguard Worker    def __init__(self, module, name, file, lineno, end_lineno, parent):
56*cda5da8dSAndroid Build Coastguard Worker        self.module = module
57*cda5da8dSAndroid Build Coastguard Worker        self.name = name
58*cda5da8dSAndroid Build Coastguard Worker        self.file = file
59*cda5da8dSAndroid Build Coastguard Worker        self.lineno = lineno
60*cda5da8dSAndroid Build Coastguard Worker        self.end_lineno = end_lineno
61*cda5da8dSAndroid Build Coastguard Worker        self.parent = parent
62*cda5da8dSAndroid Build Coastguard Worker        self.children = {}
63*cda5da8dSAndroid Build Coastguard Worker        if parent is not None:
64*cda5da8dSAndroid Build Coastguard Worker            parent.children[name] = self
65*cda5da8dSAndroid Build Coastguard Worker
66*cda5da8dSAndroid Build Coastguard Worker
67*cda5da8dSAndroid Build Coastguard Worker# Odd Function and Class signatures are for back-compatibility.
68*cda5da8dSAndroid Build Coastguard Workerclass Function(_Object):
69*cda5da8dSAndroid Build Coastguard Worker    "Information about a Python function, including methods."
70*cda5da8dSAndroid Build Coastguard Worker    def __init__(self, module, name, file, lineno,
71*cda5da8dSAndroid Build Coastguard Worker                 parent=None, is_async=False, *, end_lineno=None):
72*cda5da8dSAndroid Build Coastguard Worker        super().__init__(module, name, file, lineno, end_lineno, parent)
73*cda5da8dSAndroid Build Coastguard Worker        self.is_async = is_async
74*cda5da8dSAndroid Build Coastguard Worker        if isinstance(parent, Class):
75*cda5da8dSAndroid Build Coastguard Worker            parent.methods[name] = lineno
76*cda5da8dSAndroid Build Coastguard Worker
77*cda5da8dSAndroid Build Coastguard Worker
78*cda5da8dSAndroid Build Coastguard Workerclass Class(_Object):
79*cda5da8dSAndroid Build Coastguard Worker    "Information about a Python class."
80*cda5da8dSAndroid Build Coastguard Worker    def __init__(self, module, name, super_, file, lineno,
81*cda5da8dSAndroid Build Coastguard Worker                 parent=None, *, end_lineno=None):
82*cda5da8dSAndroid Build Coastguard Worker        super().__init__(module, name, file, lineno, end_lineno, parent)
83*cda5da8dSAndroid Build Coastguard Worker        self.super = super_ or []
84*cda5da8dSAndroid Build Coastguard Worker        self.methods = {}
85*cda5da8dSAndroid Build Coastguard Worker
86*cda5da8dSAndroid Build Coastguard Worker
87*cda5da8dSAndroid Build Coastguard Worker# These 2 functions are used in these tests
88*cda5da8dSAndroid Build Coastguard Worker# Lib/test/test_pyclbr, Lib/idlelib/idle_test/test_browser.py
89*cda5da8dSAndroid Build Coastguard Workerdef _nest_function(ob, func_name, lineno, end_lineno, is_async=False):
90*cda5da8dSAndroid Build Coastguard Worker    "Return a Function after nesting within ob."
91*cda5da8dSAndroid Build Coastguard Worker    return Function(ob.module, func_name, ob.file, lineno,
92*cda5da8dSAndroid Build Coastguard Worker                    parent=ob, is_async=is_async, end_lineno=end_lineno)
93*cda5da8dSAndroid Build Coastguard Worker
94*cda5da8dSAndroid Build Coastguard Workerdef _nest_class(ob, class_name, lineno, end_lineno, super=None):
95*cda5da8dSAndroid Build Coastguard Worker    "Return a Class after nesting within ob."
96*cda5da8dSAndroid Build Coastguard Worker    return Class(ob.module, class_name, super, ob.file, lineno,
97*cda5da8dSAndroid Build Coastguard Worker                 parent=ob, end_lineno=end_lineno)
98*cda5da8dSAndroid Build Coastguard Worker
99*cda5da8dSAndroid Build Coastguard Worker
100*cda5da8dSAndroid Build Coastguard Workerdef readmodule(module, path=None):
101*cda5da8dSAndroid Build Coastguard Worker    """Return Class objects for the top-level classes in module.
102*cda5da8dSAndroid Build Coastguard Worker
103*cda5da8dSAndroid Build Coastguard Worker    This is the original interface, before Functions were added.
104*cda5da8dSAndroid Build Coastguard Worker    """
105*cda5da8dSAndroid Build Coastguard Worker
106*cda5da8dSAndroid Build Coastguard Worker    res = {}
107*cda5da8dSAndroid Build Coastguard Worker    for key, value in _readmodule(module, path or []).items():
108*cda5da8dSAndroid Build Coastguard Worker        if isinstance(value, Class):
109*cda5da8dSAndroid Build Coastguard Worker            res[key] = value
110*cda5da8dSAndroid Build Coastguard Worker    return res
111*cda5da8dSAndroid Build Coastguard Worker
112*cda5da8dSAndroid Build Coastguard Workerdef readmodule_ex(module, path=None):
113*cda5da8dSAndroid Build Coastguard Worker    """Return a dictionary with all functions and classes in module.
114*cda5da8dSAndroid Build Coastguard Worker
115*cda5da8dSAndroid Build Coastguard Worker    Search for module in PATH + sys.path.
116*cda5da8dSAndroid Build Coastguard Worker    If possible, include imported superclasses.
117*cda5da8dSAndroid Build Coastguard Worker    Do this by reading source, without importing (and executing) it.
118*cda5da8dSAndroid Build Coastguard Worker    """
119*cda5da8dSAndroid Build Coastguard Worker    return _readmodule(module, path or [])
120*cda5da8dSAndroid Build Coastguard Worker
121*cda5da8dSAndroid Build Coastguard Worker
122*cda5da8dSAndroid Build Coastguard Workerdef _readmodule(module, path, inpackage=None):
123*cda5da8dSAndroid Build Coastguard Worker    """Do the hard work for readmodule[_ex].
124*cda5da8dSAndroid Build Coastguard Worker
125*cda5da8dSAndroid Build Coastguard Worker    If inpackage is given, it must be the dotted name of the package in
126*cda5da8dSAndroid Build Coastguard Worker    which we are searching for a submodule, and then PATH must be the
127*cda5da8dSAndroid Build Coastguard Worker    package search path; otherwise, we are searching for a top-level
128*cda5da8dSAndroid Build Coastguard Worker    module, and path is combined with sys.path.
129*cda5da8dSAndroid Build Coastguard Worker    """
130*cda5da8dSAndroid Build Coastguard Worker    # Compute the full module name (prepending inpackage if set).
131*cda5da8dSAndroid Build Coastguard Worker    if inpackage is not None:
132*cda5da8dSAndroid Build Coastguard Worker        fullmodule = "%s.%s" % (inpackage, module)
133*cda5da8dSAndroid Build Coastguard Worker    else:
134*cda5da8dSAndroid Build Coastguard Worker        fullmodule = module
135*cda5da8dSAndroid Build Coastguard Worker
136*cda5da8dSAndroid Build Coastguard Worker    # Check in the cache.
137*cda5da8dSAndroid Build Coastguard Worker    if fullmodule in _modules:
138*cda5da8dSAndroid Build Coastguard Worker        return _modules[fullmodule]
139*cda5da8dSAndroid Build Coastguard Worker
140*cda5da8dSAndroid Build Coastguard Worker    # Initialize the dict for this module's contents.
141*cda5da8dSAndroid Build Coastguard Worker    tree = {}
142*cda5da8dSAndroid Build Coastguard Worker
143*cda5da8dSAndroid Build Coastguard Worker    # Check if it is a built-in module; we don't do much for these.
144*cda5da8dSAndroid Build Coastguard Worker    if module in sys.builtin_module_names and inpackage is None:
145*cda5da8dSAndroid Build Coastguard Worker        _modules[module] = tree
146*cda5da8dSAndroid Build Coastguard Worker        return tree
147*cda5da8dSAndroid Build Coastguard Worker
148*cda5da8dSAndroid Build Coastguard Worker    # Check for a dotted module name.
149*cda5da8dSAndroid Build Coastguard Worker    i = module.rfind('.')
150*cda5da8dSAndroid Build Coastguard Worker    if i >= 0:
151*cda5da8dSAndroid Build Coastguard Worker        package = module[:i]
152*cda5da8dSAndroid Build Coastguard Worker        submodule = module[i+1:]
153*cda5da8dSAndroid Build Coastguard Worker        parent = _readmodule(package, path, inpackage)
154*cda5da8dSAndroid Build Coastguard Worker        if inpackage is not None:
155*cda5da8dSAndroid Build Coastguard Worker            package = "%s.%s" % (inpackage, package)
156*cda5da8dSAndroid Build Coastguard Worker        if not '__path__' in parent:
157*cda5da8dSAndroid Build Coastguard Worker            raise ImportError('No package named {}'.format(package))
158*cda5da8dSAndroid Build Coastguard Worker        return _readmodule(submodule, parent['__path__'], package)
159*cda5da8dSAndroid Build Coastguard Worker
160*cda5da8dSAndroid Build Coastguard Worker    # Search the path for the module.
161*cda5da8dSAndroid Build Coastguard Worker    f = None
162*cda5da8dSAndroid Build Coastguard Worker    if inpackage is not None:
163*cda5da8dSAndroid Build Coastguard Worker        search_path = path
164*cda5da8dSAndroid Build Coastguard Worker    else:
165*cda5da8dSAndroid Build Coastguard Worker        search_path = path + sys.path
166*cda5da8dSAndroid Build Coastguard Worker    spec = importlib.util._find_spec_from_path(fullmodule, search_path)
167*cda5da8dSAndroid Build Coastguard Worker    if spec is None:
168*cda5da8dSAndroid Build Coastguard Worker        raise ModuleNotFoundError(f"no module named {fullmodule!r}", name=fullmodule)
169*cda5da8dSAndroid Build Coastguard Worker    _modules[fullmodule] = tree
170*cda5da8dSAndroid Build Coastguard Worker    # Is module a package?
171*cda5da8dSAndroid Build Coastguard Worker    if spec.submodule_search_locations is not None:
172*cda5da8dSAndroid Build Coastguard Worker        tree['__path__'] = spec.submodule_search_locations
173*cda5da8dSAndroid Build Coastguard Worker    try:
174*cda5da8dSAndroid Build Coastguard Worker        source = spec.loader.get_source(fullmodule)
175*cda5da8dSAndroid Build Coastguard Worker    except (AttributeError, ImportError):
176*cda5da8dSAndroid Build Coastguard Worker        # If module is not Python source, we cannot do anything.
177*cda5da8dSAndroid Build Coastguard Worker        return tree
178*cda5da8dSAndroid Build Coastguard Worker    else:
179*cda5da8dSAndroid Build Coastguard Worker        if source is None:
180*cda5da8dSAndroid Build Coastguard Worker            return tree
181*cda5da8dSAndroid Build Coastguard Worker
182*cda5da8dSAndroid Build Coastguard Worker    fname = spec.loader.get_filename(fullmodule)
183*cda5da8dSAndroid Build Coastguard Worker    return _create_tree(fullmodule, path, fname, source, tree, inpackage)
184*cda5da8dSAndroid Build Coastguard Worker
185*cda5da8dSAndroid Build Coastguard Worker
186*cda5da8dSAndroid Build Coastguard Workerclass _ModuleBrowser(ast.NodeVisitor):
187*cda5da8dSAndroid Build Coastguard Worker    def __init__(self, module, path, file, tree, inpackage):
188*cda5da8dSAndroid Build Coastguard Worker        self.path = path
189*cda5da8dSAndroid Build Coastguard Worker        self.tree = tree
190*cda5da8dSAndroid Build Coastguard Worker        self.file = file
191*cda5da8dSAndroid Build Coastguard Worker        self.module = module
192*cda5da8dSAndroid Build Coastguard Worker        self.inpackage = inpackage
193*cda5da8dSAndroid Build Coastguard Worker        self.stack = []
194*cda5da8dSAndroid Build Coastguard Worker
195*cda5da8dSAndroid Build Coastguard Worker    def visit_ClassDef(self, node):
196*cda5da8dSAndroid Build Coastguard Worker        bases = []
197*cda5da8dSAndroid Build Coastguard Worker        for base in node.bases:
198*cda5da8dSAndroid Build Coastguard Worker            name = ast.unparse(base)
199*cda5da8dSAndroid Build Coastguard Worker            if name in self.tree:
200*cda5da8dSAndroid Build Coastguard Worker                # We know this super class.
201*cda5da8dSAndroid Build Coastguard Worker                bases.append(self.tree[name])
202*cda5da8dSAndroid Build Coastguard Worker            elif len(names := name.split(".")) > 1:
203*cda5da8dSAndroid Build Coastguard Worker                # Super class form is module.class:
204*cda5da8dSAndroid Build Coastguard Worker                # look in module for class.
205*cda5da8dSAndroid Build Coastguard Worker                *_, module, class_ = names
206*cda5da8dSAndroid Build Coastguard Worker                if module in _modules:
207*cda5da8dSAndroid Build Coastguard Worker                    bases.append(_modules[module].get(class_, name))
208*cda5da8dSAndroid Build Coastguard Worker            else:
209*cda5da8dSAndroid Build Coastguard Worker                bases.append(name)
210*cda5da8dSAndroid Build Coastguard Worker
211*cda5da8dSAndroid Build Coastguard Worker        parent = self.stack[-1] if self.stack else None
212*cda5da8dSAndroid Build Coastguard Worker        class_ = Class(self.module, node.name, bases, self.file, node.lineno,
213*cda5da8dSAndroid Build Coastguard Worker                       parent=parent, end_lineno=node.end_lineno)
214*cda5da8dSAndroid Build Coastguard Worker        if parent is None:
215*cda5da8dSAndroid Build Coastguard Worker            self.tree[node.name] = class_
216*cda5da8dSAndroid Build Coastguard Worker        self.stack.append(class_)
217*cda5da8dSAndroid Build Coastguard Worker        self.generic_visit(node)
218*cda5da8dSAndroid Build Coastguard Worker        self.stack.pop()
219*cda5da8dSAndroid Build Coastguard Worker
220*cda5da8dSAndroid Build Coastguard Worker    def visit_FunctionDef(self, node, *, is_async=False):
221*cda5da8dSAndroid Build Coastguard Worker        parent = self.stack[-1] if self.stack else None
222*cda5da8dSAndroid Build Coastguard Worker        function = Function(self.module, node.name, self.file, node.lineno,
223*cda5da8dSAndroid Build Coastguard Worker                            parent, is_async, end_lineno=node.end_lineno)
224*cda5da8dSAndroid Build Coastguard Worker        if parent is None:
225*cda5da8dSAndroid Build Coastguard Worker            self.tree[node.name] = function
226*cda5da8dSAndroid Build Coastguard Worker        self.stack.append(function)
227*cda5da8dSAndroid Build Coastguard Worker        self.generic_visit(node)
228*cda5da8dSAndroid Build Coastguard Worker        self.stack.pop()
229*cda5da8dSAndroid Build Coastguard Worker
230*cda5da8dSAndroid Build Coastguard Worker    def visit_AsyncFunctionDef(self, node):
231*cda5da8dSAndroid Build Coastguard Worker        self.visit_FunctionDef(node, is_async=True)
232*cda5da8dSAndroid Build Coastguard Worker
233*cda5da8dSAndroid Build Coastguard Worker    def visit_Import(self, node):
234*cda5da8dSAndroid Build Coastguard Worker        if node.col_offset != 0:
235*cda5da8dSAndroid Build Coastguard Worker            return
236*cda5da8dSAndroid Build Coastguard Worker
237*cda5da8dSAndroid Build Coastguard Worker        for module in node.names:
238*cda5da8dSAndroid Build Coastguard Worker            try:
239*cda5da8dSAndroid Build Coastguard Worker                try:
240*cda5da8dSAndroid Build Coastguard Worker                    _readmodule(module.name, self.path, self.inpackage)
241*cda5da8dSAndroid Build Coastguard Worker                except ImportError:
242*cda5da8dSAndroid Build Coastguard Worker                    _readmodule(module.name, [])
243*cda5da8dSAndroid Build Coastguard Worker            except (ImportError, SyntaxError):
244*cda5da8dSAndroid Build Coastguard Worker                # If we can't find or parse the imported module,
245*cda5da8dSAndroid Build Coastguard Worker                # too bad -- don't die here.
246*cda5da8dSAndroid Build Coastguard Worker                continue
247*cda5da8dSAndroid Build Coastguard Worker
248*cda5da8dSAndroid Build Coastguard Worker    def visit_ImportFrom(self, node):
249*cda5da8dSAndroid Build Coastguard Worker        if node.col_offset != 0:
250*cda5da8dSAndroid Build Coastguard Worker            return
251*cda5da8dSAndroid Build Coastguard Worker        try:
252*cda5da8dSAndroid Build Coastguard Worker            module = "." * node.level
253*cda5da8dSAndroid Build Coastguard Worker            if node.module:
254*cda5da8dSAndroid Build Coastguard Worker                module += node.module
255*cda5da8dSAndroid Build Coastguard Worker            module = _readmodule(module, self.path, self.inpackage)
256*cda5da8dSAndroid Build Coastguard Worker        except (ImportError, SyntaxError):
257*cda5da8dSAndroid Build Coastguard Worker            return
258*cda5da8dSAndroid Build Coastguard Worker
259*cda5da8dSAndroid Build Coastguard Worker        for name in node.names:
260*cda5da8dSAndroid Build Coastguard Worker            if name.name in module:
261*cda5da8dSAndroid Build Coastguard Worker                self.tree[name.asname or name.name] = module[name.name]
262*cda5da8dSAndroid Build Coastguard Worker            elif name.name == "*":
263*cda5da8dSAndroid Build Coastguard Worker                for import_name, import_value in module.items():
264*cda5da8dSAndroid Build Coastguard Worker                    if import_name.startswith("_"):
265*cda5da8dSAndroid Build Coastguard Worker                        continue
266*cda5da8dSAndroid Build Coastguard Worker                    self.tree[import_name] = import_value
267*cda5da8dSAndroid Build Coastguard Worker
268*cda5da8dSAndroid Build Coastguard Worker
269*cda5da8dSAndroid Build Coastguard Workerdef _create_tree(fullmodule, path, fname, source, tree, inpackage):
270*cda5da8dSAndroid Build Coastguard Worker    mbrowser = _ModuleBrowser(fullmodule, path, fname, tree, inpackage)
271*cda5da8dSAndroid Build Coastguard Worker    mbrowser.visit(ast.parse(source))
272*cda5da8dSAndroid Build Coastguard Worker    return mbrowser.tree
273*cda5da8dSAndroid Build Coastguard Worker
274*cda5da8dSAndroid Build Coastguard Worker
275*cda5da8dSAndroid Build Coastguard Workerdef _main():
276*cda5da8dSAndroid Build Coastguard Worker    "Print module output (default this file) for quick visual check."
277*cda5da8dSAndroid Build Coastguard Worker    import os
278*cda5da8dSAndroid Build Coastguard Worker    try:
279*cda5da8dSAndroid Build Coastguard Worker        mod = sys.argv[1]
280*cda5da8dSAndroid Build Coastguard Worker    except:
281*cda5da8dSAndroid Build Coastguard Worker        mod = __file__
282*cda5da8dSAndroid Build Coastguard Worker    if os.path.exists(mod):
283*cda5da8dSAndroid Build Coastguard Worker        path = [os.path.dirname(mod)]
284*cda5da8dSAndroid Build Coastguard Worker        mod = os.path.basename(mod)
285*cda5da8dSAndroid Build Coastguard Worker        if mod.lower().endswith(".py"):
286*cda5da8dSAndroid Build Coastguard Worker            mod = mod[:-3]
287*cda5da8dSAndroid Build Coastguard Worker    else:
288*cda5da8dSAndroid Build Coastguard Worker        path = []
289*cda5da8dSAndroid Build Coastguard Worker    tree = readmodule_ex(mod, path)
290*cda5da8dSAndroid Build Coastguard Worker    lineno_key = lambda a: getattr(a, 'lineno', 0)
291*cda5da8dSAndroid Build Coastguard Worker    objs = sorted(tree.values(), key=lineno_key, reverse=True)
292*cda5da8dSAndroid Build Coastguard Worker    indent_level = 2
293*cda5da8dSAndroid Build Coastguard Worker    while objs:
294*cda5da8dSAndroid Build Coastguard Worker        obj = objs.pop()
295*cda5da8dSAndroid Build Coastguard Worker        if isinstance(obj, list):
296*cda5da8dSAndroid Build Coastguard Worker            # Value is a __path__ key.
297*cda5da8dSAndroid Build Coastguard Worker            continue
298*cda5da8dSAndroid Build Coastguard Worker        if not hasattr(obj, 'indent'):
299*cda5da8dSAndroid Build Coastguard Worker            obj.indent = 0
300*cda5da8dSAndroid Build Coastguard Worker
301*cda5da8dSAndroid Build Coastguard Worker        if isinstance(obj, _Object):
302*cda5da8dSAndroid Build Coastguard Worker            new_objs = sorted(obj.children.values(),
303*cda5da8dSAndroid Build Coastguard Worker                              key=lineno_key, reverse=True)
304*cda5da8dSAndroid Build Coastguard Worker            for ob in new_objs:
305*cda5da8dSAndroid Build Coastguard Worker                ob.indent = obj.indent + indent_level
306*cda5da8dSAndroid Build Coastguard Worker            objs.extend(new_objs)
307*cda5da8dSAndroid Build Coastguard Worker        if isinstance(obj, Class):
308*cda5da8dSAndroid Build Coastguard Worker            print("{}class {} {} {}"
309*cda5da8dSAndroid Build Coastguard Worker                  .format(' ' * obj.indent, obj.name, obj.super, obj.lineno))
310*cda5da8dSAndroid Build Coastguard Worker        elif isinstance(obj, Function):
311*cda5da8dSAndroid Build Coastguard Worker            print("{}def {} {}".format(' ' * obj.indent, obj.name, obj.lineno))
312*cda5da8dSAndroid Build Coastguard Worker
313*cda5da8dSAndroid Build Coastguard Workerif __name__ == "__main__":
314*cda5da8dSAndroid Build Coastguard Worker    _main()
315