blob: 09ec03c142498cfa7b00baf2205101024e66549c [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 Cannon357c9fb2013-05-30 17:31:47 -04004from ._bootstrap import module_to_load
Brett Cannon2cf03a82009-03-10 05:17:37 +00005from ._bootstrap import set_loader
Brett Cannon435aad82009-03-04 16:07:00 +00006from ._bootstrap import set_package
Brett Cannond200bf52012-05-13 13:45:09 -04007from ._bootstrap import _resolve_name
8
Brett Cannon0dbb4c72013-05-31 18:56:47 -04009import functools
10import warnings
11
Brett Cannond200bf52012-05-13 13:45:09 -040012
13def resolve_name(name, package):
14 """Resolve a relative module name to an absolute one."""
15 if not name.startswith('.'):
16 return name
17 elif not package:
18 raise ValueError('{!r} is not a relative name '
19 '(no leading dot)'.format(name))
20 level = 0
21 for character in name:
22 if character != '.':
23 break
24 level += 1
25 return _resolve_name(name[level:], package, level)
Brett Cannon0dbb4c72013-05-31 18:56:47 -040026
27
28def module_for_loader(fxn):
29 """Decorator to handle selecting the proper module for loaders.
30
31 The decorated function is passed the module to use instead of the module
32 name. The module passed in to the function is either from sys.modules if
33 it already exists or is a new module. If the module is new, then __name__
34 is set the first argument to the method, __loader__ is set to self, and
35 __package__ is set accordingly (if self.is_package() is defined) will be set
36 before it is passed to the decorated function (if self.is_package() does
37 not work for the module it will be set post-load).
38
39 If an exception is raised and the decorator created the module it is
40 subsequently removed from sys.modules.
41
42 The decorator assumes that the decorated function takes the module name as
43 the second argument.
44
45 """
46 warnings.warn('To make it easier for subclasses, please use '
47 'importlib.util.module_to_load() and '
48 'importlib.abc.Loader.init_module_attrs()',
49 PendingDeprecationWarning, stacklevel=2)
50 @functools.wraps(fxn)
51 def module_for_loader_wrapper(self, fullname, *args, **kwargs):
52 with module_to_load(fullname) as module:
53 module.__loader__ = self
54 try:
55 is_package = self.is_package(fullname)
56 except (ImportError, AttributeError):
57 pass
58 else:
59 if is_package:
60 module.__package__ = fullname
61 else:
62 module.__package__ = fullname.rpartition('.')[0]
63 # If __package__ was not set above, __import__() will do it later.
64 return fxn(self, module, *args, **kwargs)
65
Brett Cannon0e75c062013-05-31 18:57:45 -040066 return module_for_loader_wrapper