| 
									
										
										
										
											2024-04-05 13:55:59 +02:00
										 |  |  | import unittest | 
					
						
							|  |  |  | import os | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-15 15:32:05 -04:00
										 |  |  | from test.support import warnings_helper | 
					
						
							| 
									
										
										
										
											2024-04-05 13:55:59 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-15 15:32:05 -04:00
										 |  |  | from importlib import resources | 
					
						
							| 
									
										
										
										
											2024-04-05 13:55:59 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | # Since the functional API forwards to Traversable, we only test | 
					
						
							|  |  |  | # filesystem resources here -- not zip files, namespace packages etc. | 
					
						
							|  |  |  | # We do test for two kinds of Anchor, though. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class StringAnchorMixin: | 
					
						
							|  |  |  |     anchor01 = 'test.test_importlib.resources.data01' | 
					
						
							|  |  |  |     anchor02 = 'test.test_importlib.resources.data02' | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class ModuleAnchorMixin: | 
					
						
							|  |  |  |     from . import data01 as anchor01 | 
					
						
							|  |  |  |     from . import data02 as anchor02 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class FunctionalAPIBase: | 
					
						
							|  |  |  |     def _gen_resourcetxt_path_parts(self): | 
					
						
							| 
									
										
										
										
											2024-08-15 15:32:05 -04:00
										 |  |  |         """Yield various names of a text file in anchor02, each in a subTest""" | 
					
						
							| 
									
										
										
										
											2024-04-05 13:55:59 +02:00
										 |  |  |         for path_parts in ( | 
					
						
							|  |  |  |             ('subdirectory', 'subsubdir', 'resource.txt'), | 
					
						
							|  |  |  |             ('subdirectory/subsubdir/resource.txt',), | 
					
						
							|  |  |  |             ('subdirectory/subsubdir', 'resource.txt'), | 
					
						
							|  |  |  |         ): | 
					
						
							|  |  |  |             with self.subTest(path_parts=path_parts): | 
					
						
							|  |  |  |                 yield path_parts | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-05 17:00:29 +02:00
										 |  |  |     def assertEndsWith(self, string, suffix): | 
					
						
							|  |  |  |         """Assert that `string` ends with `suffix`.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         Used to ignore an architecture-specific UTF-16 byte-order mark."""
 | 
					
						
							| 
									
										
										
										
											2024-08-15 15:32:05 -04:00
										 |  |  |         self.assertEqual(string[-len(suffix) :], suffix) | 
					
						
							| 
									
										
										
										
											2024-04-05 17:00:29 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-05 13:55:59 +02:00
										 |  |  |     def test_read_text(self): | 
					
						
							|  |  |  |         self.assertEqual( | 
					
						
							|  |  |  |             resources.read_text(self.anchor01, 'utf-8.file'), | 
					
						
							|  |  |  |             'Hello, UTF-8 world!\n', | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  |         self.assertEqual( | 
					
						
							|  |  |  |             resources.read_text( | 
					
						
							| 
									
										
										
										
											2024-08-15 15:32:05 -04:00
										 |  |  |                 self.anchor02, | 
					
						
							|  |  |  |                 'subdirectory', | 
					
						
							|  |  |  |                 'subsubdir', | 
					
						
							|  |  |  |                 'resource.txt', | 
					
						
							| 
									
										
										
										
											2024-04-05 13:55:59 +02:00
										 |  |  |                 encoding='utf-8', | 
					
						
							|  |  |  |             ), | 
					
						
							|  |  |  |             'a resource', | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  |         for path_parts in self._gen_resourcetxt_path_parts(): | 
					
						
							|  |  |  |             self.assertEqual( | 
					
						
							|  |  |  |                 resources.read_text( | 
					
						
							| 
									
										
										
										
											2024-08-15 15:32:05 -04:00
										 |  |  |                     self.anchor02, | 
					
						
							|  |  |  |                     *path_parts, | 
					
						
							|  |  |  |                     encoding='utf-8', | 
					
						
							| 
									
										
										
										
											2024-04-05 13:55:59 +02:00
										 |  |  |                 ), | 
					
						
							|  |  |  |                 'a resource', | 
					
						
							|  |  |  |             ) | 
					
						
							|  |  |  |         # Use generic OSError, since e.g. attempting to read a directory can | 
					
						
							|  |  |  |         # fail with PermissionError rather than IsADirectoryError | 
					
						
							|  |  |  |         with self.assertRaises(OSError): | 
					
						
							|  |  |  |             resources.read_text(self.anchor01) | 
					
						
							|  |  |  |         with self.assertRaises(OSError): | 
					
						
							|  |  |  |             resources.read_text(self.anchor01, 'no-such-file') | 
					
						
							|  |  |  |         with self.assertRaises(UnicodeDecodeError): | 
					
						
							|  |  |  |             resources.read_text(self.anchor01, 'utf-16.file') | 
					
						
							|  |  |  |         self.assertEqual( | 
					
						
							|  |  |  |             resources.read_text( | 
					
						
							| 
									
										
										
										
											2024-08-15 15:32:05 -04:00
										 |  |  |                 self.anchor01, | 
					
						
							|  |  |  |                 'binary.file', | 
					
						
							|  |  |  |                 encoding='latin1', | 
					
						
							| 
									
										
										
										
											2024-04-05 13:55:59 +02:00
										 |  |  |             ), | 
					
						
							|  |  |  |             '\x00\x01\x02\x03', | 
					
						
							|  |  |  |         ) | 
					
						
							| 
									
										
										
										
											2024-04-05 17:00:29 +02:00
										 |  |  |         self.assertEndsWith(  # ignore the BOM | 
					
						
							| 
									
										
										
										
											2024-04-05 13:55:59 +02:00
										 |  |  |             resources.read_text( | 
					
						
							| 
									
										
										
										
											2024-08-15 15:32:05 -04:00
										 |  |  |                 self.anchor01, | 
					
						
							|  |  |  |                 'utf-16.file', | 
					
						
							| 
									
										
										
										
											2024-04-05 13:55:59 +02:00
										 |  |  |                 errors='backslashreplace', | 
					
						
							|  |  |  |             ), | 
					
						
							| 
									
										
										
										
											2024-04-05 17:00:29 +02:00
										 |  |  |             'Hello, UTF-16 world!\n'.encode('utf-16-le').decode( | 
					
						
							| 
									
										
										
										
											2024-04-05 13:55:59 +02:00
										 |  |  |                 errors='backslashreplace', | 
					
						
							|  |  |  |             ), | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_read_binary(self): | 
					
						
							|  |  |  |         self.assertEqual( | 
					
						
							|  |  |  |             resources.read_binary(self.anchor01, 'utf-8.file'), | 
					
						
							|  |  |  |             b'Hello, UTF-8 world!\n', | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  |         for path_parts in self._gen_resourcetxt_path_parts(): | 
					
						
							|  |  |  |             self.assertEqual( | 
					
						
							|  |  |  |                 resources.read_binary(self.anchor02, *path_parts), | 
					
						
							|  |  |  |                 b'a resource', | 
					
						
							|  |  |  |             ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_open_text(self): | 
					
						
							|  |  |  |         with resources.open_text(self.anchor01, 'utf-8.file') as f: | 
					
						
							|  |  |  |             self.assertEqual(f.read(), 'Hello, UTF-8 world!\n') | 
					
						
							|  |  |  |         for path_parts in self._gen_resourcetxt_path_parts(): | 
					
						
							|  |  |  |             with resources.open_text( | 
					
						
							| 
									
										
										
										
											2024-08-15 15:32:05 -04:00
										 |  |  |                 self.anchor02, | 
					
						
							|  |  |  |                 *path_parts, | 
					
						
							| 
									
										
										
										
											2024-04-05 13:55:59 +02:00
										 |  |  |                 encoding='utf-8', | 
					
						
							|  |  |  |             ) as f: | 
					
						
							|  |  |  |                 self.assertEqual(f.read(), 'a resource') | 
					
						
							|  |  |  |         # Use generic OSError, since e.g. attempting to read a directory can | 
					
						
							|  |  |  |         # fail with PermissionError rather than IsADirectoryError | 
					
						
							|  |  |  |         with self.assertRaises(OSError): | 
					
						
							|  |  |  |             resources.open_text(self.anchor01) | 
					
						
							|  |  |  |         with self.assertRaises(OSError): | 
					
						
							|  |  |  |             resources.open_text(self.anchor01, 'no-such-file') | 
					
						
							|  |  |  |         with resources.open_text(self.anchor01, 'utf-16.file') as f: | 
					
						
							|  |  |  |             with self.assertRaises(UnicodeDecodeError): | 
					
						
							|  |  |  |                 f.read() | 
					
						
							|  |  |  |         with resources.open_text( | 
					
						
							| 
									
										
										
										
											2024-08-15 15:32:05 -04:00
										 |  |  |             self.anchor01, | 
					
						
							|  |  |  |             'binary.file', | 
					
						
							|  |  |  |             encoding='latin1', | 
					
						
							| 
									
										
										
										
											2024-04-05 13:55:59 +02:00
										 |  |  |         ) as f: | 
					
						
							|  |  |  |             self.assertEqual(f.read(), '\x00\x01\x02\x03') | 
					
						
							|  |  |  |         with resources.open_text( | 
					
						
							| 
									
										
										
										
											2024-08-15 15:32:05 -04:00
										 |  |  |             self.anchor01, | 
					
						
							|  |  |  |             'utf-16.file', | 
					
						
							| 
									
										
										
										
											2024-04-05 13:55:59 +02:00
										 |  |  |             errors='backslashreplace', | 
					
						
							|  |  |  |         ) as f: | 
					
						
							| 
									
										
										
										
											2024-04-05 17:00:29 +02:00
										 |  |  |             self.assertEndsWith(  # ignore the BOM | 
					
						
							| 
									
										
										
										
											2024-04-05 13:55:59 +02:00
										 |  |  |                 f.read(), | 
					
						
							| 
									
										
										
										
											2024-04-05 17:00:29 +02:00
										 |  |  |                 'Hello, UTF-16 world!\n'.encode('utf-16-le').decode( | 
					
						
							| 
									
										
										
										
											2024-04-05 13:55:59 +02:00
										 |  |  |                     errors='backslashreplace', | 
					
						
							|  |  |  |                 ), | 
					
						
							|  |  |  |             ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_open_binary(self): | 
					
						
							|  |  |  |         with resources.open_binary(self.anchor01, 'utf-8.file') as f: | 
					
						
							|  |  |  |             self.assertEqual(f.read(), b'Hello, UTF-8 world!\n') | 
					
						
							|  |  |  |         for path_parts in self._gen_resourcetxt_path_parts(): | 
					
						
							|  |  |  |             with resources.open_binary( | 
					
						
							| 
									
										
										
										
											2024-08-15 15:32:05 -04:00
										 |  |  |                 self.anchor02, | 
					
						
							|  |  |  |                 *path_parts, | 
					
						
							| 
									
										
										
										
											2024-04-05 13:55:59 +02:00
										 |  |  |             ) as f: | 
					
						
							|  |  |  |                 self.assertEqual(f.read(), b'a resource') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_path(self): | 
					
						
							|  |  |  |         with resources.path(self.anchor01, 'utf-8.file') as path: | 
					
						
							| 
									
										
										
										
											2024-08-15 15:32:05 -04:00
										 |  |  |             with open(str(path), encoding='utf-8') as f: | 
					
						
							| 
									
										
										
										
											2024-04-05 13:55:59 +02:00
										 |  |  |                 self.assertEqual(f.read(), 'Hello, UTF-8 world!\n') | 
					
						
							|  |  |  |         with resources.path(self.anchor01) as path: | 
					
						
							| 
									
										
										
										
											2024-08-15 15:32:05 -04:00
										 |  |  |             with open(os.path.join(path, 'utf-8.file'), encoding='utf-8') as f: | 
					
						
							| 
									
										
										
										
											2024-04-05 13:55:59 +02:00
										 |  |  |                 self.assertEqual(f.read(), 'Hello, UTF-8 world!\n') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_is_resource(self): | 
					
						
							|  |  |  |         is_resource = resources.is_resource | 
					
						
							|  |  |  |         self.assertTrue(is_resource(self.anchor01, 'utf-8.file')) | 
					
						
							|  |  |  |         self.assertFalse(is_resource(self.anchor01, 'no_such_file')) | 
					
						
							|  |  |  |         self.assertFalse(is_resource(self.anchor01)) | 
					
						
							|  |  |  |         self.assertFalse(is_resource(self.anchor01, 'subdirectory')) | 
					
						
							|  |  |  |         for path_parts in self._gen_resourcetxt_path_parts(): | 
					
						
							|  |  |  |             self.assertTrue(is_resource(self.anchor02, *path_parts)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_contents(self): | 
					
						
							| 
									
										
										
										
											2024-08-15 15:32:05 -04:00
										 |  |  |         with warnings_helper.check_warnings((".*contents.*", DeprecationWarning)): | 
					
						
							| 
									
										
										
										
											2024-04-05 13:55:59 +02:00
										 |  |  |             c = resources.contents(self.anchor01) | 
					
						
							|  |  |  |         self.assertGreaterEqual( | 
					
						
							|  |  |  |             set(c), | 
					
						
							|  |  |  |             {'utf-8.file', 'utf-16.file', 'binary.file', 'subdirectory'}, | 
					
						
							|  |  |  |         ) | 
					
						
							| 
									
										
										
										
											2024-08-15 15:32:05 -04:00
										 |  |  |         with self.assertRaises(OSError), warnings_helper.check_warnings(( | 
					
						
							|  |  |  |             ".*contents.*", | 
					
						
							|  |  |  |             DeprecationWarning, | 
					
						
							|  |  |  |         )): | 
					
						
							| 
									
										
										
										
											2024-04-05 13:55:59 +02:00
										 |  |  |             list(resources.contents(self.anchor01, 'utf-8.file')) | 
					
						
							| 
									
										
										
										
											2024-08-15 15:32:05 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-05 13:55:59 +02:00
										 |  |  |         for path_parts in self._gen_resourcetxt_path_parts(): | 
					
						
							| 
									
										
										
										
											2024-08-15 15:32:05 -04:00
										 |  |  |             with self.assertRaises(OSError), warnings_helper.check_warnings(( | 
					
						
							|  |  |  |                 ".*contents.*", | 
					
						
							|  |  |  |                 DeprecationWarning, | 
					
						
							|  |  |  |             )): | 
					
						
							| 
									
										
										
										
											2024-04-05 13:55:59 +02:00
										 |  |  |                 list(resources.contents(self.anchor01, *path_parts)) | 
					
						
							| 
									
										
										
										
											2024-08-15 15:32:05 -04:00
										 |  |  |         with warnings_helper.check_warnings((".*contents.*", DeprecationWarning)): | 
					
						
							| 
									
										
										
										
											2024-04-05 13:55:59 +02:00
										 |  |  |             c = resources.contents(self.anchor01, 'subdirectory') | 
					
						
							|  |  |  |         self.assertGreaterEqual( | 
					
						
							|  |  |  |             set(c), | 
					
						
							|  |  |  |             {'binary.file'}, | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-15 15:32:05 -04:00
										 |  |  |     @warnings_helper.ignore_warnings(category=DeprecationWarning) | 
					
						
							| 
									
										
										
										
											2024-04-05 13:55:59 +02:00
										 |  |  |     def test_common_errors(self): | 
					
						
							|  |  |  |         for func in ( | 
					
						
							|  |  |  |             resources.read_text, | 
					
						
							|  |  |  |             resources.read_binary, | 
					
						
							|  |  |  |             resources.open_text, | 
					
						
							|  |  |  |             resources.open_binary, | 
					
						
							|  |  |  |             resources.path, | 
					
						
							|  |  |  |             resources.is_resource, | 
					
						
							|  |  |  |             resources.contents, | 
					
						
							|  |  |  |         ): | 
					
						
							|  |  |  |             with self.subTest(func=func): | 
					
						
							|  |  |  |                 # Rejecting None anchor | 
					
						
							|  |  |  |                 with self.assertRaises(TypeError): | 
					
						
							|  |  |  |                     func(None) | 
					
						
							|  |  |  |                 # Rejecting invalid anchor type | 
					
						
							|  |  |  |                 with self.assertRaises((TypeError, AttributeError)): | 
					
						
							|  |  |  |                     func(1234) | 
					
						
							|  |  |  |                 # Unknown module | 
					
						
							|  |  |  |                 with self.assertRaises(ModuleNotFoundError): | 
					
						
							|  |  |  |                     func('$missing module$') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_text_errors(self): | 
					
						
							|  |  |  |         for func in ( | 
					
						
							|  |  |  |             resources.read_text, | 
					
						
							|  |  |  |             resources.open_text, | 
					
						
							|  |  |  |         ): | 
					
						
							|  |  |  |             with self.subTest(func=func): | 
					
						
							|  |  |  |                 # Multiple path arguments need explicit encoding argument. | 
					
						
							|  |  |  |                 with self.assertRaises(TypeError): | 
					
						
							|  |  |  |                     func( | 
					
						
							| 
									
										
										
										
											2024-08-15 15:32:05 -04:00
										 |  |  |                         self.anchor02, | 
					
						
							|  |  |  |                         'subdirectory', | 
					
						
							|  |  |  |                         'subsubdir', | 
					
						
							|  |  |  |                         'resource.txt', | 
					
						
							| 
									
										
										
										
											2024-04-05 13:55:59 +02:00
										 |  |  |                     ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class FunctionalAPITest_StringAnchor( | 
					
						
							| 
									
										
										
										
											2024-08-15 15:32:05 -04:00
										 |  |  |     unittest.TestCase, | 
					
						
							|  |  |  |     FunctionalAPIBase, | 
					
						
							|  |  |  |     StringAnchorMixin, | 
					
						
							| 
									
										
										
										
											2024-04-05 13:55:59 +02:00
										 |  |  | ): | 
					
						
							|  |  |  |     pass | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class FunctionalAPITest_ModuleAnchor( | 
					
						
							| 
									
										
										
										
											2024-08-15 15:32:05 -04:00
										 |  |  |     unittest.TestCase, | 
					
						
							|  |  |  |     FunctionalAPIBase, | 
					
						
							|  |  |  |     ModuleAnchorMixin, | 
					
						
							| 
									
										
										
										
											2024-04-05 13:55:59 +02:00
										 |  |  | ): | 
					
						
							|  |  |  |     pass |