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