| Guido van Rossum | 0a6f954 | 2002-12-03 08:14:35 +0000 | [diff] [blame] | 1 | """Parse a Python module and describe its classes and methods. | 
| 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 | 
 | 4 | method 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: | 
| Guido van Rossum | 0a6f954 | 2002-12-03 08:14:35 +0000 | [diff] [blame] | 7 |         readmodule_ex(module [, path]) | 
 | 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, | 
 | 10 | path is prepended to the system search path sys.path.  The return | 
 | 11 | value is a dictionary.  The keys of the dictionary are the names of | 
 | 12 | the classes defined in the module (including classes that are defined | 
 | 13 | via the from XXX import YYY construct).  The values are class | 
 | 14 | instances of the class Class defined here.  One special key/value pair | 
 | 15 | is present for packages: the key '__path__' has a list as its value | 
 | 16 | which contains the package search path. | 
| Sjoerd Mullender | 8cb4b1f | 1995-07-28 09:30:01 +0000 | [diff] [blame] | 17 |  | 
 | 18 | A class is described by the class Class in this module.  Instances | 
 | 19 | of this class have the following instance variables: | 
| Guido van Rossum | 0ed7aa1 | 2002-12-02 14:54:20 +0000 | [diff] [blame] | 20 |         module -- the module name | 
| Tim Peters | 2344fae | 2001-01-15 00:50:52 +0000 | [diff] [blame] | 21 |         name -- the name of the class | 
 | 22 |         super -- a list of super classes (Class instances) | 
 | 23 |         methods -- a dictionary of methods | 
 | 24 |         file -- the file in which the class was defined | 
 | 25 |         lineno -- the line in the file on which the class statement occurred | 
| Sjoerd Mullender | 8cb4b1f | 1995-07-28 09:30:01 +0000 | [diff] [blame] | 26 | The dictionary of methods uses the method names as keys and the line | 
 | 27 | numbers on which the method was defined as values. | 
 | 28 | If the name of a super class is not recognized, the corresponding | 
 | 29 | entry in the list of super classes is not a class instance but a | 
 | 30 | string giving the name of the super class.  Since import statements | 
 | 31 | are recognized and imported modules are scanned as well, this | 
 | 32 | shouldn't happen often. | 
 | 33 |  | 
| Guido van Rossum | 0ed7aa1 | 2002-12-02 14:54:20 +0000 | [diff] [blame] | 34 | A function is described by the class Function in this module. | 
 | 35 | Instances of this class have the following instance variables: | 
 | 36 |         module -- the module name | 
 | 37 |         name -- the name of the class | 
 | 38 |         file -- the file in which the class was defined | 
 | 39 |         lineno -- the line in the file on which the class statement occurred | 
| Guido van Rossum | 4b8c6ea | 2000-02-04 15:39:30 +0000 | [diff] [blame] | 40 | """ | 
| Sjoerd Mullender | 8cb4b1f | 1995-07-28 09:30:01 +0000 | [diff] [blame] | 41 |  | 
| Brett Cannon | ee78a2b | 2012-05-12 17:43:17 -0400 | [diff] [blame] | 42 | import io | 
 | 43 | import os | 
| Sjoerd Mullender | 8cb4b1f | 1995-07-28 09:30:01 +0000 | [diff] [blame] | 44 | import sys | 
| Eric Snow | 6029e08 | 2014-01-25 15:32:46 -0700 | [diff] [blame] | 45 | import importlib.util | 
| Christian Heimes | 81ee3ef | 2008-05-04 22:42:01 +0000 | [diff] [blame] | 46 | import tokenize | 
 | 47 | from token import NAME, DEDENT, OP | 
| Raymond Hettinger | 3375fc5 | 2003-12-01 20:12:15 +0000 | [diff] [blame] | 48 | from operator import itemgetter | 
| Sjoerd Mullender | 8cb4b1f | 1995-07-28 09:30:01 +0000 | [diff] [blame] | 49 |  | 
| Guido van Rossum | 0ed7aa1 | 2002-12-02 14:54:20 +0000 | [diff] [blame] | 50 | __all__ = ["readmodule", "readmodule_ex", "Class", "Function"] | 
| Skip Montanaro | c62c81e | 2001-02-12 02:00:42 +0000 | [diff] [blame] | 51 |  | 
| Guido van Rossum | ad38055 | 1999-06-07 15:25:18 +0000 | [diff] [blame] | 52 | _modules = {}                           # cache of modules we've seen | 
| Sjoerd Mullender | 8cb4b1f | 1995-07-28 09:30:01 +0000 | [diff] [blame] | 53 |  | 
 | 54 | # each Python class is represented by an instance of this class | 
 | 55 | class Class: | 
