blob: 3c6b481028a6741e33683855443206dffe3cdb27 [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 Cannon8d189072010-08-22 20:38:47 +0000120 def path_mtime(self, path:str) -> int:
121 """Return the modification time for the path."""
122 raise NotImplementedError
123
124 def set_data(self, path:str, data:bytes) -> None:
125 """Write the bytes to the path (if possible).
126
127 Any needed intermediary directories are to be created. If for some
128 reason the file cannot be written because of permissions, fail
129 silently.
130
131 """
132 raise NotImplementedError
133
Brett Cannonf23e3742010-06-27 23:57:46 +0000134
135class PyLoader(SourceLoader):
136
137 """Implement the deprecated PyLoader ABC in terms of SourceLoader.
138
139 This class has been deprecated! It is slated for removal in Python 3.4.
140 If compatibility with Python 3.1 is not needed then implement the
141 SourceLoader ABC instead of this class. If Python 3.1 compatibility is
142 needed, then use the following idiom to have a single class that is
143 compatible with Python 3.1 onwards::
144
145 try:
146 from importlib.abc import SourceLoader
147 except ImportError:
148 from importlib.abc import PyLoader as SourceLoader
149
150
151 class CustomLoader(SourceLoader):
152 def get_filename(self, fullname):
153 # Implement ...
154
155 def source_path(self, fullname):
156 '''Implement source_path in terms of get_filename.'''
157 try:
158 return self.get_filename(fullname)
159 except ImportError:
160 return None
161
162 def is_package(self, fullname):
163 filename = os.path.basename(self.get_filename(fullname))
164 return os.path.splitext(filename)[0] == '__init__'
Brett Cannon7aa21f72009-03-15 00:53:05 +0000165
166 """
Brett Cannon2a922ed2009-03-09 03:35:50 +0000167
168 @abc.abstractmethod
Brett Cannonf23e3742010-06-27 23:57:46 +0000169 def is_package(self, fullname):
Brett Cannon2a922ed2009-03-09 03:35:50 +0000170 raise NotImplementedError
171
Brett Cannonf23e3742010-06-27 23:57:46 +0000172 @abc.abstractmethod
173 def source_path(self, fullname:str) -> object:
174 """Abstract method which when implemented should return the path to the
175 source code for the module."""
176 raise NotImplementedError
Brett Cannon2a922ed2009-03-09 03:35:50 +0000177
Brett Cannonf23e3742010-06-27 23:57:46 +0000178 def get_filename(self, fullname):
179 """Implement get_filename in terms of source_path.
180
181 As get_filename should only return a source file path there is no
182 chance of the path not existing but loading still being possible, so
183 ImportError should propagate instead of being turned into returning
184 None.
185
186 """
187 warnings.warn("importlib.abc.PyLoader is deprecated and is "
188 "slated for removal in Python 3.4; "
189 "use SourceLoader instead. "
190 "See the importlib documentation on how to be "
191 "compatible with Python 3.1 onwards.",
192 PendingDeprecationWarning)
193 path = self.source_path(fullname)
194 if path is None:
195 raise ImportError
196 else:
197 return path
198
Brett Cannonf23e3742010-06-27 23:57:46 +0000199
200class PyPycLoader(PyLoader):
Brett Cannon2a922ed2009-03-09 03:35:50 +0000201
Brett Cannon7aa21f72009-03-15 00:53:05 +0000202 """Abstract base class to assist in loading source and bytecode by
203 requiring only back-end storage methods to be implemented.
Brett Cannon2a922ed2009-03-09 03:35:50 +0000204
Brett Cannonf23e3742010-06-27 23:57:46 +0000205 This class has been deprecated! Removal is slated for Python 3.4. Implement
206 the SourceLoader ABC instead. If Python 3.1 compatibility is needed, see
207 PyLoader.
208
Brett Cannon7aa21f72009-03-15 00:53:05 +0000209 The methods get_code, get_source, and load_module are implemented for the
210 user.
211
212 """
Brett Cannon2a922ed2009-03-09 03:35:50 +0000213
Brett Cannonf23e3742010-06-27 23:57:46 +0000214 def get_filename(self, fullname):
215 """Return the source or bytecode file path."""
216 path = self.source_path(fullname)
217 if path is not None:
218 return path
219 path = self.bytecode_path(fullname)
220 if path is not None:
221 return path
222 raise ImportError("no source or bytecode path available for "
223 "{0!r}".format(fullname))
224
225 def get_code(self, fullname):
226 """Get a code object from source or bytecode."""
227 warnings.warn("importlib.abc.PyPycLoader is deprecated and slated for "
228 "removal in Python 3.4; use SourceLoader instead. "
229 "If Python 3.1 compatibility is required, see the "
230 "latest documentation for PyLoader.",
231 PendingDeprecationWarning)
232 source_timestamp = self.source_mtime(fullname)
233 # Try to use bytecode if it is available.
234 bytecode_path = self.bytecode_path(fullname)
235 if bytecode_path:
236 data = self.get_data(bytecode_path)
237 try:
238 magic = data[:4]
239 if len(magic) < 4:
240 raise ImportError("bad magic number in {}".format(fullname))
241 raw_timestamp = data[4:8]
242 if len(raw_timestamp) < 4:
243 raise EOFError("bad timestamp in {}".format(fullname))
244 pyc_timestamp = marshal._r_long(raw_timestamp)
245 bytecode = data[8:]
246 # Verify that the magic number is valid.
247 if imp.get_magic() != magic:
248 raise ImportError("bad magic number in {}".format(fullname))
249 # Verify that the bytecode is not stale (only matters when
250 # there is source to fall back on.
251 if source_timestamp:
252 if pyc_timestamp < source_timestamp:
253 raise ImportError("bytecode is stale")
254 except (ImportError, EOFError):
255 # If source is available give it a shot.
256 if source_timestamp is not None:
257 pass
258 else:
259 raise
260 else:
261 # Bytecode seems fine, so try to use it.
262 return marshal.loads(bytecode)
263 elif source_timestamp is None:
264 raise ImportError("no source or bytecode available to create code "
265 "object for {0!r}".format(fullname))
266 # Use the source.
267 source_path = self.source_path(fullname)
268 if source_path is None:
269 message = "a source path must exist to load {0}".format(fullname)
270 raise ImportError(message)
271 source = self.get_data(source_path)
272 code_object = compile(source, source_path, 'exec', dont_inherit=True)
273 # Generate bytecode and write it out.
274 if not sys.dont_write_bytecode:
275 data = bytearray(imp.get_magic())
276 data.extend(marshal._w_long(source_timestamp))
277 data.extend(marshal.dumps(code_object))
278 self.write_bytecode(fullname, data)
279 return code_object
280
Brett Cannon2a922ed2009-03-09 03:35:50 +0000281 @abc.abstractmethod
282 def source_mtime(self, fullname:str) -> int:
Brett Cannon7aa21f72009-03-15 00:53:05 +0000283 """Abstract method which when implemented should return the
284 modification time for the source of the module."""
Brett Cannon2a922ed2009-03-09 03:35:50 +0000285 raise NotImplementedError
286
287 @abc.abstractmethod
288 def bytecode_path(self, fullname:str) -> object:
Brett Cannon7aa21f72009-03-15 00:53:05 +0000289 """Abstract method which when implemented should return the path to the
290 bytecode for the module."""
Brett Cannon2a922ed2009-03-09 03:35:50 +0000291 raise NotImplementedError
292
293 @abc.abstractmethod
Brett Cannonb89ee8e2009-12-12 22:35:59 +0000294 def write_bytecode(self, fullname:str, bytecode:bytes) -> bool:
Brett Cannon7aa21f72009-03-15 00:53:05 +0000295 """Abstract method which when implemented should attempt to write the
Brett Cannonb89ee8e2009-12-12 22:35:59 +0000296 bytecode for the module, returning a boolean representing whether the
297 bytecode was written or not."""
Brett Cannon2a922ed2009-03-09 03:35:50 +0000298 raise NotImplementedError