(root)/
Python-3.11.7/
Lib/
test/
test_py_compile.py
       1  import functools
       2  import importlib.util
       3  import os
       4  import py_compile
       5  import shutil
       6  import stat
       7  import subprocess
       8  import sys
       9  import tempfile
      10  import unittest
      11  
      12  from test import support
      13  from test.support import os_helper, script_helper
      14  
      15  
      16  def without_source_date_epoch(fxn):
      17      """Runs function with SOURCE_DATE_EPOCH unset."""
      18      @functools.wraps(fxn)
      19      def wrapper(*args, **kwargs):
      20          with os_helper.EnvironmentVarGuard() as env:
      21              env.unset('SOURCE_DATE_EPOCH')
      22              return fxn(*args, **kwargs)
      23      return wrapper
      24  
      25  
      26  def with_source_date_epoch(fxn):
      27      """Runs function with SOURCE_DATE_EPOCH set."""
      28      @functools.wraps(fxn)
      29      def wrapper(*args, **kwargs):
      30          with os_helper.EnvironmentVarGuard() as env:
      31              env['SOURCE_DATE_EPOCH'] = '123456789'
      32              return fxn(*args, **kwargs)
      33      return wrapper
      34  
      35  
      36  # Run tests with SOURCE_DATE_EPOCH set or unset explicitly.
      37  class ESC[4;38;5;81mSourceDateEpochTestMeta(ESC[4;38;5;149mtype(unittest.TestCase)):
      38      def __new__(mcls, name, bases, dct, *, source_date_epoch):
      39          cls = super().__new__(mcls, name, bases, dct)
      40  
      41          for attr in dir(cls):
      42              if attr.startswith('test_'):
      43                  meth = getattr(cls, attr)
      44                  if source_date_epoch:
      45                      wrapper = with_source_date_epoch(meth)
      46                  else:
      47                      wrapper = without_source_date_epoch(meth)
      48                  setattr(cls, attr, wrapper)
      49  
      50          return cls
      51  
      52  
      53  class ESC[4;38;5;81mPyCompileTestsBase:
      54  
      55      def setUp(self):
      56          self.directory = tempfile.mkdtemp(dir=os.getcwd())
      57          self.source_path = os.path.join(self.directory, '_test.py')
      58          self.pyc_path = self.source_path + 'c'
      59          self.cache_path = importlib.util.cache_from_source(self.source_path)
      60          self.cwd_drive = os.path.splitdrive(os.getcwd())[0]
      61          # In these tests we compute relative paths.  When using Windows, the
      62          # current working directory path and the 'self.source_path' might be
      63          # on different drives.  Therefore we need to switch to the drive where
      64          # the temporary source file lives.
      65          drive = os.path.splitdrive(self.source_path)[0]
      66          if drive:
      67              os.chdir(drive)
      68          with open(self.source_path, 'w') as file:
      69              file.write('x = 123\n')
      70  
      71      def tearDown(self):
      72          shutil.rmtree(self.directory)
      73          if self.cwd_drive:
      74              os.chdir(self.cwd_drive)
      75  
      76      def test_absolute_path(self):
      77          py_compile.compile(self.source_path, self.pyc_path)
      78          self.assertTrue(os.path.exists(self.pyc_path))
      79          self.assertFalse(os.path.exists(self.cache_path))
      80  
      81      def test_do_not_overwrite_symlinks(self):
      82          # In the face of a cfile argument being a symlink, bail out.
      83          # Issue #17222
      84          try:
      85              os.symlink(self.pyc_path + '.actual', self.pyc_path)
      86          except (NotImplementedError, OSError):
      87              self.skipTest('need to be able to create a symlink for a file')
      88          else:
      89              assert os.path.islink(self.pyc_path)
      90              with self.assertRaises(FileExistsError):
      91                  py_compile.compile(self.source_path, self.pyc_path)
      92  
      93      @unittest.skipIf(not os.path.exists(os.devnull) or os.path.isfile(os.devnull),
      94                       'requires os.devnull and for it to be a non-regular file')
      95      def test_do_not_overwrite_nonregular_files(self):
      96          # In the face of a cfile argument being a non-regular file, bail out.
      97          # Issue #17222
      98          with self.assertRaises(FileExistsError):
      99              py_compile.compile(self.source_path, os.devnull)
     100  
     101      def test_cache_path(self):
     102          py_compile.compile(self.source_path)
     103          self.assertTrue(os.path.exists(self.cache_path))
     104  
     105      def test_cwd(self):
     106          with os_helper.change_cwd(self.directory):
     107              py_compile.compile(os.path.basename(self.source_path),
     108                                 os.path.basename(self.pyc_path))
     109          self.assertTrue(os.path.exists(self.pyc_path))
     110          self.assertFalse(os.path.exists(self.cache_path))
     111  
     112      def test_relative_path(self):
     113          py_compile.compile(os.path.relpath(self.source_path),
     114                             os.path.relpath(self.pyc_path))
     115          self.assertTrue(os.path.exists(self.pyc_path))
     116          self.assertFalse(os.path.exists(self.cache_path))
     117  
     118      @os_helper.skip_if_dac_override
     119      @unittest.skipIf(os.name == 'nt',
     120                       'cannot control directory permissions on Windows')
     121      @os_helper.skip_unless_working_chmod
     122      def test_exceptions_propagate(self):
     123          # Make sure that exceptions raised thanks to issues with writing
     124          # bytecode.
     125          # http://bugs.python.org/issue17244
     126          mode = os.stat(self.directory)
     127          os.chmod(self.directory, stat.S_IREAD)
     128          try:
     129              with self.assertRaises(IOError):
     130                  py_compile.compile(self.source_path, self.pyc_path)
     131          finally:
     132              os.chmod(self.directory, mode.st_mode)
     133  
     134      def test_bad_coding(self):
     135          bad_coding = os.path.join(os.path.dirname(__file__),
     136                                    'tokenizedata',
     137                                    'bad_coding2.py')
     138          with support.captured_stderr():
     139              self.assertIsNone(py_compile.compile(bad_coding, doraise=False))
     140          self.assertFalse(os.path.exists(
     141              importlib.util.cache_from_source(bad_coding)))
     142  
     143      def test_source_date_epoch(self):
     144          py_compile.compile(self.source_path, self.pyc_path)
     145          self.assertTrue(os.path.exists(self.pyc_path))
     146          self.assertFalse(os.path.exists(self.cache_path))
     147          with open(self.pyc_path, 'rb') as fp:
     148              flags = importlib._bootstrap_external._classify_pyc(
     149                  fp.read(), 'test', {})
     150          if os.environ.get('SOURCE_DATE_EPOCH'):
     151              expected_flags = 0b11
     152          else:
     153              expected_flags = 0b00
     154  
     155          self.assertEqual(flags, expected_flags)
     156  
     157      @unittest.skipIf(sys.flags.optimize > 0, 'test does not work with -O')
     158      def test_double_dot_no_clobber(self):
     159          # http://bugs.python.org/issue22966
     160          # py_compile foo.bar.py -> __pycache__/foo.cpython-34.pyc
     161          weird_path = os.path.join(self.directory, 'foo.bar.py')
     162          cache_path = importlib.util.cache_from_source(weird_path)
     163          pyc_path = weird_path + 'c'
     164          head, tail = os.path.split(cache_path)
     165          penultimate_tail = os.path.basename(head)
     166          self.assertEqual(
     167              os.path.join(penultimate_tail, tail),
     168              os.path.join(
     169                  '__pycache__',
     170                  'foo.bar.{}.pyc'.format(sys.implementation.cache_tag)))
     171          with open(weird_path, 'w') as file:
     172              file.write('x = 123\n')
     173          py_compile.compile(weird_path)
     174          self.assertTrue(os.path.exists(cache_path))
     175          self.assertFalse(os.path.exists(pyc_path))
     176  
     177      def test_optimization_path(self):
     178          # Specifying optimized bytecode should lead to a path reflecting that.
     179          self.assertIn('opt-2', py_compile.compile(self.source_path, optimize=2))
     180  
     181      def test_invalidation_mode(self):
     182          py_compile.compile(
     183              self.source_path,
     184              invalidation_mode=py_compile.PycInvalidationMode.CHECKED_HASH,
     185          )
     186          with open(self.cache_path, 'rb') as fp:
     187              flags = importlib._bootstrap_external._classify_pyc(
     188                  fp.read(), 'test', {})
     189          self.assertEqual(flags, 0b11)
     190          py_compile.compile(
     191              self.source_path,
     192              invalidation_mode=py_compile.PycInvalidationMode.UNCHECKED_HASH,
     193          )
     194          with open(self.cache_path, 'rb') as fp:
     195              flags = importlib._bootstrap_external._classify_pyc(
     196                  fp.read(), 'test', {})
     197          self.assertEqual(flags, 0b1)
     198  
     199      def test_quiet(self):
     200          bad_coding = os.path.join(os.path.dirname(__file__),
     201                                    'tokenizedata',
     202                                    'bad_coding2.py')
     203          with support.captured_stderr() as stderr:
     204              self.assertIsNone(py_compile.compile(bad_coding, doraise=False, quiet=2))
     205              self.assertIsNone(py_compile.compile(bad_coding, doraise=True, quiet=2))
     206              self.assertEqual(stderr.getvalue(), '')
     207              with self.assertRaises(py_compile.PyCompileError):
     208                  py_compile.compile(bad_coding, doraise=True, quiet=1)
     209  
     210  
     211  class ESC[4;38;5;81mPyCompileTestsWithSourceEpoch(ESC[4;38;5;149mPyCompileTestsBase,
     212                                      ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase,
     213                                      metaclass=ESC[4;38;5;149mSourceDateEpochTestMeta,
     214                                      source_date_epoch=ESC[4;38;5;149mTrue):
     215      pass
     216  
     217  
     218  class ESC[4;38;5;81mPyCompileTestsWithoutSourceEpoch(ESC[4;38;5;149mPyCompileTestsBase,
     219                                         ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase,
     220                                         metaclass=ESC[4;38;5;149mSourceDateEpochTestMeta,
     221                                         source_date_epoch=ESC[4;38;5;149mFalse):
     222      pass
     223  
     224  
     225  class ESC[4;38;5;81mPyCompileCLITestCase(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
     226  
     227      def setUp(self):
     228          self.directory = tempfile.mkdtemp()
     229          self.source_path = os.path.join(self.directory, '_test.py')
     230          self.cache_path = importlib.util.cache_from_source(self.source_path)
     231          with open(self.source_path, 'w') as file:
     232              file.write('x = 123\n')
     233  
     234      def tearDown(self):
     235          os_helper.rmtree(self.directory)
     236  
     237      @support.requires_subprocess()
     238      def pycompilecmd(self, *args, **kwargs):
     239          # assert_python_* helpers don't return proc object. We'll just use
     240          # subprocess.run() instead of spawn_python() and its friends to test
     241          # stdin support of the CLI.
     242          if args and args[0] == '-' and 'input' in kwargs:
     243              return subprocess.run([sys.executable, '-m', 'py_compile', '-'],
     244                                    input=kwargs['input'].encode(),
     245                                    capture_output=True)
     246          return script_helper.assert_python_ok('-m', 'py_compile', *args, **kwargs)
     247  
     248      def pycompilecmd_failure(self, *args):
     249          return script_helper.assert_python_failure('-m', 'py_compile', *args)
     250  
     251      def test_stdin(self):
     252          result = self.pycompilecmd('-', input=self.source_path)
     253          self.assertEqual(result.returncode, 0)
     254          self.assertEqual(result.stdout, b'')
     255          self.assertEqual(result.stderr, b'')
     256          self.assertTrue(os.path.exists(self.cache_path))
     257  
     258      def test_with_files(self):
     259          rc, stdout, stderr = self.pycompilecmd(self.source_path, self.source_path)
     260          self.assertEqual(rc, 0)
     261          self.assertEqual(stdout, b'')
     262          self.assertEqual(stderr, b'')
     263          self.assertTrue(os.path.exists(self.cache_path))
     264  
     265      def test_bad_syntax(self):
     266          bad_syntax = os.path.join(os.path.dirname(__file__),
     267                                    'tokenizedata',
     268                                    'badsyntax_3131.py')
     269          rc, stdout, stderr = self.pycompilecmd_failure(bad_syntax)
     270          self.assertEqual(rc, 1)
     271          self.assertEqual(stdout, b'')
     272          self.assertIn(b'SyntaxError', stderr)
     273  
     274      def test_bad_syntax_with_quiet(self):
     275          bad_syntax = os.path.join(os.path.dirname(__file__),
     276                                    'tokenizedata',
     277                                    'badsyntax_3131.py')
     278          rc, stdout, stderr = self.pycompilecmd_failure('-q', bad_syntax)
     279          self.assertEqual(rc, 1)
     280          self.assertEqual(stdout, b'')
     281          self.assertEqual(stderr, b'')
     282  
     283      def test_file_not_exists(self):
     284          should_not_exists = os.path.join(os.path.dirname(__file__), 'should_not_exists.py')
     285          rc, stdout, stderr = self.pycompilecmd_failure(self.source_path, should_not_exists)
     286          self.assertEqual(rc, 1)
     287          self.assertEqual(stdout, b'')
     288          self.assertIn(b'no such file or directory', stderr.lower())
     289  
     290      def test_file_not_exists_with_quiet(self):
     291          should_not_exists = os.path.join(os.path.dirname(__file__), 'should_not_exists.py')
     292          rc, stdout, stderr = self.pycompilecmd_failure('-q', self.source_path, should_not_exists)
     293          self.assertEqual(rc, 1)
     294          self.assertEqual(stdout, b'')
     295          self.assertEqual(stderr, b'')
     296  
     297  
     298  if __name__ == "__main__":
     299      unittest.main()