| Tim Peters | 2344fae | 2001-01-15 00:50:52 +0000 | [diff] [blame] | 56 |     '''Class to represent a Python class.''' | 
 | 57 |     def __init__(self, module, name, super, file, lineno): | 
 | 58 |         self.module = module | 
 | 59 |         self.name = name | 
 | 60 |         if super is None: | 
 | 61 |             super = [] | 
 | 62 |         self.super = super | 
 | 63 |         self.methods = {} | 
 | 64 |         self.file = file | 
 | 65 |         self.lineno = lineno | 
| Sjoerd Mullender | 8cb4b1f | 1995-07-28 09:30:01 +0000 | [diff] [blame] | 66 |  | 
| Tim Peters | 2344fae | 2001-01-15 00:50:52 +0000 | [diff] [blame] | 67 |     def _addmethod(self, name, lineno): | 
 | 68 |         self.methods[name] = lineno | 
| Sjoerd Mullender | 8cb4b1f | 1995-07-28 09:30:01 +0000 | [diff] [blame] | 69 |  | 
| Guido van Rossum | 0ed7aa1 | 2002-12-02 14:54:20 +0000 | [diff] [blame] | 70 | class Function: | 
| Tim Peters | 2344fae | 2001-01-15 00:50:52 +0000 | [diff] [blame] | 71 |     '''Class to represent a top-level Python function''' | 
 | 72 |     def __init__(self, module, name, file, lineno): | 
| Guido van Rossum | 0ed7aa1 | 2002-12-02 14:54:20 +0000 | [diff] [blame] | 73 |         self.module = module | 
 | 74 |         self.name = name | 
 | 75 |         self.file = file | 
 | 76 |         self.lineno = lineno | 
| Guido van Rossum | a3b4a33 | 1999-06-10 14:39:39 +0000 | [diff] [blame] | 77 |  | 
| Christian Heimes | 81ee3ef | 2008-05-04 22:42:01 +0000 | [diff] [blame] | 78 | def readmodule(module, path=None): | 
| Tim Peters | 2344fae | 2001-01-15 00:50:52 +0000 | [diff] [blame] | 79 |     '''Backwards compatible interface. | 
| Guido van Rossum | a3b4a33 | 1999-06-10 14:39:39 +0000 | [diff] [blame] | 80 |  | 
| Guido van Rossum | 0ed7aa1 | 2002-12-02 14:54:20 +0000 | [diff] [blame] | 81 |     Call readmodule_ex() and then only keep Class objects from the | 
| Tim Peters | 2344fae | 2001-01-15 00:50:52 +0000 | [diff] [blame] | 82 |     resulting dictionary.''' | 
| Guido van Rossum | a3b4a33 | 1999-06-10 14:39:39 +0000 | [diff] [blame] | 83 |  | 
| Tim Peters | 2344fae | 2001-01-15 00:50:52 +0000 | [diff] [blame] | 84 |     res = {} | 
| Christian Heimes | 81ee3ef | 2008-05-04 22:42:01 +0000 | [diff] [blame] | 85 |     for key, value in _readmodule(module, path or []).items(): | 
| Guido van Rossum | 0ed7aa1 | 2002-12-02 14:54:20 +0000 | [diff] [blame] | 86 |         if isinstance(value, Class): | 
| Tim Peters | 2344fae | 2001-01-15 00:50:52 +0000 | [diff] [blame] | 87 |             res[key] = value | 
 | 88 |     return res | 
