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

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

Issue 739393004: Revert "Revert "pylint: upgrade to 1.3.1"" (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/depot_tools/
Patch Set: Created 6 years, 1 month 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 | Annotate | Revision Log
« no previous file with comments | « third_party/logilab/astroid/inspector.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
(Empty)
1 # copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
2 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
3 #
4 # This file is part of astroid.
5 #
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
8 # Free Software Foundation, either version 2.1 of the License, or (at your
9 # option) any later version.
10 #
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
13 # FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
14 # for more details.
15 #
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/>.
18 """astroid manager: avoid multiple astroid build of a same module when
19 possible by providing a class responsible to get astroid representation
20 from various source and using a cache of built modules)
21 """
22
23 __docformat__ = "restructuredtext en"
24
25 import os
26 from os.path import dirname, join, isdir, exists
27 from warnings import warn
28
29 from logilab.common.configuration import OptionsProviderMixIn
30
31 from astroid.exceptions import AstroidBuildingException
32 from astroid.modutils import NoSourceFile, is_python_source, \
33 file_from_modpath, load_module_from_name, modpath_from_file, \
34 get_module_files, get_source_file, zipimport
35
36
37 def astroid_wrapper(func, modname):
38 """wrapper to give to AstroidManager.project_from_files"""
39 print 'parsing %s...' % modname
40 try:
41 return func(modname)
42 except AstroidBuildingException, exc:
43 print exc
44 except Exception, exc:
45 import traceback
46 traceback.print_exc()
47
48 def _silent_no_wrap(func, modname):
49 """silent wrapper that doesn't do anything; can be used for tests"""
50 return func(modname)
51
52 def safe_repr(obj):
53 try:
54 return repr(obj)
55 except:
56 return '???'
57
58
59
60 class AstroidManager(OptionsProviderMixIn):
61 """the astroid manager, responsible to build astroid from files
62 or modules.
63
64 Use the Borg pattern.
65 """
66
67 name = 'astroid loader'
68 options = (("ignore",
69 {'type' : "csv", 'metavar' : "<file>",
70 'dest' : "black_list", "default" : ('CVS',),
71 'help' : "add <file> (may be a directory) to the black list\
72 . It should be a base name, not a path. You may set this option multiple times\
73 ."}),
74 ("project",
75 {'default': "No Name", 'type' : 'string', 'short': 'p',
76 'metavar' : '<project name>',
77 'help' : 'set the project name.'}),
78 )
79 brain = {}
80 def __init__(self):
81 self.__dict__ = AstroidManager.brain
82 if not self.__dict__:
83 OptionsProviderMixIn.__init__(self)
84 self.load_defaults()
85 # NOTE: cache entries are added by the [re]builder
86 self.astroid_cache = {}
87 self._mod_file_cache = {}
88 self.transforms = {}
89
90 def ast_from_file(self, filepath, modname=None, fallback=True, source=False) :
91 """given a module name, return the astroid object"""
92 try:
93 filepath = get_source_file(filepath, include_no_ext=True)
94 source = True
95 except NoSourceFile:
96 pass
97 if modname is None:
98 try:
99 modname = '.'.join(modpath_from_file(filepath))
100 except ImportError:
101 modname = filepath
102 if modname in self.astroid_cache and self.astroid_cache[modname].file == filepath:
103 return self.astroid_cache[modname]
104 if source:
105 from astroid.builder import AstroidBuilder
106 return AstroidBuilder(self).file_build(filepath, modname)
107 elif fallback and modname:
108 return self.ast_from_module_name(modname)
109 raise AstroidBuildingException('unable to get astroid for file %s' %
110 filepath)
111
112 def ast_from_module_name(self, modname, context_file=None):
113 """given a module name, return the astroid object"""
114 if modname in self.astroid_cache:
115 return self.astroid_cache[modname]
116 if modname == '__main__':
117 from astroid.builder import AstroidBuilder
118 return AstroidBuilder(self).string_build('', modname)
119 old_cwd = os.getcwd()
120 if context_file:
121 os.chdir(dirname(context_file))
122 try:
123 filepath = self.file_from_module_name(modname, context_file)
124 if filepath is not None and not is_python_source(filepath):
125 module = self.zip_import_data(filepath)
126 if module is not None:
127 return module
128 if filepath is None or not is_python_source(filepath):
129 try:
130 module = load_module_from_name(modname)
131 except Exception, ex:
132 msg = 'Unable to load module %s (%s)' % (modname, ex)
133 raise AstroidBuildingException(msg)
134 return self.ast_from_module(module, modname)
135 return self.ast_from_file(filepath, modname, fallback=False)
136 finally:
137 os.chdir(old_cwd)
138
139 def zip_import_data(self, filepath):
140 if zipimport is None:
141 return None
142 from astroid.builder import AstroidBuilder
143 builder = AstroidBuilder(self)
144 for ext in ('.zip', '.egg'):
145 try:
146 eggpath, resource = filepath.rsplit(ext + '/', 1)
147 except ValueError:
148 continue
149 try:
150 importer = zipimport.zipimporter(eggpath + ext)
151 zmodname = resource.replace('/', '.')
152 if importer.is_package(resource):
153 zmodname = zmodname + '.__init__'
154 module = builder.string_build(importer.get_source(resource),
155 zmodname, filepath)
156 return module
157 except:
158 continue
159 return None
160
161 def file_from_module_name(self, modname, contextfile):
162 try:
163 value = self._mod_file_cache[(modname, contextfile)]
164 except KeyError:
165 try:
166 value = file_from_modpath(modname.split('.'),
167 context_file=contextfile)
168 except ImportError, ex:
169 msg = 'Unable to load module %s (%s)' % (modname, ex)
170 value = AstroidBuildingException(msg)
171 self._mod_file_cache[(modname, contextfile)] = value
172 if isinstance(value, AstroidBuildingException):
173 raise value
174 return value
175
176 def ast_from_module(self, module, modname=None):
177 """given an imported module, return the astroid object"""
178 modname = modname or module.__name__
179 if modname in self.astroid_cache:
180 return self.astroid_cache[modname]
181 try:
182 # some builtin modules don't have __file__ attribute
183 filepath = module.__file__
184 if is_python_source(filepath):
185 return self.ast_from_file(filepath, modname)
186 except AttributeError:
187 pass
188 from astroid.builder import AstroidBuilder
189 return AstroidBuilder(self).module_build(module, modname)
190
191 def ast_from_class(self, klass, modname=None):
192 """get astroid for the given class"""
193 if modname is None:
194 try:
195 modname = klass.__module__
196 except AttributeError:
197 raise AstroidBuildingException(
198 'Unable to get module for class %s' % safe_repr(klass))
199 modastroid = self.ast_from_module_name(modname)
200 return modastroid.getattr(klass.__name__)[0] # XXX
201
202
203 def infer_ast_from_something(self, obj, context=None):
204 """infer astroid for the given class"""
205 if hasattr(obj, '__class__') and not isinstance(obj, type):
206 klass = obj.__class__
207 else:
208 klass = obj
209 try:
210 modname = klass.__module__
211 except AttributeError:
212 raise AstroidBuildingException(
213 'Unable to get module for %s' % safe_repr(klass))
214 except Exception, ex:
215 raise AstroidBuildingException(
216 'Unexpected error while retrieving module for %s: %s'
217 % (safe_repr(klass), ex))
218 try:
219 name = klass.__name__
220 except AttributeError:
221 raise AstroidBuildingException(
222 'Unable to get name for %s' % safe_repr(klass))
223 except Exception, ex:
224 raise AstroidBuildingException(
225 'Unexpected error while retrieving name for %s: %s'
226 % (safe_repr(klass), ex))
227 # take care, on living object __module__ is regularly wrong :(
228 modastroid = self.ast_from_module_name(modname)
229 if klass is obj:
230 for infered in modastroid.igetattr(name, context):
231 yield infered
232 else:
233 for infered in modastroid.igetattr(name, context):
234 yield infered.instanciate_class()
235
236 def project_from_files(self, files, func_wrapper=astroid_wrapper,
237 project_name=None, black_list=None):
238 """return a Project from a list of files or modules"""
239 # build the project representation
240 project_name = project_name or self.config.project
241 black_list = black_list or self.config.black_list
242 project = Project(project_name)
243 for something in files:
244 if not exists(something):
245 fpath = file_from_modpath(something.split('.'))
246 elif isdir(something):
247 fpath = join(something, '__init__.py')
248 else:
249 fpath = something
250 astroid = func_wrapper(self.ast_from_file, fpath)
251 if astroid is None:
252 continue
253 # XXX why is first file defining the project.path ?
254 project.path = project.path or astroid.file
255 project.add_module(astroid)
256 base_name = astroid.name
257 # recurse in package except if __init__ was explicitly given
258 if astroid.package and something.find('__init__') == -1:
259 # recurse on others packages / modules if this is a package
260 for fpath in get_module_files(dirname(astroid.file),
261 black_list):
262 astroid = func_wrapper(self.ast_from_file, fpath)
263 if astroid is None or astroid.name == base_name:
264 continue
265 project.add_module(astroid)
266 return project
267
268 def register_transform(self, node_class, transform, predicate=None):
269 """Register `transform(node)` function to be applied on the given
270 Astroid's `node_class` if `predicate` is None or return a true value
271 when called with the node as argument.
272
273 The transform function may return a value which is then used to
274 substitute the original node in the tree.
275 """
276 self.transforms.setdefault(node_class, []).append((transform, predicate) )
277
278 def unregister_transform(self, node_class, transform, predicate=None):
279 """Unregister the given transform."""
280 self.transforms[node_class].remove((transform, predicate))
281
282 def transform(self, node):
283 """Call matching transforms for the given node if any and return the
284 transformed node.
285 """
286 cls = node.__class__
287 if cls not in self.transforms:
288 # no transform registered for this class of node
289 return node
290
291 transforms = self.transforms[cls]
292 orig_node = node # copy the reference
293 for transform_func, predicate in transforms:
294 if predicate is None or predicate(node):
295 ret = transform_func(node)
296 # if the transformation function returns something, it's
297 # expected to be a replacement for the node
298 if ret is not None:
299 if node is not orig_node:
300 # node has already be modified by some previous
301 # transformation, warn about it
302 warn('node %s substituted multiple times' % node)
303 node = ret
304 return node
305
306 def cache_module(self, module):
307 """Cache a module if no module with the same name is known yet."""
308 self.astroid_cache.setdefault(module.name, module)
309
310 def clear_cache(self):
311 self.astroid_cache.clear()
312 # force bootstrap again, else we may ends up with cache inconsistency
313 # between the manager and CONST_PROXY, making
314 # unittest_lookup.LookupTC.test_builtin_lookup fail depending on the
315 # test order
316 from astroid.raw_building import astroid_bootstrapping
317 astroid_bootstrapping()
318
319
320 class Project(object):
321 """a project handle a set of modules / packages"""
322 def __init__(self, name=''):
323 self.name = name
324 self.path = None
325 self.modules = []
326 self.locals = {}
327 self.__getitem__ = self.locals.__getitem__
328 self.__iter__ = self.locals.__iter__
329 self.values = self.locals.values
330 self.keys = self.locals.keys
331 self.items = self.locals.items
332
333 def add_module(self, node):
334 self.locals[node.name] = node
335 self.modules.append(node)
336
337 def get_module(self, name):
338 return self.locals[name]
339
340 def get_children(self):
341 return self.modules
342
343 def __repr__(self):
344 return '<Project %r at %s (%s modules)>' % (self.name, id(self),
345 len(self.modules))
346
347
OLDNEW
« no previous file with comments | « third_party/logilab/astroid/inspector.py ('k') | third_party/logilab/astroid/mixins.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698