• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1import io
2import marshal
3import os
4import sys
5from test import support
6import types
7import unittest
8from unittest import mock
9import warnings
10
11from . import util as test_util
12
13init = test_util.import_importlib('importlib')
14abc = test_util.import_importlib('importlib.abc')
15machinery = test_util.import_importlib('importlib.machinery')
16util = test_util.import_importlib('importlib.util')
17
18
19##### Inheritance ##############################################################
20class InheritanceTests:
21
22    """Test that the specified class is a subclass/superclass of the expected
23    classes."""
24
25    subclasses = []
26    superclasses = []
27
28    def setUp(self):
29        self.superclasses = [getattr(self.abc, class_name)
30                             for class_name in self.superclass_names]
31        if hasattr(self, 'subclass_names'):
32            # Because test.support.import_fresh_module() creates a new
33            # importlib._bootstrap per module, inheritance checks fail when
34            # checking across module boundaries (i.e. the _bootstrap in abc is
35            # not the same as the one in machinery). That means stealing one of
36            # the modules from the other to make sure the same instance is used.
37            machinery = self.abc.machinery
38            self.subclasses = [getattr(machinery, class_name)
39                               for class_name in self.subclass_names]
40        assert self.subclasses or self.superclasses, self.__class__
41        self.__test = getattr(self.abc, self._NAME)
42
43    def test_subclasses(self):
44        # Test that the expected subclasses inherit.
45        for subclass in self.subclasses:
46            self.assertTrue(issubclass(subclass, self.__test),
47                "{0} is not a subclass of {1}".format(subclass, self.__test))
48
49    def test_superclasses(self):
50        # Test that the class inherits from the expected superclasses.
51        for superclass in self.superclasses:
52            self.assertTrue(issubclass(self.__test, superclass),
53               "{0} is not a superclass of {1}".format(superclass, self.__test))
54
55
56class MetaPathFinder(InheritanceTests):
57    superclass_names = ['Finder']
58    subclass_names = ['BuiltinImporter', 'FrozenImporter', 'PathFinder',
59                      'WindowsRegistryFinder']
60
61
62(Frozen_MetaPathFinderInheritanceTests,
63 Source_MetaPathFinderInheritanceTests
64 ) = test_util.test_both(MetaPathFinder, abc=abc)
65
66
67class PathEntryFinder(InheritanceTests):
68    superclass_names = ['Finder']
69    subclass_names = ['FileFinder']
70
71
72(Frozen_PathEntryFinderInheritanceTests,
73 Source_PathEntryFinderInheritanceTests
74 ) = test_util.test_both(PathEntryFinder, abc=abc)
75
76
77class ResourceLoader(InheritanceTests):
78    superclass_names = ['Loader']
79
80
81(Frozen_ResourceLoaderInheritanceTests,
82 Source_ResourceLoaderInheritanceTests
83 ) = test_util.test_both(ResourceLoader, abc=abc)
84
85
86class InspectLoader(InheritanceTests):
87    superclass_names = ['Loader']
88    subclass_names = ['BuiltinImporter', 'FrozenImporter', 'ExtensionFileLoader']
89
90
91(Frozen_InspectLoaderInheritanceTests,
92 Source_InspectLoaderInheritanceTests
93 ) = test_util.test_both(InspectLoader, abc=abc)
94
95
96class ExecutionLoader(InheritanceTests):
97    superclass_names = ['InspectLoader']
98    subclass_names = ['ExtensionFileLoader']
99
100
101(Frozen_ExecutionLoaderInheritanceTests,
102 Source_ExecutionLoaderInheritanceTests
103 ) = test_util.test_both(ExecutionLoader, abc=abc)
104
105
106class FileLoader(InheritanceTests):
107    superclass_names = ['ResourceLoader', 'ExecutionLoader']
108    subclass_names = ['SourceFileLoader', 'SourcelessFileLoader']
109
110
111(Frozen_FileLoaderInheritanceTests,
112 Source_FileLoaderInheritanceTests
113 ) = test_util.test_both(FileLoader, abc=abc)
114
115
116class SourceLoader(InheritanceTests):
117    superclass_names = ['ResourceLoader', 'ExecutionLoader']
118    subclass_names = ['SourceFileLoader']
119
120
121(Frozen_SourceLoaderInheritanceTests,
122 Source_SourceLoaderInheritanceTests
123 ) = test_util.test_both(SourceLoader, abc=abc)
124
125
126##### Default return values ####################################################
127
128def make_abc_subclasses(base_class, name=None, inst=False, **kwargs):
129    if name is None:
130        name = base_class.__name__
131    base = {kind: getattr(splitabc, name)
132            for kind, splitabc in abc.items()}
133    return {cls._KIND: cls() if inst else cls
134            for cls in test_util.split_frozen(base_class, base, **kwargs)}
135
136
137class ABCTestHarness:
138
139    @property
140    def ins(self):
141        # Lazily set ins on the class.
142        cls = self.SPLIT[self._KIND]
143        ins = cls()
144        self.__class__.ins = ins
145        return ins
146
147
148class MetaPathFinder:
149
150    def find_module(self, fullname, path):
151        return super().find_module(fullname, path)
152
153
154class MetaPathFinderDefaultsTests(ABCTestHarness):
155
156    SPLIT = make_abc_subclasses(MetaPathFinder)
157
158    def test_find_module(self):
159        # Default should return None.
160        with self.assertWarns(DeprecationWarning):
161            found = self.ins.find_module('something', None)
162        self.assertIsNone(found)
163
164    def test_invalidate_caches(self):
165        # Calling the method is a no-op.
166        self.ins.invalidate_caches()
167
168
169(Frozen_MPFDefaultTests,
170 Source_MPFDefaultTests
171 ) = test_util.test_both(MetaPathFinderDefaultsTests)
172
173
174class PathEntryFinder:
175
176    def find_loader(self, fullname):
177        return super().find_loader(fullname)
178
179
180class PathEntryFinderDefaultsTests(ABCTestHarness):
181
182    SPLIT = make_abc_subclasses(PathEntryFinder)
183
184    def test_find_loader(self):
185        with self.assertWarns(DeprecationWarning):
186            found = self.ins.find_loader('something')
187        self.assertEqual(found, (None, []))
188
189    def find_module(self):
190        self.assertEqual(None, self.ins.find_module('something'))
191
192    def test_invalidate_caches(self):
193        # Should be a no-op.
194        self.ins.invalidate_caches()
195
196
197(Frozen_PEFDefaultTests,
198 Source_PEFDefaultTests
199 ) = test_util.test_both(PathEntryFinderDefaultsTests)
200
201
202class Loader:
203
204    def load_module(self, fullname):
205        return super().load_module(fullname)
206
207
208class LoaderDefaultsTests(ABCTestHarness):
209
210    SPLIT = make_abc_subclasses(Loader)
211
212    def test_create_module(self):
213        spec = 'a spec'
214        self.assertIsNone(self.ins.create_module(spec))
215
216    def test_load_module(self):
217        with self.assertRaises(ImportError):
218            self.ins.load_module('something')
219
220    def test_module_repr(self):
221        mod = types.ModuleType('blah')
222        with self.assertRaises(NotImplementedError):
223            self.ins.module_repr(mod)
224        original_repr = repr(mod)
225        mod.__loader__ = self.ins
226        # Should still return a proper repr.
227        self.assertTrue(repr(mod))
228
229
230(Frozen_LDefaultTests,
231 SourceLDefaultTests
232 ) = test_util.test_both(LoaderDefaultsTests)
233
234
235class ResourceLoader(Loader):
236
237    def get_data(self, path):
238        return super().get_data(path)
239
240
241class ResourceLoaderDefaultsTests(ABCTestHarness):
242
243    SPLIT = make_abc_subclasses(ResourceLoader)
244
245    def test_get_data(self):
246        with self.assertRaises(IOError):
247            self.ins.get_data('/some/path')
248
249
250(Frozen_RLDefaultTests,
251 Source_RLDefaultTests
252 ) = test_util.test_both(ResourceLoaderDefaultsTests)
253
254
255class InspectLoader(Loader):
256
257    def is_package(self, fullname):
258        return super().is_package(fullname)
259
260    def get_source(self, fullname):
261        return super().get_source(fullname)
262
263
264SPLIT_IL = make_abc_subclasses(InspectLoader)
265
266
267class InspectLoaderDefaultsTests(ABCTestHarness):
268
269    SPLIT = SPLIT_IL
270
271    def test_is_package(self):
272        with self.assertRaises(ImportError):
273            self.ins.is_package('blah')
274
275    def test_get_source(self):
276        with self.assertRaises(ImportError):
277            self.ins.get_source('blah')
278
279
280(Frozen_ILDefaultTests,
281 Source_ILDefaultTests
282 ) = test_util.test_both(InspectLoaderDefaultsTests)
283
284
285class ExecutionLoader(InspectLoader):
286
287    def get_filename(self, fullname):
288        return super().get_filename(fullname)
289
290
291SPLIT_EL = make_abc_subclasses(ExecutionLoader)
292
293
294class ExecutionLoaderDefaultsTests(ABCTestHarness):
295
296    SPLIT = SPLIT_EL
297
298    def test_get_filename(self):
299        with self.assertRaises(ImportError):
300            self.ins.get_filename('blah')
301
302
303(Frozen_ELDefaultTests,
304 Source_ELDefaultsTests
305 ) = test_util.test_both(InspectLoaderDefaultsTests)
306
307
308class ResourceReader:
309
310    def open_resource(self, *args, **kwargs):
311        return super().open_resource(*args, **kwargs)
312
313    def resource_path(self, *args, **kwargs):
314        return super().resource_path(*args, **kwargs)
315
316    def is_resource(self, *args, **kwargs):
317        return super().is_resource(*args, **kwargs)
318
319    def contents(self, *args, **kwargs):
320        return super().contents(*args, **kwargs)
321
322
323class ResourceReaderDefaultsTests(ABCTestHarness):
324
325    SPLIT = make_abc_subclasses(ResourceReader)
326
327    def test_open_resource(self):
328        with self.assertRaises(FileNotFoundError):
329            self.ins.open_resource('dummy_file')
330
331    def test_resource_path(self):
332        with self.assertRaises(FileNotFoundError):
333            self.ins.resource_path('dummy_file')
334
335    def test_is_resource(self):
336        with self.assertRaises(FileNotFoundError):
337            self.ins.is_resource('dummy_file')
338
339    def test_contents(self):
340        self.assertEqual([], list(self.ins.contents()))
341
342(Frozen_RRDefaultTests,
343 Source_RRDefaultsTests
344 ) = test_util.test_both(ResourceReaderDefaultsTests)
345
346
347##### MetaPathFinder concrete methods ##########################################
348class MetaPathFinderFindModuleTests:
349
350    @classmethod
351    def finder(cls, spec):
352        class MetaPathSpecFinder(cls.abc.MetaPathFinder):
353
354            def find_spec(self, fullname, path, target=None):
355                self.called_for = fullname, path
356                return spec
357
358        return MetaPathSpecFinder()
359
360    def test_find_module(self):
361        finder = self.finder(None)
362        path = ['a', 'b', 'c']
363        name = 'blah'
364        with self.assertWarns(DeprecationWarning):
365            found = finder.find_module(name, path)
366        self.assertIsNone(found)
367
368    def test_find_spec_with_explicit_target(self):
369        loader = object()
370        spec = self.util.spec_from_loader('blah', loader)
371        finder = self.finder(spec)
372        found = finder.find_spec('blah', 'blah', None)
373        self.assertEqual(found, spec)
374
375    def test_no_spec(self):
376        finder = self.finder(None)
377        path = ['a', 'b', 'c']
378        name = 'blah'
379        found = finder.find_spec(name, path, None)
380        self.assertIsNone(found)
381        self.assertEqual(name, finder.called_for[0])
382        self.assertEqual(path, finder.called_for[1])
383
384    def test_spec(self):
385        loader = object()
386        spec = self.util.spec_from_loader('blah', loader)
387        finder = self.finder(spec)
388        found = finder.find_spec('blah', None)
389        self.assertIs(found, spec)
390
391
392(Frozen_MPFFindModuleTests,
393 Source_MPFFindModuleTests
394 ) = test_util.test_both(MetaPathFinderFindModuleTests, abc=abc, util=util)
395
396
397##### PathEntryFinder concrete methods #########################################
398class PathEntryFinderFindLoaderTests:
399
400    @classmethod
401    def finder(cls, spec):
402        class PathEntrySpecFinder(cls.abc.PathEntryFinder):
403
404            def find_spec(self, fullname, target=None):
405                self.called_for = fullname
406                return spec
407
408        return PathEntrySpecFinder()
409
410    def test_no_spec(self):
411        finder = self.finder(None)
412        name = 'blah'
413        with self.assertWarns(DeprecationWarning):
414            found = finder.find_loader(name)
415        self.assertIsNone(found[0])
416        self.assertEqual([], found[1])
417        self.assertEqual(name, finder.called_for)
418
419    def test_spec_with_loader(self):
420        loader = object()
421        spec = self.util.spec_from_loader('blah', loader)
422        finder = self.finder(spec)
423        with self.assertWarns(DeprecationWarning):
424            found = finder.find_loader('blah')
425        self.assertIs(found[0], spec.loader)
426
427    def test_spec_with_portions(self):
428        spec = self.machinery.ModuleSpec('blah', None)
429        paths = ['a', 'b', 'c']
430        spec.submodule_search_locations = paths
431        finder = self.finder(spec)
432        with self.assertWarns(DeprecationWarning):
433            found = finder.find_loader('blah')
434        self.assertIsNone(found[0])
435        self.assertEqual(paths, found[1])
436
437
438(Frozen_PEFFindLoaderTests,
439 Source_PEFFindLoaderTests
440 ) = test_util.test_both(PathEntryFinderFindLoaderTests, abc=abc, util=util,
441                         machinery=machinery)
442
443
444##### Loader concrete methods ##################################################
445class LoaderLoadModuleTests:
446
447    def loader(self):
448        class SpecLoader(self.abc.Loader):
449            found = None
450            def exec_module(self, module):
451                self.found = module
452
453            def is_package(self, fullname):
454                """Force some non-default module state to be set."""
455                return True
456
457        return SpecLoader()
458
459    def test_fresh(self):
460        loader = self.loader()
461        name = 'blah'
462        with test_util.uncache(name):
463            loader.load_module(name)
464            module = loader.found
465            self.assertIs(sys.modules[name], module)
466        self.assertEqual(loader, module.__loader__)
467        self.assertEqual(loader, module.__spec__.loader)
468        self.assertEqual(name, module.__name__)
469        self.assertEqual(name, module.__spec__.name)
470        self.assertIsNotNone(module.__path__)
471        self.assertIsNotNone(module.__path__,
472                             module.__spec__.submodule_search_locations)
473
474    def test_reload(self):
475        name = 'blah'
476        loader = self.loader()
477        module = types.ModuleType(name)
478        module.__spec__ = self.util.spec_from_loader(name, loader)
479        module.__loader__ = loader
480        with test_util.uncache(name):
481            sys.modules[name] = module
482            loader.load_module(name)
483            found = loader.found
484            self.assertIs(found, sys.modules[name])
485            self.assertIs(module, sys.modules[name])
486
487
488(Frozen_LoaderLoadModuleTests,
489 Source_LoaderLoadModuleTests
490 ) = test_util.test_both(LoaderLoadModuleTests, abc=abc, util=util)
491
492
493##### InspectLoader concrete methods ###########################################
494class InspectLoaderSourceToCodeTests:
495
496    def source_to_module(self, data, path=None):
497        """Help with source_to_code() tests."""
498        module = types.ModuleType('blah')
499        loader = self.InspectLoaderSubclass()
500        if path is None:
501            code = loader.source_to_code(data)
502        else:
503            code = loader.source_to_code(data, path)
504        exec(code, module.__dict__)
505        return module
506
507    def test_source_to_code_source(self):
508        # Since compile() can handle strings, so should source_to_code().
509        source = 'attr = 42'
510        module = self.source_to_module(source)
511        self.assertTrue(hasattr(module, 'attr'))
512        self.assertEqual(module.attr, 42)
513
514    def test_source_to_code_bytes(self):
515        # Since compile() can handle bytes, so should source_to_code().
516        source = b'attr = 42'
517        module = self.source_to_module(source)
518        self.assertTrue(hasattr(module, 'attr'))
519        self.assertEqual(module.attr, 42)
520
521    def test_source_to_code_path(self):
522        # Specifying a path should set it for the code object.
523        path = 'path/to/somewhere'
524        loader = self.InspectLoaderSubclass()
525        code = loader.source_to_code('', path)
526        self.assertEqual(code.co_filename, path)
527
528    def test_source_to_code_no_path(self):
529        # Not setting a path should still work and be set to <string> since that
530        # is a pre-existing practice as a default to compile().
531        loader = self.InspectLoaderSubclass()
532        code = loader.source_to_code('')
533        self.assertEqual(code.co_filename, '<string>')
534
535
536(Frozen_ILSourceToCodeTests,
537 Source_ILSourceToCodeTests
538 ) = test_util.test_both(InspectLoaderSourceToCodeTests,
539                         InspectLoaderSubclass=SPLIT_IL)
540
541
542class InspectLoaderGetCodeTests:
543
544    def test_get_code(self):
545        # Test success.
546        module = types.ModuleType('blah')
547        with mock.patch.object(self.InspectLoaderSubclass, 'get_source') as mocked:
548            mocked.return_value = 'attr = 42'
549            loader = self.InspectLoaderSubclass()
550            code = loader.get_code('blah')
551        exec(code, module.__dict__)
552        self.assertEqual(module.attr, 42)
553
554    def test_get_code_source_is_None(self):
555        # If get_source() is None then this should be None.
556        with mock.patch.object(self.InspectLoaderSubclass, 'get_source') as mocked:
557            mocked.return_value = None
558            loader = self.InspectLoaderSubclass()
559            code = loader.get_code('blah')
560        self.assertIsNone(code)
561
562    def test_get_code_source_not_found(self):
563        # If there is no source then there is no code object.
564        loader = self.InspectLoaderSubclass()
565        with self.assertRaises(ImportError):
566            loader.get_code('blah')
567
568
569(Frozen_ILGetCodeTests,
570 Source_ILGetCodeTests
571 ) = test_util.test_both(InspectLoaderGetCodeTests,
572                         InspectLoaderSubclass=SPLIT_IL)
573
574
575class InspectLoaderLoadModuleTests:
576
577    """Test InspectLoader.load_module()."""
578
579    module_name = 'blah'
580
581    def setUp(self):
582        support.unload(self.module_name)
583        self.addCleanup(support.unload, self.module_name)
584
585    def load(self, loader):
586        spec = self.util.spec_from_loader(self.module_name, loader)
587        with warnings.catch_warnings():
588            warnings.simplefilter('ignore', DeprecationWarning)
589            return self.init._bootstrap._load_unlocked(spec)
590
591    def mock_get_code(self):
592        return mock.patch.object(self.InspectLoaderSubclass, 'get_code')
593
594    def test_get_code_ImportError(self):
595        # If get_code() raises ImportError, it should propagate.
596        with self.mock_get_code() as mocked_get_code:
597            mocked_get_code.side_effect = ImportError
598            with self.assertRaises(ImportError):
599                loader = self.InspectLoaderSubclass()
600                self.load(loader)
601
602    def test_get_code_None(self):
603        # If get_code() returns None, raise ImportError.
604        with self.mock_get_code() as mocked_get_code:
605            mocked_get_code.return_value = None
606            with self.assertRaises(ImportError):
607                loader = self.InspectLoaderSubclass()
608                self.load(loader)
609
610    def test_module_returned(self):
611        # The loaded module should be returned.
612        code = compile('attr = 42', '<string>', 'exec')
613        with self.mock_get_code() as mocked_get_code:
614            mocked_get_code.return_value = code
615            loader = self.InspectLoaderSubclass()
616            module = self.load(loader)
617            self.assertEqual(module, sys.modules[self.module_name])
618
619
620(Frozen_ILLoadModuleTests,
621 Source_ILLoadModuleTests
622 ) = test_util.test_both(InspectLoaderLoadModuleTests,
623                         InspectLoaderSubclass=SPLIT_IL,
624                         init=init,
625                         util=util)
626
627
628##### ExecutionLoader concrete methods #########################################
629class ExecutionLoaderGetCodeTests:
630
631    def mock_methods(self, *, get_source=False, get_filename=False):
632        source_mock_context, filename_mock_context = None, None
633        if get_source:
634            source_mock_context = mock.patch.object(self.ExecutionLoaderSubclass,
635                                                    'get_source')
636        if get_filename:
637            filename_mock_context = mock.patch.object(self.ExecutionLoaderSubclass,
638                                                      'get_filename')
639        return source_mock_context, filename_mock_context
640
641    def test_get_code(self):
642        path = 'blah.py'
643        source_mock_context, filename_mock_context = self.mock_methods(
644                get_source=True, get_filename=True)
645        with source_mock_context as source_mock, filename_mock_context as name_mock:
646            source_mock.return_value = 'attr = 42'
647            name_mock.return_value = path
648            loader = self.ExecutionLoaderSubclass()
649            code = loader.get_code('blah')
650        self.assertEqual(code.co_filename, path)
651        module = types.ModuleType('blah')
652        exec(code, module.__dict__)
653        self.assertEqual(module.attr, 42)
654
655    def test_get_code_source_is_None(self):
656        # If get_source() is None then this should be None.
657        source_mock_context, _ = self.mock_methods(get_source=True)
658        with source_mock_context as mocked:
659            mocked.return_value = None
660            loader = self.ExecutionLoaderSubclass()
661            code = loader.get_code('blah')
662        self.assertIsNone(code)
663
664    def test_get_code_source_not_found(self):
665        # If there is no source then there is no code object.
666        loader = self.ExecutionLoaderSubclass()
667        with self.assertRaises(ImportError):
668            loader.get_code('blah')
669
670    def test_get_code_no_path(self):
671        # If get_filename() raises ImportError then simply skip setting the path
672        # on the code object.
673        source_mock_context, filename_mock_context = self.mock_methods(
674                get_source=True, get_filename=True)
675        with source_mock_context as source_mock, filename_mock_context as name_mock:
676            source_mock.return_value = 'attr = 42'
677            name_mock.side_effect = ImportError
678            loader = self.ExecutionLoaderSubclass()
679            code = loader.get_code('blah')
680        self.assertEqual(code.co_filename, '<string>')
681        module = types.ModuleType('blah')
682        exec(code, module.__dict__)
683        self.assertEqual(module.attr, 42)
684
685
686(Frozen_ELGetCodeTests,
687 Source_ELGetCodeTests
688 ) = test_util.test_both(ExecutionLoaderGetCodeTests,
689                         ExecutionLoaderSubclass=SPLIT_EL)
690
691
692##### SourceLoader concrete methods ############################################
693class SourceOnlyLoader:
694
695    # Globals that should be defined for all modules.
696    source = (b"_ = '::'.join([__name__, __file__, __cached__, __package__, "
697              b"repr(__loader__)])")
698
699    def __init__(self, path):
700        self.path = path
701
702    def get_data(self, path):
703        if path != self.path:
704            raise IOError
705        return self.source
706
707    def get_filename(self, fullname):
708        return self.path
709
710    def module_repr(self, module):
711        return '<module>'
712
713
714SPLIT_SOL = make_abc_subclasses(SourceOnlyLoader, 'SourceLoader')
715
716
717class SourceLoader(SourceOnlyLoader):
718
719    source_mtime = 1
720
721    def __init__(self, path, magic=None):
722        super().__init__(path)
723        self.bytecode_path = self.util.cache_from_source(self.path)
724        self.source_size = len(self.source)
725        if magic is None:
726            magic = self.util.MAGIC_NUMBER
727        data = bytearray(magic)
728        data.extend(self.init._pack_uint32(0))
729        data.extend(self.init._pack_uint32(self.source_mtime))
730        data.extend(self.init._pack_uint32(self.source_size))
731        code_object = compile(self.source, self.path, 'exec',
732                                dont_inherit=True)
733        data.extend(marshal.dumps(code_object))
734        self.bytecode = bytes(data)
735        self.written = {}
736
737    def get_data(self, path):
738        if path == self.path:
739            return super().get_data(path)
740        elif path == self.bytecode_path:
741            return self.bytecode
742        else:
743            raise OSError
744
745    def path_stats(self, path):
746        if path != self.path:
747            raise IOError
748        return {'mtime': self.source_mtime, 'size': self.source_size}
749
750    def set_data(self, path, data):
751        self.written[path] = bytes(data)
752        return path == self.bytecode_path
753
754
755SPLIT_SL = make_abc_subclasses(SourceLoader, util=util, init=init)
756
757
758class SourceLoaderTestHarness:
759
760    def setUp(self, *, is_package=True, **kwargs):
761        self.package = 'pkg'
762        if is_package:
763            self.path = os.path.join(self.package, '__init__.py')
764            self.name = self.package
765        else:
766            module_name = 'mod'
767            self.path = os.path.join(self.package, '.'.join(['mod', 'py']))
768            self.name = '.'.join([self.package, module_name])
769        self.cached = self.util.cache_from_source(self.path)
770        self.loader = self.loader_mock(self.path, **kwargs)
771
772    def verify_module(self, module):
773        self.assertEqual(module.__name__, self.name)
774        self.assertEqual(module.__file__, self.path)
775        self.assertEqual(module.__cached__, self.cached)
776        self.assertEqual(module.__package__, self.package)
777        self.assertEqual(module.__loader__, self.loader)
778        values = module._.split('::')
779        self.assertEqual(values[0], self.name)
780        self.assertEqual(values[1], self.path)
781        self.assertEqual(values[2], self.cached)
782        self.assertEqual(values[3], self.package)
783        self.assertEqual(values[4], repr(self.loader))
784
785    def verify_code(self, code_object):
786        module = types.ModuleType(self.name)
787        module.__file__ = self.path
788        module.__cached__ = self.cached
789        module.__package__ = self.package
790        module.__loader__ = self.loader
791        module.__path__ = []
792        exec(code_object, module.__dict__)
793        self.verify_module(module)
794
795
796class SourceOnlyLoaderTests(SourceLoaderTestHarness):
797
798    """Test importlib.abc.SourceLoader for source-only loading.
799
800    Reload testing is subsumed by the tests for
801    importlib.util.module_for_loader.
802
803    """
804
805    def test_get_source(self):
806        # Verify the source code is returned as a string.
807        # If an OSError is raised by get_data then raise ImportError.
808        expected_source = self.loader.source.decode('utf-8')
809        self.assertEqual(self.loader.get_source(self.name), expected_source)
810        def raise_OSError(path):
811            raise OSError
812        self.loader.get_data = raise_OSError
813        with self.assertRaises(ImportError) as cm:
814            self.loader.get_source(self.name)
815        self.assertEqual(cm.exception.name, self.name)
816
817    def test_is_package(self):
818        # Properly detect when loading a package.
819        self.setUp(is_package=False)
820        self.assertFalse(self.loader.is_package(self.name))
821        self.setUp(is_package=True)
822        self.assertTrue(self.loader.is_package(self.name))
823        self.assertFalse(self.loader.is_package(self.name + '.__init__'))
824
825    def test_get_code(self):
826        # Verify the code object is created.
827        code_object = self.loader.get_code(self.name)
828        self.verify_code(code_object)
829
830    def test_source_to_code(self):
831        # Verify the compiled code object.
832        code = self.loader.source_to_code(self.loader.source, self.path)
833        self.verify_code(code)
834
835    def test_load_module(self):
836        # Loading a module should set __name__, __loader__, __package__,
837        # __path__ (for packages), __file__, and __cached__.
838        # The module should also be put into sys.modules.
839        with test_util.uncache(self.name):
840            with warnings.catch_warnings():
841                warnings.simplefilter('ignore', DeprecationWarning)
842                module = self.loader.load_module(self.name)
843            self.verify_module(module)
844            self.assertEqual(module.__path__, [os.path.dirname(self.path)])
845            self.assertIn(self.name, sys.modules)
846
847    def test_package_settings(self):
848        # __package__ needs to be set, while __path__ is set on if the module
849        # is a package.
850        # Testing the values for a package are covered by test_load_module.
851        self.setUp(is_package=False)
852        with test_util.uncache(self.name):
853            with warnings.catch_warnings():
854                warnings.simplefilter('ignore', DeprecationWarning)
855                module = self.loader.load_module(self.name)
856            self.verify_module(module)
857            self.assertFalse(hasattr(module, '__path__'))
858
859    def test_get_source_encoding(self):
860        # Source is considered encoded in UTF-8 by default unless otherwise
861        # specified by an encoding line.
862        source = "_ = 'ü'"
863        self.loader.source = source.encode('utf-8')
864        returned_source = self.loader.get_source(self.name)
865        self.assertEqual(returned_source, source)
866        source = "# coding: latin-1\n_ = ü"
867        self.loader.source = source.encode('latin-1')
868        returned_source = self.loader.get_source(self.name)
869        self.assertEqual(returned_source, source)
870
871
872(Frozen_SourceOnlyLoaderTests,
873 Source_SourceOnlyLoaderTests
874 ) = test_util.test_both(SourceOnlyLoaderTests, util=util,
875                         loader_mock=SPLIT_SOL)
876
877
878@unittest.skipIf(sys.dont_write_bytecode, "sys.dont_write_bytecode is true")
879class SourceLoaderBytecodeTests(SourceLoaderTestHarness):
880
881    """Test importlib.abc.SourceLoader's use of bytecode.
882
883    Source-only testing handled by SourceOnlyLoaderTests.
884
885    """
886
887    def verify_code(self, code_object, *, bytecode_written=False):
888        super().verify_code(code_object)
889        if bytecode_written:
890            self.assertIn(self.cached, self.loader.written)
891            data = bytearray(self.util.MAGIC_NUMBER)
892            data.extend(self.init._pack_uint32(0))
893            data.extend(self.init._pack_uint32(self.loader.source_mtime))
894            data.extend(self.init._pack_uint32(self.loader.source_size))
895            data.extend(marshal.dumps(code_object))
896            self.assertEqual(self.loader.written[self.cached], bytes(data))
897
898    def test_code_with_everything(self):
899        # When everything should work.
900        code_object = self.loader.get_code(self.name)
901        self.verify_code(code_object)
902
903    def test_no_bytecode(self):
904        # If no bytecode exists then move on to the source.
905        self.loader.bytecode_path = "<does not exist>"
906        # Sanity check
907        with self.assertRaises(OSError):
908            bytecode_path = self.util.cache_from_source(self.path)
909            self.loader.get_data(bytecode_path)
910        code_object = self.loader.get_code(self.name)
911        self.verify_code(code_object, bytecode_written=True)
912
913    def test_code_bad_timestamp(self):
914        # Bytecode is only used when the timestamp matches the source EXACTLY.
915        for source_mtime in (0, 2):
916            assert source_mtime != self.loader.source_mtime
917            original = self.loader.source_mtime
918            self.loader.source_mtime = source_mtime
919            # If bytecode is used then EOFError would be raised by marshal.
920            self.loader.bytecode = self.loader.bytecode[8:]
921            code_object = self.loader.get_code(self.name)
922            self.verify_code(code_object, bytecode_written=True)
923            self.loader.source_mtime = original
924
925    def test_code_bad_magic(self):
926        # Skip over bytecode with a bad magic number.
927        self.setUp(magic=b'0000')
928        # If bytecode is used then EOFError would be raised by marshal.
929        self.loader.bytecode = self.loader.bytecode[8:]
930        code_object = self.loader.get_code(self.name)
931        self.verify_code(code_object, bytecode_written=True)
932
933    def test_dont_write_bytecode(self):
934        # Bytecode is not written if sys.dont_write_bytecode is true.
935        # Can assume it is false already thanks to the skipIf class decorator.
936        try:
937            sys.dont_write_bytecode = True
938            self.loader.bytecode_path = "<does not exist>"
939            code_object = self.loader.get_code(self.name)
940            self.assertNotIn(self.cached, self.loader.written)
941        finally:
942            sys.dont_write_bytecode = False
943
944    def test_no_set_data(self):
945        # If set_data is not defined, one can still read bytecode.
946        self.setUp(magic=b'0000')
947        original_set_data = self.loader.__class__.mro()[1].set_data
948        try:
949            del self.loader.__class__.mro()[1].set_data
950            code_object = self.loader.get_code(self.name)
951            self.verify_code(code_object)
952        finally:
953            self.loader.__class__.mro()[1].set_data = original_set_data
954
955    def test_set_data_raises_exceptions(self):
956        # Raising NotImplementedError or OSError is okay for set_data.
957        def raise_exception(exc):
958            def closure(*args, **kwargs):
959                raise exc
960            return closure
961
962        self.setUp(magic=b'0000')
963        self.loader.set_data = raise_exception(NotImplementedError)
964        code_object = self.loader.get_code(self.name)
965        self.verify_code(code_object)
966
967
968(Frozen_SLBytecodeTests,
969 SourceSLBytecodeTests
970 ) = test_util.test_both(SourceLoaderBytecodeTests, init=init, util=util,
971                         loader_mock=SPLIT_SL)
972
973
974class SourceLoaderGetSourceTests:
975
976    """Tests for importlib.abc.SourceLoader.get_source()."""
977
978    def test_default_encoding(self):
979        # Should have no problems with UTF-8 text.
980        name = 'mod'
981        mock = self.SourceOnlyLoaderMock('mod.file')
982        source = 'x = "ü"'
983        mock.source = source.encode('utf-8')
984        returned_source = mock.get_source(name)
985        self.assertEqual(returned_source, source)
986
987    def test_decoded_source(self):
988        # Decoding should work.
989        name = 'mod'
990        mock = self.SourceOnlyLoaderMock("mod.file")
991        source = "# coding: Latin-1\nx='ü'"
992        assert source.encode('latin-1') != source.encode('utf-8')
993        mock.source = source.encode('latin-1')
994        returned_source = mock.get_source(name)
995        self.assertEqual(returned_source, source)
996
997    def test_universal_newlines(self):
998        # PEP 302 says universal newlines should be used.
999        name = 'mod'
1000        mock = self.SourceOnlyLoaderMock('mod.file')
1001        source = "x = 42\r\ny = -13\r\n"
1002        mock.source = source.encode('utf-8')
1003        expect = io.IncrementalNewlineDecoder(None, True).decode(source)
1004        self.assertEqual(mock.get_source(name), expect)
1005
1006
1007(Frozen_SourceOnlyLoaderGetSourceTests,
1008 Source_SourceOnlyLoaderGetSourceTests
1009 ) = test_util.test_both(SourceLoaderGetSourceTests,
1010                         SourceOnlyLoaderMock=SPLIT_SOL)
1011
1012
1013if __name__ == '__main__':
1014    unittest.main()
1015