| Guido van Rossum | a3b4a33 | 1999-06-10 14:39:39 +0000 | [diff] [blame] | 89 |  | 
| Christian Heimes | 81ee3ef | 2008-05-04 22:42:01 +0000 | [diff] [blame] | 90 | def readmodule_ex(module, path=None): | 
| Tim Peters | 2344fae | 2001-01-15 00:50:52 +0000 | [diff] [blame] | 91 |     '''Read a module file and return a dictionary of classes. | 
| Sjoerd Mullender | 8cb4b1f | 1995-07-28 09:30:01 +0000 | [diff] [blame] | 92 |  | 
| Tim Peters | 2344fae | 2001-01-15 00:50:52 +0000 | [diff] [blame] | 93 |     Search for MODULE in PATH and sys.path, read and parse the | 
 | 94 |     module and return a dictionary with one entry for each class | 
| Guido van Rossum | 0ed7aa1 | 2002-12-02 14:54:20 +0000 | [diff] [blame] | 95 |     found in the module. | 
| Christian Heimes | 81ee3ef | 2008-05-04 22:42:01 +0000 | [diff] [blame] | 96 |     ''' | 
 | 97 |     return _readmodule(module, path or []) | 
| Sjoerd Mullender | 8cb4b1f | 1995-07-28 09:30:01 +0000 | [diff] [blame] | 98 |  | 
| Christian Heimes | 81ee3ef | 2008-05-04 22:42:01 +0000 | [diff] [blame] | 99 | def _readmodule(module, path, inpackage=None): | 
 | 100 |     '''Do the hard work for readmodule[_ex]. | 
 | 101 |  | 
 | 102 |     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] | 103 |     which we are searching for a submodule, and then PATH must be the | 
 | 104 |     package search path; otherwise, we are searching for a top-level | 
 | 105 |     module, and PATH is combined with sys.path. | 
 | 106 |     ''' | 
| Guido van Rossum | 0ed7aa1 | 2002-12-02 14:54:20 +0000 | [diff] [blame] | 107 |     # Compute the full module name (prepending inpackage if set) | 
| Christian Heimes | 81ee3ef | 2008-05-04 22:42:01 +0000 | [diff] [blame] | 108 |     if inpackage is not None: | 
| Guido van Rossum | 0ed7aa1 | 2002-12-02 14:54:20 +0000 | [diff] [blame] | 109 |         fullmodule = "%s.%s" % (inpackage, module) | 
 | 110 |     else: | 
 | 111 |         fullmodule = module | 
 | 112 |  | 
 | 113 |     # Check in the cache | 
 | 114 |     if fullmodule in _modules: | 
 | 115 |         return _modules[fullmodule] | 
 | 116 |  | 
 | 117 |     # Initialize the dict for this module's contents | 
| Tim Peters | 2344fae | 2001-01-15 00:50:52 +0000 | [diff] [blame] | 118 |     dict = {} | 
| Guido van Rossum | 3d54871 | 1999-06-09 15:49:09 +0000 | [diff] [blame] | 119 |  | 
| Guido van Rossum | 0ed7aa1 | 2002-12-02 14:54:20 +0000 | [diff] [blame] | 120 |     # 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] | 121 |     if module in sys.builtin_module_names and inpackage is None: | 
| Tim Peters | 2344fae | 2001-01-15 00:50:52 +0000 | [diff] [blame] | 122 |         _modules[module] = dict | 
 | 123 |         return dict | 
| Sjoerd Mullender | 8cb4b1f | 1995-07-28 09:30:01 +0000 | [diff] [blame] | 124 |  | 
| Guido van Rossum | 0ed7aa1 | 2002-12-02 14:54:20 +0000 | [diff] [blame] | 125 |     # Check for a dotted module name | 
 | 126 |     i = module.rfind('.') | 
 | 127 |     if i >= 0: | 
 | 128 |         package = module[:i] | 
 | 129 |         submodule = module[i+1:] | 
| Guido van Rossum | 0a6f954 | 2002-12-03 08:14:35 +0000 | [diff] [blame] | 130 |         parent = _readmodule(package, path, inpackage) | 
| 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 |             package = "%s.%s" % (inpackage, package) | 
| Petri Lehtinen | 8d88604 | 2012-05-18 21:51:11 +0300 | [diff] [blame] | 133 |         if not '__path__' in parent: | 
 | 134 |             raise ImportError('No package named {}'.format(package)) | 
