blob: a9cd3c8d27e3807e667a6c17e03f29d55a10fc2f [file] [log] [blame]
Brett Cannon2a922ed2009-03-09 03:35:50 +00001"""Abstract base classes related to import."""
2from . import _bootstrap
3from . import machinery
Brett Cannonf23e3742010-06-27 23:57:46 +00004from . import util
Brett Cannon2a922ed2009-03-09 03:35:50 +00005import abc
Brett Cannonf23e3742010-06-27 23:57:46 +00006import imp
7import io
8import marshal
9import os.path
10import sys
11import tokenize
Brett Cannon2a922ed2009-03-09 03:35:50 +000012import types
Brett Cannonf23e3742010-06-27 23:57:46 +000013import warnings
Brett Cannon2a922ed2009-03-09 03:35:50 +000014
15
16class Loader(metaclass=abc.ABCMeta):
17
Brett Cannon7aa21f72009-03-15 00:53:05 +000018 """Abstract base class for import loaders."""
Brett Cannon2a922ed2009-03-09 03:35:50 +000019
Brett Cannon7aa21f72009-03-15 00:53:05 +000020 @abc.abstractmethod
Brett Cannon2a922ed2009-03-09 03:35:50 +000021 def load_module(self, fullname:str) -> types.ModuleType:
Brett Cannon7aa21f72009-03-15 00:53:05 +000022 """Abstract method which when implemented should load a module."""
Brett Cannon2a922ed2009-03-09 03:35:50 +000023 raise NotImplementedError
24
Brett Cannon2a922ed2009-03-09 03:35:50 +000025
26class Finder(metaclass=abc.ABCMeta):
27
Brett Cannon7aa21f72009-03-15 00:53:05 +000028 """Abstract base class for import finders."""
Brett Cannon2a922ed2009-03-09 03:35:50 +000029
30 @abc.abstractmethod
31 def find_module(self, fullname:str, path:[str]=None) -> Loader:
Brett Cannon7aa21f72009-03-15 00:53:05 +000032 """Abstract method which when implemented should find a module."""
Brett Cannon2a922ed2009-03-09 03:35:50 +000033 raise NotImplementedError
34
35Finder.register(machinery.BuiltinImporter)
36Finder.register(machinery.FrozenImporter)
37Finder.register(machinery.PathFinder)
38
39
Brett Cannon2a922ed2009-03-09 03:35:50 +000040class ResourceLoader(Loader):
41
Brett Cannon7aa21f72009-03-15 00:53:05 +000042 """Abstract base class for loaders which can return data from their
43 back-end storage.
Brett Cannon2a922ed2009-03-09 03:35:50 +000044
45 This ABC represents one of the optional protocols specified by PEP 302.
46
47 """
48
49 @abc.abstractmethod
50 def get_data(self, path:str) -> bytes:
Brett Cannon7aa21f72009-03-15 00:53:05 +000051 """Abstract method which when implemented should return the bytes for
52 the specified path."""
Brett Cannon2a922ed2009-03-09 03:35:50 +000053 raise NotImplementedError
54
55
56class InspectLoader(Loader):
57
Brett Cannon7aa21f72009-03-15 00:53:05 +000058 """Abstract base class for loaders which support inspection about the
59 modules they can load.
Brett Cannon2a922ed2009-03-09 03:35:50 +000060
61 This ABC represents one of the optional protocols specified by PEP 302.
62
63 """
64
65 @abc.abstractmethod
66 def is_package(self, fullname:str) -> bool:
Brett Cannon7aa21f72009-03-15 00:53:05 +000067 """Abstract method which when implemented should return whether the
68 module is a package."""
Brett Cannonf23e3742010-06-27 23:57:46 +000069 raise NotImplementedError
Brett Cannon2a922ed2009-03-09 03:35:50 +000070
71 @abc.abstractmethod
72 def get_code(self, fullname:str) -> types.CodeType:
Brett Cannon7aa21f72009-03-15 00:53:05 +000073 """Abstract method which when implemented should return the code object
74 for the module"""
Brett Cannonf23e3742010-06-27 23:57:46 +000075 raise NotImplementedError
Brett Cannon2a922ed2009-03-09 03:35:50 +000076
77 @abc.abstractmethod
78 def get_source(self, fullname:str) -> str:
Brett Cannon7aa21f72009-03-15 00:53:05 +000079 """Abstract method which should return the source code for the
80 module."""
Brett Cannonf23e3742010-06-27 23:57:46 +000081 raise NotImplementedError
Brett Cannon2a922ed2009-03-09 03:35:50 +000082
Brett Cannona113ac52009-03-15 01:41:33 +000083InspectLoader.register(machinery.BuiltinImporter)
Brett Cannon8d110132009-03-15 02:20:16 +000084InspectLoader.register(machinery.FrozenImporter)
Brett Cannona113ac52009-03-15 01:41:33 +000085
Brett Cannon2a922ed2009-03-09 03:35:50 +000086
Brett Cannon69194272009-07-20 04:23:48 +000087class ExecutionLoader(InspectLoader):
88
89 """Abstract base class for loaders that wish to support the execution of
90 modules as scripts.
91
92 This ABC represents one of the optional protocols specified in PEP 302.
93
94 """
95
96 @abc.abstractmethod
97 def get_filename(self, fullname:str) -> str:
98 """Abstract method which should return the value that __file__ is to be
99 set to."""
100 raise NotImplementedError
101
102
Brett Cannon0cf9e6a2010-06-28 04:57:24 +0000103class SourceLoader(_bootstrap.SourceLoader, ResourceLoader, ExecutionLoader):
Brett Cannon2a922ed2009-03-09 03:35:50 +0000104
Brett Cannonf23e3742010-06-27 23:57:46 +0000105 """Abstract base class for loading source code (and optionally any
106 corresponding bytecode).
Brett Cannon2a922ed2009-03-09 03:35:50 +0000107
Brett Cannonf23e3742010-06-27 23:57:46 +0000108 To support loading from source code, the abstractmethods inherited from
109 ResourceLoader and ExecutionLoader need to be implemented. To also support
110 loading from bytecode, the optional methods specified directly by this ABC
111 is required.
112
113 Inherited abstractmethods not implemented in this ABC:
114
115 * ResourceLoader.get_data
116 * ExecutionLoader.get_filename
117
118 """
119
Brett Cannonf23e3742010-06-27 23:57:46 +0000120
121class PyLoader(SourceLoader):
122
123 """Implement the deprecated PyLoader ABC in terms of SourceLoader.
124
125 This class has been deprecated! It is slated for removal in Python 3.4.
126 If compatibility with Python 3.1 is not needed then implement the
127 SourceLoader ABC instead of this class. If Python 3.1 compatibility is
128 needed, then use the following idiom to have a single class that is
129 compatible with Python 3.1 onwards::
130
131 try:
132 from importlib.abc import SourceLoader
133 except ImportError:
134 from importlib.abc import PyLoader as SourceLoader
135
136
137 class CustomLoader(SourceLoader):
138 def get_filename(self, fullname):
139 # Implement ...
140
141 def source_path(self, fullname):
142 '''Implement source_path in terms of get_filename.'''
143 try:
144 return self.get_filename(fullname)
145 except ImportError:
146 return None
147
148 def is_package(self, fullname):
149 filename = os.path.basename(self.get_filename(fullname))
150 return os.path.splitext(filename)[0] == '__init__'
Brett Cannon7aa21f72009-03-15 00:53:05 +0000151
152 """
Brett Cannon2a922ed2009-03-09 03:35:50 +0000153
154 @abc.abstractmethod
Brett Cannonf23e3742010-06-27 23:57:46 +0000155 def is_package(self, fullname):
Brett Cannon2a922ed2009-03-09 03:35:50 +0000156 raise NotImplementedError
157
Brett Cannonf23e3742010-06-27 23:57:46 +0000158 @abc.abstractmethod
159 def source_path(self, fullname:str) -> object:
160 """Abstract method which when implemented should return the path to the
161 source code for the module."""
162 raise NotImplementedError
Brett Cannon2a922ed2009-03-09 03:35:50 +0000163
Brett Cannonf23e3742010-06-27 23:57:46 +0000164 def get_filename(self, fullname):
165 """Implement get_filename in terms of source_path.
166
167 As get_filename should only return a source file path there is no
168 chance of the path not existing but loading still being possible, so
169 ImportError should propagate instead of being turned into returning
170 None.
171
172 """
173 warnings.warn("importlib.abc.PyLoader is deprecated and is "
174 "slated for removal in Python 3.4; "
175 "use SourceLoader instead. "
176 "See the importlib documentation on how to be "
177 "compatible with Python 3.1 onwards.",
178 PendingDeprecationWarning)
179 path = self.source_path(fullname)
180 if path is None:
181 raise ImportError
182 else:
183 return path
184
185PyLoader.register(_bootstrap.PyLoader)
186
187
188class PyPycLoader(PyLoader):
Brett Cannon2a922ed2009-03-09 03:35:50 +0000189
Brett Cannon7aa21f72009-03-15 00:53:05 +0000190 """Abstract base class to assist in loading source and bytecode by
191 requiring only back-end storage methods to be implemented.
Brett Cannon2a922ed2009-03-09 03:35:50 +0000192
Brett Cannonf23e3742010-06-27 23:57:46 +0000193 This class has been deprecated! Removal is slated for Python 3.4. Implement
194 the SourceLoader ABC instead. If Python 3.1 compatibility is needed, see
195 PyLoader.
196
Brett Cannon7aa21f72009-03-15 00:53:05 +0000197 The methods get_code, get_source, and load_module are implemented for the
198 user.
199
200 """
Brett Cannon2a922ed2009-03-09 03:35:50 +0000201
Brett Cannonf23e3742010-06-27 23:57:46 +0000202 def get_filename(self, fullname):
203 """Return the source or bytecode file path."""
204 path = self.source_path(fullname)
205 if path is not None:
206 return path
207 path = self.bytecode_path(fullname)
208 if path is not None:
209 return path
210 raise ImportError("no source or bytecode path available for "
211 "{0!r}".format(fullname))
212
213 def get_code(self, fullname):
214 """Get a code object from source or bytecode."""
215 warnings.warn("importlib.abc.PyPycLoader is deprecated and slated for "
216 "removal in Python 3.4; use SourceLoader instead. "
217 "If Python 3.1 compatibility is required, see the "
218 "latest documentation for PyLoader.",
219 PendingDeprecationWarning)
220 source_timestamp = self.source_mtime(fullname)
221 # Try to use bytecode if it is available.
222 bytecode_path = self.bytecode_path(fullname)
223 if bytecode_path:
224 data = self.get_data(bytecode_path)
225 try:
226 magic = data[:4]
227 if len(magic) < 4:
228 raise ImportError("bad magic number in {}".format(fullname))
229 raw_timestamp = data[4:8]
230 if len(raw_timestamp) < 4:
231 raise EOFError("bad timestamp in {}".format(fullname))
232 pyc_timestamp = marshal._r_long(raw_timestamp)
233 bytecode = data[8:]
234 # Verify that the magic number is valid.
235 if imp.get_magic() != magic:
236 raise ImportError("bad magic number in {}".format(fullname))
237 # Verify that the bytecode is not stale (only matters when
238 # there is source to fall back on.
239 if source_timestamp:
240 if pyc_timestamp < source_timestamp:
241 raise ImportError("bytecode is stale")
242 except (ImportError, EOFError):
243 # If source is available give it a shot.
244 if source_timestamp is not None:
245 pass
246 else:
247 raise
248 else:
249 # Bytecode seems fine, so try to use it.
250 return marshal.loads(bytecode)
251 elif source_timestamp is None:
252 raise ImportError("no source or bytecode available to create code "
253 "object for {0!r}".format(fullname))
254 # Use the source.
255 source_path = self.source_path(fullname)
256 if source_path is None:
257 message = "a source path must exist to load {0}".format(fullname)
258 raise ImportError(message)
259 source = self.get_data(source_path)
260 code_object = compile(source, source_path, 'exec', dont_inherit=True)
261 # Generate bytecode and write it out.
262 if not sys.dont_write_bytecode:
263 data = bytearray(imp.get_magic())
264 data.extend(marshal._w_long(source_timestamp))
265 data.extend(marshal.dumps(code_object))
266 self.write_bytecode(fullname, data)
267 return code_object
268
269
Brett Cannon2a922ed2009-03-09 03:35:50 +0000270 @abc.abstractmethod
271 def source_mtime(self, fullname:str) -> int:
Brett Cannon7aa21f72009-03-15 00:53:05 +0000272 """Abstract method which when implemented should return the
273 modification time for the source of the module."""
Brett Cannon2a922ed2009-03-09 03:35:50 +0000274 raise NotImplementedError
275
276 @abc.abstractmethod
277 def bytecode_path(self, fullname:str) -> object:
Brett Cannon7aa21f72009-03-15 00:53:05 +0000278 """Abstract method which when implemented should return the path to the
279 bytecode for the module."""
Brett Cannon2a922ed2009-03-09 03:35:50 +0000280 raise NotImplementedError
281
282 @abc.abstractmethod
Brett Cannonb89ee8e2009-12-12 22:35:59 +0000283 def write_bytecode(self, fullname:str, bytecode:bytes) -> bool:
Brett Cannon7aa21f72009-03-15 00:53:05 +0000284 """Abstract method which when implemented should attempt to write the
Brett Cannonb89ee8e2009-12-12 22:35:59 +0000285 bytecode for the module, returning a boolean representing whether the
286 bytecode was written or not."""
Brett Cannon2a922ed2009-03-09 03:35:50 +0000287 raise NotImplementedError
Brett Cannonf23e3742010-06-27 23:57:46 +0000288
289PyPycLoader.register(_bootstrap.PyPycLoader)