(root)/
Python-3.11.7/
Lib/
test/
test_importlib/
test_util.py
       1  from test.test_importlib import util
       2  
       3  abc = util.import_importlib('importlib.abc')
       4  init = util.import_importlib('importlib')
       5  machinery = util.import_importlib('importlib.machinery')
       6  importlib_util = util.import_importlib('importlib.util')
       7  
       8  import importlib.util
       9  import os
      10  import pathlib
      11  import string
      12  import sys
      13  from test import support
      14  import types
      15  import unittest
      16  import unittest.mock
      17  import warnings
      18  
      19  
      20  class ESC[4;38;5;81mDecodeSourceBytesTests:
      21  
      22      source = "string ='ΓΌ'"
      23  
      24      def test_ut8_default(self):
      25          source_bytes = self.source.encode('utf-8')
      26          self.assertEqual(self.util.decode_source(source_bytes), self.source)
      27  
      28      def test_specified_encoding(self):
      29          source = '# coding=latin-1\n' + self.source
      30          source_bytes = source.encode('latin-1')
      31          assert source_bytes != source.encode('utf-8')
      32          self.assertEqual(self.util.decode_source(source_bytes), source)
      33  
      34      def test_universal_newlines(self):
      35          source = '\r\n'.join([self.source, self.source])
      36          source_bytes = source.encode('utf-8')
      37          self.assertEqual(self.util.decode_source(source_bytes),
      38                           '\n'.join([self.source, self.source]))
      39  
      40  
      41  (Frozen_DecodeSourceBytesTests,
      42   Source_DecodeSourceBytesTests
      43   ) = util.test_both(DecodeSourceBytesTests, util=importlib_util)
      44  
      45  
      46  class ESC[4;38;5;81mModuleFromSpecTests:
      47  
      48      def test_no_create_module(self):
      49          class ESC[4;38;5;81mLoader:
      50              def exec_module(self, module):
      51                  pass
      52          spec = self.machinery.ModuleSpec('test', Loader())
      53          with self.assertRaises(ImportError):
      54              module = self.util.module_from_spec(spec)
      55  
      56      def test_create_module_returns_None(self):
      57          class ESC[4;38;5;81mLoader(ESC[4;38;5;149mselfESC[4;38;5;149m.ESC[4;38;5;149mabcESC[4;38;5;149m.ESC[4;38;5;149mLoader):
      58              def create_module(self, spec):
      59                  return None
      60          spec = self.machinery.ModuleSpec('test', Loader())
      61          module = self.util.module_from_spec(spec)
      62          self.assertIsInstance(module, types.ModuleType)
      63          self.assertEqual(module.__name__, spec.name)
      64  
      65      def test_create_module(self):
      66          name = 'already set'
      67          class ESC[4;38;5;81mCustomModule(ESC[4;38;5;149mtypesESC[4;38;5;149m.ESC[4;38;5;149mModuleType):
      68              pass
      69          class ESC[4;38;5;81mLoader(ESC[4;38;5;149mselfESC[4;38;5;149m.ESC[4;38;5;149mabcESC[4;38;5;149m.ESC[4;38;5;149mLoader):
      70              def create_module(self, spec):
      71                  module = CustomModule(spec.name)
      72                  module.__name__ = name
      73                  return module
      74          spec = self.machinery.ModuleSpec('test', Loader())
      75          module = self.util.module_from_spec(spec)
      76          self.assertIsInstance(module, CustomModule)
      77          self.assertEqual(module.__name__, name)
      78  
      79      def test___name__(self):
      80          spec = self.machinery.ModuleSpec('test', object())
      81          module = self.util.module_from_spec(spec)
      82          self.assertEqual(module.__name__, spec.name)
      83  
      84      def test___spec__(self):
      85          spec = self.machinery.ModuleSpec('test', object())
      86          module = self.util.module_from_spec(spec)
      87          self.assertEqual(module.__spec__, spec)
      88  
      89      def test___loader__(self):
      90          loader = object()
      91          spec = self.machinery.ModuleSpec('test', loader)
      92          module = self.util.module_from_spec(spec)
      93          self.assertIs(module.__loader__, loader)
      94  
      95      def test___package__(self):
      96          spec = self.machinery.ModuleSpec('test.pkg', object())
      97          module = self.util.module_from_spec(spec)
      98          self.assertEqual(module.__package__, spec.parent)
      99  
     100      def test___path__(self):
     101          spec = self.machinery.ModuleSpec('test', object(), is_package=True)
     102          module = self.util.module_from_spec(spec)
     103          self.assertEqual(module.__path__, spec.submodule_search_locations)
     104  
     105      def test___file__(self):
     106          spec = self.machinery.ModuleSpec('test', object(), origin='some/path')
     107          spec.has_location = True
     108          module = self.util.module_from_spec(spec)
     109          self.assertEqual(module.__file__, spec.origin)
     110  
     111      def test___cached__(self):
     112          spec = self.machinery.ModuleSpec('test', object())
     113          spec.cached = 'some/path'
     114          spec.has_location = True
     115          module = self.util.module_from_spec(spec)
     116          self.assertEqual(module.__cached__, spec.cached)
     117  
     118  (Frozen_ModuleFromSpecTests,
     119   Source_ModuleFromSpecTests
     120  ) = util.test_both(ModuleFromSpecTests, abc=abc, machinery=machinery,
     121                     util=importlib_util)
     122  
     123  
     124  class ESC[4;38;5;81mModuleForLoaderTests:
     125  
     126      """Tests for importlib.util.module_for_loader."""
     127  
     128      @classmethod
     129      def module_for_loader(cls, func):
     130          with warnings.catch_warnings():
     131              warnings.simplefilter('ignore', DeprecationWarning)
     132              return cls.util.module_for_loader(func)
     133  
     134      def test_warning(self):
     135          # Should raise a PendingDeprecationWarning when used.
     136          with warnings.catch_warnings():
     137              warnings.simplefilter('error', DeprecationWarning)
     138              with self.assertRaises(DeprecationWarning):
     139                  func = self.util.module_for_loader(lambda x: x)
     140  
     141      def return_module(self, name):
     142          fxn = self.module_for_loader(lambda self, module: module)
     143          return fxn(self, name)
     144  
     145      def raise_exception(self, name):
     146          def to_wrap(self, module):
     147              raise ImportError
     148          fxn = self.module_for_loader(to_wrap)
     149          try:
     150              fxn(self, name)
     151          except ImportError:
     152              pass
     153  
     154      def test_new_module(self):
     155          # Test that when no module exists in sys.modules a new module is
     156          # created.
     157          module_name = 'a.b.c'
     158          with util.uncache(module_name):
     159              module = self.return_module(module_name)
     160              self.assertIn(module_name, sys.modules)
     161          self.assertIsInstance(module, types.ModuleType)
     162          self.assertEqual(module.__name__, module_name)
     163  
     164      def test_reload(self):
     165          # Test that a module is reused if already in sys.modules.
     166          class ESC[4;38;5;81mFakeLoader:
     167              def is_package(self, name):
     168                  return True
     169              @self.module_for_loader
     170              def load_module(self, module):
     171                  return module
     172          name = 'a.b.c'
     173          module = types.ModuleType('a.b.c')
     174          module.__loader__ = 42
     175          module.__package__ = 42
     176          with util.uncache(name):
     177              sys.modules[name] = module
     178              loader = FakeLoader()
     179              returned_module = loader.load_module(name)
     180              self.assertIs(returned_module, sys.modules[name])
     181              self.assertEqual(module.__loader__, loader)
     182              self.assertEqual(module.__package__, name)
     183  
     184      def test_new_module_failure(self):
     185          # Test that a module is removed from sys.modules if added but an
     186          # exception is raised.
     187          name = 'a.b.c'
     188          with util.uncache(name):
     189              self.raise_exception(name)
     190              self.assertNotIn(name, sys.modules)
     191  
     192      def test_reload_failure(self):
     193          # Test that a failure on reload leaves the module in-place.
     194          name = 'a.b.c'
     195          module = types.ModuleType(name)
     196          with util.uncache(name):
     197              sys.modules[name] = module
     198              self.raise_exception(name)
     199              self.assertIs(module, sys.modules[name])
     200  
     201      def test_decorator_attrs(self):
     202          def fxn(self, module): pass
     203          wrapped = self.module_for_loader(fxn)
     204          self.assertEqual(wrapped.__name__, fxn.__name__)
     205          self.assertEqual(wrapped.__qualname__, fxn.__qualname__)
     206  
     207      def test_false_module(self):
     208          # If for some odd reason a module is considered false, still return it
     209          # from sys.modules.
     210          class ESC[4;38;5;81mFalseModule(ESC[4;38;5;149mtypesESC[4;38;5;149m.ESC[4;38;5;149mModuleType):
     211              def __bool__(self): return False
     212  
     213          name = 'mod'
     214          module = FalseModule(name)
     215          with util.uncache(name):
     216              self.assertFalse(module)
     217              sys.modules[name] = module
     218              given = self.return_module(name)
     219              self.assertIs(given, module)
     220  
     221      def test_attributes_set(self):
     222          # __name__, __loader__, and __package__ should be set (when
     223          # is_package() is defined; undefined implicitly tested elsewhere).
     224          class ESC[4;38;5;81mFakeLoader:
     225              def __init__(self, is_package):
     226                  self._pkg = is_package
     227              def is_package(self, name):
     228                  return self._pkg
     229              @self.module_for_loader
     230              def load_module(self, module):
     231                  return module
     232  
     233          name = 'pkg.mod'
     234          with util.uncache(name):
     235              loader = FakeLoader(False)
     236              module = loader.load_module(name)
     237              self.assertEqual(module.__name__, name)
     238              self.assertIs(module.__loader__, loader)
     239              self.assertEqual(module.__package__, 'pkg')
     240  
     241          name = 'pkg.sub'
     242          with util.uncache(name):
     243              loader = FakeLoader(True)
     244              module = loader.load_module(name)
     245              self.assertEqual(module.__name__, name)
     246              self.assertIs(module.__loader__, loader)
     247              self.assertEqual(module.__package__, name)
     248  
     249  
     250  (Frozen_ModuleForLoaderTests,
     251   Source_ModuleForLoaderTests
     252   ) = util.test_both(ModuleForLoaderTests, util=importlib_util)
     253  
     254  
     255  class ESC[4;38;5;81mSetPackageTests:
     256  
     257      """Tests for importlib.util.set_package."""
     258  
     259      def verify(self, module, expect):
     260          """Verify the module has the expected value for __package__ after
     261          passing through set_package."""
     262          fxn = lambda: module
     263          wrapped = self.util.set_package(fxn)
     264          with warnings.catch_warnings():
     265              warnings.simplefilter('ignore', DeprecationWarning)
     266              wrapped()
     267          self.assertTrue(hasattr(module, '__package__'))
     268          self.assertEqual(expect, module.__package__)
     269  
     270      def test_top_level(self):
     271          # __package__ should be set to the empty string if a top-level module.
     272          # Implicitly tests when package is set to None.
     273          module = types.ModuleType('module')
     274          module.__package__ = None
     275          self.verify(module, '')
     276  
     277      def test_package(self):
     278          # Test setting __package__ for a package.
     279          module = types.ModuleType('pkg')
     280          module.__path__ = ['<path>']
     281          module.__package__ = None
     282          self.verify(module, 'pkg')
     283  
     284      def test_submodule(self):
     285          # Test __package__ for a module in a package.
     286          module = types.ModuleType('pkg.mod')
     287          module.__package__ = None
     288          self.verify(module, 'pkg')
     289  
     290      def test_setting_if_missing(self):
     291          # __package__ should be set if it is missing.
     292          module = types.ModuleType('mod')
     293          if hasattr(module, '__package__'):
     294              delattr(module, '__package__')
     295          self.verify(module, '')
     296  
     297      def test_leaving_alone(self):
     298          # If __package__ is set and not None then leave it alone.
     299          for value in (True, False):
     300              module = types.ModuleType('mod')
     301              module.__package__ = value
     302              self.verify(module, value)
     303  
     304      def test_decorator_attrs(self):
     305          def fxn(module): pass
     306          with warnings.catch_warnings():
     307              warnings.simplefilter('ignore', DeprecationWarning)
     308              wrapped = self.util.set_package(fxn)
     309          self.assertEqual(wrapped.__name__, fxn.__name__)
     310          self.assertEqual(wrapped.__qualname__, fxn.__qualname__)
     311  
     312  
     313  (Frozen_SetPackageTests,
     314   Source_SetPackageTests
     315   ) = util.test_both(SetPackageTests, util=importlib_util)
     316  
     317  
     318  class ESC[4;38;5;81mSetLoaderTests:
     319  
     320      """Tests importlib.util.set_loader()."""
     321  
     322      @property
     323      def DummyLoader(self):
     324          # Set DummyLoader on the class lazily.
     325          class ESC[4;38;5;81mDummyLoader:
     326              @self.util.set_loader
     327              def load_module(self, module):
     328                  return self.module
     329          self.__class__.DummyLoader = DummyLoader
     330          return DummyLoader
     331  
     332      def test_no_attribute(self):
     333          loader = self.DummyLoader()
     334          loader.module = types.ModuleType('blah')
     335          try:
     336              del loader.module.__loader__
     337          except AttributeError:
     338              pass
     339          with warnings.catch_warnings():
     340              warnings.simplefilter('ignore', DeprecationWarning)
     341              self.assertEqual(loader, loader.load_module('blah').__loader__)
     342  
     343      def test_attribute_is_None(self):
     344          loader = self.DummyLoader()
     345          loader.module = types.ModuleType('blah')
     346          loader.module.__loader__ = None
     347          with warnings.catch_warnings():
     348              warnings.simplefilter('ignore', DeprecationWarning)
     349              self.assertEqual(loader, loader.load_module('blah').__loader__)
     350  
     351      def test_not_reset(self):
     352          loader = self.DummyLoader()
     353          loader.module = types.ModuleType('blah')
     354          loader.module.__loader__ = 42
     355          with warnings.catch_warnings():
     356              warnings.simplefilter('ignore', DeprecationWarning)
     357              self.assertEqual(42, loader.load_module('blah').__loader__)
     358  
     359  
     360  (Frozen_SetLoaderTests,
     361   Source_SetLoaderTests
     362   ) = util.test_both(SetLoaderTests, util=importlib_util)
     363  
     364  
     365  class ESC[4;38;5;81mResolveNameTests:
     366  
     367      """Tests importlib.util.resolve_name()."""
     368  
     369      def test_absolute(self):
     370          # bacon
     371          self.assertEqual('bacon', self.util.resolve_name('bacon', None))
     372  
     373      def test_absolute_within_package(self):
     374          # bacon in spam
     375          self.assertEqual('bacon', self.util.resolve_name('bacon', 'spam'))
     376  
     377      def test_no_package(self):
     378          # .bacon in ''
     379          with self.assertRaises(ImportError):
     380              self.util.resolve_name('.bacon', '')
     381  
     382      def test_in_package(self):
     383          # .bacon in spam
     384          self.assertEqual('spam.eggs.bacon',
     385                           self.util.resolve_name('.bacon', 'spam.eggs'))
     386  
     387      def test_other_package(self):
     388          # ..bacon in spam.bacon
     389          self.assertEqual('spam.bacon',
     390                           self.util.resolve_name('..bacon', 'spam.eggs'))
     391  
     392      def test_escape(self):
     393          # ..bacon in spam
     394          with self.assertRaises(ImportError):
     395              self.util.resolve_name('..bacon', 'spam')
     396  
     397  
     398  (Frozen_ResolveNameTests,
     399   Source_ResolveNameTests
     400   ) = util.test_both(ResolveNameTests, util=importlib_util)
     401  
     402  
     403  class ESC[4;38;5;81mFindSpecTests:
     404  
     405      class ESC[4;38;5;81mFakeMetaFinder:
     406          @staticmethod
     407          def find_spec(name, path=None, target=None): return name, path, target
     408  
     409      def test_sys_modules(self):
     410          name = 'some_mod'
     411          with util.uncache(name):
     412              module = types.ModuleType(name)
     413              loader = 'a loader!'
     414              spec = self.machinery.ModuleSpec(name, loader)
     415              module.__loader__ = loader
     416              module.__spec__ = spec
     417              sys.modules[name] = module
     418              found = self.util.find_spec(name)
     419              self.assertEqual(found, spec)
     420  
     421      def test_sys_modules_without___loader__(self):
     422          name = 'some_mod'
     423          with util.uncache(name):
     424              module = types.ModuleType(name)
     425              del module.__loader__
     426              loader = 'a loader!'
     427              spec = self.machinery.ModuleSpec(name, loader)
     428              module.__spec__ = spec
     429              sys.modules[name] = module
     430              found = self.util.find_spec(name)
     431              self.assertEqual(found, spec)
     432  
     433      def test_sys_modules_spec_is_None(self):
     434          name = 'some_mod'
     435          with util.uncache(name):
     436              module = types.ModuleType(name)
     437              module.__spec__ = None
     438              sys.modules[name] = module
     439              with self.assertRaises(ValueError):
     440                  self.util.find_spec(name)
     441  
     442      def test_sys_modules_loader_is_None(self):
     443          name = 'some_mod'
     444          with util.uncache(name):
     445              module = types.ModuleType(name)
     446              spec = self.machinery.ModuleSpec(name, None)
     447              module.__spec__ = spec
     448              sys.modules[name] = module
     449              found = self.util.find_spec(name)
     450              self.assertEqual(found, spec)
     451  
     452      def test_sys_modules_spec_is_not_set(self):
     453          name = 'some_mod'
     454          with util.uncache(name):
     455              module = types.ModuleType(name)
     456              try:
     457                  del module.__spec__
     458              except AttributeError:
     459                  pass
     460              sys.modules[name] = module
     461              with self.assertRaises(ValueError):
     462                  self.util.find_spec(name)
     463  
     464      def test_success(self):
     465          name = 'some_mod'
     466          with util.uncache(name):
     467              with util.import_state(meta_path=[self.FakeMetaFinder]):
     468                  self.assertEqual((name, None, None),
     469                                   self.util.find_spec(name))
     470  
     471      def test_nothing(self):
     472          # None is returned upon failure to find a loader.
     473          self.assertIsNone(self.util.find_spec('nevergoingtofindthismodule'))
     474  
     475      def test_find_submodule(self):
     476          name = 'spam'
     477          subname = 'ham'
     478          with util.temp_module(name, pkg=True) as pkg_dir:
     479              fullname, _ = util.submodule(name, subname, pkg_dir)
     480              spec = self.util.find_spec(fullname)
     481              self.assertIsNot(spec, None)
     482              self.assertIn(name, sorted(sys.modules))
     483              self.assertNotIn(fullname, sorted(sys.modules))
     484              # Ensure successive calls behave the same.
     485              spec_again = self.util.find_spec(fullname)
     486              self.assertEqual(spec_again, spec)
     487  
     488      def test_find_submodule_parent_already_imported(self):
     489          name = 'spam'
     490          subname = 'ham'
     491          with util.temp_module(name, pkg=True) as pkg_dir:
     492              self.init.import_module(name)
     493              fullname, _ = util.submodule(name, subname, pkg_dir)
     494              spec = self.util.find_spec(fullname)
     495              self.assertIsNot(spec, None)
     496              self.assertIn(name, sorted(sys.modules))
     497              self.assertNotIn(fullname, sorted(sys.modules))
     498              # Ensure successive calls behave the same.
     499              spec_again = self.util.find_spec(fullname)
     500              self.assertEqual(spec_again, spec)
     501  
     502      def test_find_relative_module(self):
     503          name = 'spam'
     504          subname = 'ham'
     505          with util.temp_module(name, pkg=True) as pkg_dir:
     506              fullname, _ = util.submodule(name, subname, pkg_dir)
     507              relname = '.' + subname
     508              spec = self.util.find_spec(relname, name)
     509              self.assertIsNot(spec, None)
     510              self.assertIn(name, sorted(sys.modules))
     511              self.assertNotIn(fullname, sorted(sys.modules))
     512              # Ensure successive calls behave the same.
     513              spec_again = self.util.find_spec(fullname)
     514              self.assertEqual(spec_again, spec)
     515  
     516      def test_find_relative_module_missing_package(self):
     517          name = 'spam'
     518          subname = 'ham'
     519          with util.temp_module(name, pkg=True) as pkg_dir:
     520              fullname, _ = util.submodule(name, subname, pkg_dir)
     521              relname = '.' + subname
     522              with self.assertRaises(ImportError):
     523                  self.util.find_spec(relname)
     524              self.assertNotIn(name, sorted(sys.modules))
     525              self.assertNotIn(fullname, sorted(sys.modules))
     526  
     527      def test_find_submodule_in_module(self):
     528          # ModuleNotFoundError raised when a module is specified as
     529          # a parent instead of a package.
     530          with self.assertRaises(ModuleNotFoundError):
     531              self.util.find_spec('module.name')
     532  
     533  
     534  (Frozen_FindSpecTests,
     535   Source_FindSpecTests
     536   ) = util.test_both(FindSpecTests, init=init, util=importlib_util,
     537                           machinery=machinery)
     538  
     539  
     540  class ESC[4;38;5;81mMagicNumberTests:
     541  
     542      def test_length(self):
     543          # Should be 4 bytes.
     544          self.assertEqual(len(self.util.MAGIC_NUMBER), 4)
     545  
     546      def test_incorporates_rn(self):
     547          # The magic number uses \r\n to come out wrong when splitting on lines.
     548          self.assertTrue(self.util.MAGIC_NUMBER.endswith(b'\r\n'))
     549  
     550  
     551  (Frozen_MagicNumberTests,
     552   Source_MagicNumberTests
     553   ) = util.test_both(MagicNumberTests, util=importlib_util)
     554  
     555  
     556  class ESC[4;38;5;81mPEP3147Tests:
     557  
     558      """Tests of PEP 3147-related functions: cache_from_source and source_from_cache."""
     559  
     560      tag = sys.implementation.cache_tag
     561  
     562      @unittest.skipIf(sys.implementation.cache_tag is None,
     563                       'requires sys.implementation.cache_tag not be None')
     564      def test_cache_from_source(self):
     565          # Given the path to a .py file, return the path to its PEP 3147
     566          # defined .pyc file (i.e. under __pycache__).
     567          path = os.path.join('foo', 'bar', 'baz', 'qux.py')
     568          expect = os.path.join('foo', 'bar', 'baz', '__pycache__',
     569                                'qux.{}.pyc'.format(self.tag))
     570          self.assertEqual(self.util.cache_from_source(path, optimization=''),
     571                           expect)
     572  
     573      def test_cache_from_source_no_cache_tag(self):
     574          # No cache tag means NotImplementedError.
     575          with support.swap_attr(sys.implementation, 'cache_tag', None):
     576              with self.assertRaises(NotImplementedError):
     577                  self.util.cache_from_source('whatever.py')
     578  
     579      def test_cache_from_source_no_dot(self):
     580          # Directory with a dot, filename without dot.
     581          path = os.path.join('foo.bar', 'file')
     582          expect = os.path.join('foo.bar', '__pycache__',
     583                                'file{}.pyc'.format(self.tag))
     584          self.assertEqual(self.util.cache_from_source(path, optimization=''),
     585                           expect)
     586  
     587      def test_cache_from_source_debug_override(self):
     588          # Given the path to a .py file, return the path to its PEP 3147/PEP 488
     589          # defined .pyc file (i.e. under __pycache__).
     590          path = os.path.join('foo', 'bar', 'baz', 'qux.py')
     591          with warnings.catch_warnings():
     592              warnings.simplefilter('ignore')
     593              self.assertEqual(self.util.cache_from_source(path, False),
     594                               self.util.cache_from_source(path, optimization=1))
     595              self.assertEqual(self.util.cache_from_source(path, True),
     596                               self.util.cache_from_source(path, optimization=''))
     597          with warnings.catch_warnings():
     598              warnings.simplefilter('error')
     599              with self.assertRaises(DeprecationWarning):
     600                  self.util.cache_from_source(path, False)
     601              with self.assertRaises(DeprecationWarning):
     602                  self.util.cache_from_source(path, True)
     603  
     604      def test_cache_from_source_cwd(self):
     605          path = 'foo.py'
     606          expect = os.path.join('__pycache__', 'foo.{}.pyc'.format(self.tag))
     607          self.assertEqual(self.util.cache_from_source(path, optimization=''),
     608                           expect)
     609  
     610      def test_cache_from_source_override(self):
     611          # When debug_override is not None, it can be any true-ish or false-ish
     612          # value.
     613          path = os.path.join('foo', 'bar', 'baz.py')
     614          # However if the bool-ishness can't be determined, the exception
     615          # propagates.
     616          class ESC[4;38;5;81mBearish:
     617              def __bool__(self): raise RuntimeError
     618          with warnings.catch_warnings():
     619              warnings.simplefilter('ignore')
     620              self.assertEqual(self.util.cache_from_source(path, []),
     621                               self.util.cache_from_source(path, optimization=1))
     622              self.assertEqual(self.util.cache_from_source(path, [17]),
     623                               self.util.cache_from_source(path, optimization=''))
     624              with self.assertRaises(RuntimeError):
     625                  self.util.cache_from_source('/foo/bar/baz.py', Bearish())
     626  
     627  
     628      def test_cache_from_source_optimization_empty_string(self):
     629          # Setting 'optimization' to '' leads to no optimization tag (PEP 488).
     630          path = 'foo.py'
     631          expect = os.path.join('__pycache__', 'foo.{}.pyc'.format(self.tag))
     632          self.assertEqual(self.util.cache_from_source(path, optimization=''),
     633                           expect)
     634  
     635      def test_cache_from_source_optimization_None(self):
     636          # Setting 'optimization' to None uses the interpreter's optimization.
     637          # (PEP 488)
     638          path = 'foo.py'
     639          optimization_level = sys.flags.optimize
     640          almost_expect = os.path.join('__pycache__', 'foo.{}'.format(self.tag))
     641          if optimization_level == 0:
     642              expect = almost_expect + '.pyc'
     643          elif optimization_level <= 2:
     644              expect = almost_expect + '.opt-{}.pyc'.format(optimization_level)
     645          else:
     646              msg = '{!r} is a non-standard optimization level'.format(optimization_level)
     647              self.skipTest(msg)
     648          self.assertEqual(self.util.cache_from_source(path, optimization=None),
     649                           expect)
     650  
     651      def test_cache_from_source_optimization_set(self):
     652          # The 'optimization' parameter accepts anything that has a string repr
     653          # that passes str.alnum().
     654          path = 'foo.py'
     655          valid_characters = string.ascii_letters + string.digits
     656          almost_expect = os.path.join('__pycache__', 'foo.{}'.format(self.tag))
     657          got = self.util.cache_from_source(path, optimization=valid_characters)
     658          # Test all valid characters are accepted.
     659          self.assertEqual(got,
     660                           almost_expect + '.opt-{}.pyc'.format(valid_characters))
     661          # str() should be called on argument.
     662          self.assertEqual(self.util.cache_from_source(path, optimization=42),
     663                           almost_expect + '.opt-42.pyc')
     664          # Invalid characters raise ValueError.
     665          with self.assertRaises(ValueError):
     666              self.util.cache_from_source(path, optimization='path/is/bad')
     667  
     668      def test_cache_from_source_debug_override_optimization_both_set(self):
     669          # Can only set one of the optimization-related parameters.
     670          with warnings.catch_warnings():
     671              warnings.simplefilter('ignore')
     672              with self.assertRaises(TypeError):
     673                  self.util.cache_from_source('foo.py', False, optimization='')
     674  
     675      @unittest.skipUnless(os.sep == '\\' and os.altsep == '/',
     676                       'test meaningful only where os.altsep is defined')
     677      def test_sep_altsep_and_sep_cache_from_source(self):
     678          # Windows path and PEP 3147 where sep is right of altsep.
     679          self.assertEqual(
     680              self.util.cache_from_source('\\foo\\bar\\baz/qux.py', optimization=''),
     681              '\\foo\\bar\\baz\\__pycache__\\qux.{}.pyc'.format(self.tag))
     682  
     683      @unittest.skipIf(sys.implementation.cache_tag is None,
     684                       'requires sys.implementation.cache_tag not be None')
     685      def test_cache_from_source_path_like_arg(self):
     686          path = pathlib.PurePath('foo', 'bar', 'baz', 'qux.py')
     687          expect = os.path.join('foo', 'bar', 'baz', '__pycache__',
     688                                'qux.{}.pyc'.format(self.tag))
     689          self.assertEqual(self.util.cache_from_source(path, optimization=''),
     690                           expect)
     691  
     692      @unittest.skipIf(sys.implementation.cache_tag is None,
     693                       'requires sys.implementation.cache_tag to not be None')
     694      def test_source_from_cache(self):
     695          # Given the path to a PEP 3147 defined .pyc file, return the path to
     696          # its source.  This tests the good path.
     697          path = os.path.join('foo', 'bar', 'baz', '__pycache__',
     698                              'qux.{}.pyc'.format(self.tag))
     699          expect = os.path.join('foo', 'bar', 'baz', 'qux.py')
     700          self.assertEqual(self.util.source_from_cache(path), expect)
     701  
     702      def test_source_from_cache_no_cache_tag(self):
     703          # If sys.implementation.cache_tag is None, raise NotImplementedError.
     704          path = os.path.join('blah', '__pycache__', 'whatever.pyc')
     705          with support.swap_attr(sys.implementation, 'cache_tag', None):
     706              with self.assertRaises(NotImplementedError):
     707                  self.util.source_from_cache(path)
     708  
     709      def test_source_from_cache_bad_path(self):
     710          # When the path to a pyc file is not in PEP 3147 format, a ValueError
     711          # is raised.
     712          self.assertRaises(
     713              ValueError, self.util.source_from_cache, '/foo/bar/bazqux.pyc')
     714  
     715      def test_source_from_cache_no_slash(self):
     716          # No slashes at all in path -> ValueError
     717          self.assertRaises(
     718              ValueError, self.util.source_from_cache, 'foo.cpython-32.pyc')
     719  
     720      def test_source_from_cache_too_few_dots(self):
     721          # Too few dots in final path component -> ValueError
     722          self.assertRaises(
     723              ValueError, self.util.source_from_cache, '__pycache__/foo.pyc')
     724  
     725      def test_source_from_cache_too_many_dots(self):
     726          with self.assertRaises(ValueError):
     727              self.util.source_from_cache(
     728                      '__pycache__/foo.cpython-32.opt-1.foo.pyc')
     729  
     730      def test_source_from_cache_not_opt(self):
     731          # Non-`opt-` path component -> ValueError
     732          self.assertRaises(
     733              ValueError, self.util.source_from_cache,
     734              '__pycache__/foo.cpython-32.foo.pyc')
     735  
     736      def test_source_from_cache_no__pycache__(self):
     737          # Another problem with the path -> ValueError
     738          self.assertRaises(
     739              ValueError, self.util.source_from_cache,
     740              '/foo/bar/foo.cpython-32.foo.pyc')
     741  
     742      def test_source_from_cache_optimized_bytecode(self):
     743          # Optimized bytecode is not an issue.
     744          path = os.path.join('__pycache__', 'foo.{}.opt-1.pyc'.format(self.tag))
     745          self.assertEqual(self.util.source_from_cache(path), 'foo.py')
     746  
     747      def test_source_from_cache_missing_optimization(self):
     748          # An empty optimization level is a no-no.
     749          path = os.path.join('__pycache__', 'foo.{}.opt-.pyc'.format(self.tag))
     750          with self.assertRaises(ValueError):
     751              self.util.source_from_cache(path)
     752  
     753      @unittest.skipIf(sys.implementation.cache_tag is None,
     754                       'requires sys.implementation.cache_tag to not be None')
     755      def test_source_from_cache_path_like_arg(self):
     756          path = pathlib.PurePath('foo', 'bar', 'baz', '__pycache__',
     757                                  'qux.{}.pyc'.format(self.tag))
     758          expect = os.path.join('foo', 'bar', 'baz', 'qux.py')
     759          self.assertEqual(self.util.source_from_cache(path), expect)
     760  
     761      @unittest.skipIf(sys.implementation.cache_tag is None,
     762                       'requires sys.implementation.cache_tag to not be None')
     763      def test_cache_from_source_respects_pycache_prefix(self):
     764          # If pycache_prefix is set, cache_from_source will return a bytecode
     765          # path inside that directory (in a subdirectory mirroring the .py file's
     766          # path) rather than in a __pycache__ dir next to the py file.
     767          pycache_prefixes = [
     768              os.path.join(os.path.sep, 'tmp', 'bytecode'),
     769              os.path.join(os.path.sep, 'tmp', '\u2603'),  # non-ASCII in path!
     770              os.path.join(os.path.sep, 'tmp', 'trailing-slash') + os.path.sep,
     771          ]
     772          drive = ''
     773          if os.name == 'nt':
     774              drive = 'C:'
     775              pycache_prefixes = [
     776                  f'{drive}{prefix}' for prefix in pycache_prefixes]
     777              pycache_prefixes += [r'\\?\C:\foo', r'\\localhost\c$\bar']
     778          for pycache_prefix in pycache_prefixes:
     779              with self.subTest(path=pycache_prefix):
     780                  path = drive + os.path.join(
     781                      os.path.sep, 'foo', 'bar', 'baz', 'qux.py')
     782                  expect = os.path.join(
     783                      pycache_prefix, 'foo', 'bar', 'baz',
     784                      'qux.{}.pyc'.format(self.tag))
     785                  with util.temporary_pycache_prefix(pycache_prefix):
     786                      self.assertEqual(
     787                          self.util.cache_from_source(path, optimization=''),
     788                          expect)
     789  
     790      @unittest.skipIf(sys.implementation.cache_tag is None,
     791                       'requires sys.implementation.cache_tag to not be None')
     792      def test_cache_from_source_respects_pycache_prefix_relative(self):
     793          # If the .py path we are given is relative, we will resolve to an
     794          # absolute path before prefixing with pycache_prefix, to avoid any
     795          # possible ambiguity.
     796          pycache_prefix = os.path.join(os.path.sep, 'tmp', 'bytecode')
     797          path = os.path.join('foo', 'bar', 'baz', 'qux.py')
     798          root = os.path.splitdrive(os.getcwd())[0] + os.path.sep
     799          expect = os.path.join(
     800              pycache_prefix,
     801              os.path.relpath(os.getcwd(), root),
     802              'foo', 'bar', 'baz', f'qux.{self.tag}.pyc')
     803          with util.temporary_pycache_prefix(pycache_prefix):
     804              self.assertEqual(
     805                  self.util.cache_from_source(path, optimization=''),
     806                  expect)
     807  
     808      @unittest.skipIf(sys.implementation.cache_tag is None,
     809                       'requires sys.implementation.cache_tag to not be None')
     810      def test_source_from_cache_inside_pycache_prefix(self):
     811          # If pycache_prefix is set and the cache path we get is inside it,
     812          # we return an absolute path to the py file based on the remainder of
     813          # the path within pycache_prefix.
     814          pycache_prefix = os.path.join(os.path.sep, 'tmp', 'bytecode')
     815          path = os.path.join(pycache_prefix, 'foo', 'bar', 'baz',
     816                              f'qux.{self.tag}.pyc')
     817          expect = os.path.join(os.path.sep, 'foo', 'bar', 'baz', 'qux.py')
     818          with util.temporary_pycache_prefix(pycache_prefix):
     819              self.assertEqual(self.util.source_from_cache(path), expect)
     820  
     821      @unittest.skipIf(sys.implementation.cache_tag is None,
     822                       'requires sys.implementation.cache_tag to not be None')
     823      def test_source_from_cache_outside_pycache_prefix(self):
     824          # If pycache_prefix is set but the cache path we get is not inside
     825          # it, just ignore it and handle the cache path according to the default
     826          # behavior.
     827          pycache_prefix = os.path.join(os.path.sep, 'tmp', 'bytecode')
     828          path = os.path.join('foo', 'bar', 'baz', '__pycache__',
     829                              f'qux.{self.tag}.pyc')
     830          expect = os.path.join('foo', 'bar', 'baz', 'qux.py')
     831          with util.temporary_pycache_prefix(pycache_prefix):
     832              self.assertEqual(self.util.source_from_cache(path), expect)
     833  
     834  
     835  (Frozen_PEP3147Tests,
     836   Source_PEP3147Tests
     837   ) = util.test_both(PEP3147Tests, util=importlib_util)
     838  
     839  
     840  class ESC[4;38;5;81mMagicNumberTests(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
     841      """
     842      Test release compatibility issues relating to importlib
     843      """
     844      @unittest.skipUnless(
     845          sys.version_info.releaselevel in ('candidate', 'final'),
     846          'only applies to candidate or final python release levels'
     847      )
     848      def test_magic_number(self):
     849          # Each python minor release should generally have a MAGIC_NUMBER
     850          # that does not change once the release reaches candidate status.
     851  
     852          # Once a release reaches candidate status, the value of the constant
     853          # EXPECTED_MAGIC_NUMBER in this test should be changed.
     854          # This test will then check that the actual MAGIC_NUMBER matches
     855          # the expected value for the release.
     856  
     857          # In exceptional cases, it may be required to change the MAGIC_NUMBER
     858          # for a maintenance release. In this case the change should be
     859          # discussed in python-dev. If a change is required, community
     860          # stakeholders such as OS package maintainers must be notified
     861          # in advance. Such exceptional releases will then require an
     862          # adjustment to this test case.
     863          EXPECTED_MAGIC_NUMBER = 3495
     864          actual = int.from_bytes(importlib.util.MAGIC_NUMBER[:2], 'little')
     865  
     866          msg = (
     867              "To avoid breaking backwards compatibility with cached bytecode "
     868              "files that can't be automatically regenerated by the current "
     869              "user, candidate and final releases require the current  "
     870              "importlib.util.MAGIC_NUMBER to match the expected "
     871              "magic number in this test. Set the expected "
     872              "magic number in this test to the current MAGIC_NUMBER to "
     873              "continue with the release.\n\n"
     874              "Changing the MAGIC_NUMBER for a maintenance release "
     875              "requires discussion in python-dev and notification of "
     876              "community stakeholders."
     877          )
     878          self.assertEqual(EXPECTED_MAGIC_NUMBER, actual, msg)
     879  
     880  
     881  if __name__ == '__main__':
     882      unittest.main()