blob: 04b19515fd0ea4613ef787eabf9b272424f931a2 [file] [log] [blame]
Brett Cannond2e7b332009-02-17 02:45:03 +00001"""Utility code for constructing importers, etc."""
Barry Warsaw28a691b2010-04-17 00:19:56 +00002
Brett Cannon05a647d2013-06-14 19:02:34 -04003from ._bootstrap import MAGIC_NUMBER
Brett Cannona3c96152013-06-14 22:26:30 -04004from ._bootstrap import cache_from_source
Brett Cannonf24fecd2013-06-16 18:37:53 -04005from ._bootstrap import decode_source
Brett Cannona3c96152013-06-14 22:26:30 -04006from ._bootstrap import source_from_cache
Eric Snowb523f842013-11-22 09:05:39 -07007from ._bootstrap import spec_from_loader
8from ._bootstrap import spec_from_file_location
Brett Cannond200bf52012-05-13 13:45:09 -04009from ._bootstrap import _resolve_name
10
Eric Snowb523f842013-11-22 09:05:39 -070011from contextlib import contextmanager
Brett Cannon0dbb4c72013-05-31 18:56:47 -040012import functools
Eric Snowb523f842013-11-22 09:05:39 -070013import sys
Brett Cannon0dbb4c72013-05-31 18:56:47 -040014import warnings
15
Brett Cannond200bf52012-05-13 13:45:09 -040016
17def resolve_name(name, package):
18 """Resolve a relative module name to an absolute one."""
19 if not name.startswith('.'):
20 return name
21 elif not package:
22 raise ValueError('{!r} is not a relative name '
23 '(no leading dot)'.format(name))
24 level = 0
25 for character in name:
26 if character != '.':
27 break
28 level += 1
29 return _resolve_name(name[level:], package, level)
Brett Cannon0dbb4c72013-05-31 18:56:47 -040030
31
Eric Snowb523f842013-11-22 09:05:39 -070032@contextmanager
33def _module_to_load(name):
34 is_reload = name in sys.modules
35
36 module = sys.modules.get(name)
37 if not is_reload:
38 # This must be done before open() is called as the 'io' module
39 # implicitly imports 'locale' and would otherwise trigger an
40 # infinite loop.
41 module = type(sys)(name)
42 # This must be done before putting the module in sys.modules
43 # (otherwise an optimization shortcut in import.c becomes wrong)
44 module.__initializing__ = True
45 sys.modules[name] = module
46 try:
47 yield module
48 except Exception:
49 if not is_reload:
50 try:
51 del sys.modules[name]
52 except KeyError:
53 pass
54 finally:
55 module.__initializing__ = False
56
57
58# XXX deprecate
59def set_package(fxn):
60 """Set __package__ on the returned module."""
61 @functools.wraps(fxn)
62 def set_package_wrapper(*args, **kwargs):
63 module = fxn(*args, **kwargs)
64 if getattr(module, '__package__', None) is None:
65 module.__package__ = module.__name__
66 if not hasattr(module, '__path__'):
67 module.__package__ = module.__package__.rpartition('.')[0]
68 return module
69 return set_package_wrapper
70
71
72# XXX deprecate
73def set_loader(fxn):
74 """Set __loader__ on the returned module."""
75 @functools.wraps(fxn)
76 def set_loader_wrapper(self, *args, **kwargs):
77 module = fxn(self, *args, **kwargs)
78 if getattr(module, '__loader__', None) is None:
79 module.__loader__ = self
80 return module
81 return set_loader_wrapper
82
83
Brett Cannon0dbb4c72013-05-31 18:56:47 -040084def module_for_loader(fxn):
85 """Decorator to handle selecting the proper module for loaders.
86
87 The decorated function is passed the module to use instead of the module
88 name. The module passed in to the function is either from sys.modules if
89 it already exists or is a new module. If the module is new, then __name__
90 is set the first argument to the method, __loader__ is set to self, and
91 __package__ is set accordingly (if self.is_package() is defined) will be set
92 before it is passed to the decorated function (if self.is_package() does
93 not work for the module it will be set post-load).
94
95 If an exception is raised and the decorator created the module it is
96 subsequently removed from sys.modules.
97
98 The decorator assumes that the decorated function takes the module name as
99 the second argument.
100
101 """
Eric Snowb523f842013-11-22 09:05:39 -0700102 warnings.warn('The import system now takes care of this automatically.',
Brett Cannon0dbb4c72013-05-31 18:56:47 -0400103 PendingDeprecationWarning, stacklevel=2)
104 @functools.wraps(fxn)
105 def module_for_loader_wrapper(self, fullname, *args, **kwargs):
Eric Snowb523f842013-11-22 09:05:39 -0700106 with _module_to_load(fullname) as module:
Brett Cannon0dbb4c72013-05-31 18:56:47 -0400107 module.__loader__ = self
108 try:
109 is_package = self.is_package(fullname)
110 except (ImportError, AttributeError):
111 pass
112 else:
113 if is_package:
114 module.__package__ = fullname
115 else:
116 module.__package__ = fullname.rpartition('.')[0]
117 # If __package__ was not set above, __import__() will do it later.
118 return fxn(self, module, *args, **kwargs)
119
Brett Cannon0e75c062013-05-31 18:57:45 -0400120 return module_for_loader_wrapper