| Guido van Rossum | 0a6f954 | 2002-12-03 08:14:35 +0000 | [diff] [blame] | 135 |         return _readmodule(submodule, parent['__path__'], package) | 
| Guido van Rossum | 0ed7aa1 | 2002-12-02 14:54:20 +0000 | [diff] [blame] | 136 |  | 
 | 137 |     # Search the path for the module | 
| Tim Peters | 2344fae | 2001-01-15 00:50:52 +0000 | [diff] [blame] | 138 |     f = None | 
| Christian Heimes | 81ee3ef | 2008-05-04 22:42:01 +0000 | [diff] [blame] | 139 |     if inpackage is not None: | 
| Brett Cannon | ee78a2b | 2012-05-12 17:43:17 -0400 | [diff] [blame] | 140 |         search_path = path | 
| Guido van Rossum | 0ed7aa1 | 2002-12-02 14:54:20 +0000 | [diff] [blame] | 141 |     else: | 
| Brett Cannon | ee78a2b | 2012-05-12 17:43:17 -0400 | [diff] [blame] | 142 |         search_path = path + sys.path | 
| Eric Snow | 02b9f9d | 2014-01-06 20:42:59 -0700 | [diff] [blame] | 143 |     # XXX This will change once issue19944 lands. | 
| Eric Snow | 6029e08 | 2014-01-25 15:32:46 -0700 | [diff] [blame] | 144 |     spec = importlib.util._find_spec_from_path(fullmodule, search_path) | 
| Guido van Rossum | 0ed7aa1 | 2002-12-02 14:54:20 +0000 | [diff] [blame] | 145 |     _modules[fullmodule] = dict | 
| Victor Stinner | 5c13aa1 | 2016-03-17 09:06:41 +0100 | [diff] [blame] | 146 |     # is module a package? | 
 | 147 |     if spec.submodule_search_locations is not None: | 
 | 148 |         dict['__path__'] = spec.submodule_search_locations | 
| Brett Cannon | ee78a2b | 2012-05-12 17:43:17 -0400 | [diff] [blame] | 149 |     try: | 
| Eric Snow | 02b9f9d | 2014-01-06 20:42:59 -0700 | [diff] [blame] | 150 |         source = spec.loader.get_source(fullmodule) | 
| Brett Cannon | ee78a2b | 2012-05-12 17:43:17 -0400 | [diff] [blame] | 151 |         if source is None: | 
 | 152 |             return dict | 
 | 153 |     except (AttributeError, ImportError): | 
| Tim Peters | 2344fae | 2001-01-15 00:50:52 +0000 | [diff] [blame] | 154 |         # not Python source, can't do anything with this module | 
| Tim Peters | 2344fae | 2001-01-15 00:50:52 +0000 | [diff] [blame] | 155 |         return dict | 
| Sjoerd Mullender | 8cb4b1f | 1995-07-28 09:30:01 +0000 | [diff] [blame] | 156 |  | 
| Victor Stinner | 5c13aa1 | 2016-03-17 09:06:41 +0100 | [diff] [blame] | 157 |     fname = spec.loader.get_filename(fullmodule) | 
 | 158 |  | 
| Brett Cannon | ee78a2b | 2012-05-12 17:43:17 -0400 | [diff] [blame] | 159 |     f = io.StringIO(source) | 
 | 160 |  | 
| Guido van Rossum | 0a6f954 | 2002-12-03 08:14:35 +0000 | [diff] [blame] | 161 |     stack = [] # stack of (class, indent) pairs | 
| Guido van Rossum | ad38055 | 1999-06-07 15:25:18 +0000 | [diff] [blame] | 162 |  | 
| Guido van Rossum | 040d7ca | 2002-08-23 01:36:01 +0000 | [diff] [blame] | 163 |     g = tokenize.generate_tokens(f.readline) | 
 | 164 |     try: | 
