1import sys
2import unittest
3import uuid
4import pathlib
5
6from . import data01
7from . import zipdata01, zipdata02
8from .resources import util
9from importlib import resources, import_module
10from test.support import import_helper
11from test.support.os_helper import unlink
12
13
14class ResourceTests:
15    # Subclasses are expected to set the `data` attribute.
16
17    def test_is_file_exists(self):
18        target = resources.files(self.data) / 'binary.file'
19        self.assertTrue(target.is_file())
20
21    def test_is_file_missing(self):
22        target = resources.files(self.data) / 'not-a-file'
23        self.assertFalse(target.is_file())
24
25    def test_is_dir(self):
26        target = resources.files(self.data) / 'subdirectory'
27        self.assertFalse(target.is_file())
28        self.assertTrue(target.is_dir())
29
30
31class ResourceDiskTests(ResourceTests, unittest.TestCase):
32    def setUp(self):
33        self.data = data01
34
35
36class ResourceZipTests(ResourceTests, util.ZipSetup, unittest.TestCase):
37    pass
38
39
40def names(traversable):
41    return {item.name for item in traversable.iterdir()}
42
43
44class ResourceLoaderTests(unittest.TestCase):
45    def test_resource_contents(self):
46        package = util.create_package(
47            file=data01, path=data01.__file__, contents=['A', 'B', 'C']
48        )
49        self.assertEqual(names(resources.files(package)), {'A', 'B', 'C'})
50
51    def test_is_file(self):
52        package = util.create_package(
53            file=data01, path=data01.__file__, contents=['A', 'B', 'C', 'D/E', 'D/F']
54        )
55        self.assertTrue(resources.files(package).joinpath('B').is_file())
56
57    def test_is_dir(self):
58        package = util.create_package(
59            file=data01, path=data01.__file__, contents=['A', 'B', 'C', 'D/E', 'D/F']
60        )
61        self.assertTrue(resources.files(package).joinpath('D').is_dir())
62
63    def test_resource_missing(self):
64        package = util.create_package(
65            file=data01, path=data01.__file__, contents=['A', 'B', 'C', 'D/E', 'D/F']
66        )
67        self.assertFalse(resources.files(package).joinpath('Z').is_file())
68
69
70class ResourceCornerCaseTests(unittest.TestCase):
71    def test_package_has_no_reader_fallback(self):
72        # Test odd ball packages which:
73        # 1. Do not have a ResourceReader as a loader
74        # 2. Are not on the file system
75        # 3. Are not in a zip file
76        module = util.create_package(
77            file=data01, path=data01.__file__, contents=['A', 'B', 'C']
78        )
79        # Give the module a dummy loader.
80        module.__loader__ = object()
81        # Give the module a dummy origin.
82        module.__file__ = '/path/which/shall/not/be/named'
83        module.__spec__.loader = module.__loader__
84        module.__spec__.origin = module.__file__
85        self.assertFalse(resources.files(module).joinpath('A').is_file())
86
87
88class ResourceFromZipsTest01(util.ZipSetupBase, unittest.TestCase):
89    ZIP_MODULE = zipdata01  # type: ignore
90
91    def test_is_submodule_resource(self):
92        submodule = import_module('ziptestdata.subdirectory')
93        self.assertTrue(resources.files(submodule).joinpath('binary.file').is_file())
94
95    def test_read_submodule_resource_by_name(self):
96        self.assertTrue(
97            resources.files('ziptestdata.subdirectory')
98            .joinpath('binary.file')
99            .is_file()
100        )
101
102    def test_submodule_contents(self):
103        submodule = import_module('ziptestdata.subdirectory')
104        self.assertEqual(
105            names(resources.files(submodule)), {'__init__.py', 'binary.file'}
106        )
107
108    def test_submodule_contents_by_name(self):
109        self.assertEqual(
110            names(resources.files('ziptestdata.subdirectory')),
111            {'__init__.py', 'binary.file'},
112        )
113
114
115class ResourceFromZipsTest02(util.ZipSetupBase, unittest.TestCase):
116    ZIP_MODULE = zipdata02  # type: ignore
117
118    def test_unrelated_contents(self):
119        """
120        Test thata zip with two unrelated subpackages return
121        distinct resources. Ref python/importlib_resources#44.
122        """
123        self.assertEqual(
124            names(resources.files('ziptestdata.one')),
125            {'__init__.py', 'resource1.txt'},
126        )
127        self.assertEqual(
128            names(resources.files('ziptestdata.two')),
129            {'__init__.py', 'resource2.txt'},
130        )
131
132
133class DeletingZipsTest(unittest.TestCase):
134    """Having accessed resources in a zip file should not keep an open
135    reference to the zip.
136    """
137
138    ZIP_MODULE = zipdata01
139
140    def setUp(self):
141        modules = import_helper.modules_setup()
142        self.addCleanup(import_helper.modules_cleanup, *modules)
143
144        data_path = pathlib.Path(self.ZIP_MODULE.__file__)
145        data_dir = data_path.parent
146        self.source_zip_path = data_dir / 'ziptestdata.zip'
147        self.zip_path = pathlib.Path(f'{uuid.uuid4()}.zip').absolute()
148        self.zip_path.write_bytes(self.source_zip_path.read_bytes())
149        sys.path.append(str(self.zip_path))
150        self.data = import_module('ziptestdata')
151
152    def tearDown(self):
153        try:
154            sys.path.remove(str(self.zip_path))
155        except ValueError:
156            pass
157
158        try:
159            del sys.path_importer_cache[str(self.zip_path)]
160            del sys.modules[self.data.__name__]
161        except KeyError:
162            pass
163
164        try:
165            unlink(self.zip_path)
166        except OSError:
167            # If the test fails, this will probably fail too
168            pass
169
170    def test_iterdir_does_not_keep_open(self):
171        c = [item.name for item in resources.files('ziptestdata').iterdir()]
172        self.zip_path.unlink()
173        del c
174
175    def test_is_file_does_not_keep_open(self):
176        c = resources.files('ziptestdata').joinpath('binary.file').is_file()
177        self.zip_path.unlink()
178        del c
179
180    def test_is_file_failure_does_not_keep_open(self):
181        c = resources.files('ziptestdata').joinpath('not-present').is_file()
182        self.zip_path.unlink()
183        del c
184
185    @unittest.skip("Desired but not supported.")
186    def test_as_file_does_not_keep_open(self):  # pragma: no cover
187        c = resources.as_file(resources.files('ziptestdata') / 'binary.file')
188        self.zip_path.unlink()
189        del c
190
191    def test_entered_path_does_not_keep_open(self):
192        # This is what certifi does on import to make its bundle
193        # available for the process duration.
194        c = resources.as_file(
195            resources.files('ziptestdata') / 'binary.file'
196        ).__enter__()
197        self.zip_path.unlink()
198        del c
199
200    def test_read_binary_does_not_keep_open(self):
201        c = resources.files('ziptestdata').joinpath('binary.file').read_bytes()
202        self.zip_path.unlink()
203        del c
204
205    def test_read_text_does_not_keep_open(self):
206        c = resources.files('ziptestdata').joinpath('utf-8.file').read_text()
207        self.zip_path.unlink()
208        del c
209
210
211class ResourceFromNamespaceTest01(unittest.TestCase):
212    site_dir = str(pathlib.Path(__file__).parent)
213
214    @classmethod
215    def setUpClass(cls):
216        sys.path.append(cls.site_dir)
217
218    @classmethod
219    def tearDownClass(cls):
220        sys.path.remove(cls.site_dir)
221
222    def test_is_submodule_resource(self):
223        self.assertTrue(
224            resources.files(import_module('namespacedata01'))
225            .joinpath('binary.file')
226            .is_file()
227        )
228
229    def test_read_submodule_resource_by_name(self):
230        self.assertTrue(
231            resources.files('namespacedata01').joinpath('binary.file').is_file()
232        )
233
234    def test_submodule_contents(self):
235        contents = names(resources.files(import_module('namespacedata01')))
236        try:
237            contents.remove('__pycache__')
238        except KeyError:
239            pass
240        self.assertEqual(contents, {'binary.file', 'utf-8.file', 'utf-16.file'})
241
242    def test_submodule_contents_by_name(self):
243        contents = names(resources.files('namespacedata01'))
244        try:
245            contents.remove('__pycache__')
246        except KeyError:
247            pass
248        self.assertEqual(contents, {'binary.file', 'utf-8.file', 'utf-16.file'})
249
250
251if __name__ == '__main__':
252    unittest.main()
253