1"""Abstract base classes related to import."""
2from . import _bootstrap
3from . import _bootstrap_external
4from . import machinery
5try:
6    import _frozen_importlib
7except ImportError as exc:
8    if exc.name != '_frozen_importlib':
9        raise
10    _frozen_importlib = None
11try:
12    import _frozen_importlib_external
13except ImportError as exc:
14    _frozen_importlib_external = _bootstrap_external
15import abc
16import warnings
17
18
19def _register(abstract_cls, *classes):
20    for cls in classes:
21        abstract_cls.register(cls)
22        if _frozen_importlib is not None:
23            try:
24                frozen_cls = getattr(_frozen_importlib, cls.__name__)
25            except AttributeError:
26                frozen_cls = getattr(_frozen_importlib_external, cls.__name__)
27            abstract_cls.register(frozen_cls)
28
29
30class Finder(metaclass=abc.ABCMeta):
31
32    """Legacy abstract base class for import finders.
33
34    It may be subclassed for compatibility with legacy third party
35    reimplementations of the import system.  Otherwise, finder
36    implementations should derive from the more specific MetaPathFinder
37    or PathEntryFinder ABCs.
38
39    Deprecated since Python 3.3
40    """
41
42    @abc.abstractmethod
43    def find_module(self, fullname, path=None):
44        """An abstract method that should find a module.
45        The fullname is a str and the optional path is a str or None.
46        Returns a Loader object or None.
47        """
48
49
50class MetaPathFinder(Finder):
51
52    """Abstract base class for import finders on sys.meta_path."""
53
54    # We don't define find_spec() here since that would break
55    # hasattr checks we do to support backward compatibility.
56
57    def find_module(self, fullname, path):
58        """Return a loader for the module.
59
60        If no module is found, return None.  The fullname is a str and
61        the path is a list of strings or None.
62
63        This method is deprecated since Python 3.4 in favor of
64        finder.find_spec(). If find_spec() exists then backwards-compatible
65        functionality is provided for this method.
66
67        """
68        warnings.warn("MetaPathFinder.find_module() is deprecated since Python "
69                      "3.4 in favor of MetaPathFinder.find_spec() "
70                      "(available since 3.4)",
71                      DeprecationWarning,
72                      stacklevel=2)
73        if not hasattr(self, 'find_spec'):
74            return None
75        found = self.find_spec(fullname, path)
76        return found.loader if found is not None else None
77
78    def invalidate_caches(self):
79        """An optional method for clearing the finder's cache, if any.
80        This method is used by importlib.invalidate_caches().
81        """
82
83_register(MetaPathFinder, machinery.BuiltinImporter, machinery.FrozenImporter,
84          machinery.PathFinder, machinery.WindowsRegistryFinder)
85
86
87class PathEntryFinder(Finder):
88
89    """Abstract base class for path entry finders used by PathFinder."""
90
91    # We don't define find_spec() here since that would break
92    # hasattr checks we do to support backward compatibility.
93
94    def find_loader(self, fullname):
95        """Return (loader, namespace portion) for the path entry.
96
97        The fullname is a str.  The namespace portion is a sequence of
98        path entries contributing to part of a namespace package. The
99        sequence may be empty.  If loader is not None, the portion will
100        be ignored.
101
102        The portion will be discarded if another path entry finder
103        locates the module as a normal module or package.
104
105        This method is deprecated since Python 3.4 in favor of
106        finder.find_spec(). If find_spec() is provided than backwards-compatible
107        functionality is provided.
108        """
109        warnings.warn("PathEntryFinder.find_loader() is deprecated since Python "
110                      "3.4 in favor of PathEntryFinder.find_spec() "
111                      "(available since 3.4)",
112                      DeprecationWarning,
113                      stacklevel=2)
114        if not hasattr(self, 'find_spec'):
115            return None, []
116        found = self.find_spec(fullname)
117        if found is not None:
118            if not found.submodule_search_locations:
119                portions = []
120            else:
121                portions = found.submodule_search_locations
122            return found.loader, portions
123        else:
124            return None, []
125
126    find_module = _bootstrap_external._find_module_shim
127
128    def invalidate_caches(self):
129        """An optional method for clearing the finder's cache, if any.
130        This method is used by PathFinder.invalidate_caches().
131        """
132
133_register(PathEntryFinder, machinery.FileFinder)
134
135
136class Loader(metaclass=abc.ABCMeta):
137
138    """Abstract base class for import loaders."""
139
140    def create_module(self, spec):
141        """Return a module to initialize and into which to load.
142
143        This method should raise ImportError if anything prevents it
144        from creating a new module.  It may return None to indicate
145        that the spec should create the new module.
146        """
147        # By default, defer to default semantics for the new module.
148        return None
149
150    # We don't define exec_module() here since that would break
151    # hasattr checks we do to support backward compatibility.
152
153    def load_module(self, fullname):
154        """Return the loaded module.
155
156        The module must be added to sys.modules and have import-related
157        attributes set properly.  The fullname is a str.
158
159        ImportError is raised on failure.
160
161        This method is deprecated in favor of loader.exec_module(). If
162        exec_module() exists then it is used to provide a backwards-compatible
163        functionality for this method.
164
165        """
166        if not hasattr(self, 'exec_module'):
167            raise ImportError
168        return _bootstrap._load_module_shim(self, fullname)
169
170    def module_repr(self, module):
171        """Return a module's repr.
172
173        Used by the module type when the method does not raise
174        NotImplementedError.
175
176        This method is deprecated.
177
178        """
179        # The exception will cause ModuleType.__repr__ to ignore this method.
180        raise NotImplementedError
181
182
183class ResourceLoader(Loader):
184
185    """Abstract base class for loaders which can return data from their
186    back-end storage.
187
188    This ABC represents one of the optional protocols specified by PEP 302.
189
190    """
191
192    @abc.abstractmethod
193    def get_data(self, path):
194        """Abstract method which when implemented should return the bytes for
195        the specified path.  The path must be a str."""
196        raise OSError
197
198
199class InspectLoader(Loader):
200
201    """Abstract base class for loaders which support inspection about the
202    modules they can load.
203
204    This ABC represents one of the optional protocols specified by PEP 302.
205
206    """
207
208    def is_package(self, fullname):
209        """Optional method which when implemented should return whether the
210        module is a package.  The fullname is a str.  Returns a bool.
211
212        Raises ImportError if the module cannot be found.
213        """
214        raise ImportError
215
216    def get_code(self, fullname):
217        """Method which returns the code object for the module.
218
219        The fullname is a str.  Returns a types.CodeType if possible, else
220        returns None if a code object does not make sense
221        (e.g. built-in module). Raises ImportError if the module cannot be
222        found.
223        """
224        source = self.get_source(fullname)
225        if source is None:
226            return None
227        return self.source_to_code(source)
228
229    @abc.abstractmethod
230    def get_source(self, fullname):
231        """Abstract method which should return the source code for the
232        module.  The fullname is a str.  Returns a str.
233
234        Raises ImportError if the module cannot be found.
235        """
236        raise ImportError
237
238    @staticmethod
239    def source_to_code(data, path='<string>'):
240        """Compile 'data' into a code object.
241
242        The 'data' argument can be anything that compile() can handle. The'path'
243        argument should be where the data was retrieved (when applicable)."""
244        return compile(data, path, 'exec', dont_inherit=True)
245
246    exec_module = _bootstrap_external._LoaderBasics.exec_module
247    load_module = _bootstrap_external._LoaderBasics.load_module
248
249_register(InspectLoader, machinery.BuiltinImporter, machinery.FrozenImporter)
250
251
252class ExecutionLoader(InspectLoader):
253
254    """Abstract base class for loaders that wish to support the execution of
255    modules as scripts.
256
257    This ABC represents one of the optional protocols specified in PEP 302.
258
259    """
260
261    @abc.abstractmethod
262    def get_filename(self, fullname):
263        """Abstract method which should return the value that __file__ is to be
264        set to.
265
266        Raises ImportError if the module cannot be found.
267        """
268        raise ImportError
269
270    def get_code(self, fullname):
271        """Method to return the code object for fullname.
272
273        Should return None if not applicable (e.g. built-in module).
274        Raise ImportError if the module cannot be found.
275        """
276        source = self.get_source(fullname)
277        if source is None:
278            return None
279        try:
280            path = self.get_filename(fullname)
281        except ImportError:
282            return self.source_to_code(source)
283        else:
284            return self.source_to_code(source, path)
285
286_register(ExecutionLoader, machinery.ExtensionFileLoader)
287
288
289class FileLoader(_bootstrap_external.FileLoader, ResourceLoader, ExecutionLoader):
290
291    """Abstract base class partially implementing the ResourceLoader and
292    ExecutionLoader ABCs."""
293
294_register(FileLoader, machinery.SourceFileLoader,
295            machinery.SourcelessFileLoader)
296
297
298class SourceLoader(_bootstrap_external.SourceLoader, ResourceLoader, ExecutionLoader):
299
300    """Abstract base class for loading source code (and optionally any
301    corresponding bytecode).
302
303    To support loading from source code, the abstractmethods inherited from
304    ResourceLoader and ExecutionLoader need to be implemented. To also support
305    loading from bytecode, the optional methods specified directly by this ABC
306    is required.
307
308    Inherited abstractmethods not implemented in this ABC:
309
310        * ResourceLoader.get_data
311        * ExecutionLoader.get_filename
312
313    """
314
315    def path_mtime(self, path):
316        """Return the (int) modification time for the path (str)."""
317        if self.path_stats.__func__ is SourceLoader.path_stats:
318            raise OSError
319        return int(self.path_stats(path)['mtime'])
320
321    def path_stats(self, path):
322        """Return a metadata dict for the source pointed to by the path (str).
323        Possible keys:
324        - 'mtime' (mandatory) is the numeric timestamp of last source
325          code modification;
326        - 'size' (optional) is the size in bytes of the source code.
327        """
328        if self.path_mtime.__func__ is SourceLoader.path_mtime:
329            raise OSError
330        return {'mtime': self.path_mtime(path)}
331
332    def set_data(self, path, data):
333        """Write the bytes to the path (if possible).
334
335        Accepts a str path and data as bytes.
336
337        Any needed intermediary directories are to be created. If for some
338        reason the file cannot be written because of permissions, fail
339        silently.
340        """
341
342_register(SourceLoader, machinery.SourceFileLoader)
343
344
345class ResourceReader(metaclass=abc.ABCMeta):
346
347    """Abstract base class to provide resource-reading support.
348
349    Loaders that support resource reading are expected to implement
350    the ``get_resource_reader(fullname)`` method and have it either return None
351    or an object compatible with this ABC.
352    """
353
354    @abc.abstractmethod
355    def open_resource(self, resource):
356        """Return an opened, file-like object for binary reading.
357
358        The 'resource' argument is expected to represent only a file name
359        and thus not contain any subdirectory components.
360
361        If the resource cannot be found, FileNotFoundError is raised.
362        """
363        raise FileNotFoundError
364
365    @abc.abstractmethod
366    def resource_path(self, resource):
367        """Return the file system path to the specified resource.
368
369        The 'resource' argument is expected to represent only a file name
370        and thus not contain any subdirectory components.
371
372        If the resource does not exist on the file system, raise
373        FileNotFoundError.
374        """
375        raise FileNotFoundError
376
377    @abc.abstractmethod
378    def is_resource(self, name):
379        """Return True if the named 'name' is consider a resource."""
380        raise FileNotFoundError
381
382    @abc.abstractmethod
383    def contents(self):
384        """Return an iterable of strings over the contents of the package."""
385        return []
386
387
388_register(ResourceReader, machinery.SourceFileLoader)
389