| Christian Heimes | 81ee3ef | 2008-05-04 22:42:01 +0000 | [diff] [blame] | 165 |         for tokentype, token, start, _end, _line in g: | 
| Guido van Rossum | 0a6f954 | 2002-12-03 08:14:35 +0000 | [diff] [blame] | 166 |             if tokentype == DEDENT: | 
| Guido van Rossum | 040d7ca | 2002-08-23 01:36:01 +0000 | [diff] [blame] | 167 |                 lineno, thisindent = start | 
| Guido van Rossum | 0a6f954 | 2002-12-03 08:14:35 +0000 | [diff] [blame] | 168 |                 # close nested classes and defs | 
 | 169 |                 while stack and stack[-1][1] >= thisindent: | 
 | 170 |                     del stack[-1] | 
 | 171 |             elif token == 'def': | 
 | 172 |                 lineno, thisindent = start | 
 | 173 |                 # close previous nested classes and defs | 
 | 174 |                 while stack and stack[-1][1] >= thisindent: | 
 | 175 |                     del stack[-1] | 
| Christian Heimes | 81ee3ef | 2008-05-04 22:42:01 +0000 | [diff] [blame] | 176 |                 tokentype, meth_name, start = next(g)[0:3] | 
| Guido van Rossum | 040d7ca | 2002-08-23 01:36:01 +0000 | [diff] [blame] | 177 |                 if tokentype != NAME: | 
 | 178 |                     continue # Syntax error | 
| Guido van Rossum | 0a6f954 | 2002-12-03 08:14:35 +0000 | [diff] [blame] | 179 |                 if stack: | 
 | 180 |                     cur_class = stack[-1][0] | 
 | 181 |                     if isinstance(cur_class, Class): | 
 | 182 |                         # it's a method | 
 | 183 |                         cur_class._addmethod(meth_name, lineno) | 
 | 184 |                     # else it's a nested def | 
| Guido van Rossum | 040d7ca | 2002-08-23 01:36:01 +0000 | [diff] [blame] | 185 |                 else: | 
 | 186 |                     # it's a function | 
| Christian Heimes | 81ee3ef | 2008-05-04 22:42:01 +0000 | [diff] [blame] | 187 |                     dict[meth_name] = Function(fullmodule, meth_name, | 
 | 188 |                                                fname, lineno) | 
| Guido van Rossum | 0a6f954 | 2002-12-03 08:14:35 +0000 | [diff] [blame] | 189 |                 stack.append((None, thisindent)) # Marker for nested fns | 
| Guido van Rossum | 040d7ca | 2002-08-23 01:36:01 +0000 | [diff] [blame] | 190 |             elif token == 'class': | 
 | 191 |                 lineno, thisindent = start | 
| Guido van Rossum | 0a6f954 | 2002-12-03 08:14:35 +0000 | [diff] [blame] | 192 |                 # close previous nested classes and defs | 
 | 193 |                 while stack and stack[-1][1] >= thisindent: | 
 | 194 |                     del stack[-1] | 
| Christian Heimes | 81ee3ef | 2008-05-04 22:42:01 +0000 | [diff] [blame] | 195 |                 tokentype, class_name, start = next(g)[0:3] | 
| Guido van Rossum | 040d7ca | 2002-08-23 01:36:01 +0000 | [diff] [blame] | 196 |                 if tokentype != NAME: | 
 | 197 |                     continue # Syntax error | 
| Guido van Rossum | 040d7ca | 2002-08-23 01:36:01 +0000 | [diff] [blame] | 198 |                 # parse what follows the class name | 
| Christian Heimes | 81ee3ef | 2008-05-04 22:42:01 +0000 | [diff] [blame] | 199 |                 tokentype, token, start = next(g)[0:3] | 
| Guido van Rossum | 040d7ca | 2002-08-23 01:36:01 +0000 | [diff] [blame] | 200 |                 inherit = None | 
 | 201 |                 if token == '(': | 
 | 202 |                     names = [] # List of superclasses | 
 | 203 |                     # there's a list of superclasses | 
 | 204 |                     level = 1 | 
 | 205 |                     super = [] # Tokens making up current superclass | 
 | 206 |                     while True: | 
