(root)/
Python-3.11.7/
Lib/
test/
test_importlib/
test_namespace_pkgs.py
       1  import contextlib
       2  import importlib
       3  import importlib.abc
       4  import importlib.machinery
       5  import os
       6  import sys
       7  import tempfile
       8  import unittest
       9  import warnings
      10  
      11  from test.test_importlib import util
      12  
      13  # needed tests:
      14  #
      15  # need to test when nested, so that the top-level path isn't sys.path
      16  # need to test dynamic path detection, both at top-level and nested
      17  # with dynamic path, check when a loader is returned on path reload (that is,
      18  #  trying to switch from a namespace package to a regular package)
      19  
      20  
      21  @contextlib.contextmanager
      22  def sys_modules_context():
      23      """
      24      Make sure sys.modules is the same object and has the same content
      25      when exiting the context as when entering.
      26  
      27      Similar to importlib.test.util.uncache, but doesn't require explicit
      28      names.
      29      """
      30      sys_modules_saved = sys.modules
      31      sys_modules_copy = sys.modules.copy()
      32      try:
      33          yield
      34      finally:
      35          sys.modules = sys_modules_saved
      36          sys.modules.clear()
      37          sys.modules.update(sys_modules_copy)
      38  
      39  
      40  @contextlib.contextmanager
      41  def namespace_tree_context(**kwargs):
      42      """
      43      Save import state and sys.modules cache and restore it on exit.
      44      Typical usage:
      45  
      46      >>> with namespace_tree_context(path=['/tmp/xxyy/portion1',
      47      ...         '/tmp/xxyy/portion2']):
      48      ...     pass
      49      """
      50      # use default meta_path and path_hooks unless specified otherwise
      51      kwargs.setdefault('meta_path', sys.meta_path)
      52      kwargs.setdefault('path_hooks', sys.path_hooks)
      53      import_context = util.import_state(**kwargs)
      54      with import_context, sys_modules_context():
      55          yield
      56  
      57  class ESC[4;38;5;81mNamespacePackageTest(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
      58      """
      59      Subclasses should define self.root and self.paths (under that root)
      60      to be added to sys.path.
      61      """
      62      root = os.path.join(os.path.dirname(__file__), 'namespace_pkgs')
      63  
      64      def setUp(self):
      65          self.resolved_paths = [
      66              os.path.join(self.root, path) for path in self.paths
      67          ]
      68          self.enterContext(namespace_tree_context(path=self.resolved_paths))
      69  
      70  
      71  class ESC[4;38;5;81mSingleNamespacePackage(ESC[4;38;5;149mNamespacePackageTest):
      72      paths = ['portion1']
      73  
      74      def test_simple_package(self):
      75          import foo.one
      76          self.assertEqual(foo.one.attr, 'portion1 foo one')
      77  
      78      def test_cant_import_other(self):
      79          with self.assertRaises(ImportError):
      80              import foo.two
      81  
      82      def test_module_repr(self):
      83          import foo.one
      84          with warnings.catch_warnings():
      85              warnings.simplefilter("ignore")
      86              self.assertEqual(foo.__spec__.loader.module_repr(foo),
      87                              "<module 'foo' (namespace)>")
      88  
      89  
      90  class ESC[4;38;5;81mDynamicPathNamespacePackage(ESC[4;38;5;149mNamespacePackageTest):
      91      paths = ['portion1']
      92  
      93      def test_dynamic_path(self):
      94          # Make sure only 'foo.one' can be imported
      95          import foo.one
      96          self.assertEqual(foo.one.attr, 'portion1 foo one')
      97  
      98          with self.assertRaises(ImportError):
      99              import foo.two
     100  
     101          # Now modify sys.path
     102          sys.path.append(os.path.join(self.root, 'portion2'))
     103  
     104          # And make sure foo.two is now importable
     105          import foo.two
     106          self.assertEqual(foo.two.attr, 'portion2 foo two')
     107  
     108  
     109  class ESC[4;38;5;81mCombinedNamespacePackages(ESC[4;38;5;149mNamespacePackageTest):
     110      paths = ['both_portions']
     111  
     112      def test_imports(self):
     113          import foo.one
     114          import foo.two
     115          self.assertEqual(foo.one.attr, 'both_portions foo one')
     116          self.assertEqual(foo.two.attr, 'both_portions foo two')
     117  
     118  
     119  class ESC[4;38;5;81mSeparatedNamespacePackages(ESC[4;38;5;149mNamespacePackageTest):
     120      paths = ['portion1', 'portion2']
     121  
     122      def test_imports(self):
     123          import foo.one
     124          import foo.two
     125          self.assertEqual(foo.one.attr, 'portion1 foo one')
     126          self.assertEqual(foo.two.attr, 'portion2 foo two')
     127  
     128  
     129  class ESC[4;38;5;81mSeparatedNamespacePackagesCreatedWhileRunning(ESC[4;38;5;149mNamespacePackageTest):
     130      paths = ['portion1']
     131  
     132      def test_invalidate_caches(self):
     133          with tempfile.TemporaryDirectory() as temp_dir:
     134              # we manipulate sys.path before anything is imported to avoid
     135              # accidental cache invalidation when changing it
     136              sys.path.append(temp_dir)
     137  
     138              import foo.one
     139              self.assertEqual(foo.one.attr, 'portion1 foo one')
     140  
     141              # the module does not exist, so it cannot be imported
     142              with self.assertRaises(ImportError):
     143                  import foo.just_created
     144  
     145              # util.create_modules() manipulates sys.path
     146              # so we must create the modules manually instead
     147              namespace_path = os.path.join(temp_dir, 'foo')
     148              os.mkdir(namespace_path)
     149              module_path = os.path.join(namespace_path, 'just_created.py')
     150              with open(module_path, 'w', encoding='utf-8') as file:
     151                  file.write('attr = "just_created foo"')
     152  
     153              # the module is not known, so it cannot be imported yet
     154              with self.assertRaises(ImportError):
     155                  import foo.just_created
     156  
     157              # but after explicit cache invalidation, it is importable
     158              importlib.invalidate_caches()
     159              import foo.just_created
     160              self.assertEqual(foo.just_created.attr, 'just_created foo')
     161  
     162  
     163  class ESC[4;38;5;81mSeparatedOverlappingNamespacePackages(ESC[4;38;5;149mNamespacePackageTest):
     164      paths = ['portion1', 'both_portions']
     165  
     166      def test_first_path_wins(self):
     167          import foo.one
     168          import foo.two
     169          self.assertEqual(foo.one.attr, 'portion1 foo one')
     170          self.assertEqual(foo.two.attr, 'both_portions foo two')
     171  
     172      def test_first_path_wins_again(self):
     173          sys.path.reverse()
     174          import foo.one
     175          import foo.two
     176          self.assertEqual(foo.one.attr, 'both_portions foo one')
     177          self.assertEqual(foo.two.attr, 'both_portions foo two')
     178  
     179      def test_first_path_wins_importing_second_first(self):
     180          import foo.two
     181          import foo.one
     182          self.assertEqual(foo.one.attr, 'portion1 foo one')
     183          self.assertEqual(foo.two.attr, 'both_portions foo two')
     184  
     185  
     186  class ESC[4;38;5;81mSingleZipNamespacePackage(ESC[4;38;5;149mNamespacePackageTest):
     187      paths = ['top_level_portion1.zip']
     188  
     189      def test_simple_package(self):
     190          import foo.one
     191          self.assertEqual(foo.one.attr, 'portion1 foo one')
     192  
     193      def test_cant_import_other(self):
     194          with self.assertRaises(ImportError):
     195              import foo.two
     196  
     197  
     198  class ESC[4;38;5;81mSeparatedZipNamespacePackages(ESC[4;38;5;149mNamespacePackageTest):
     199      paths = ['top_level_portion1.zip', 'portion2']
     200  
     201      def test_imports(self):
     202          import foo.one
     203          import foo.two
     204          self.assertEqual(foo.one.attr, 'portion1 foo one')
     205          self.assertEqual(foo.two.attr, 'portion2 foo two')
     206          self.assertIn('top_level_portion1.zip', foo.one.__file__)
     207          self.assertNotIn('.zip', foo.two.__file__)
     208  
     209  
     210  class ESC[4;38;5;81mSingleNestedZipNamespacePackage(ESC[4;38;5;149mNamespacePackageTest):
     211      paths = ['nested_portion1.zip/nested_portion1']
     212  
     213      def test_simple_package(self):
     214          import foo.one
     215          self.assertEqual(foo.one.attr, 'portion1 foo one')
     216  
     217      def test_cant_import_other(self):
     218          with self.assertRaises(ImportError):
     219              import foo.two
     220  
     221  
     222  class ESC[4;38;5;81mSeparatedNestedZipNamespacePackages(ESC[4;38;5;149mNamespacePackageTest):
     223      paths = ['nested_portion1.zip/nested_portion1', 'portion2']
     224  
     225      def test_imports(self):
     226          import foo.one
     227          import foo.two
     228          self.assertEqual(foo.one.attr, 'portion1 foo one')
     229          self.assertEqual(foo.two.attr, 'portion2 foo two')
     230          fn = os.path.join('nested_portion1.zip', 'nested_portion1')
     231          self.assertIn(fn, foo.one.__file__)
     232          self.assertNotIn('.zip', foo.two.__file__)
     233  
     234  
     235  class ESC[4;38;5;81mLegacySupport(ESC[4;38;5;149mNamespacePackageTest):
     236      paths = ['not_a_namespace_pkg', 'portion1', 'portion2', 'both_portions']
     237  
     238      def test_non_namespace_package_takes_precedence(self):
     239          import foo.one
     240          with self.assertRaises(ImportError):
     241              import foo.two
     242          self.assertIn('__init__', foo.__file__)
     243          self.assertNotIn('namespace', str(foo.__loader__).lower())
     244  
     245  
     246  class ESC[4;38;5;81mDynamicPathCalculation(ESC[4;38;5;149mNamespacePackageTest):
     247      paths = ['project1', 'project2']
     248  
     249      def test_project3_fails(self):
     250          import parent.child.one
     251          self.assertEqual(len(parent.__path__), 2)
     252          self.assertEqual(len(parent.child.__path__), 2)
     253          import parent.child.two
     254          self.assertEqual(len(parent.__path__), 2)
     255          self.assertEqual(len(parent.child.__path__), 2)
     256  
     257          self.assertEqual(parent.child.one.attr, 'parent child one')
     258          self.assertEqual(parent.child.two.attr, 'parent child two')
     259  
     260          with self.assertRaises(ImportError):
     261              import parent.child.three
     262  
     263          self.assertEqual(len(parent.__path__), 2)
     264          self.assertEqual(len(parent.child.__path__), 2)
     265  
     266      def test_project3_succeeds(self):
     267          import parent.child.one
     268          self.assertEqual(len(parent.__path__), 2)
     269          self.assertEqual(len(parent.child.__path__), 2)
     270          import parent.child.two
     271          self.assertEqual(len(parent.__path__), 2)
     272          self.assertEqual(len(parent.child.__path__), 2)
     273  
     274          self.assertEqual(parent.child.one.attr, 'parent child one')
     275          self.assertEqual(parent.child.two.attr, 'parent child two')
     276  
     277          with self.assertRaises(ImportError):
     278              import parent.child.three
     279  
     280          # now add project3
     281          sys.path.append(os.path.join(self.root, 'project3'))
     282          import parent.child.three
     283  
     284          # the paths dynamically get longer, to include the new directories
     285          self.assertEqual(len(parent.__path__), 3)
     286          self.assertEqual(len(parent.child.__path__), 3)
     287  
     288          self.assertEqual(parent.child.three.attr, 'parent child three')
     289  
     290  
     291  class ESC[4;38;5;81mZipWithMissingDirectory(ESC[4;38;5;149mNamespacePackageTest):
     292      paths = ['missing_directory.zip']
     293  
     294      @unittest.expectedFailure
     295      def test_missing_directory(self):
     296          # This will fail because missing_directory.zip contains:
     297          #   Length      Date    Time    Name
     298          # ---------  ---------- -----   ----
     299          #        29  2012-05-03 18:13   foo/one.py
     300          #         0  2012-05-03 20:57   bar/
     301          #        38  2012-05-03 20:57   bar/two.py
     302          # ---------                     -------
     303          #        67                     3 files
     304  
     305          # Because there is no 'foo/', the zipimporter currently doesn't
     306          #  know that foo is a namespace package
     307  
     308          import foo.one
     309  
     310      def test_present_directory(self):
     311          # This succeeds because there is a "bar/" in the zip file
     312          import bar.two
     313          self.assertEqual(bar.two.attr, 'missing_directory foo two')
     314  
     315  
     316  class ESC[4;38;5;81mModuleAndNamespacePackageInSameDir(ESC[4;38;5;149mNamespacePackageTest):
     317      paths = ['module_and_namespace_package']
     318  
     319      def test_module_before_namespace_package(self):
     320          # Make sure we find the module in preference to the
     321          #  namespace package.
     322          import a_test
     323          self.assertEqual(a_test.attr, 'in module')
     324  
     325  
     326  class ESC[4;38;5;81mReloadTests(ESC[4;38;5;149mNamespacePackageTest):
     327      paths = ['portion1']
     328  
     329      def test_simple_package(self):
     330          import foo.one
     331          foo = importlib.reload(foo)
     332          self.assertEqual(foo.one.attr, 'portion1 foo one')
     333  
     334      def test_cant_import_other(self):
     335          import foo
     336          with self.assertRaises(ImportError):
     337              import foo.two
     338          foo = importlib.reload(foo)
     339          with self.assertRaises(ImportError):
     340              import foo.two
     341  
     342      def test_dynamic_path(self):
     343          import foo.one
     344          with self.assertRaises(ImportError):
     345              import foo.two
     346  
     347          # Now modify sys.path and reload.
     348          sys.path.append(os.path.join(self.root, 'portion2'))
     349          foo = importlib.reload(foo)
     350  
     351          # And make sure foo.two is now importable
     352          import foo.two
     353          self.assertEqual(foo.two.attr, 'portion2 foo two')
     354  
     355  
     356  class ESC[4;38;5;81mLoaderTests(ESC[4;38;5;149mNamespacePackageTest):
     357      paths = ['portion1']
     358  
     359      def test_namespace_loader_consistency(self):
     360          # bpo-32303
     361          import foo
     362          self.assertEqual(foo.__loader__, foo.__spec__.loader)
     363          self.assertIsNotNone(foo.__loader__)
     364  
     365      def test_namespace_origin_consistency(self):
     366          # bpo-32305
     367          import foo
     368          self.assertIsNone(foo.__spec__.origin)
     369          self.assertIsNone(foo.__file__)
     370  
     371      def test_path_indexable(self):
     372          # bpo-35843
     373          import foo
     374          expected_path = os.path.join(self.root, 'portion1', 'foo')
     375          self.assertEqual(foo.__path__[0], expected_path)
     376  
     377      def test_loader_abc(self):
     378          import foo
     379          self.assertTrue(isinstance(foo.__loader__, importlib.abc.Loader))
     380          self.assertTrue(isinstance(foo.__loader__, importlib.machinery.NamespaceLoader))
     381  
     382  
     383  if __name__ == "__main__":
     384      unittest.main()