text: import contextlib
  import importlib
  import importlib.abc
  import importlib.machinery
  import os
  import sys
  import tempfile
  import unittest
  import warnings
  
  from test.test_importlib import util
  
  # needed tests:
  #
  # need to test when nested, so that the top-level path isn't sys.path
  # need to test dynamic path detection, both at top-level and nested
  # with dynamic path, check when a loader is returned on path reload (that is,
  #  trying to switch from a namespace package to a regular package)
  
  
  @contextlib.contextmanager
  def sys_modules_context():
      """
      Make sure sys.modules is the same object and has the same content
      when exiting the context as when entering.
  
      Similar to importlib.test.util.uncache, but doesn't require explicit
      names.
      """
      sys_modules_saved = sys.modules
      sys_modules_copy = sys.modules.copy()
      try:
          yield
      finally:
          sys.modules = sys_modules_saved
          sys.modules.clear()
          sys.modules.update(sys_modules_copy)
  
  
  @contextlib.contextmanager
  def namespace_tree_context(**kwargs):
      """
      Save import state and sys.modules cache and restore it on exit.
      Typical usage:
  
      >>> with namespace_tree_context(path=['/tmp/xxyy/portion1',
      ...         '/tmp/xxyy/portion2']):
      ...     pass
      """
      # use default meta_path and path_hooks unless specified otherwise
      kwargs.setdefault('meta_path', sys.meta_path)
      kwargs.setdefault('path_hooks', sys.path_hooks)
      import_context = util.import_state(**kwargs)
      with import_context, sys_modules_context():
          yield
  
  class NamespacePackageTest(unittest.TestCase):
      """
      Subclasses should define self.root and self.paths (under that root)
      to be added to sys.path.
      """
      root = os.path.join(os.path.dirname(__file__), 'namespace_pkgs')
  
      def setUp(self):
          self.resolved_paths = [
              os.path.join(self.root, path) for path in self.paths
          ]
          self.ctx = namespace_tree_context(path=self.resolved_paths)
          self.ctx.__enter__()
  
      def tearDown(self):
          # TODO: will we ever want to pass exc_info to __exit__?
          self.ctx.__exit__(None, None, None)
  
  
  class SingleNamespacePackage(NamespacePackageTest):
      paths = ['portion1']
  
      def test_simple_package(self):
          import foo.one
          self.assertEqual(foo.one.attr, 'portion1 foo one')
  
      def test_cant_import_other(self):
          with self.assertRaises(ImportError):
              import foo.two
  
      def test_module_repr(self):
          import foo.one
          with warnings.catch_warnings():
              warnings.simplefilter("ignore")
              self.assertEqual(foo.__spec__.loader.module_repr(foo),
                              "<module 'foo' (namespace)>")
  
  
  class DynamicPathNamespacePackage(NamespacePackageTest):
      paths = ['portion1']
  
      def test_dynamic_path(self):
          # Make sure only 'foo.one' can be imported
          import foo.one
          self.assertEqual(foo.one.attr, 'portion1 foo one')
  
          with self.assertRaises(ImportError):
              import foo.two
  
          # Now modify sys.path
          sys.path.append(os.path.join(self.root, 'portion2'))
  
          # And make sure foo.two is now importable
          import foo.two
          self.assertEqual(foo.two.attr, 'portion2 foo two')
  
  
  class CombinedNamespacePackages(NamespacePackageTest):
      paths = ['both_portions']
  
      def test_imports(self):
          import foo.one
          import foo.two
          self.assertEqual(foo.one.attr, 'both_portions foo one')
          self.assertEqual(foo.two.attr, 'both_portions foo two')
  
  
  class SeparatedNamespacePackages(NamespacePackageTest):
      paths = ['portion1', 'portion2']
  
      def test_imports(self):
          import foo.one
          import foo.two
          self.assertEqual(foo.one.attr, 'portion1 foo one')
          self.assertEqual(foo.two.attr, 'portion2 foo two')
  
  
  class SeparatedNamespacePackagesCreatedWhileRunning(NamespacePackageTest):
      paths = ['portion1']
  
      def test_invalidate_caches(self):
          with tempfile.TemporaryDirectory() as temp_dir:
              # we manipulate sys.path before anything is imported to avoid
              # accidental cache invalidation when changing it
              sys.path.append(temp_dir)
  
              import foo.one
              self.assertEqual(foo.one.attr, 'portion1 foo one')
  
              # the module does not exist, so it cannot be imported
              with self.assertRaises(ImportError):
                  import foo.just_created
  
              # util.create_modules() manipulates sys.path
              # so we must create the modules manually instead
              namespace_path = os.path.join(temp_dir, 'foo')
              os.mkdir(namespace_path)
              module_path = os.path.join(namespace_path, 'just_created.py')
              with open(module_path, 'w', encoding='utf-8') as file:
                  file.write('attr = "just_created foo"')
  
              # the module is not known, so it cannot be imported yet
              with self.assertRaises(ImportError):
                  import foo.just_created
  
              # but after explicit cache invalidation, it is importable
              importlib.invalidate_caches()
              import foo.just_created
              self.assertEqual(foo.just_created.attr, 'just_created foo')
  
  
  class SeparatedOverlappingNamespacePackages(NamespacePackageTest):
      paths = ['portion1', 'both_portions']
  
      def test_first_path_wins(self):
          import foo.one
          import foo.two
          self.assertEqual(foo.one.attr, 'portion1 foo one')
          self.assertEqual(foo.two.attr, 'both_portions foo two')
  
      def test_first_path_wins_again(self):
          sys.path.reverse()
          import foo.one
          import foo.two
          self.assertEqual(foo.one.attr, 'both_portions foo one')
          self.assertEqual(foo.two.attr, 'both_portions foo two')
  
      def test_first_path_wins_importing_second_first(self):
          import foo.two
          import foo.one
          self.assertEqual(foo.one.attr, 'portion1 foo one')
          self.assertEqual(foo.two.attr, 'both_portions foo two')
  
  
  class SingleZipNamespacePackage(NamespacePackageTest):
      paths = ['top_level_portion1.zip']
  
      def test_simple_package(self):
          import foo.one
          self.assertEqual(foo.one.attr, 'portion1 foo one')
  
      def test_cant_import_other(self):
          with self.assertRaises(ImportError):
              import foo.two
  
  
  class SeparatedZipNamespacePackages(NamespacePackageTest):
      paths = ['top_level_portion1.zip', 'portion2']
  
      def test_imports(self):
          import foo.one
          import foo.two
          self.assertEqual(foo.one.attr, 'portion1 foo one')
          self.assertEqual(foo.two.attr, 'portion2 foo two')
          self.assertIn('top_level_portion1.zip', foo.one.__file__)
          self.assertNotIn('.zip', foo.two.__file__)
  
  
  class SingleNestedZipNamespacePackage(NamespacePackageTest):
      paths = ['nested_portion1.zip/nested_portion1']
  
      def test_simple_package(self):
          import foo.one
          self.assertEqual(foo.one.attr, 'portion1 foo one')
  
      def test_cant_import_other(self):
          with self.assertRaises(ImportError):
              import foo.two
  
  
  class SeparatedNestedZipNamespacePackages(NamespacePackageTest):
      paths = ['nested_portion1.zip/nested_portion1', 'portion2']
  
      def test_imports(self):
          import foo.one
          import foo.two
          self.assertEqual(foo.one.attr, 'portion1 foo one')
          self.assertEqual(foo.two.attr, 'portion2 foo two')
          fn = os.path.join('nested_portion1.zip', 'nested_portion1')
          self.assertIn(fn, foo.one.__file__)
          self.assertNotIn('.zip', foo.two.__file__)
  
  
  class LegacySupport(NamespacePackageTest):
      paths = ['not_a_namespace_pkg', 'portion1', 'portion2', 'both_portions']
  
      def test_non_namespace_package_takes_precedence(self):
          import foo.one
          with self.assertRaises(ImportError):
              import foo.two
          self.assertIn('__init__', foo.__file__)
          self.assertNotIn('namespace', str(foo.__loader__).lower())
  
  
  class DynamicPathCalculation(NamespacePackageTest):
      paths = ['project1', 'project2']
  
      def test_project3_fails(self):
          import parent.child.one
          self.assertEqual(len(parent.__path__), 2)
          self.assertEqual(len(parent.child.__path__), 2)
          import parent.child.two
          self.assertEqual(len(parent.__path__), 2)
          self.assertEqual(len(parent.child.__path__), 2)
  
          self.assertEqual(parent.child.one.attr, 'parent child one')
          self.assertEqual(parent.child.two.attr, 'parent child two')
  
          with self.assertRaises(ImportError):
              import parent.child.three
  
          self.assertEqual(len(parent.__path__), 2)
          self.assertEqual(len(parent.child.__path__), 2)
  
      def test_project3_succeeds(self):
          import parent.child.one
          self.assertEqual(len(parent.__path__), 2)
          self.assertEqual(len(parent.child.__path__), 2)
          import parent.child.two
          self.assertEqual(len(parent.__path__), 2)
          self.assertEqual(len(parent.child.__path__), 2)
  
          self.assertEqual(parent.child.one.attr, 'parent child one')
          self.assertEqual(parent.child.two.attr, 'parent child two')
  
          with self.assertRaises(ImportError):
              import parent.child.three
  
          # now add project3
          sys.path.append(os.path.join(self.root, 'project3'))
          import parent.child.three
  
          # the paths dynamically get longer, to include the new directories
          self.assertEqual(len(parent.__path__), 3)
          self.assertEqual(len(parent.child.__path__), 3)
  
          self.assertEqual(parent.child.three.attr, 'parent child three')
  
  
  class ZipWithMissingDirectory(NamespacePackageTest):
      paths = ['missing_directory.zip']
  
      @unittest.expectedFailure
      def test_missing_directory(self):
          # This will fail because missing_directory.zip contains:
          #   Length      Date    Time    Name
          # ---------  ---------- -----   ----
          #        29  2012-05-03 18:13   foo/one.py
          #         0  2012-05-03 20:57   bar/
          #        38  2012-05-03 20:57   bar/two.py
          # ---------                     -------
          #        67                     3 files
  
          # Because there is no 'foo/', the zipimporter currently doesn't
          #  know that foo is a namespace package
  
          import foo.one
  
      def test_present_directory(self):
          # This succeeds because there is a "bar/" in the zip file
          import bar.two
          self.assertEqual(bar.two.attr, 'missing_directory foo two')
  
  
  class ModuleAndNamespacePackageInSameDir(NamespacePackageTest):
      paths = ['module_and_namespace_package']
  
      def test_module_before_namespace_package(self):
          # Make sure we find the module in preference to the
          #  namespace package.
          import a_test
          self.assertEqual(a_test.attr, 'in module')
  
  
  class ReloadTests(NamespacePackageTest):
      paths = ['portion1']
  
      def test_simple_package(self):
          import foo.one
          foo = importlib.reload(foo)
          self.assertEqual(foo.one.attr, 'portion1 foo one')
  
      def test_cant_import_other(self):
          import foo
          with self.assertRaises(ImportError):
              import foo.two
          foo = importlib.reload(foo)
          with self.assertRaises(ImportError):
              import foo.two
  
      def test_dynamic_path(self):
          import foo.one
          with self.assertRaises(ImportError):
              import foo.two
  
          # Now modify sys.path and reload.
          sys.path.append(os.path.join(self.root, 'portion2'))
          foo = importlib.reload(foo)
  
          # And make sure foo.two is now importable
          import foo.two
          self.assertEqual(foo.two.attr, 'portion2 foo two')
  
  
  class LoaderTests(NamespacePackageTest):
      paths = ['portion1']
  
      def test_namespace_loader_consistency(self):
          # bpo-32303
          import foo
          self.assertEqual(foo.__loader__, foo.__spec__.loader)
          self.assertIsNotNone(foo.__loader__)
  
      def test_namespace_origin_consistency(self):
          # bpo-32305
          import foo
          self.assertIsNone(foo.__spec__.origin)
          self.assertIsNone(foo.__file__)
  
      def test_path_indexable(self):
          # bpo-35843
          import foo
          expected_path = os.path.join(self.root, 'portion1', 'foo')
          self.assertEqual(foo.__path__[0], expected_path)
  
      def test_loader_abc(self):
          import foo
          self.assertTrue(isinstance(foo.__loader__, importlib.abc.Loader))
          self.assertTrue(isinstance(foo.__loader__, importlib.machinery.NamespaceLoader))
  
  
  if __name__ == "__main__":
      unittest.main()
  