| Christian Heimes | 81ee3ef | 2008-05-04 22:42:01 +0000 | [diff] [blame] | 207 |                         tokentype, token, start = next(g)[0:3] | 
| Guido van Rossum | 040d7ca | 2002-08-23 01:36:01 +0000 | [diff] [blame] | 208 |                         if token in (')', ',') and level == 1: | 
 | 209 |                             n = "".join(super) | 
 | 210 |                             if n in dict: | 
 | 211 |                                 # we know this super class | 
 | 212 |                                 n = dict[n] | 
 | 213 |                             else: | 
 | 214 |                                 c = n.split('.') | 
 | 215 |                                 if len(c) > 1: | 
 | 216 |                                     # super class is of the form | 
 | 217 |                                     # module.class: look in module for | 
 | 218 |                                     # class | 
 | 219 |                                     m = c[-2] | 
 | 220 |                                     c = c[-1] | 
 | 221 |                                     if m in _modules: | 
 | 222 |                                         d = _modules[m] | 
 | 223 |                                         if c in d: | 
 | 224 |                                             n = d[c] | 
 | 225 |                             names.append(n) | 
| Guido van Rossum | 0a6f954 | 2002-12-03 08:14:35 +0000 | [diff] [blame] | 226 |                             super = [] | 
| Guido van Rossum | 040d7ca | 2002-08-23 01:36:01 +0000 | [diff] [blame] | 227 |                         if token == '(': | 
 | 228 |                             level += 1 | 
 | 229 |                         elif token == ')': | 
 | 230 |                             level -= 1 | 
 | 231 |                             if level == 0: | 
 | 232 |                                 break | 
 | 233 |                         elif token == ',' and level == 1: | 
 | 234 |                             pass | 
| Thomas Wouters | 4d70c3d | 2006-06-08 14:42:34 +0000 | [diff] [blame] | 235 |                         # only use NAME and OP (== dot) tokens for type name | 
 | 236 |                         elif tokentype in (NAME, OP) and level == 1: | 
| Guido van Rossum | 040d7ca | 2002-08-23 01:36:01 +0000 | [diff] [blame] | 237 |                             super.append(token) | 
| Thomas Wouters | 4d70c3d | 2006-06-08 14:42:34 +0000 | [diff] [blame] | 238 |                         # expressions in the base list are not supported | 
| Guido van Rossum | 040d7ca | 2002-08-23 01:36:01 +0000 | [diff] [blame] | 239 |                     inherit = names | 
| Christian Heimes | 81ee3ef | 2008-05-04 22:42:01 +0000 | [diff] [blame] | 240 |                 cur_class = Class(fullmodule, class_name, inherit, | 
 | 241 |                                   fname, lineno) | 
| Guido van Rossum | 0a6f954 | 2002-12-03 08:14:35 +0000 | [diff] [blame] | 242 |                 if not stack: | 
 | 243 |                     dict[class_name] = cur_class | 
 | 244 |                 stack.append((cur_class, thisindent)) | 
| Guido van Rossum | 040d7ca | 2002-08-23 01:36:01 +0000 | [diff] [blame] | 245 |             elif token == 'import' and start[1] == 0: | 
 | 246 |                 modules = _getnamelist(g) | 
| Christian Heimes | 81ee3ef | 2008-05-04 22:42:01 +0000 | [diff] [blame] | 247 |                 for mod, _mod2 in modules: | 
| Guido van Rossum | 258cba8 | 2002-09-16 16:36:02 +0000 | [diff] [blame] | 248 |                     try: | 
 | 249 |                         # Recursively read the imported module | 
| Christian Heimes | 81ee3ef | 2008-05-04 22:42:01 +0000 | [diff] [blame] | 250 |                         if inpackage is None: | 
| Guido van Rossum | 0a6f954 | 2002-12-03 08:14:35 +0000 | [diff] [blame] | 251 |                             _readmodule(mod, path) | 
| Guido van Rossum | 0ed7aa1 | 2002-12-02 14:54:20 +0000 | [diff] [blame] | 252 |                         else: | 
 | 253 |                             try: | 
| Guido van Rossum | 0a6f954 | 2002-12-03 08:14:35 +0000 | [diff] [blame] | 254 |                                 _readmodule(mod, path, inpackage) | 
| Guido van Rossum | 0ed7aa1 | 2002-12-02 14:54:20 +0000 | [diff] [blame] | 255 |                             except ImportError: | 
| Guido van Rossum | 0a6f954 | 2002-12-03 08:14:35 +0000 | [diff] [blame] | 256 |                                 _readmodule(mod, []) | 
| Guido van Rossum | 258cba8 | 2002-09-16 16:36:02 +0000 | [diff] [blame] | 257 |                     except: | 
 | 258 |                         # If we can't find or parse the imported module, | 
 | 259 |                         # too bad -- don't die here. | 
 | 260 |                         pass | 
