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

Side by Side Diff: third_party/logilab/astroid/modutils.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 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/mixins.py ('k') | third_party/logilab/astroid/node_classes.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-2014 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 under
7 # the terms of the GNU Lesser General Public License as published by the Free
8 # Software Foundation, either version 2.1 of the License, or (at your option) an y
9 # later version.
10 #
11 # astroid is distributed in the hope that it will be useful, but WITHOUT
12 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13 # FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
14 # 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 """Python modules manipulation utility functions.
19
20 :type PY_SOURCE_EXTS: tuple(str)
21 :var PY_SOURCE_EXTS: list of possible python source file extension
22
23 :type STD_LIB_DIR: str
24 :var STD_LIB_DIR: directory where standard modules are located
25
26 :type BUILTIN_MODULES: dict
27 :var BUILTIN_MODULES: dictionary with builtin module names has key
28 """
29 from __future__ import with_statement
30
31 __docformat__ = "restructuredtext en"
32
33 import sys
34 import os
35 from os.path import splitext, join, abspath, isdir, dirname, exists
36 from imp import find_module, load_module, C_BUILTIN, PY_COMPILED, PKG_DIRECTORY
37 from distutils.sysconfig import get_python_lib
38 from distutils.errors import DistutilsPlatformError
39
40 try:
41 import zipimport
42 except ImportError:
43 zipimport = None
44
45 ZIPFILE = object()
46
47 from logilab.common import _handle_blacklist
48
49 # Notes about STD_LIB_DIR
50 # Consider arch-specific installation for STD_LIB_DIR definition
51 # :mod:`distutils.sysconfig` contains to much hardcoded values to rely on
52 #
53 # :see: `Problems with /usr/lib64 builds <http://bugs.python.org/issue1294959>`_
54 # :see: `FHS <http://www.pathname.com/fhs/pub/fhs-2.3.html#LIBLTQUALGTALTERNATEF ORMATESSENTIAL>`_
55 if sys.platform.startswith('win'):
56 PY_SOURCE_EXTS = ('py', 'pyw')
57 PY_COMPILED_EXTS = ('dll', 'pyd')
58 else:
59 PY_SOURCE_EXTS = ('py',)
60 PY_COMPILED_EXTS = ('so',)
61
62 try:
63 STD_LIB_DIR = get_python_lib(standard_lib=1)
64 # get_python_lib(standard_lib=1) is not available on pypy, set STD_LIB_DIR to
65 # non-valid path, see https://bugs.pypy.org/issue1164
66 except DistutilsPlatformError:
67 STD_LIB_DIR = '//'
68
69 EXT_LIB_DIR = get_python_lib()
70
71 BUILTIN_MODULES = dict(zip(sys.builtin_module_names,
72 [1]*len(sys.builtin_module_names)))
73
74
75 class NoSourceFile(Exception):
76 """exception raised when we are not able to get a python
77 source file for a precompiled file
78 """
79
80
81 def load_module_from_name(dotted_name, path=None, use_sys=1):
82 """Load a Python module from its name.
83
84 :type dotted_name: str
85 :param dotted_name: python name of a module or package
86
87 :type path: list or None
88 :param path:
89 optional list of path where the module or package should be
90 searched (use sys.path if nothing or None is given)
91
92 :type use_sys: bool
93 :param use_sys:
94 boolean indicating whether the sys.modules dictionary should be
95 used or not
96
97
98 :raise ImportError: if the module or package is not found
99
100 :rtype: module
101 :return: the loaded module
102 """
103 return load_module_from_modpath(dotted_name.split('.'), path, use_sys)
104
105
106 def load_module_from_modpath(parts, path=None, use_sys=1):
107 """Load a python module from its splitted name.
108
109 :type parts: list(str) or tuple(str)
110 :param parts:
111 python name of a module or package splitted on '.'
112
113 :type path: list or None
114 :param path:
115 optional list of path where the module or package should be
116 searched (use sys.path if nothing or None is given)
117
118 :type use_sys: bool
119 :param use_sys:
120 boolean indicating whether the sys.modules dictionary should be used or no t
121
122 :raise ImportError: if the module or package is not found
123
124 :rtype: module
125 :return: the loaded module
126 """
127 if use_sys:
128 try:
129 return sys.modules['.'.join(parts)]
130 except KeyError:
131 pass
132 modpath = []
133 prevmodule = None
134 for part in parts:
135 modpath.append(part)
136 curname = '.'.join(modpath)
137 module = None
138 if len(modpath) != len(parts):
139 # even with use_sys=False, should try to get outer packages from sys .modules
140 module = sys.modules.get(curname)
141 elif use_sys:
142 # because it may have been indirectly loaded through a parent
143 module = sys.modules.get(curname)
144 if module is None:
145 mp_file, mp_filename, mp_desc = find_module(part, path)
146 module = load_module(curname, mp_file, mp_filename, mp_desc)
147 if prevmodule:
148 setattr(prevmodule, part, module)
149 _file = getattr(module, '__file__', '')
150 if not _file and len(modpath) != len(parts):
151 raise ImportError('no module in %s' % '.'.join(parts[len(modpath):]) )
152 path = [dirname(_file)]
153 prevmodule = module
154 return module
155
156
157 def load_module_from_file(filepath, path=None, use_sys=1, extrapath=None):
158 """Load a Python module from it's path.
159
160 :type filepath: str
161 :param filepath: path to the python module or package
162
163 :type path: list or None
164 :param path:
165 optional list of path where the module or package should be
166 searched (use sys.path if nothing or None is given)
167
168 :type use_sys: bool
169 :param use_sys:
170 boolean indicating whether the sys.modules dictionary should be
171 used or not
172
173
174 :raise ImportError: if the module or package is not found
175
176 :rtype: module
177 :return: the loaded module
178 """
179 modpath = modpath_from_file(filepath, extrapath)
180 return load_module_from_modpath(modpath, path, use_sys)
181
182
183 def _check_init(path, mod_path):
184 """check there are some __init__.py all along the way"""
185 for part in mod_path:
186 path = join(path, part)
187 if not _has_init(path):
188 return False
189 return True
190
191
192 def modpath_from_file(filename, extrapath=None):
193 """given a file path return the corresponding splitted module's name
194 (i.e name of a module or package splitted on '.')
195
196 :type filename: str
197 :param filename: file's path for which we want the module's name
198
199 :type extrapath: dict
200 :param extrapath:
201 optional extra search path, with path as key and package name for the path
202 as value. This is usually useful to handle package splitted in multiple
203 directories using __path__ trick.
204
205
206 :raise ImportError:
207 if the corresponding module's name has not been found
208
209 :rtype: list(str)
210 :return: the corresponding splitted module's name
211 """
212 base = splitext(abspath(filename))[0]
213 if extrapath is not None:
214 for path_ in extrapath:
215 path = _abspath(path_)
216 if path and base[:len(path)] == path:
217 submodpath = [pkg for pkg in base[len(path):].split(os.sep)
218 if pkg]
219 if _check_init(path, submodpath[:-1]):
220 return extrapath[path_].split('.') + submodpath
221 for path in sys.path:
222 path = _abspath(path)
223 if path and base.startswith(path):
224 modpath = [pkg for pkg in base[len(path):].split(os.sep) if pkg]
225 if _check_init(path, modpath[:-1]):
226 return modpath
227 raise ImportError('Unable to find module for %s in %s' % (
228 filename, ', \n'.join(sys.path)))
229
230
231
232 def file_from_modpath(modpath, path=None, context_file=None):
233 """given a mod path (i.e. splitted module / package name), return the
234 corresponding file, giving priority to source file over precompiled
235 file if it exists
236
237 :type modpath: list or tuple
238 :param modpath:
239 splitted module's name (i.e name of a module or package splitted
240 on '.')
241 (this means explicit relative imports that start with dots have
242 empty strings in this list!)
243
244 :type path: list or None
245 :param path:
246 optional list of path where the module or package should be
247 searched (use sys.path if nothing or None is given)
248
249 :type context_file: str or None
250 :param context_file:
251 context file to consider, necessary if the identifier has been
252 introduced using a relative import unresolvable in the actual
253 context (i.e. modutils)
254
255 :raise ImportError: if there is no such module in the directory
256
257 :rtype: str or None
258 :return:
259 the path to the module's file or None if it's an integrated
260 builtin module such as 'sys'
261 """
262 if context_file is not None:
263 context = dirname(context_file)
264 else:
265 context = context_file
266 if modpath[0] == 'xml':
267 # handle _xmlplus
268 try:
269 return _file_from_modpath(['_xmlplus'] + modpath[1:], path, context)
270 except ImportError:
271 return _file_from_modpath(modpath, path, context)
272 elif modpath == ['os', 'path']:
273 # FIXME: currently ignoring search_path...
274 return os.path.__file__
275 return _file_from_modpath(modpath, path, context)
276
277
278
279 def get_module_part(dotted_name, context_file=None):
280 """given a dotted name return the module part of the name :
281
282 >>> get_module_part('logilab.common.modutils.get_module_part')
283 'logilab.common.modutils'
284
285 :type dotted_name: str
286 :param dotted_name: full name of the identifier we are interested in
287
288 :type context_file: str or None
289 :param context_file:
290 context file to consider, necessary if the identifier has been
291 introduced using a relative import unresolvable in the actual
292 context (i.e. modutils)
293
294
295 :raise ImportError: if there is no such module in the directory
296
297 :rtype: str or None
298 :return:
299 the module part of the name or None if we have not been able at
300 all to import the given name
301
302 XXX: deprecated, since it doesn't handle package precedence over module
303 (see #10066)
304 """
305 # os.path trick
306 if dotted_name.startswith('os.path'):
307 return 'os.path'
308 parts = dotted_name.split('.')
309 if context_file is not None:
310 # first check for builtin module which won't be considered latter
311 # in that case (path != None)
312 if parts[0] in BUILTIN_MODULES:
313 if len(parts) > 2:
314 raise ImportError(dotted_name)
315 return parts[0]
316 # don't use += or insert, we want a new list to be created !
317 path = None
318 starti = 0
319 if parts[0] == '':
320 assert context_file is not None, \
321 'explicit relative import, but no context_file?'
322 path = [] # prevent resolving the import non-relatively
323 starti = 1
324 while parts[starti] == '': # for all further dots: change context
325 starti += 1
326 context_file = dirname(context_file)
327 for i in range(starti, len(parts)):
328 try:
329 file_from_modpath(parts[starti:i+1], path=path,
330 context_file=context_file)
331 except ImportError:
332 if not i >= max(1, len(parts) - 2):
333 raise
334 return '.'.join(parts[:i])
335 return dotted_name
336
337
338 def get_module_files(src_directory, blacklist):
339 """given a package directory return a list of all available python
340 module's files in the package and its subpackages
341
342 :type src_directory: str
343 :param src_directory:
344 path of the directory corresponding to the package
345
346 :type blacklist: list or tuple
347 :param blacklist:
348 optional list of files or directory to ignore, default to the value of
349 `logilab.common.STD_BLACKLIST`
350
351 :rtype: list
352 :return:
353 the list of all available python module's files in the package and
354 its subpackages
355 """
356 files = []
357 for directory, dirnames, filenames in os.walk(src_directory):
358 _handle_blacklist(blacklist, dirnames, filenames)
359 # check for __init__.py
360 if not '__init__.py' in filenames:
361 dirnames[:] = ()
362 continue
363 for filename in filenames:
364 if _is_python_file(filename):
365 src = join(directory, filename)
366 files.append(src)
367 return files
368
369
370 def get_source_file(filename, include_no_ext=False):
371 """given a python module's file name return the matching source file
372 name (the filename will be returned identically if it's a already an
373 absolute path to a python source file...)
374
375 :type filename: str
376 :param filename: python module's file name
377
378
379 :raise NoSourceFile: if no source file exists on the file system
380
381 :rtype: str
382 :return: the absolute path of the source file if it exists
383 """
384 base, orig_ext = splitext(abspath(filename))
385 for ext in PY_SOURCE_EXTS:
386 source_path = '%s.%s' % (base, ext)
387 if exists(source_path):
388 return source_path
389 if include_no_ext and not orig_ext and exists(base):
390 return base
391 raise NoSourceFile(filename)
392
393
394 def is_python_source(filename):
395 """
396 rtype: bool
397 return: True if the filename is a python source file
398 """
399 return splitext(filename)[1][1:] in PY_SOURCE_EXTS
400
401
402 def is_standard_module(modname, std_path=(STD_LIB_DIR,)):
403 """try to guess if a module is a standard python module (by default,
404 see `std_path` parameter's description)
405
406 :type modname: str
407 :param modname: name of the module we are interested in
408
409 :type std_path: list(str) or tuple(str)
410 :param std_path: list of path considered has standard
411
412
413 :rtype: bool
414 :return:
415 true if the module:
416 - is located on the path listed in one of the directory in `std_path`
417 - is a built-in module
418 """
419 modname = modname.split('.')[0]
420 try:
421 filename = file_from_modpath([modname])
422 except ImportError:
423 # import failed, i'm probably not so wrong by supposing it's
424 # not standard...
425 return False
426 # modules which are not living in a file are considered standard
427 # (sys and __builtin__ for instance)
428 if filename is None:
429 return True
430 filename = abspath(filename)
431 if filename.startswith(EXT_LIB_DIR):
432 return False
433 for path in std_path:
434 if filename.startswith(_abspath(path)):
435 return True
436 return False
437
438
439
440 def is_relative(modname, from_file):
441 """return true if the given module name is relative to the given
442 file name
443
444 :type modname: str
445 :param modname: name of the module we are interested in
446
447 :type from_file: str
448 :param from_file:
449 path of the module from which modname has been imported
450
451 :rtype: bool
452 :return:
453 true if the module has been imported relatively to `from_file`
454 """
455 if not isdir(from_file):
456 from_file = dirname(from_file)
457 if from_file in sys.path:
458 return False
459 try:
460 find_module(modname.split('.')[0], [from_file])
461 return True
462 except ImportError:
463 return False
464
465
466 # internal only functions #####################################################
467
468 def _file_from_modpath(modpath, path=None, context=None):
469 """given a mod path (i.e. splitted module / package name), return the
470 corresponding file
471
472 this function is used internally, see `file_from_modpath`'s
473 documentation for more information
474 """
475 assert len(modpath) > 0
476 if context is not None:
477 try:
478 mtype, mp_filename = _module_file(modpath, [context])
479 except ImportError:
480 mtype, mp_filename = _module_file(modpath, path)
481 else:
482 mtype, mp_filename = _module_file(modpath, path)
483 if mtype == PY_COMPILED:
484 try:
485 return get_source_file(mp_filename)
486 except NoSourceFile:
487 return mp_filename
488 elif mtype == C_BUILTIN:
489 # integrated builtin module
490 return None
491 elif mtype == PKG_DIRECTORY:
492 mp_filename = _has_init(mp_filename)
493 return mp_filename
494
495 def _search_zip(modpath, pic):
496 for filepath, importer in pic.items():
497 if importer is not None:
498 if importer.find_module(modpath[0]):
499 if not importer.find_module(os.path.sep.join(modpath)):
500 raise ImportError('No module named %s in %s/%s' % (
501 '.'.join(modpath[1:]), filepath, modpath))
502 return ZIPFILE, abspath(filepath) + os.path.sep + os.path.sep.jo in(modpath), filepath
503 raise ImportError('No module named %s' % '.'.join(modpath))
504
505
506 def _abspath(path, _abspathcache={}): #pylint: disable=dangerous-default-value
507 """abspath with caching"""
508 # _module_file calls abspath on every path in sys.path every time it's
509 # called; on a larger codebase this easily adds up to half a second just
510 # assembling path components. This cache alleviates that.
511 try:
512 return _abspathcache[path]
513 except KeyError:
514 if not path: # don't cache result for ''
515 return abspath(path)
516 _abspathcache[path] = abspath(path)
517 return _abspathcache[path]
518
519 try:
520 import pkg_resources
521 except ImportError:
522 pkg_resources = None
523
524 def _module_file(modpath, path=None):
525 """get a module type / file path
526
527 :type modpath: list or tuple
528 :param modpath:
529 splitted module's name (i.e name of a module or package splitted
530 on '.'), with leading empty strings for explicit relative import
531
532 :type path: list or None
533 :param path:
534 optional list of path where the module or package should be
535 searched (use sys.path if nothing or None is given)
536
537
538 :rtype: tuple(int, str)
539 :return: the module type flag and the file path for a module
540 """
541 # egg support compat
542 try:
543 pic = sys.path_importer_cache
544 _path = (path is None and sys.path or path)
545 for __path in _path:
546 if not __path in pic:
547 try:
548 pic[__path] = zipimport.zipimporter(__path)
549 except zipimport.ZipImportError:
550 pic[__path] = None
551 checkeggs = True
552 except AttributeError:
553 checkeggs = False
554 # pkg_resources support (aka setuptools namespace packages)
555 if pkg_resources is not None and modpath[0] in pkg_resources._namespace_pack ages and len(modpath) > 1:
556 # setuptools has added into sys.modules a module object with proper
557 # __path__, get back information from there
558 module = sys.modules[modpath.pop(0)]
559 path = module.__path__
560 imported = []
561 while modpath:
562 modname = modpath[0]
563 # take care to changes in find_module implementation wrt builtin modules
564 #
565 # Python 2.6.6 (r266:84292, Sep 11 2012, 08:34:23)
566 # >>> imp.find_module('posix')
567 # (None, 'posix', ('', '', 6))
568 #
569 # Python 3.3.1 (default, Apr 26 2013, 12:08:46)
570 # >>> imp.find_module('posix')
571 # (None, None, ('', '', 6))
572 try:
573 _, mp_filename, mp_desc = find_module(modname, path)
574 except ImportError:
575 if checkeggs:
576 return _search_zip(modpath, pic)[:2]
577 raise
578 else:
579 if checkeggs and mp_filename:
580 fullabspath = [_abspath(x) for x in _path]
581 try:
582 pathindex = fullabspath.index(dirname(abspath(mp_filename)))
583 emtype, emp_filename, zippath = _search_zip(modpath, pic)
584 if pathindex > _path.index(zippath):
585 # an egg takes priority
586 return emtype, emp_filename
587 except ValueError:
588 # XXX not in _path
589 pass
590 except ImportError:
591 pass
592 checkeggs = False
593 imported.append(modpath.pop(0))
594 mtype = mp_desc[2]
595 if modpath:
596 if mtype != PKG_DIRECTORY:
597 raise ImportError('No module %s in %s' % ('.'.join(modpath),
598 '.'.join(imported)))
599 # XXX guess if package is using pkgutil.extend_path by looking for
600 # those keywords in the first four Kbytes
601 try:
602 with open(join(mp_filename, '__init__.py')) as stream:
603 data = stream.read(4096)
604 except IOError:
605 path = [mp_filename]
606 else:
607 if 'pkgutil' in data and 'extend_path' in data:
608 # extend_path is called, search sys.path for module/packages
609 # of this name see pkgutil.extend_path documentation
610 path = [join(p, *imported) for p in sys.path
611 if isdir(join(p, *imported))]
612 else:
613 path = [mp_filename]
614 return mtype, mp_filename
615
616 def _is_python_file(filename):
617 """return true if the given filename should be considered as a python file
618
619 .pyc and .pyo are ignored
620 """
621 for ext in ('.py', '.so', '.pyd', '.pyw'):
622 if filename.endswith(ext):
623 return True
624 return False
625
626
627 def _has_init(directory):
628 """if the given directory has a valid __init__ file, return its path,
629 else return None
630 """
631 mod_or_pack = join(directory, '__init__')
632 for ext in PY_SOURCE_EXTS + ('pyc', 'pyo'):
633 if exists(mod_or_pack + '.' + ext):
634 return mod_or_pack + '.' + ext
635 return None
OLDNEW
« no previous file with comments | « third_party/logilab/astroid/mixins.py ('k') | third_party/logilab/astroid/node_classes.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698