1  import contextlib
       2  import sys
       3  import unittest
       4  import uuid
       5  import pathlib
       6  
       7  from . import data01
       8  from . import zipdata01, zipdata02
       9  from . import util
      10  from importlib import resources, import_module
      11  from test.support import import_helper, os_helper
      12  from test.support.os_helper import unlink
      13  
      14  
      15  class ESC[4;38;5;81mResourceTests:
      16      # Subclasses are expected to set the `data` attribute.
      17  
      18      def test_is_file_exists(self):
      19          target = resources.files(self.data) / 'binary.file'
      20          self.assertTrue(target.is_file())
      21  
      22      def test_is_file_missing(self):
      23          target = resources.files(self.data) / 'not-a-file'
      24          self.assertFalse(target.is_file())
      25  
      26      def test_is_dir(self):
      27          target = resources.files(self.data) / 'subdirectory'
      28          self.assertFalse(target.is_file())
      29          self.assertTrue(target.is_dir())
      30  
      31  
      32  class ESC[4;38;5;81mResourceDiskTests(ESC[4;38;5;149mResourceTests, ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
      33      def setUp(self):
      34          self.data = data01
      35  
      36  
      37  class ESC[4;38;5;81mResourceZipTests(ESC[4;38;5;149mResourceTests, ESC[4;38;5;149mutilESC[4;38;5;149m.ESC[4;38;5;149mZipSetup, ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
      38      pass
      39  
      40  
      41  def names(traversable):
      42      return {item.name for item in traversable.iterdir()}
      43  
      44  
      45  class ESC[4;38;5;81mResourceLoaderTests(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
      46      def test_resource_contents(self):
      47          package = util.create_package(
      48              file=data01, path=data01.__file__, contents=['A', 'B', 'C']
      49          )
      50          self.assertEqual(names(resources.files(package)), {'A', 'B', 'C'})
      51  
      52      def test_is_file(self):
      53          package = util.create_package(
      54              file=data01, path=data01.__file__, contents=['A', 'B', 'C', 'D/E', 'D/F']
      55          )
      56          self.assertTrue(resources.files(package).joinpath('B').is_file())
      57  
      58      def test_is_dir(self):
      59          package = util.create_package(
      60              file=data01, path=data01.__file__, contents=['A', 'B', 'C', 'D/E', 'D/F']
      61          )
      62          self.assertTrue(resources.files(package).joinpath('D').is_dir())
      63  
      64      def test_resource_missing(self):
      65          package = util.create_package(
      66              file=data01, path=data01.__file__, contents=['A', 'B', 'C', 'D/E', 'D/F']
      67          )
      68          self.assertFalse(resources.files(package).joinpath('Z').is_file())
      69  
      70  
      71  class ESC[4;38;5;81mResourceCornerCaseTests(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
      72      def test_package_has_no_reader_fallback(self):
      73          """
      74          Test odd ball packages which:
      75          # 1. Do not have a ResourceReader as a loader
      76          # 2. Are not on the file system
      77          # 3. Are not in a zip file
      78          """
      79          module = util.create_package(
      80              file=data01, path=data01.__file__, contents=['A', 'B', 'C']
      81          )
      82          # Give the module a dummy loader.
      83          module.__loader__ = object()
      84          # Give the module a dummy origin.
      85          module.__file__ = '/path/which/shall/not/be/named'
      86          module.__spec__.loader = module.__loader__
      87          module.__spec__.origin = module.__file__
      88          self.assertFalse(resources.files(module).joinpath('A').is_file())
      89  
      90  
      91  class ESC[4;38;5;81mResourceFromZipsTest01(ESC[4;38;5;149mutilESC[4;38;5;149m.ESC[4;38;5;149mZipSetupBase, ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
      92      ZIP_MODULE = zipdata01  # type: ignore
      93  
      94      def test_is_submodule_resource(self):
      95          submodule = import_module('ziptestdata.subdirectory')
      96          self.assertTrue(resources.files(submodule).joinpath('binary.file').is_file())
      97  
      98      def test_read_submodule_resource_by_name(self):
      99          self.assertTrue(
     100              resources.files('ziptestdata.subdirectory')
     101              .joinpath('binary.file')
     102              .is_file()
     103          )
     104  
     105      def test_submodule_contents(self):
     106          submodule = import_module('ziptestdata.subdirectory')
     107          self.assertEqual(
     108              names(resources.files(submodule)), {'__init__.py', 'binary.file'}
     109          )
     110  
     111      def test_submodule_contents_by_name(self):
     112          self.assertEqual(
     113              names(resources.files('ziptestdata.subdirectory')),
     114              {'__init__.py', 'binary.file'},
     115          )
     116  
     117      def test_as_file_directory(self):
     118          with resources.as_file(resources.files('ziptestdata')) as data:
     119              assert data.name == 'ziptestdata'
     120              assert data.is_dir()
     121              assert data.joinpath('subdirectory').is_dir()
     122              assert len(list(data.iterdir()))
     123          assert not data.parent.exists()
     124  
     125  
     126  class ESC[4;38;5;81mResourceFromZipsTest02(ESC[4;38;5;149mutilESC[4;38;5;149m.ESC[4;38;5;149mZipSetupBase, ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
     127      ZIP_MODULE = zipdata02  # type: ignore
     128  
     129      def test_unrelated_contents(self):
     130          """
     131          Test thata zip with two unrelated subpackages return
     132          distinct resources. Ref python/importlib_resources#44.
     133          """
     134          self.assertEqual(
     135              names(resources.files('ziptestdata.one')),
     136              {'__init__.py', 'resource1.txt'},
     137          )
     138          self.assertEqual(
     139              names(resources.files('ziptestdata.two')),
     140              {'__init__.py', 'resource2.txt'},
     141          )
     142  
     143  
     144  @contextlib.contextmanager
     145  def zip_on_path(dir):
     146      data_path = pathlib.Path(zipdata01.__file__)
     147      source_zip_path = data_path.parent.joinpath('ziptestdata.zip')
     148      zip_path = pathlib.Path(dir) / f'{uuid.uuid4()}.zip'
     149      zip_path.write_bytes(source_zip_path.read_bytes())
     150      sys.path.append(str(zip_path))
     151      import_module('ziptestdata')
     152  
     153      try:
     154          yield
     155      finally:
     156          with contextlib.suppress(ValueError):
     157              sys.path.remove(str(zip_path))
     158  
     159          with contextlib.suppress(KeyError):
     160              del sys.path_importer_cache[str(zip_path)]
     161              del sys.modules['ziptestdata']
     162  
     163          with contextlib.suppress(OSError):
     164              unlink(zip_path)
     165  
     166  
     167  class ESC[4;38;5;81mDeletingZipsTest(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
     168      """Having accessed resources in a zip file should not keep an open
     169      reference to the zip.
     170      """
     171  
     172      def setUp(self):
     173          self.fixtures = contextlib.ExitStack()
     174          self.addCleanup(self.fixtures.close)
     175  
     176          modules = import_helper.modules_setup()
     177          self.addCleanup(import_helper.modules_cleanup, *modules)
     178  
     179          temp_dir = self.fixtures.enter_context(os_helper.temp_dir())
     180          self.fixtures.enter_context(zip_on_path(temp_dir))
     181  
     182      def test_iterdir_does_not_keep_open(self):
     183          [item.name for item in resources.files('ziptestdata').iterdir()]
     184  
     185      def test_is_file_does_not_keep_open(self):
     186          resources.files('ziptestdata').joinpath('binary.file').is_file()
     187  
     188      def test_is_file_failure_does_not_keep_open(self):
     189          resources.files('ziptestdata').joinpath('not-present').is_file()
     190  
     191      @unittest.skip("Desired but not supported.")
     192      def test_as_file_does_not_keep_open(self):  # pragma: no cover
     193          resources.as_file(resources.files('ziptestdata') / 'binary.file')
     194  
     195      def test_entered_path_does_not_keep_open(self):
     196          """
     197          Mimic what certifi does on import to make its bundle
     198          available for the process duration.
     199          """
     200          resources.as_file(resources.files('ziptestdata') / 'binary.file').__enter__()
     201  
     202      def test_read_binary_does_not_keep_open(self):
     203          resources.files('ziptestdata').joinpath('binary.file').read_bytes()
     204  
     205      def test_read_text_does_not_keep_open(self):
     206          resources.files('ziptestdata').joinpath('utf-8.file').read_text(
     207              encoding='utf-8'
     208          )
     209  
     210  
     211  class ESC[4;38;5;81mResourceFromNamespaceTest01(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
     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  
     251  if __name__ == '__main__':
     252      unittest.main()