| Guido van Rossum | 040d7ca | 2002-08-23 01:36:01 +0000 | [diff] [blame] | 261 |             elif token == 'from' and start[1] == 0: | 
 | 262 |                 mod, token = _getname(g) | 
 | 263 |                 if not mod or token != "import": | 
 | 264 |                     continue | 
 | 265 |                 names = _getnamelist(g) | 
| Tim Peters | 2344fae | 2001-01-15 00:50:52 +0000 | [diff] [blame] | 266 |                 try: | 
| Guido van Rossum | 258cba8 | 2002-09-16 16:36:02 +0000 | [diff] [blame] | 267 |                     # Recursively read the imported module | 
| Guido van Rossum | 0a6f954 | 2002-12-03 08:14:35 +0000 | [diff] [blame] | 268 |                     d = _readmodule(mod, path, inpackage) | 
| Tim Peters | 2344fae | 2001-01-15 00:50:52 +0000 | [diff] [blame] | 269 |                 except: | 
| Guido van Rossum | 258cba8 | 2002-09-16 16:36:02 +0000 | [diff] [blame] | 270 |                     # If we can't find or parse the imported module, | 
 | 271 |                     # too bad -- don't die here. | 
| Guido van Rossum | 040d7ca | 2002-08-23 01:36:01 +0000 | [diff] [blame] | 272 |                     continue | 
 | 273 |                 # add any classes that were defined in the imported module | 
 | 274 |                 # to our name space if they were mentioned in the list | 
 | 275 |                 for n, n2 in names: | 
 | 276 |                     if n in d: | 
 | 277 |                         dict[n2 or n] = d[n] | 
 | 278 |                     elif n == '*': | 
| Guido van Rossum | 0a6f954 | 2002-12-03 08:14:35 +0000 | [diff] [blame] | 279 |                         # don't add names that start with _ | 
| Guido van Rossum | 040d7ca | 2002-08-23 01:36:01 +0000 | [diff] [blame] | 280 |                         for n in d: | 
| Guido van Rossum | 0a6f954 | 2002-12-03 08:14:35 +0000 | [diff] [blame] | 281 |                             if n[0] != '_': | 
| Guido van Rossum | 040d7ca | 2002-08-23 01:36:01 +0000 | [diff] [blame] | 282 |                                 dict[n] = d[n] | 
 | 283 |     except StopIteration: | 
 | 284 |         pass | 
| Guido van Rossum | ad38055 | 1999-06-07 15:25:18 +0000 | [diff] [blame] | 285 |  | 
| Guido van Rossum | 040d7ca | 2002-08-23 01:36:01 +0000 | [diff] [blame] | 286 |     f.close() | 
| Tim Peters | 2344fae | 2001-01-15 00:50:52 +0000 | [diff] [blame] | 287 |     return dict | 
| Guido van Rossum | df9f7a3 | 1999-06-08 12:53:21 +0000 | [diff] [blame] | 288 |  | 
| Guido van Rossum | 040d7ca | 2002-08-23 01:36:01 +0000 | [diff] [blame] | 289 | def _getnamelist(g): | 
 | 290 |     # Helper to get a comma-separated list of dotted names plus 'as' | 
 | 291 |     # clauses.  Return a list of pairs (name, name2) where name2 is | 
 | 292 |     # the 'as' name, or None if there is no 'as' clause. | 
 | 293 |     names = [] | 
 | 294 |     while True: | 
 | 295 |         name, token = _getname(g) | 
 | 296 |         if not name: | 
 | 297 |             break | 
 | 298 |         if token == 'as': | 
 | 299 |             name2, token = _getname(g) | 
 | 300 |         else: | 
 | 301 |             name2 = None | 
 | 302 |         names.append((name, name2)) | 
 | 303 |         while token != "," and "\n" not in token: | 
