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