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