OLD | NEW |
| (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 | |
OLD | NEW |