| Christian Heimes | 81ee3ef | 2008-05-04 22:42:01 +0000 | [diff] [blame] | 304 |             token = next(g)[1] | 
| Guido van Rossum | 040d7ca | 2002-08-23 01:36:01 +0000 | [diff] [blame] | 305 |         if token != ",": | 
 | 306 |             break | 
 | 307 |     return names | 
 | 308 |  | 
 | 309 | def _getname(g): | 
 | 310 |     # Helper to get a dotted name, return a pair (name, token) where | 
 | 311 |     # name is the dotted name, or None if there was no dotted name, | 
 | 312 |     # and token is the next input token. | 
 | 313 |     parts = [] | 
| Christian Heimes | 81ee3ef | 2008-05-04 22:42:01 +0000 | [diff] [blame] | 314 |     tokentype, token = next(g)[0:2] | 
| Guido van Rossum | 040d7ca | 2002-08-23 01:36:01 +0000 | [diff] [blame] | 315 |     if tokentype != NAME and token != '*': | 
 | 316 |         return (None, token) | 
 | 317 |     parts.append(token) | 
 | 318 |     while True: | 
| Christian Heimes | 81ee3ef | 2008-05-04 22:42:01 +0000 | [diff] [blame] | 319 |         tokentype, token = next(g)[0:2] | 
| Guido van Rossum | 040d7ca | 2002-08-23 01:36:01 +0000 | [diff] [blame] | 320 |         if token != '.': | 
 | 321 |             break | 
| Christian Heimes | 81ee3ef | 2008-05-04 22:42:01 +0000 | [diff] [blame] | 322 |         tokentype, token = next(g)[0:2] | 
| Guido van Rossum | 040d7ca | 2002-08-23 01:36:01 +0000 | [diff] [blame] | 323 |         if tokentype != NAME: | 
 | 324 |             break | 
 | 325 |         parts.append(token) | 
 | 326 |     return (".".join(parts), token) | 
| Guido van Rossum | 0a6f954 | 2002-12-03 08:14:35 +0000 | [diff] [blame] | 327 |  | 
 | 328 | def _main(): | 
 | 329 |     # Main program for testing. | 
 | 330 |     import os | 
 | 331 |     mod = sys.argv[1] | 
 | 332 |     if os.path.exists(mod): | 
 | 333 |         path = [os.path.dirname(mod)] | 
 | 334 |         mod = os.path.basename(mod) | 
 | 335 |         if mod.lower().endswith(".py"): | 
 | 336 |             mod = mod[:-3] | 
 | 337 |     else: | 
 | 338 |         path = [] | 
 | 339 |     dict = readmodule_ex(mod, path) | 
| Raymond Hettinger | 8b5eb2f | 2011-01-27 00:06:54 +0000 | [diff] [blame] | 340 |     objs = list(dict.values()) | 
| Raymond Hettinger | d4cb56d | 2008-01-30 02:55:10 +0000 | [diff] [blame] | 341 |     objs.sort(key=lambda a: getattr(a, 'lineno', 0)) | 
| Guido van Rossum | 0a6f954 | 2002-12-03 08:14:35 +0000 | [diff] [blame] | 342 |     for obj in objs: | 
 | 343 |         if isinstance(obj, Class): | 
| Guido van Rossum | be19ed7 | 2007-02-09 05:37:30 +0000 | [diff] [blame] | 344 |             print("class", obj.name, obj.super, obj.lineno) | 
| Guido van Rossum | cc2b016 | 2007-02-11 06:12:03 +0000 | [diff] [blame] | 345 |             methods = sorted(obj.methods.items(), key=itemgetter(1)) | 
| Guido van Rossum | 0a6f954 | 2002-12-03 08:14:35 +0000 | [diff] [blame] | 346 |             for name, lineno in methods: | 
 | 347 |                 if name != "__path__": | 
| Guido van Rossum | be19ed7 | 2007-02-09 05:37:30 +0000 | [diff] [blame] | 348 |                     print("  def", name, lineno) | 
| Guido van Rossum | 0a6f954 | 2002-12-03 08:14:35 +0000 | [diff] [blame] | 349 |         elif isinstance(obj, Function): | 
| Guido van Rossum | be19ed7 | 2007-02-09 05:37:30 +0000 | [diff] [blame] | 350 |             print("def", obj.name, obj.lineno) | 
| Guido van Rossum | 0a6f954 | 2002-12-03 08:14:35 +0000 | [diff] [blame] | 351 |  | 
 | 352 | if __name__ == "__main__": | 
 | 353 |     _main() |