test_imp.py revision 19a2f5961ce235e9ca9944069b0f5f21b340de9c
12294c0d4ecd81d01ddf741fff02cc04bd8de0638Neal Norwitzimport imp
2c049952de76cbcd00e490e48445ed7a50d3dc83aBrett Cannonimport importlib
30ad59d467d06c8c9dce81658f4f278783cb70b9fGuido van Rossumimport os
40ad59d467d06c8c9dce81658f4f278783cb70b9fGuido van Rossumimport os.path
528a691b7fdde1b8abafa4c4a5025e6bfa44f48b9Barry Warsawimport shutil
68a9583ec5c00384514fe9f5045866ad6ebd2be5aBrett Cannonimport sys
7ee8712cda46338d223509cc5751fd36509ad3860Benjamin Petersonfrom test import support
8c049952de76cbcd00e490e48445ed7a50d3dc83aBrett Cannonimport unittest
9c049952de76cbcd00e490e48445ed7a50d3dc83aBrett Cannonimport warnings
102294c0d4ecd81d01ddf741fff02cc04bd8de0638Neal Norwitz
1189f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Woutersclass LockTests(unittest.TestCase):
12579bed7300816590eca2855aa3c971d2ee38d6beTim Peters
1389f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters    """Very basic test of import lock functions."""
14579bed7300816590eca2855aa3c971d2ee38d6beTim Peters
1589f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters    def verify_lock_state(self, expected):
16c9c0f201fed21efcf669dbbf5f923eaf0eeb1db9Benjamin Peterson        self.assertEqual(imp.lock_held(), expected,
1789f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters                             "expected imp.lock_held() to be %r" % expected)
1889f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters    def testLock(self):
1989f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters        LOOPS = 50
202294c0d4ecd81d01ddf741fff02cc04bd8de0638Neal Norwitz
2189f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters        # The import lock may already be held, e.g. if the test suite is run
2289f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters        # via "import test.autotest".
2389f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters        lock_held_at_start = imp.lock_held()
2489f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters        self.verify_lock_state(lock_held_at_start)
25579bed7300816590eca2855aa3c971d2ee38d6beTim Peters
2689f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters        for i in range(LOOPS):
2789f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters            imp.acquire_lock()
2889f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters            self.verify_lock_state(True)
29579bed7300816590eca2855aa3c971d2ee38d6beTim Peters
3089f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters        for i in range(LOOPS):
31579bed7300816590eca2855aa3c971d2ee38d6beTim Peters            imp.release_lock()
3289f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters
3389f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters        # The original state should be restored now.
3489f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters        self.verify_lock_state(lock_held_at_start)
3589f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters
3689f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters        if not lock_held_at_start:
3789f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters            try:
3889f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters                imp.release_lock()
3989f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters            except RuntimeError:
4089f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters                pass
4189f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters            else:
4289f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters                self.fail("release_lock() without lock should raise "
4389f507fe8c497b3f70fdcecce8bc240f9af2bbe2Thomas Wouters                            "RuntimeError")
442294c0d4ecd81d01ddf741fff02cc04bd8de0638Neal Norwitz
45ce3a72aec6eaa0293c397c8d0407f7afe0072b2fGuido van Rossumclass ImportTests(unittest.TestCase):
46e8f583244acd61d7ba43d0f98c06d57c25977039Alexander Belopolsky    def setUp(self):
47e8f583244acd61d7ba43d0f98c06d57c25977039Alexander Belopolsky        mod = importlib.import_module('test.encoded_modules')
48e8f583244acd61d7ba43d0f98c06d57c25977039Alexander Belopolsky        self.test_strings = mod.test_strings
49e8f583244acd61d7ba43d0f98c06d57c25977039Alexander Belopolsky        self.test_path = mod.__path__
50e8f583244acd61d7ba43d0f98c06d57c25977039Alexander Belopolsky
51e8f583244acd61d7ba43d0f98c06d57c25977039Alexander Belopolsky    def test_import_encoded_module(self):
52e8f583244acd61d7ba43d0f98c06d57c25977039Alexander Belopolsky        for modname, encoding, teststr in self.test_strings:
53e8f583244acd61d7ba43d0f98c06d57c25977039Alexander Belopolsky            mod = importlib.import_module('test.encoded_modules.'
54e8f583244acd61d7ba43d0f98c06d57c25977039Alexander Belopolsky                                          'module_' + modname)
55e8f583244acd61d7ba43d0f98c06d57c25977039Alexander Belopolsky            self.assertEqual(teststr, mod.test)
56ce3a72aec6eaa0293c397c8d0407f7afe0072b2fGuido van Rossum
57ce3a72aec6eaa0293c397c8d0407f7afe0072b2fGuido van Rossum    def test_find_module_encoding(self):
58e8f583244acd61d7ba43d0f98c06d57c25977039Alexander Belopolsky        for mod, encoding, _ in self.test_strings:
59749afa95ce28ffded9c6f75c776dae15cade6ecbBrett Cannon            with imp.find_module('module_' + mod, self.test_path)[0] as fd:
60749afa95ce28ffded9c6f75c776dae15cade6ecbBrett Cannon                self.assertEqual(fd.encoding, encoding)
61ce3a72aec6eaa0293c397c8d0407f7afe0072b2fGuido van Rossum
62fe7c5b5bdf7c21551b56be563fc604f2d4d3c756Victor Stinner        path = [os.path.dirname(__file__)]
63dd9a56953e561076b5573d53f6e4fdd7f42b208cBrett Cannon        with self.assertRaises(SyntaxError):
64dd9a56953e561076b5573d53f6e4fdd7f42b208cBrett Cannon            imp.find_module('badsyntax_pep3120', path)
65fe7c5b5bdf7c21551b56be563fc604f2d4d3c756Victor Stinner
6640d20bcf1fccfe8af2393f1aec88ba18e38d0bc1Guido van Rossum    def test_issue1267(self):
67e8f583244acd61d7ba43d0f98c06d57c25977039Alexander Belopolsky        for mod, encoding, _ in self.test_strings:
68e8f583244acd61d7ba43d0f98c06d57c25977039Alexander Belopolsky            fp, filename, info  = imp.find_module('module_' + mod,
69e8f583244acd61d7ba43d0f98c06d57c25977039Alexander Belopolsky                                                  self.test_path)
70749afa95ce28ffded9c6f75c776dae15cade6ecbBrett Cannon            with fp:
71749afa95ce28ffded9c6f75c776dae15cade6ecbBrett Cannon                self.assertNotEqual(fp, None)
72749afa95ce28ffded9c6f75c776dae15cade6ecbBrett Cannon                self.assertEqual(fp.encoding, encoding)
73749afa95ce28ffded9c6f75c776dae15cade6ecbBrett Cannon                self.assertEqual(fp.tell(), 0)
74749afa95ce28ffded9c6f75c776dae15cade6ecbBrett Cannon                self.assertEqual(fp.readline(), '# test %s encoding\n'
75749afa95ce28ffded9c6f75c776dae15cade6ecbBrett Cannon                                 % encoding)
7640d20bcf1fccfe8af2393f1aec88ba18e38d0bc1Guido van Rossum
7740d20bcf1fccfe8af2393f1aec88ba18e38d0bc1Guido van Rossum        fp, filename, info = imp.find_module("tokenize")
78749afa95ce28ffded9c6f75c776dae15cade6ecbBrett Cannon        with fp:
79749afa95ce28ffded9c6f75c776dae15cade6ecbBrett Cannon            self.assertNotEqual(fp, None)
80749afa95ce28ffded9c6f75c776dae15cade6ecbBrett Cannon            self.assertEqual(fp.encoding, "utf-8")
81749afa95ce28ffded9c6f75c776dae15cade6ecbBrett Cannon            self.assertEqual(fp.tell(), 0)
82749afa95ce28ffded9c6f75c776dae15cade6ecbBrett Cannon            self.assertEqual(fp.readline(),
83749afa95ce28ffded9c6f75c776dae15cade6ecbBrett Cannon                             '"""Tokenization help for Python programs.\n')
8440d20bcf1fccfe8af2393f1aec88ba18e38d0bc1Guido van Rossum
858a9583ec5c00384514fe9f5045866ad6ebd2be5aBrett Cannon    def test_issue3594(self):
868a9583ec5c00384514fe9f5045866ad6ebd2be5aBrett Cannon        temp_mod_name = 'test_imp_helper'
878a9583ec5c00384514fe9f5045866ad6ebd2be5aBrett Cannon        sys.path.insert(0, '.')
888a9583ec5c00384514fe9f5045866ad6ebd2be5aBrett Cannon        try:
898a9583ec5c00384514fe9f5045866ad6ebd2be5aBrett Cannon            with open(temp_mod_name + '.py', 'w') as file:
908a9583ec5c00384514fe9f5045866ad6ebd2be5aBrett Cannon                file.write("# coding: cp1252\nu = 'test.test_imp'\n")
918a9583ec5c00384514fe9f5045866ad6ebd2be5aBrett Cannon            file, filename, info = imp.find_module(temp_mod_name)
928a9583ec5c00384514fe9f5045866ad6ebd2be5aBrett Cannon            file.close()
93b3aedd48621ed9d33b5f42f946b256bce4a50673Ezio Melotti            self.assertEqual(file.encoding, 'cp1252')
948a9583ec5c00384514fe9f5045866ad6ebd2be5aBrett Cannon        finally:
958a9583ec5c00384514fe9f5045866ad6ebd2be5aBrett Cannon            del sys.path[0]
968a9583ec5c00384514fe9f5045866ad6ebd2be5aBrett Cannon            support.unlink(temp_mod_name + '.py')
978a9583ec5c00384514fe9f5045866ad6ebd2be5aBrett Cannon            support.unlink(temp_mod_name + '.pyc')
988a9583ec5c00384514fe9f5045866ad6ebd2be5aBrett Cannon            support.unlink(temp_mod_name + '.pyo')
998a9583ec5c00384514fe9f5045866ad6ebd2be5aBrett Cannon
1000ad59d467d06c8c9dce81658f4f278783cb70b9fGuido van Rossum    def test_issue5604(self):
1010ad59d467d06c8c9dce81658f4f278783cb70b9fGuido van Rossum        # Test cannot cover imp.load_compiled function.
1020ad59d467d06c8c9dce81658f4f278783cb70b9fGuido van Rossum        # Martin von Loewis note what shared library cannot have non-ascii
1030ad59d467d06c8c9dce81658f4f278783cb70b9fGuido van Rossum        # character because init_xxx function cannot be compiled
1040ad59d467d06c8c9dce81658f4f278783cb70b9fGuido van Rossum        # and issue never happens for dynamic modules.
1050ad59d467d06c8c9dce81658f4f278783cb70b9fGuido van Rossum        # But sources modified to follow generic way for processing pathes.
1060ad59d467d06c8c9dce81658f4f278783cb70b9fGuido van Rossum
107435b5318732b3fa8e5a0dd82d3538aa6bc045d8eEzio Melotti        # the return encoding could be uppercase or None
108435b5318732b3fa8e5a0dd82d3538aa6bc045d8eEzio Melotti        fs_encoding = sys.getfilesystemencoding()
1090ad59d467d06c8c9dce81658f4f278783cb70b9fGuido van Rossum
1100ad59d467d06c8c9dce81658f4f278783cb70b9fGuido van Rossum        # covers utf-8 and Windows ANSI code pages
1110ad59d467d06c8c9dce81658f4f278783cb70b9fGuido van Rossum        # one non-space symbol from every page
1120ad59d467d06c8c9dce81658f4f278783cb70b9fGuido van Rossum        # (http://en.wikipedia.org/wiki/Code_page)
1130ad59d467d06c8c9dce81658f4f278783cb70b9fGuido van Rossum        known_locales = {
1149a7d5ac9f647bad9e48c98255ab54b71ec366127Ezio Melotti            'utf-8' : b'\xc3\xa4',
1150ad59d467d06c8c9dce81658f4f278783cb70b9fGuido van Rossum            'cp1250' : b'\x8C',
1160ad59d467d06c8c9dce81658f4f278783cb70b9fGuido van Rossum            'cp1251' : b'\xc0',
1170ad59d467d06c8c9dce81658f4f278783cb70b9fGuido van Rossum            'cp1252' : b'\xc0',
1180ad59d467d06c8c9dce81658f4f278783cb70b9fGuido van Rossum            'cp1253' : b'\xc1',
1190ad59d467d06c8c9dce81658f4f278783cb70b9fGuido van Rossum            'cp1254' : b'\xc0',
1200ad59d467d06c8c9dce81658f4f278783cb70b9fGuido van Rossum            'cp1255' : b'\xe0',
1210ad59d467d06c8c9dce81658f4f278783cb70b9fGuido van Rossum            'cp1256' : b'\xe0',
1220ad59d467d06c8c9dce81658f4f278783cb70b9fGuido van Rossum            'cp1257' : b'\xc0',
1230ad59d467d06c8c9dce81658f4f278783cb70b9fGuido van Rossum            'cp1258' : b'\xc0',
1240ad59d467d06c8c9dce81658f4f278783cb70b9fGuido van Rossum            }
1250ad59d467d06c8c9dce81658f4f278783cb70b9fGuido van Rossum
12621164ce8f1f1c45fa1305fee4851b19405c19a67Florent Xicluna        if sys.platform == 'darwin':
12721164ce8f1f1c45fa1305fee4851b19405c19a67Florent Xicluna            self.assertEqual(fs_encoding, 'utf-8')
12821164ce8f1f1c45fa1305fee4851b19405c19a67Florent Xicluna            # Mac OS X uses the Normal Form D decomposition
12921164ce8f1f1c45fa1305fee4851b19405c19a67Florent Xicluna            # http://developer.apple.com/mac/library/qa/qa2001/qa1173.html
13021164ce8f1f1c45fa1305fee4851b19405c19a67Florent Xicluna            special_char = b'a\xcc\x88'
13121164ce8f1f1c45fa1305fee4851b19405c19a67Florent Xicluna        else:
13221164ce8f1f1c45fa1305fee4851b19405c19a67Florent Xicluna            special_char = known_locales.get(fs_encoding)
13321164ce8f1f1c45fa1305fee4851b19405c19a67Florent Xicluna
1349a7d5ac9f647bad9e48c98255ab54b71ec366127Ezio Melotti        if not special_char:
13576e0d1a6efac2efbb6b9de739d43be827e7ea8ecEzio Melotti            self.skipTest("can't run this test with %s as filesystem encoding"
13676e0d1a6efac2efbb6b9de739d43be827e7ea8ecEzio Melotti                          % fs_encoding)
13776e0d1a6efac2efbb6b9de739d43be827e7ea8ecEzio Melotti        decoded_char = special_char.decode(fs_encoding)
1389a7d5ac9f647bad9e48c98255ab54b71ec366127Ezio Melotti        temp_mod_name = 'test_imp_helper_' + decoded_char
1399a7d5ac9f647bad9e48c98255ab54b71ec366127Ezio Melotti        test_package_name = 'test_imp_helper_package_' + decoded_char
1409a7d5ac9f647bad9e48c98255ab54b71ec366127Ezio Melotti        init_file_name = os.path.join(test_package_name, '__init__.py')
1419a7d5ac9f647bad9e48c98255ab54b71ec366127Ezio Melotti        try:
14241a6b04b1fb5dc58b07abd60598a2932a7b0649cEzio Melotti            # if the curdir is not in sys.path the test fails when run with
14341a6b04b1fb5dc58b07abd60598a2932a7b0649cEzio Melotti            # ./python ./Lib/test/regrtest.py test_imp
14441a6b04b1fb5dc58b07abd60598a2932a7b0649cEzio Melotti            sys.path.insert(0, os.curdir)
1459a7d5ac9f647bad9e48c98255ab54b71ec366127Ezio Melotti            with open(temp_mod_name + '.py', 'w') as file:
1469a7d5ac9f647bad9e48c98255ab54b71ec366127Ezio Melotti                file.write('a = 1\n')
1479a7d5ac9f647bad9e48c98255ab54b71ec366127Ezio Melotti            file, filename, info = imp.find_module(temp_mod_name)
148749afa95ce28ffded9c6f75c776dae15cade6ecbBrett Cannon            with file:
149749afa95ce28ffded9c6f75c776dae15cade6ecbBrett Cannon                self.assertIsNotNone(file)
150749afa95ce28ffded9c6f75c776dae15cade6ecbBrett Cannon                self.assertTrue(filename[:-3].endswith(temp_mod_name))
151749afa95ce28ffded9c6f75c776dae15cade6ecbBrett Cannon                self.assertEqual(info[0], '.py')
152749afa95ce28ffded9c6f75c776dae15cade6ecbBrett Cannon                self.assertEqual(info[1], 'U')
153749afa95ce28ffded9c6f75c776dae15cade6ecbBrett Cannon                self.assertEqual(info[2], imp.PY_SOURCE)
154749afa95ce28ffded9c6f75c776dae15cade6ecbBrett Cannon
155749afa95ce28ffded9c6f75c776dae15cade6ecbBrett Cannon                mod = imp.load_module(temp_mod_name, file, filename, info)
156749afa95ce28ffded9c6f75c776dae15cade6ecbBrett Cannon                self.assertEqual(mod.a, 1)
1579a7d5ac9f647bad9e48c98255ab54b71ec366127Ezio Melotti
158c049952de76cbcd00e490e48445ed7a50d3dc83aBrett Cannon            with warnings.catch_warnings():
159c049952de76cbcd00e490e48445ed7a50d3dc83aBrett Cannon                warnings.simplefilter('ignore')
160c049952de76cbcd00e490e48445ed7a50d3dc83aBrett Cannon                mod = imp.load_source(temp_mod_name, temp_mod_name + '.py')
161435b5318732b3fa8e5a0dd82d3538aa6bc045d8eEzio Melotti            self.assertEqual(mod.a, 1)
1629a7d5ac9f647bad9e48c98255ab54b71ec366127Ezio Melotti
163c049952de76cbcd00e490e48445ed7a50d3dc83aBrett Cannon            with warnings.catch_warnings():
164c049952de76cbcd00e490e48445ed7a50d3dc83aBrett Cannon                warnings.simplefilter('ignore')
165c049952de76cbcd00e490e48445ed7a50d3dc83aBrett Cannon                mod = imp.load_compiled(
166c049952de76cbcd00e490e48445ed7a50d3dc83aBrett Cannon                    temp_mod_name, imp.cache_from_source(temp_mod_name + '.py'))
167435b5318732b3fa8e5a0dd82d3538aa6bc045d8eEzio Melotti            self.assertEqual(mod.a, 1)
1689a7d5ac9f647bad9e48c98255ab54b71ec366127Ezio Melotti
1699a7d5ac9f647bad9e48c98255ab54b71ec366127Ezio Melotti            if not os.path.exists(test_package_name):
1709a7d5ac9f647bad9e48c98255ab54b71ec366127Ezio Melotti                os.mkdir(test_package_name)
1719a7d5ac9f647bad9e48c98255ab54b71ec366127Ezio Melotti            with open(init_file_name, 'w') as file:
1729a7d5ac9f647bad9e48c98255ab54b71ec366127Ezio Melotti                file.write('b = 2\n')
173c049952de76cbcd00e490e48445ed7a50d3dc83aBrett Cannon            with warnings.catch_warnings():
174c049952de76cbcd00e490e48445ed7a50d3dc83aBrett Cannon                warnings.simplefilter('ignore')
175c049952de76cbcd00e490e48445ed7a50d3dc83aBrett Cannon                package = imp.load_package(test_package_name, test_package_name)
176435b5318732b3fa8e5a0dd82d3538aa6bc045d8eEzio Melotti            self.assertEqual(package.b, 2)
1779a7d5ac9f647bad9e48c98255ab54b71ec366127Ezio Melotti        finally:
17841a6b04b1fb5dc58b07abd60598a2932a7b0649cEzio Melotti            del sys.path[0]
179435b5318732b3fa8e5a0dd82d3538aa6bc045d8eEzio Melotti            for ext in ('.py', '.pyc', '.pyo'):
180435b5318732b3fa8e5a0dd82d3538aa6bc045d8eEzio Melotti                support.unlink(temp_mod_name + ext)
181435b5318732b3fa8e5a0dd82d3538aa6bc045d8eEzio Melotti                support.unlink(init_file_name + ext)
1829a7d5ac9f647bad9e48c98255ab54b71ec366127Ezio Melotti            support.rmtree(test_package_name)
1830ad59d467d06c8c9dce81658f4f278783cb70b9fGuido van Rossum
184c68b6aaec8b08caf682ebb7c95f94ddf49a6b66cVictor Stinner    def test_issue9319(self):
18511846905d33d696f14a4ae26383104feacdc0f17Antoine Pitrou        path = os.path.dirname(__file__)
1867fdd0fe48f3f342273c1d396df142b1d4b9a1a5cVictor Stinner        self.assertRaises(SyntaxError,
18711846905d33d696f14a4ae26383104feacdc0f17Antoine Pitrou                          imp.find_module, "badsyntax_pep3120", [path])
188c68b6aaec8b08caf682ebb7c95f94ddf49a6b66cVictor Stinner
189f0434e647aa3e7b82a740be4a820aec951a885acBrett Cannon    def test_load_dynamic_ImportError_path(self):
190f0434e647aa3e7b82a740be4a820aec951a885acBrett Cannon        # Issue #1559549 added `name` and `path` attributes to ImportError
191f0434e647aa3e7b82a740be4a820aec951a885acBrett Cannon        # in order to provide better detail. Issue #10854 implemented those
192f0434e647aa3e7b82a740be4a820aec951a885acBrett Cannon        # attributes on import failures of extensions on Windows.
193f0434e647aa3e7b82a740be4a820aec951a885acBrett Cannon        path = 'bogus file path'
194f0434e647aa3e7b82a740be4a820aec951a885acBrett Cannon        name = 'extension'
195f0434e647aa3e7b82a740be4a820aec951a885acBrett Cannon        with self.assertRaises(ImportError) as err:
196f0434e647aa3e7b82a740be4a820aec951a885acBrett Cannon            imp.load_dynamic(name, path)
197f0434e647aa3e7b82a740be4a820aec951a885acBrett Cannon        self.assertIn(path, err.exception.path)
198f0434e647aa3e7b82a740be4a820aec951a885acBrett Cannon        self.assertEqual(name, err.exception.name)
199f0434e647aa3e7b82a740be4a820aec951a885acBrett Cannon
2000ad59d467d06c8c9dce81658f4f278783cb70b9fGuido van Rossum
2016ead552b47ecc4e7d85a03b3b662c6d46e59bec3Nick Coghlanclass ReloadTests(unittest.TestCase):
2026ead552b47ecc4e7d85a03b3b662c6d46e59bec3Nick Coghlan
2036ead552b47ecc4e7d85a03b3b662c6d46e59bec3Nick Coghlan    """Very basic tests to make sure that imp.reload() operates just like
2046ead552b47ecc4e7d85a03b3b662c6d46e59bec3Nick Coghlan    reload()."""
2056ead552b47ecc4e7d85a03b3b662c6d46e59bec3Nick Coghlan
2066ead552b47ecc4e7d85a03b3b662c6d46e59bec3Nick Coghlan    def test_source(self):
20797133720fc38f914a3da927c0edbb1dacdd3ed41Florent Xicluna        # XXX (ncoghlan): It would be nice to use test.support.CleanImport
2086ead552b47ecc4e7d85a03b3b662c6d46e59bec3Nick Coghlan        # here, but that breaks because the os module registers some
2096ead552b47ecc4e7d85a03b3b662c6d46e59bec3Nick Coghlan        # handlers in copy_reg on import. Since CleanImport doesn't
2106ead552b47ecc4e7d85a03b3b662c6d46e59bec3Nick Coghlan        # revert that registration, the module is left in a broken
2116ead552b47ecc4e7d85a03b3b662c6d46e59bec3Nick Coghlan        # state after reversion. Reinitialising the module contents
2126ead552b47ecc4e7d85a03b3b662c6d46e59bec3Nick Coghlan        # and just reverting os.environ to its previous state is an OK
2136ead552b47ecc4e7d85a03b3b662c6d46e59bec3Nick Coghlan        # workaround
2146ead552b47ecc4e7d85a03b3b662c6d46e59bec3Nick Coghlan        with support.EnvironmentVarGuard():
2156ead552b47ecc4e7d85a03b3b662c6d46e59bec3Nick Coghlan            import os
2166ead552b47ecc4e7d85a03b3b662c6d46e59bec3Nick Coghlan            imp.reload(os)
2176ead552b47ecc4e7d85a03b3b662c6d46e59bec3Nick Coghlan
2186ead552b47ecc4e7d85a03b3b662c6d46e59bec3Nick Coghlan    def test_extension(self):
2196ead552b47ecc4e7d85a03b3b662c6d46e59bec3Nick Coghlan        with support.CleanImport('time'):
2206ead552b47ecc4e7d85a03b3b662c6d46e59bec3Nick Coghlan            import time
2216ead552b47ecc4e7d85a03b3b662c6d46e59bec3Nick Coghlan            imp.reload(time)
2226ead552b47ecc4e7d85a03b3b662c6d46e59bec3Nick Coghlan
2236ead552b47ecc4e7d85a03b3b662c6d46e59bec3Nick Coghlan    def test_builtin(self):
2246ead552b47ecc4e7d85a03b3b662c6d46e59bec3Nick Coghlan        with support.CleanImport('marshal'):
2256ead552b47ecc4e7d85a03b3b662c6d46e59bec3Nick Coghlan            import marshal
2266ead552b47ecc4e7d85a03b3b662c6d46e59bec3Nick Coghlan            imp.reload(marshal)
22713a7a21258f0cd241c2cf1367a954d6742daa2a6Christian Heimes
22840d20bcf1fccfe8af2393f1aec88ba18e38d0bc1Guido van Rossum
22928a691b7fdde1b8abafa4c4a5025e6bfa44f48b9Barry Warsawclass PEP3147Tests(unittest.TestCase):
23028a691b7fdde1b8abafa4c4a5025e6bfa44f48b9Barry Warsaw    """Tests of PEP 3147."""
23128a691b7fdde1b8abafa4c4a5025e6bfa44f48b9Barry Warsaw
23228a691b7fdde1b8abafa4c4a5025e6bfa44f48b9Barry Warsaw    tag = imp.get_tag()
23328a691b7fdde1b8abafa4c4a5025e6bfa44f48b9Barry Warsaw
23419a2f5961ce235e9ca9944069b0f5f21b340de9cBrett Cannon    @unittest.skipUnless(sys.implementation.cache_tag is not None,
23519a2f5961ce235e9ca9944069b0f5f21b340de9cBrett Cannon                         'requires sys.implementation.cache_tag not be None')
23628a691b7fdde1b8abafa4c4a5025e6bfa44f48b9Barry Warsaw    def test_cache_from_source(self):
23728a691b7fdde1b8abafa4c4a5025e6bfa44f48b9Barry Warsaw        # Given the path to a .py file, return the path to its PEP 3147
23828a691b7fdde1b8abafa4c4a5025e6bfa44f48b9Barry Warsaw        # defined .pyc file (i.e. under __pycache__).
239410e88d5fccd85cd2a5ad73c98a8552fa7a0a166Brett Cannon        path = os.path.join('foo', 'bar', 'baz', 'qux.py')
240410e88d5fccd85cd2a5ad73c98a8552fa7a0a166Brett Cannon        expect = os.path.join('foo', 'bar', 'baz', '__pycache__',
241410e88d5fccd85cd2a5ad73c98a8552fa7a0a166Brett Cannon                              'qux.{}.pyc'.format(self.tag))
242410e88d5fccd85cd2a5ad73c98a8552fa7a0a166Brett Cannon        self.assertEqual(imp.cache_from_source(path, True), expect)
243410e88d5fccd85cd2a5ad73c98a8552fa7a0a166Brett Cannon
24419a2f5961ce235e9ca9944069b0f5f21b340de9cBrett Cannon    def test_cache_from_source_no_cache_tag(self):
24519a2f5961ce235e9ca9944069b0f5f21b340de9cBrett Cannon        # Non cache tag means NotImplementedError.
24619a2f5961ce235e9ca9944069b0f5f21b340de9cBrett Cannon        with support.swap_attr(sys.implementation, 'cache_tag', None):
24719a2f5961ce235e9ca9944069b0f5f21b340de9cBrett Cannon            with self.assertRaises(NotImplementedError):
24819a2f5961ce235e9ca9944069b0f5f21b340de9cBrett Cannon                imp.cache_from_source('whatever.py')
24919a2f5961ce235e9ca9944069b0f5f21b340de9cBrett Cannon
250410e88d5fccd85cd2a5ad73c98a8552fa7a0a166Brett Cannon    def test_cache_from_source_no_dot(self):
251410e88d5fccd85cd2a5ad73c98a8552fa7a0a166Brett Cannon        # Directory with a dot, filename without dot.
252410e88d5fccd85cd2a5ad73c98a8552fa7a0a166Brett Cannon        path = os.path.join('foo.bar', 'file')
253410e88d5fccd85cd2a5ad73c98a8552fa7a0a166Brett Cannon        expect = os.path.join('foo.bar', '__pycache__',
254410e88d5fccd85cd2a5ad73c98a8552fa7a0a166Brett Cannon                              'file{}.pyc'.format(self.tag))
255410e88d5fccd85cd2a5ad73c98a8552fa7a0a166Brett Cannon        self.assertEqual(imp.cache_from_source(path, True), expect)
25628a691b7fdde1b8abafa4c4a5025e6bfa44f48b9Barry Warsaw
25728a691b7fdde1b8abafa4c4a5025e6bfa44f48b9Barry Warsaw    def test_cache_from_source_optimized(self):
25828a691b7fdde1b8abafa4c4a5025e6bfa44f48b9Barry Warsaw        # Given the path to a .py file, return the path to its PEP 3147
25928a691b7fdde1b8abafa4c4a5025e6bfa44f48b9Barry Warsaw        # defined .pyo file (i.e. under __pycache__).
260410e88d5fccd85cd2a5ad73c98a8552fa7a0a166Brett Cannon        path = os.path.join('foo', 'bar', 'baz', 'qux.py')
261410e88d5fccd85cd2a5ad73c98a8552fa7a0a166Brett Cannon        expect = os.path.join('foo', 'bar', 'baz', '__pycache__',
262410e88d5fccd85cd2a5ad73c98a8552fa7a0a166Brett Cannon                              'qux.{}.pyo'.format(self.tag))
263410e88d5fccd85cd2a5ad73c98a8552fa7a0a166Brett Cannon        self.assertEqual(imp.cache_from_source(path, False), expect)
26428a691b7fdde1b8abafa4c4a5025e6bfa44f48b9Barry Warsaw
26528a691b7fdde1b8abafa4c4a5025e6bfa44f48b9Barry Warsaw    def test_cache_from_source_cwd(self):
266410e88d5fccd85cd2a5ad73c98a8552fa7a0a166Brett Cannon        path = 'foo.py'
267410e88d5fccd85cd2a5ad73c98a8552fa7a0a166Brett Cannon        expect = os.path.join('__pycache__', 'foo.{}.pyc'.format(self.tag))
268410e88d5fccd85cd2a5ad73c98a8552fa7a0a166Brett Cannon        self.assertEqual(imp.cache_from_source(path, True), expect)
26928a691b7fdde1b8abafa4c4a5025e6bfa44f48b9Barry Warsaw
27028a691b7fdde1b8abafa4c4a5025e6bfa44f48b9Barry Warsaw    def test_cache_from_source_override(self):
27128a691b7fdde1b8abafa4c4a5025e6bfa44f48b9Barry Warsaw        # When debug_override is not None, it can be any true-ish or false-ish
27228a691b7fdde1b8abafa4c4a5025e6bfa44f48b9Barry Warsaw        # value.
273410e88d5fccd85cd2a5ad73c98a8552fa7a0a166Brett Cannon        path = os.path.join('foo', 'bar', 'baz.py')
274410e88d5fccd85cd2a5ad73c98a8552fa7a0a166Brett Cannon        partial_expect = os.path.join('foo', 'bar', '__pycache__',
275410e88d5fccd85cd2a5ad73c98a8552fa7a0a166Brett Cannon                                      'baz.{}.py'.format(self.tag))
276410e88d5fccd85cd2a5ad73c98a8552fa7a0a166Brett Cannon        self.assertEqual(imp.cache_from_source(path, []), partial_expect + 'o')
277410e88d5fccd85cd2a5ad73c98a8552fa7a0a166Brett Cannon        self.assertEqual(imp.cache_from_source(path, [17]),
278410e88d5fccd85cd2a5ad73c98a8552fa7a0a166Brett Cannon                         partial_expect + 'c')
27928a691b7fdde1b8abafa4c4a5025e6bfa44f48b9Barry Warsaw        # However if the bool-ishness can't be determined, the exception
28028a691b7fdde1b8abafa4c4a5025e6bfa44f48b9Barry Warsaw        # propagates.
28128a691b7fdde1b8abafa4c4a5025e6bfa44f48b9Barry Warsaw        class Bearish:
28228a691b7fdde1b8abafa4c4a5025e6bfa44f48b9Barry Warsaw            def __bool__(self): raise RuntimeError
283410e88d5fccd85cd2a5ad73c98a8552fa7a0a166Brett Cannon        with self.assertRaises(RuntimeError):
284410e88d5fccd85cd2a5ad73c98a8552fa7a0a166Brett Cannon            imp.cache_from_source('/foo/bar/baz.py', Bearish())
28528a691b7fdde1b8abafa4c4a5025e6bfa44f48b9Barry Warsaw
286410e88d5fccd85cd2a5ad73c98a8552fa7a0a166Brett Cannon    @unittest.skipUnless(os.sep == '\\' and os.altsep == '/',
28728a691b7fdde1b8abafa4c4a5025e6bfa44f48b9Barry Warsaw                     'test meaningful only where os.altsep is defined')
28828a691b7fdde1b8abafa4c4a5025e6bfa44f48b9Barry Warsaw    def test_sep_altsep_and_sep_cache_from_source(self):
28928a691b7fdde1b8abafa4c4a5025e6bfa44f48b9Barry Warsaw        # Windows path and PEP 3147 where sep is right of altsep.
29028a691b7fdde1b8abafa4c4a5025e6bfa44f48b9Barry Warsaw        self.assertEqual(
29128a691b7fdde1b8abafa4c4a5025e6bfa44f48b9Barry Warsaw            imp.cache_from_source('\\foo\\bar\\baz/qux.py', True),
292410e88d5fccd85cd2a5ad73c98a8552fa7a0a166Brett Cannon            '\\foo\\bar\\baz\\__pycache__\\qux.{}.pyc'.format(self.tag))
29328a691b7fdde1b8abafa4c4a5025e6bfa44f48b9Barry Warsaw
29419a2f5961ce235e9ca9944069b0f5f21b340de9cBrett Cannon    @unittest.skipUnless(sys.implementation.cache_tag is not None,
29519a2f5961ce235e9ca9944069b0f5f21b340de9cBrett Cannon                         'requires sys.implementation.cache_tag to not be '
29619a2f5961ce235e9ca9944069b0f5f21b340de9cBrett Cannon                         'None')
29728a691b7fdde1b8abafa4c4a5025e6bfa44f48b9Barry Warsaw    def test_source_from_cache(self):
29828a691b7fdde1b8abafa4c4a5025e6bfa44f48b9Barry Warsaw        # Given the path to a PEP 3147 defined .pyc file, return the path to
29928a691b7fdde1b8abafa4c4a5025e6bfa44f48b9Barry Warsaw        # its source.  This tests the good path.
300410e88d5fccd85cd2a5ad73c98a8552fa7a0a166Brett Cannon        path = os.path.join('foo', 'bar', 'baz', '__pycache__',
301410e88d5fccd85cd2a5ad73c98a8552fa7a0a166Brett Cannon                            'qux.{}.pyc'.format(self.tag))
302410e88d5fccd85cd2a5ad73c98a8552fa7a0a166Brett Cannon        expect = os.path.join('foo', 'bar', 'baz', 'qux.py')
303410e88d5fccd85cd2a5ad73c98a8552fa7a0a166Brett Cannon        self.assertEqual(imp.source_from_cache(path), expect)
30428a691b7fdde1b8abafa4c4a5025e6bfa44f48b9Barry Warsaw
30519a2f5961ce235e9ca9944069b0f5f21b340de9cBrett Cannon    def test_source_from_cache_no_cache_tag(self):
30619a2f5961ce235e9ca9944069b0f5f21b340de9cBrett Cannon        # If sys.implementation.cache_tag is None, raise NotImplementedError.
30719a2f5961ce235e9ca9944069b0f5f21b340de9cBrett Cannon        path = os.path.join('blah', '__pycache__', 'whatever.pyc')
30819a2f5961ce235e9ca9944069b0f5f21b340de9cBrett Cannon        with support.swap_attr(sys.implementation, 'cache_tag', None):
30919a2f5961ce235e9ca9944069b0f5f21b340de9cBrett Cannon            with self.assertRaises(NotImplementedError):
31019a2f5961ce235e9ca9944069b0f5f21b340de9cBrett Cannon                imp.source_from_cache(path)
31119a2f5961ce235e9ca9944069b0f5f21b340de9cBrett Cannon
31228a691b7fdde1b8abafa4c4a5025e6bfa44f48b9Barry Warsaw    def test_source_from_cache_bad_path(self):
31328a691b7fdde1b8abafa4c4a5025e6bfa44f48b9Barry Warsaw        # When the path to a pyc file is not in PEP 3147 format, a ValueError
31428a691b7fdde1b8abafa4c4a5025e6bfa44f48b9Barry Warsaw        # is raised.
31528a691b7fdde1b8abafa4c4a5025e6bfa44f48b9Barry Warsaw        self.assertRaises(
31628a691b7fdde1b8abafa4c4a5025e6bfa44f48b9Barry Warsaw            ValueError, imp.source_from_cache, '/foo/bar/bazqux.pyc')
31728a691b7fdde1b8abafa4c4a5025e6bfa44f48b9Barry Warsaw
31828a691b7fdde1b8abafa4c4a5025e6bfa44f48b9Barry Warsaw    def test_source_from_cache_no_slash(self):
31928a691b7fdde1b8abafa4c4a5025e6bfa44f48b9Barry Warsaw        # No slashes at all in path -> ValueError
32028a691b7fdde1b8abafa4c4a5025e6bfa44f48b9Barry Warsaw        self.assertRaises(
32128a691b7fdde1b8abafa4c4a5025e6bfa44f48b9Barry Warsaw            ValueError, imp.source_from_cache, 'foo.cpython-32.pyc')
32228a691b7fdde1b8abafa4c4a5025e6bfa44f48b9Barry Warsaw
32328a691b7fdde1b8abafa4c4a5025e6bfa44f48b9Barry Warsaw    def test_source_from_cache_too_few_dots(self):
32428a691b7fdde1b8abafa4c4a5025e6bfa44f48b9Barry Warsaw        # Too few dots in final path component -> ValueError
32528a691b7fdde1b8abafa4c4a5025e6bfa44f48b9Barry Warsaw        self.assertRaises(
32628a691b7fdde1b8abafa4c4a5025e6bfa44f48b9Barry Warsaw            ValueError, imp.source_from_cache, '__pycache__/foo.pyc')
32728a691b7fdde1b8abafa4c4a5025e6bfa44f48b9Barry Warsaw
32828a691b7fdde1b8abafa4c4a5025e6bfa44f48b9Barry Warsaw    def test_source_from_cache_too_many_dots(self):
32928a691b7fdde1b8abafa4c4a5025e6bfa44f48b9Barry Warsaw        # Too many dots in final path component -> ValueError
33028a691b7fdde1b8abafa4c4a5025e6bfa44f48b9Barry Warsaw        self.assertRaises(
33128a691b7fdde1b8abafa4c4a5025e6bfa44f48b9Barry Warsaw            ValueError, imp.source_from_cache,
33228a691b7fdde1b8abafa4c4a5025e6bfa44f48b9Barry Warsaw            '__pycache__/foo.cpython-32.foo.pyc')
33328a691b7fdde1b8abafa4c4a5025e6bfa44f48b9Barry Warsaw
33428a691b7fdde1b8abafa4c4a5025e6bfa44f48b9Barry Warsaw    def test_source_from_cache_no__pycache__(self):
33528a691b7fdde1b8abafa4c4a5025e6bfa44f48b9Barry Warsaw        # Another problem with the path -> ValueError
33628a691b7fdde1b8abafa4c4a5025e6bfa44f48b9Barry Warsaw        self.assertRaises(
33728a691b7fdde1b8abafa4c4a5025e6bfa44f48b9Barry Warsaw            ValueError, imp.source_from_cache,
33828a691b7fdde1b8abafa4c4a5025e6bfa44f48b9Barry Warsaw            '/foo/bar/foo.cpython-32.foo.pyc')
33928a691b7fdde1b8abafa4c4a5025e6bfa44f48b9Barry Warsaw
34028a691b7fdde1b8abafa4c4a5025e6bfa44f48b9Barry Warsaw    def test_package___file__(self):
34106e37587ffddc5fb77f93ab2cefafd6cbd326aeeAntoine Pitrou        try:
34206e37587ffddc5fb77f93ab2cefafd6cbd326aeeAntoine Pitrou            m = __import__('pep3147')
34306e37587ffddc5fb77f93ab2cefafd6cbd326aeeAntoine Pitrou        except ImportError:
34406e37587ffddc5fb77f93ab2cefafd6cbd326aeeAntoine Pitrou            pass
34506e37587ffddc5fb77f93ab2cefafd6cbd326aeeAntoine Pitrou        else:
34606e37587ffddc5fb77f93ab2cefafd6cbd326aeeAntoine Pitrou            self.fail("pep3147 module already exists: %r" % (m,))
34728a691b7fdde1b8abafa4c4a5025e6bfa44f48b9Barry Warsaw        # Test that a package's __file__ points to the right source directory.
34828a691b7fdde1b8abafa4c4a5025e6bfa44f48b9Barry Warsaw        os.mkdir('pep3147')
34928a691b7fdde1b8abafa4c4a5025e6bfa44f48b9Barry Warsaw        sys.path.insert(0, os.curdir)
35028a691b7fdde1b8abafa4c4a5025e6bfa44f48b9Barry Warsaw        def cleanup():
35128a691b7fdde1b8abafa4c4a5025e6bfa44f48b9Barry Warsaw            if sys.path[0] == os.curdir:
35228a691b7fdde1b8abafa4c4a5025e6bfa44f48b9Barry Warsaw                del sys.path[0]
35328a691b7fdde1b8abafa4c4a5025e6bfa44f48b9Barry Warsaw            shutil.rmtree('pep3147')
35428a691b7fdde1b8abafa4c4a5025e6bfa44f48b9Barry Warsaw        self.addCleanup(cleanup)
35528a691b7fdde1b8abafa4c4a5025e6bfa44f48b9Barry Warsaw        # Touch the __init__.py file.
356bf816223dfe8f1d36a020b4bc02060b8d1ce7d26Victor Stinner        support.create_empty_file('pep3147/__init__.py')
3574f92a68a813c8a4090fd175037c494b498e62587Antoine Pitrou        importlib.invalidate_caches()
358abe72d7eb3c5288c35214073e2cb25a61f2174eaAntoine Pitrou        expected___file__ = os.sep.join(('.', 'pep3147', '__init__.py'))
35928a691b7fdde1b8abafa4c4a5025e6bfa44f48b9Barry Warsaw        m = __import__('pep3147')
3609a4d7ddb6c09af03953840ff8a2c1215fc6742a7Antoine Pitrou        self.assertEqual(m.__file__, expected___file__, (m.__file__, m.__path__, sys.path, sys.path_importer_cache))
36128a691b7fdde1b8abafa4c4a5025e6bfa44f48b9Barry Warsaw        # Ensure we load the pyc file.
362037615e1ef0784f5602393ce5fa6802811ae1559Antoine Pitrou        support.unload('pep3147')
36328a691b7fdde1b8abafa4c4a5025e6bfa44f48b9Barry Warsaw        m = __import__('pep3147')
364037615e1ef0784f5602393ce5fa6802811ae1559Antoine Pitrou        support.unload('pep3147')
3659a4d7ddb6c09af03953840ff8a2c1215fc6742a7Antoine Pitrou        self.assertEqual(m.__file__, expected___file__, (m.__file__, m.__path__, sys.path, sys.path_importer_cache))
36628a691b7fdde1b8abafa4c4a5025e6bfa44f48b9Barry Warsaw
36728a691b7fdde1b8abafa4c4a5025e6bfa44f48b9Barry Warsaw
3681a4d12d74681d35a40474790925a8ec9c8069b4eVictor Stinnerclass NullImporterTests(unittest.TestCase):
36909c449c7de0fea077ceaee5cb04017d6994341e7Victor Stinner    @unittest.skipIf(support.TESTFN_UNENCODABLE is None,
3701a4d12d74681d35a40474790925a8ec9c8069b4eVictor Stinner                     "Need an undecodeable filename")
3711a4d12d74681d35a40474790925a8ec9c8069b4eVictor Stinner    def test_unencodeable(self):
37209c449c7de0fea077ceaee5cb04017d6994341e7Victor Stinner        name = support.TESTFN_UNENCODABLE
3731a4d12d74681d35a40474790925a8ec9c8069b4eVictor Stinner        os.mkdir(name)
3741a4d12d74681d35a40474790925a8ec9c8069b4eVictor Stinner        try:
3751a4d12d74681d35a40474790925a8ec9c8069b4eVictor Stinner            self.assertRaises(ImportError, imp.NullImporter, name)
3761a4d12d74681d35a40474790925a8ec9c8069b4eVictor Stinner        finally:
3771a4d12d74681d35a40474790925a8ec9c8069b4eVictor Stinner            os.rmdir(name)
3781a4d12d74681d35a40474790925a8ec9c8069b4eVictor Stinner
3791a4d12d74681d35a40474790925a8ec9c8069b4eVictor Stinner
380996acf122dbf8d9aa694a16a32ced065f5805cd2Neal Norwitzdef test_main():
38136144098e39598526ca981762cd4e7fd13c2412aHirokazu Yamamoto    tests = [
38236144098e39598526ca981762cd4e7fd13c2412aHirokazu Yamamoto        ImportTests,
38328a691b7fdde1b8abafa4c4a5025e6bfa44f48b9Barry Warsaw        PEP3147Tests,
3846ead552b47ecc4e7d85a03b3b662c6d46e59bec3Nick Coghlan        ReloadTests,
3851a4d12d74681d35a40474790925a8ec9c8069b4eVictor Stinner        NullImporterTests,
38628a691b7fdde1b8abafa4c4a5025e6bfa44f48b9Barry Warsaw        ]
38736144098e39598526ca981762cd4e7fd13c2412aHirokazu Yamamoto    try:
38836144098e39598526ca981762cd4e7fd13c2412aHirokazu Yamamoto        import _thread
38936144098e39598526ca981762cd4e7fd13c2412aHirokazu Yamamoto    except ImportError:
39036144098e39598526ca981762cd4e7fd13c2412aHirokazu Yamamoto        pass
39136144098e39598526ca981762cd4e7fd13c2412aHirokazu Yamamoto    else:
39236144098e39598526ca981762cd4e7fd13c2412aHirokazu Yamamoto        tests.append(LockTests)
39336144098e39598526ca981762cd4e7fd13c2412aHirokazu Yamamoto    support.run_unittest(*tests)
394996acf122dbf8d9aa694a16a32ced065f5805cd2Neal Norwitz
3952294c0d4ecd81d01ddf741fff02cc04bd8de0638Neal Norwitzif __name__ == "__main__":
396996acf122dbf8d9aa694a16a32ced065f5805cd2Neal Norwitz    test_main()
397