1import abc 2import importlib 3import io 4import sys 5import types 6from pathlib import Path, PurePath 7 8from .. import data01 9from .. import zipdata01 10from importlib.abc import ResourceReader 11from test.support import import_helper 12 13 14from importlib.machinery import ModuleSpec 15 16 17class Reader(ResourceReader): 18 def __init__(self, **kwargs): 19 vars(self).update(kwargs) 20 21 def get_resource_reader(self, package): 22 return self 23 24 def open_resource(self, path): 25 self._path = path 26 if isinstance(self.file, Exception): 27 raise self.file 28 return self.file 29 30 def resource_path(self, path_): 31 self._path = path_ 32 if isinstance(self.path, Exception): 33 raise self.path 34 return self.path 35 36 def is_resource(self, path_): 37 self._path = path_ 38 if isinstance(self.path, Exception): 39 raise self.path 40 41 def part(entry): 42 return entry.split('/') 43 44 return any( 45 len(parts) == 1 and parts[0] == path_ for parts in map(part, self._contents) 46 ) 47 48 def contents(self): 49 if isinstance(self.path, Exception): 50 raise self.path 51 yield from self._contents 52 53 54def create_package_from_loader(loader, is_package=True): 55 name = 'testingpackage' 56 module = types.ModuleType(name) 57 spec = ModuleSpec(name, loader, origin='does-not-exist', is_package=is_package) 58 module.__spec__ = spec 59 module.__loader__ = loader 60 return module 61 62 63def create_package(file=None, path=None, is_package=True, contents=()): 64 return create_package_from_loader( 65 Reader(file=file, path=path, _contents=contents), 66 is_package, 67 ) 68 69 70class CommonTests(metaclass=abc.ABCMeta): 71 """ 72 Tests shared by test_open, test_path, and test_read. 73 """ 74 75 @abc.abstractmethod 76 def execute(self, package, path): 77 """ 78 Call the pertinent legacy API function (e.g. open_text, path) 79 on package and path. 80 """ 81 82 def test_package_name(self): 83 # Passing in the package name should succeed. 84 self.execute(data01.__name__, 'utf-8.file') 85 86 def test_package_object(self): 87 # Passing in the package itself should succeed. 88 self.execute(data01, 'utf-8.file') 89 90 def test_string_path(self): 91 # Passing in a string for the path should succeed. 92 path = 'utf-8.file' 93 self.execute(data01, path) 94 95 def test_pathlib_path(self): 96 # Passing in a pathlib.PurePath object for the path should succeed. 97 path = PurePath('utf-8.file') 98 self.execute(data01, path) 99 100 def test_importing_module_as_side_effect(self): 101 # The anchor package can already be imported. 102 del sys.modules[data01.__name__] 103 self.execute(data01.__name__, 'utf-8.file') 104 105 def test_non_package_by_name(self): 106 # The anchor package cannot be a module. 107 with self.assertRaises(TypeError): 108 self.execute(__name__, 'utf-8.file') 109 110 def test_non_package_by_package(self): 111 # The anchor package cannot be a module. 112 with self.assertRaises(TypeError): 113 module = sys.modules['test.test_importlib.resources.util'] 114 self.execute(module, 'utf-8.file') 115 116 def test_missing_path(self): 117 # Attempting to open or read or request the path for a 118 # non-existent path should succeed if open_resource 119 # can return a viable data stream. 120 bytes_data = io.BytesIO(b'Hello, world!') 121 package = create_package(file=bytes_data, path=FileNotFoundError()) 122 self.execute(package, 'utf-8.file') 123 self.assertEqual(package.__loader__._path, 'utf-8.file') 124 125 def test_extant_path(self): 126 # Attempting to open or read or request the path when the 127 # path does exist should still succeed. Does not assert 128 # anything about the result. 129 bytes_data = io.BytesIO(b'Hello, world!') 130 # any path that exists 131 path = __file__ 132 package = create_package(file=bytes_data, path=path) 133 self.execute(package, 'utf-8.file') 134 self.assertEqual(package.__loader__._path, 'utf-8.file') 135 136 def test_useless_loader(self): 137 package = create_package(file=FileNotFoundError(), path=FileNotFoundError()) 138 with self.assertRaises(FileNotFoundError): 139 self.execute(package, 'utf-8.file') 140 141 142class ZipSetupBase: 143 ZIP_MODULE = None 144 145 @classmethod 146 def setUpClass(cls): 147 data_path = Path(cls.ZIP_MODULE.__file__) 148 data_dir = data_path.parent 149 cls._zip_path = str(data_dir / 'ziptestdata.zip') 150 sys.path.append(cls._zip_path) 151 cls.data = importlib.import_module('ziptestdata') 152 153 @classmethod 154 def tearDownClass(cls): 155 try: 156 sys.path.remove(cls._zip_path) 157 except ValueError: 158 pass 159 160 try: 161 del sys.path_importer_cache[cls._zip_path] 162 del sys.modules[cls.data.__name__] 163 except KeyError: 164 pass 165 166 try: 167 del cls.data 168 del cls._zip_path 169 except AttributeError: 170 pass 171 172 def setUp(self): 173 modules = import_helper.modules_setup() 174 self.addCleanup(import_helper.modules_cleanup, *modules) 175 176 177class ZipSetup(ZipSetupBase): 178 ZIP_MODULE = zipdata01 # type: ignore 179