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

Side by Side Diff: Tools/Scripts/webkitpy/thirdparty/logilab/common/modutils.py

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

Powered by Google App Engine
This is Rietveld 408576698