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

Side by Side Diff: third_party/logilab/logilab/common/modutils.py

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

Powered by Google App Engine
This is Rietveld 408576698