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