Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(245)

Side by Side Diff: third_party/logilab/astroid/manager.py

Issue 753543006: pylint: upgrade to 1.4.0 (Closed) Base URL: https://chromium.googlesource.com/chromium/tools/depot_tools.git@master
Patch Set: Created 6 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « third_party/logilab/astroid/inference.py ('k') | third_party/logilab/astroid/mixins.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 # copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved. 1 # copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
2 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr 2 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
3 # 3 #
4 # This file is part of astroid. 4 # This file is part of astroid.
5 # 5 #
6 # astroid is free software: you can redistribute it and/or modify it 6 # astroid is free software: you can redistribute it and/or modify it
7 # under the terms of the GNU Lesser General Public License as published by the 7 # under the terms of the GNU Lesser General Public License as published by the
8 # Free Software Foundation, either version 2.1 of the License, or (at your 8 # Free Software Foundation, either version 2.1 of the License, or (at your
9 # option) any later version. 9 # option) any later version.
10 # 10 #
11 # astroid is distributed in the hope that it will be useful, but 11 # astroid is distributed in the hope that it will be useful, but
12 # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12 # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 # FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License 13 # FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
14 # for more details. 14 # for more details.
15 # 15 #
16 # You should have received a copy of the GNU Lesser General Public License along 16 # You should have received a copy of the GNU Lesser General Public License along
17 # with astroid. If not, see <http://www.gnu.org/licenses/>. 17 # with astroid. If not, see <http://www.gnu.org/licenses/>.
18 """astroid manager: avoid multiple astroid build of a same module when 18 """astroid manager: avoid multiple astroid build of a same module when
19 possible by providing a class responsible to get astroid representation 19 possible by providing a class responsible to get astroid representation
20 from various source and using a cache of built modules) 20 from various source and using a cache of built modules)
21 """ 21 """
22 from __future__ import print_function
22 23
23 __docformat__ = "restructuredtext en" 24 __docformat__ = "restructuredtext en"
24 25
26 import collections
27 import imp
25 import os 28 import os
26 from os.path import dirname, join, isdir, exists 29 from os.path import dirname, join, isdir, exists
27 from warnings import warn 30 from warnings import warn
31 import zipimport
28 32
29 from logilab.common.configuration import OptionsProviderMixIn 33 from logilab.common.configuration import OptionsProviderMixIn
30 34
31 from astroid.exceptions import AstroidBuildingException 35 from astroid.exceptions import AstroidBuildingException
32 from astroid.modutils import NoSourceFile, is_python_source, \ 36 from astroid import modutils
33 file_from_modpath, load_module_from_name, modpath_from_file, \
34 get_module_files, get_source_file, zipimport
35 37
36 38
37 def astroid_wrapper(func, modname): 39 def astroid_wrapper(func, modname):
38 """wrapper to give to AstroidManager.project_from_files""" 40 """wrapper to give to AstroidManager.project_from_files"""
39 print 'parsing %s...' % modname 41 print('parsing %s...' % modname)
40 try: 42 try:
41 return func(modname) 43 return func(modname)
42 except AstroidBuildingException, exc: 44 except AstroidBuildingException as exc:
43 print exc 45 print(exc)
44 except Exception, exc: 46 except Exception as exc:
45 import traceback 47 import traceback
46 traceback.print_exc() 48 traceback.print_exc()
47 49
48 def _silent_no_wrap(func, modname): 50 def _silent_no_wrap(func, modname):
49 """silent wrapper that doesn't do anything; can be used for tests""" 51 """silent wrapper that doesn't do anything; can be used for tests"""
50 return func(modname) 52 return func(modname)
51 53
52 def safe_repr(obj): 54 def safe_repr(obj):
53 try: 55 try:
54 return repr(obj) 56 return repr(obj)
(...skipping 23 matching lines...) Expand all
78 ) 80 )
79 brain = {} 81 brain = {}
80 def __init__(self): 82 def __init__(self):
81 self.__dict__ = AstroidManager.brain 83 self.__dict__ = AstroidManager.brain
82 if not self.__dict__: 84 if not self.__dict__:
83 OptionsProviderMixIn.__init__(self) 85 OptionsProviderMixIn.__init__(self)
84 self.load_defaults() 86 self.load_defaults()
85 # NOTE: cache entries are added by the [re]builder 87 # NOTE: cache entries are added by the [re]builder
86 self.astroid_cache = {} 88 self.astroid_cache = {}
87 self._mod_file_cache = {} 89 self._mod_file_cache = {}
88 self.transforms = {} 90 self.transforms = collections.defaultdict(list)
91 self._failed_import_hooks = []
92 self.always_load_extensions = False
93 self.extension_package_whitelist = set()
89 94
90 def ast_from_file(self, filepath, modname=None, fallback=True, source=False) : 95 def ast_from_file(self, filepath, modname=None, fallback=True, source=False) :
91 """given a module name, return the astroid object""" 96 """given a module name, return the astroid object"""
92 try: 97 try:
93 filepath = get_source_file(filepath, include_no_ext=True) 98 filepath = modutils.get_source_file(filepath, include_no_ext=True)
94 source = True 99 source = True
95 except NoSourceFile: 100 except modutils.NoSourceFile:
96 pass 101 pass
97 if modname is None: 102 if modname is None:
98 try: 103 try:
99 modname = '.'.join(modpath_from_file(filepath)) 104 modname = '.'.join(modutils.modpath_from_file(filepath))
100 except ImportError: 105 except ImportError:
101 modname = filepath 106 modname = filepath
102 if modname in self.astroid_cache and self.astroid_cache[modname].file == filepath: 107 if modname in self.astroid_cache and self.astroid_cache[modname].file == filepath:
103 return self.astroid_cache[modname] 108 return self.astroid_cache[modname]
104 if source: 109 if source:
105 from astroid.builder import AstroidBuilder 110 from astroid.builder import AstroidBuilder
106 return AstroidBuilder(self).file_build(filepath, modname) 111 return AstroidBuilder(self).file_build(filepath, modname)
107 elif fallback and modname: 112 elif fallback and modname:
108 return self.ast_from_module_name(modname) 113 return self.ast_from_module_name(modname)
109 raise AstroidBuildingException('unable to get astroid for file %s' % 114 raise AstroidBuildingException('unable to get astroid for file %s' %
110 filepath) 115 filepath)
111 116
117 def _build_stub_module(self, modname):
118 from astroid.builder import AstroidBuilder
119 return AstroidBuilder(self).string_build('', modname)
120
121 def _can_load_extension(self, modname):
122 if self.always_load_extensions:
123 return True
124 if modutils.is_standard_module(modname):
125 return True
126 parts = modname.split('.')
127 return any(
128 '.'.join(parts[:x]) in self.extension_package_whitelist
129 for x in range(1, len(parts) + 1))
130
112 def ast_from_module_name(self, modname, context_file=None): 131 def ast_from_module_name(self, modname, context_file=None):
113 """given a module name, return the astroid object""" 132 """given a module name, return the astroid object"""
114 if modname in self.astroid_cache: 133 if modname in self.astroid_cache:
115 return self.astroid_cache[modname] 134 return self.astroid_cache[modname]
116 if modname == '__main__': 135 if modname == '__main__':
117 from astroid.builder import AstroidBuilder 136 return self._build_stub_module(modname)
118 return AstroidBuilder(self).string_build('', modname)
119 old_cwd = os.getcwd() 137 old_cwd = os.getcwd()
120 if context_file: 138 if context_file:
121 os.chdir(dirname(context_file)) 139 os.chdir(dirname(context_file))
122 try: 140 try:
123 filepath = self.file_from_module_name(modname, context_file) 141 filepath, mp_type = self.file_from_module_name(modname, context_file )
124 if filepath is not None and not is_python_source(filepath): 142 if mp_type == modutils.PY_ZIPMODULE:
125 module = self.zip_import_data(filepath) 143 module = self.zip_import_data(filepath)
126 if module is not None: 144 if module is not None:
127 return module 145 return module
128 if filepath is None or not is_python_source(filepath): 146 elif mp_type in (imp.C_BUILTIN, imp.C_EXTENSION):
147 if mp_type == imp.C_EXTENSION and not self._can_load_extension(m odname):
148 return self._build_stub_module(modname)
129 try: 149 try:
130 module = load_module_from_name(modname) 150 module = modutils.load_module_from_name(modname)
131 except Exception, ex: 151 except Exception as ex:
132 msg = 'Unable to load module %s (%s)' % (modname, ex) 152 msg = 'Unable to load module %s (%s)' % (modname, ex)
133 raise AstroidBuildingException(msg) 153 raise AstroidBuildingException(msg)
134 return self.ast_from_module(module, modname) 154 return self.ast_from_module(module, modname)
155 elif mp_type == imp.PY_COMPILED:
156 raise AstroidBuildingException("Unable to load compiled module % s" % (modname,))
157 if filepath is None:
158 raise AstroidBuildingException("Unable to load module %s" % (mod name,))
135 return self.ast_from_file(filepath, modname, fallback=False) 159 return self.ast_from_file(filepath, modname, fallback=False)
160 except AstroidBuildingException as e:
161 for hook in self._failed_import_hooks:
162 try:
163 return hook(modname)
164 except AstroidBuildingException:
165 pass
166 raise e
136 finally: 167 finally:
137 os.chdir(old_cwd) 168 os.chdir(old_cwd)
138 169
139 def zip_import_data(self, filepath): 170 def zip_import_data(self, filepath):
140 if zipimport is None: 171 if zipimport is None:
141 return None 172 return None
142 from astroid.builder import AstroidBuilder 173 from astroid.builder import AstroidBuilder
143 builder = AstroidBuilder(self) 174 builder = AstroidBuilder(self)
144 for ext in ('.zip', '.egg'): 175 for ext in ('.zip', '.egg'):
145 try: 176 try:
146 eggpath, resource = filepath.rsplit(ext + '/', 1) 177 eggpath, resource = filepath.rsplit(ext + os.path.sep, 1)
147 except ValueError: 178 except ValueError:
148 continue 179 continue
149 try: 180 try:
150 importer = zipimport.zipimporter(eggpath + ext) 181 importer = zipimport.zipimporter(eggpath + ext)
151 zmodname = resource.replace('/', '.') 182 zmodname = resource.replace(os.path.sep, '.')
152 if importer.is_package(resource): 183 if importer.is_package(resource):
153 zmodname = zmodname + '.__init__' 184 zmodname = zmodname + '.__init__'
154 module = builder.string_build(importer.get_source(resource), 185 module = builder.string_build(importer.get_source(resource),
155 zmodname, filepath) 186 zmodname, filepath)
156 return module 187 return module
157 except: 188 except:
158 continue 189 continue
159 return None 190 return None
160 191
161 def file_from_module_name(self, modname, contextfile): 192 def file_from_module_name(self, modname, contextfile):
162 try: 193 try:
163 value = self._mod_file_cache[(modname, contextfile)] 194 value = self._mod_file_cache[(modname, contextfile)]
164 except KeyError: 195 except KeyError:
165 try: 196 try:
166 value = file_from_modpath(modname.split('.'), 197 value = modutils.file_info_from_modpath(
167 context_file=contextfile) 198 modname.split('.'), context_file=contextfile)
168 except ImportError, ex: 199 except ImportError as ex:
169 msg = 'Unable to load module %s (%s)' % (modname, ex) 200 msg = 'Unable to load module %s (%s)' % (modname, ex)
170 value = AstroidBuildingException(msg) 201 value = AstroidBuildingException(msg)
171 self._mod_file_cache[(modname, contextfile)] = value 202 self._mod_file_cache[(modname, contextfile)] = value
172 if isinstance(value, AstroidBuildingException): 203 if isinstance(value, AstroidBuildingException):
173 raise value 204 raise value
174 return value 205 return value
175 206
176 def ast_from_module(self, module, modname=None): 207 def ast_from_module(self, module, modname=None):
177 """given an imported module, return the astroid object""" 208 """given an imported module, return the astroid object"""
178 modname = modname or module.__name__ 209 modname = modname or module.__name__
179 if modname in self.astroid_cache: 210 if modname in self.astroid_cache:
180 return self.astroid_cache[modname] 211 return self.astroid_cache[modname]
181 try: 212 try:
182 # some builtin modules don't have __file__ attribute 213 # some builtin modules don't have __file__ attribute
183 filepath = module.__file__ 214 filepath = module.__file__
184 if is_python_source(filepath): 215 if modutils.is_python_source(filepath):
185 return self.ast_from_file(filepath, modname) 216 return self.ast_from_file(filepath, modname)
186 except AttributeError: 217 except AttributeError:
187 pass 218 pass
188 from astroid.builder import AstroidBuilder 219 from astroid.builder import AstroidBuilder
189 return AstroidBuilder(self).module_build(module, modname) 220 return AstroidBuilder(self).module_build(module, modname)
190 221
191 def ast_from_class(self, klass, modname=None): 222 def ast_from_class(self, klass, modname=None):
192 """get astroid for the given class""" 223 """get astroid for the given class"""
193 if modname is None: 224 if modname is None:
194 try: 225 try:
195 modname = klass.__module__ 226 modname = klass.__module__
196 except AttributeError: 227 except AttributeError:
197 raise AstroidBuildingException( 228 raise AstroidBuildingException(
198 'Unable to get module for class %s' % safe_repr(klass)) 229 'Unable to get module for class %s' % safe_repr(klass))
199 modastroid = self.ast_from_module_name(modname) 230 modastroid = self.ast_from_module_name(modname)
200 return modastroid.getattr(klass.__name__)[0] # XXX 231 return modastroid.getattr(klass.__name__)[0] # XXX
201 232
202 233
203 def infer_ast_from_something(self, obj, context=None): 234 def infer_ast_from_something(self, obj, context=None):
204 """infer astroid for the given class""" 235 """infer astroid for the given class"""
205 if hasattr(obj, '__class__') and not isinstance(obj, type): 236 if hasattr(obj, '__class__') and not isinstance(obj, type):
206 klass = obj.__class__ 237 klass = obj.__class__
207 else: 238 else:
208 klass = obj 239 klass = obj
209 try: 240 try:
210 modname = klass.__module__ 241 modname = klass.__module__
211 except AttributeError: 242 except AttributeError:
212 raise AstroidBuildingException( 243 raise AstroidBuildingException(
213 'Unable to get module for %s' % safe_repr(klass)) 244 'Unable to get module for %s' % safe_repr(klass))
214 except Exception, ex: 245 except Exception as ex:
215 raise AstroidBuildingException( 246 raise AstroidBuildingException(
216 'Unexpected error while retrieving module for %s: %s' 247 'Unexpected error while retrieving module for %s: %s'
217 % (safe_repr(klass), ex)) 248 % (safe_repr(klass), ex))
218 try: 249 try:
219 name = klass.__name__ 250 name = klass.__name__
220 except AttributeError: 251 except AttributeError:
221 raise AstroidBuildingException( 252 raise AstroidBuildingException(
222 'Unable to get name for %s' % safe_repr(klass)) 253 'Unable to get name for %s' % safe_repr(klass))
223 except Exception, ex: 254 except Exception as ex:
224 raise AstroidBuildingException( 255 raise AstroidBuildingException(
225 'Unexpected error while retrieving name for %s: %s' 256 'Unexpected error while retrieving name for %s: %s'
226 % (safe_repr(klass), ex)) 257 % (safe_repr(klass), ex))
227 # take care, on living object __module__ is regularly wrong :( 258 # take care, on living object __module__ is regularly wrong :(
228 modastroid = self.ast_from_module_name(modname) 259 modastroid = self.ast_from_module_name(modname)
229 if klass is obj: 260 if klass is obj:
230 for infered in modastroid.igetattr(name, context): 261 for infered in modastroid.igetattr(name, context):
231 yield infered 262 yield infered
232 else: 263 else:
233 for infered in modastroid.igetattr(name, context): 264 for infered in modastroid.igetattr(name, context):
234 yield infered.instanciate_class() 265 yield infered.instanciate_class()
235 266
236 def project_from_files(self, files, func_wrapper=astroid_wrapper, 267 def project_from_files(self, files, func_wrapper=astroid_wrapper,
237 project_name=None, black_list=None): 268 project_name=None, black_list=None):
238 """return a Project from a list of files or modules""" 269 """return a Project from a list of files or modules"""
239 # build the project representation 270 # build the project representation
240 project_name = project_name or self.config.project 271 project_name = project_name or self.config.project
241 black_list = black_list or self.config.black_list 272 black_list = black_list or self.config.black_list
242 project = Project(project_name) 273 project = Project(project_name)
243 for something in files: 274 for something in files:
244 if not exists(something): 275 if not exists(something):
245 fpath = file_from_modpath(something.split('.')) 276 fpath = modutils.file_from_modpath(something.split('.'))
246 elif isdir(something): 277 elif isdir(something):
247 fpath = join(something, '__init__.py') 278 fpath = join(something, '__init__.py')
248 else: 279 else:
249 fpath = something 280 fpath = something
250 astroid = func_wrapper(self.ast_from_file, fpath) 281 astroid = func_wrapper(self.ast_from_file, fpath)
251 if astroid is None: 282 if astroid is None:
252 continue 283 continue
253 # XXX why is first file defining the project.path ? 284 # XXX why is first file defining the project.path ?
254 project.path = project.path or astroid.file 285 project.path = project.path or astroid.file
255 project.add_module(astroid) 286 project.add_module(astroid)
256 base_name = astroid.name 287 base_name = astroid.name
257 # recurse in package except if __init__ was explicitly given 288 # recurse in package except if __init__ was explicitly given
258 if astroid.package and something.find('__init__') == -1: 289 if astroid.package and something.find('__init__') == -1:
259 # recurse on others packages / modules if this is a package 290 # recurse on others packages / modules if this is a package
260 for fpath in get_module_files(dirname(astroid.file), 291 for fpath in modutils.get_module_files(dirname(astroid.file),
261 black_list): 292 black_list):
262 astroid = func_wrapper(self.ast_from_file, fpath) 293 astroid = func_wrapper(self.ast_from_file, fpath)
263 if astroid is None or astroid.name == base_name: 294 if astroid is None or astroid.name == base_name:
264 continue 295 continue
265 project.add_module(astroid) 296 project.add_module(astroid)
266 return project 297 return project
267 298
268 def register_transform(self, node_class, transform, predicate=None): 299 def register_transform(self, node_class, transform, predicate=None):
269 """Register `transform(node)` function to be applied on the given 300 """Register `transform(node)` function to be applied on the given
270 Astroid's `node_class` if `predicate` is None or return a true value 301 Astroid's `node_class` if `predicate` is None or returns true
271 when called with the node as argument. 302 when called with the node as argument.
272 303
273 The transform function may return a value which is then used to 304 The transform function may return a value which is then used to
274 substitute the original node in the tree. 305 substitute the original node in the tree.
275 """ 306 """
276 self.transforms.setdefault(node_class, []).append((transform, predicate) ) 307 self.transforms[node_class].append((transform, predicate))
277 308
278 def unregister_transform(self, node_class, transform, predicate=None): 309 def unregister_transform(self, node_class, transform, predicate=None):
279 """Unregister the given transform.""" 310 """Unregister the given transform."""
280 self.transforms[node_class].remove((transform, predicate)) 311 self.transforms[node_class].remove((transform, predicate))
281 312
313 def register_failed_import_hook(self, hook):
314 """"Registers a hook to resolve imports that cannot be found otherwise.
315
316 `hook` must be a function that accepts a single argument `modname` which
317 contains the name of the module or package that could not be imported.
318 If `hook` can resolve the import, must return a node of type `astroid.Mo dule`,
319 otherwise, it must raise `AstroidBuildingException`.
320 """
321 self._failed_import_hooks.append(hook)
322
282 def transform(self, node): 323 def transform(self, node):
283 """Call matching transforms for the given node if any and return the 324 """Call matching transforms for the given node if any and return the
284 transformed node. 325 transformed node.
285 """ 326 """
286 cls = node.__class__ 327 cls = node.__class__
287 if cls not in self.transforms: 328 if cls not in self.transforms:
288 # no transform registered for this class of node 329 # no transform registered for this class of node
289 return node 330 return node
290 331
291 transforms = self.transforms[cls] 332 transforms = self.transforms[cls]
292 orig_node = node # copy the reference 333 orig_node = node # copy the reference
293 for transform_func, predicate in transforms: 334 for transform_func, predicate in transforms:
294 if predicate is None or predicate(node): 335 if predicate is None or predicate(node):
295 ret = transform_func(node) 336 ret = transform_func(node)
296 # if the transformation function returns something, it's 337 # if the transformation function returns something, it's
297 # expected to be a replacement for the node 338 # expected to be a replacement for the node
298 if ret is not None: 339 if ret is not None:
299 if node is not orig_node: 340 if node is not orig_node:
300 # node has already be modified by some previous 341 # node has already be modified by some previous
301 # transformation, warn about it 342 # transformation, warn about it
302 warn('node %s substituted multiple times' % node) 343 warn('node %s substituted multiple times' % node)
303 node = ret 344 node = ret
304 return node 345 return node
305 346
306 def cache_module(self, module): 347 def cache_module(self, module):
307 """Cache a module if no module with the same name is known yet.""" 348 """Cache a module if no module with the same name is known yet."""
308 self.astroid_cache.setdefault(module.name, module) 349 self.astroid_cache.setdefault(module.name, module)
309 350
310 def clear_cache(self): 351 def clear_cache(self):
352 # XXX clear transforms
311 self.astroid_cache.clear() 353 self.astroid_cache.clear()
312 # force bootstrap again, else we may ends up with cache inconsistency 354 # force bootstrap again, else we may ends up with cache inconsistency
313 # between the manager and CONST_PROXY, making 355 # between the manager and CONST_PROXY, making
314 # unittest_lookup.LookupTC.test_builtin_lookup fail depending on the 356 # unittest_lookup.LookupTC.test_builtin_lookup fail depending on the
315 # test order 357 # test order
316 from astroid.raw_building import astroid_bootstrapping 358 from astroid.raw_building import astroid_bootstrapping
317 astroid_bootstrapping() 359 astroid_bootstrapping()
318 360
319 361
320 class Project(object): 362 class Project(object):
(...skipping 17 matching lines...) Expand all
338 return self.locals[name] 380 return self.locals[name]
339 381
340 def get_children(self): 382 def get_children(self):
341 return self.modules 383 return self.modules
342 384
343 def __repr__(self): 385 def __repr__(self):
344 return '<Project %r at %s (%s modules)>' % (self.name, id(self), 386 return '<Project %r at %s (%s modules)>' % (self.name, id(self),
345 len(self.modules)) 387 len(self.modules))
346 388
347 389
OLDNEW
« no previous file with comments | « third_party/logilab/astroid/inference.py ('k') | third_party/logilab/astroid/mixins.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698