|
| 1 | +from test.test_importlib import fixtures |
1 | 2 | from test.test_importlib import util |
2 | 3 |
|
3 | 4 | abc = util.import_importlib('importlib.abc') |
|
12 | 13 | import string |
13 | 14 | import sys |
14 | 15 | from test import support |
| 16 | +from test.support import import_helper, os_helper |
15 | 17 | import textwrap |
16 | 18 | import types |
17 | 19 | import unittest |
@@ -758,5 +760,101 @@ def test_complete_multi_phase_init_module(self): |
758 | 760 | self.run_with_own_gil(script) |
759 | 761 |
|
760 | 762 |
|
| 763 | +class LoadSourcePathTests(unittest.TestCase): |
| 764 | + def check_module(self, mod, modname, filename, is_package=False): |
| 765 | + abs_filename = os.path.abspath(filename) |
| 766 | + |
| 767 | + self.assertIsInstance(mod, types.ModuleType) |
| 768 | + self.assertEqual(mod.__name__, modname) |
| 769 | + self.assertEqual(mod.__file__, abs_filename) |
| 770 | + self.assertIn(modname, sys.modules) |
| 771 | + self.assertIs(sys.modules[modname], mod) |
| 772 | + self.assertEqual(mod.__path__, [os.path.dirname(abs_filename)]) |
| 773 | + |
| 774 | + loader = mod.__loader__ |
| 775 | + self.assertEqual(loader.is_package(modname), is_package) |
| 776 | + |
| 777 | + spec = mod.__spec__ |
| 778 | + self.assertEqual(spec.name, modname) |
| 779 | + self.assertEqual(spec.origin, abs_filename) |
| 780 | + |
| 781 | + def test_filename(self): |
| 782 | + modname = 'test_load_source_path_mod' |
| 783 | + # Filename doesn't have to end with ".py" suffix |
| 784 | + filename = 'load_source_path_filename' |
| 785 | + side_effect = 'load_source_path_side_effect' |
| 786 | + |
| 787 | + def delete_side_effect(): |
| 788 | + try: |
| 789 | + delattr(sys, side_effect) |
| 790 | + except AttributeError: |
| 791 | + pass |
| 792 | + |
| 793 | + self.assertNotIn(modname, sys.modules) |
| 794 | + self.addCleanup(import_helper.unload, modname) |
| 795 | + |
| 796 | + self.assertFalse(hasattr(sys, side_effect)) |
| 797 | + self.addCleanup(delete_side_effect) |
| 798 | + |
| 799 | + # Use a temporary directory to remove __pycache__/ subdirectory |
| 800 | + with fixtures.tempdir_as_cwd(): |
| 801 | + with open(filename, "w", encoding="utf8") as fp: |
| 802 | + print("attr = 'load_source_path_attr'", file=fp) |
| 803 | + print(f"import sys; sys.{side_effect} = 1", file=fp) |
| 804 | + |
| 805 | + mod = importlib.util.load_source_path(modname, filename) |
| 806 | + |
| 807 | + self.check_module(mod, modname, filename) |
| 808 | + self.assertEqual(mod.attr, 'load_source_path_attr') |
| 809 | + self.assertEqual(getattr(sys, side_effect), 1) |
| 810 | + |
| 811 | + # reload cached in sys.modules: the module is executed again |
| 812 | + self.assertIn(modname, sys.modules) |
| 813 | + setattr(sys, side_effect, 0) |
| 814 | + mod = importlib.util.load_source_path(modname, filename) |
| 815 | + self.assertEqual(getattr(sys, side_effect), 1) |
| 816 | + |
| 817 | + # reload uncached in sys.modules: the module is executed again |
| 818 | + del sys.modules[modname] |
| 819 | + setattr(sys, side_effect, 0) |
| 820 | + mod = importlib.util.load_source_path(modname, filename) |
| 821 | + self.assertEqual(getattr(sys, side_effect), 1) |
| 822 | + |
| 823 | + def test_dots(self): |
| 824 | + modname = 'package.submodule' |
| 825 | + filename = __file__ |
| 826 | + with self.assertRaises(ValueError) as cm: |
| 827 | + importlib.util.load_source_path(modname, filename) |
| 828 | + |
| 829 | + err_msg = str(cm.exception) |
| 830 | + self.assertIn("module name must not contain dots", err_msg) |
| 831 | + self.assertIn(repr(modname), err_msg) |
| 832 | + |
| 833 | + def test_package(self): |
| 834 | + modname = 'test_load_source_path_package' |
| 835 | + dirname = 'load_source_path_dir' |
| 836 | + filename = os.path.join('load_source_path_dir', '__init__.py') |
| 837 | + |
| 838 | + self.assertNotIn(modname, sys.modules) |
| 839 | + self.addCleanup(import_helper.unload, modname) |
| 840 | + |
| 841 | + # Use a temporary directory to remove __pycache__/ subdirectory |
| 842 | + with fixtures.tempdir_as_cwd(): |
| 843 | + os.mkdir(dirname) |
| 844 | + with open(filename, "w", encoding="utf8") as fp: |
| 845 | + print("attr = 'load_source_path_pkg'", file=fp) |
| 846 | + |
| 847 | + # Package cannot be imported from a directory. It can with |
| 848 | + # IsADirectoryError on Unix and PermissionError on Windows. |
| 849 | + with self.assertRaises(OSError): |
| 850 | + importlib.util.load_source_path(modname, dirname) |
| 851 | + |
| 852 | + # whereas loading a package __init__.py file is ok |
| 853 | + mod = importlib.util.load_source_path(modname, filename) |
| 854 | + |
| 855 | + self.check_module(mod, modname, filename, is_package=True) |
| 856 | + self.assertEqual(mod.attr, 'load_source_path_pkg') |
| 857 | + |
| 858 | + |
761 | 859 | if __name__ == '__main__': |
762 | 860 | unittest.main() |
0 commit comments