python (3.12.0)

(root)/
lib/
python3.12/
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__), 'bad_coding2.py')
     136          with support.captured_stderr():
     137              self.assertIsNone(py_compile.compile(bad_coding, doraise=False))
     138          self.assertFalse(os.path.exists(
     139              importlib.util.cache_from_source(bad_coding)))
     140  
     141      def test_source_date_epoch(self):
     142          py_compile.compile(self.source_path, self.pyc_path)
     143          self.assertTrue(os.path.exists(self.pyc_path))
     144          self.assertFalse(os.path.exists(self.cache_path))
     145          with open(self.pyc_path, 'rb') as fp:
     146              flags = importlib._bootstrap_external._classify_pyc(
     147                  fp.read(), 'test', {})
     148          if os.environ.get('SOURCE_DATE_EPOCH'):
     149              expected_flags = 0b11
     150          else:
     151              expected_flags = 0b00
     152  
     153          self.assertEqual(flags, expected_flags)
     154  
     155      @unittest.skipIf(sys.flags.optimize > 0, 'test does not work with -O')
     156      def test_double_dot_no_clobber(self):
     157          # http://bugs.python.org/issue22966
     158          # py_compile foo.bar.py -> __pycache__/foo.cpython-34.pyc
     159          weird_path = os.path.join(self.directory, 'foo.bar.py')
     160          cache_path = importlib.util.cache_from_source(weird_path)
     161          pyc_path = weird_path + 'c'
     162          head, tail = os.path.split(cache_path)
     163          penultimate_tail = os.path.basename(head)
     164          self.assertEqual(
     165              os.path.join(penultimate_tail, tail),
     166              os.path.join(
     167                  '__pycache__',
     168                  'foo.bar.{}.pyc'.format(sys.implementation.cache_tag)))
     169          with open(weird_path, 'w') as file:
     170              file.write('x = 123\n')
     171          py_compile.compile(weird_path)
     172          self.assertTrue(os.path.exists(cache_path))
     173          self.assertFalse(os.path.exists(pyc_path))
     174  
     175      def test_optimization_path(self):
     176          # Specifying optimized bytecode should lead to a path reflecting that.
     177          self.assertIn('opt-2', py_compile.compile(self.source_path, optimize=2))
     178  
     179      def test_invalidation_mode(self):
     180          py_compile.compile(
     181              self.source_path,
     182              invalidation_mode=py_compile.PycInvalidationMode.CHECKED_HASH,
     183          )
     184          with open(self.cache_path, 'rb') as fp:
     185              flags = importlib._bootstrap_external._classify_pyc(
     186                  fp.read(), 'test', {})
     187          self.assertEqual(flags, 0b11)
     188          py_compile.compile(
     189              self.source_path,
     190              invalidation_mode=py_compile.PycInvalidationMode.UNCHECKED_HASH,
     191          )
     192          with open(self.cache_path, 'rb') as fp:
     193              flags = importlib._bootstrap_external._classify_pyc(
     194                  fp.read(), 'test', {})
     195          self.assertEqual(flags, 0b1)
     196  
     197      def test_quiet(self):
     198          bad_coding = os.path.join(os.path.dirname(__file__), 'bad_coding2.py')
     199          with support.captured_stderr() as stderr:
     200              self.assertIsNone(py_compile.compile(bad_coding, doraise=False, quiet=2))
     201              self.assertIsNone(py_compile.compile(bad_coding, doraise=True, quiet=2))
     202              self.assertEqual(stderr.getvalue(), '')
     203              with self.assertRaises(py_compile.PyCompileError):
     204                  py_compile.compile(bad_coding, doraise=True, quiet=1)
     205  
     206  
     207  class ESC[4;38;5;81mPyCompileTestsWithSourceEpoch(ESC[4;38;5;149mPyCompileTestsBase,
     208                                      ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase,
     209                                      metaclass=ESC[4;38;5;149mSourceDateEpochTestMeta,
     210                                      source_date_epoch=ESC[4;38;5;149mTrue):
     211      pass
     212  
     213  
     214  class ESC[4;38;5;81mPyCompileTestsWithoutSourceEpoch(ESC[4;38;5;149mPyCompileTestsBase,
     215                                         ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase,
     216                                         metaclass=ESC[4;38;5;149mSourceDateEpochTestMeta,
     217                                         source_date_epoch=ESC[4;38;5;149mFalse):
     218      pass
     219  
     220  
     221  class ESC[4;38;5;81mPyCompileCLITestCase(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
     222  
     223      def setUp(self):
     224          self.directory = tempfile.mkdtemp()
     225          self.source_path = os.path.join(self.directory, '_test.py')
     226          self.cache_path = importlib.util.cache_from_source(self.source_path)
     227          with open(self.source_path, 'w') as file:
     228              file.write('x = 123\n')
     229  
     230      def tearDown(self):
     231          os_helper.rmtree(self.directory)
     232  
     233      @support.requires_subprocess()
     234      def pycompilecmd(self, *args, **kwargs):
     235          # assert_python_* helpers don't return proc object. We'll just use
     236          # subprocess.run() instead of spawn_python() and its friends to test
     237          # stdin support of the CLI.
     238          opts = '-m' if __debug__ else '-Om'
     239          if args and args[0] == '-' and 'input' in kwargs:
     240              return subprocess.run([sys.executable, opts, 'py_compile', '-'],
     241                                    input=kwargs['input'].encode(),
     242                                    capture_output=True)
     243          return script_helper.assert_python_ok(opts, 'py_compile', *args, **kwargs)
     244  
     245      def pycompilecmd_failure(self, *args):
     246          return script_helper.assert_python_failure('-m', 'py_compile', *args)
     247  
     248      def test_stdin(self):
     249          result = self.pycompilecmd('-', input=self.source_path)
     250          self.assertEqual(result.returncode, 0)
     251          self.assertEqual(result.stdout, b'')
     252          self.assertEqual(result.stderr, b'')
     253          self.assertTrue(os.path.exists(self.cache_path))
     254  
     255      def test_with_files(self):
     256          rc, stdout, stderr = self.pycompilecmd(self.source_path, self.source_path)
     257          self.assertEqual(rc, 0)
     258          self.assertEqual(stdout, b'')
     259          self.assertEqual(stderr, b'')
     260          self.assertTrue(os.path.exists(self.cache_path))
     261  
     262      def test_bad_syntax(self):
     263          bad_syntax = os.path.join(os.path.dirname(__file__), 'badsyntax_3131.py')
     264          rc, stdout, stderr = self.pycompilecmd_failure(bad_syntax)
     265          self.assertEqual(rc, 1)
     266          self.assertEqual(stdout, b'')
     267          self.assertIn(b'SyntaxError', stderr)
     268  
     269      def test_bad_syntax_with_quiet(self):
     270          bad_syntax = os.path.join(os.path.dirname(__file__), 'badsyntax_3131.py')
     271          rc, stdout, stderr = self.pycompilecmd_failure('-q', bad_syntax)
     272          self.assertEqual(rc, 1)
     273          self.assertEqual(stdout, b'')
     274          self.assertEqual(stderr, b'')
     275  
     276      def test_file_not_exists(self):
     277          should_not_exists = os.path.join(os.path.dirname(__file__), 'should_not_exists.py')
     278          rc, stdout, stderr = self.pycompilecmd_failure(self.source_path, should_not_exists)
     279          self.assertEqual(rc, 1)
     280          self.assertEqual(stdout, b'')
     281          self.assertIn(b'no such file or directory', stderr.lower())
     282  
     283      def test_file_not_exists_with_quiet(self):
     284          should_not_exists = os.path.join(os.path.dirname(__file__), 'should_not_exists.py')
     285          rc, stdout, stderr = self.pycompilecmd_failure('-q', self.source_path, should_not_exists)
     286          self.assertEqual(rc, 1)
     287          self.assertEqual(stdout, b'')
     288          self.assertEqual(stderr, b'')
     289  
     290  
     291  if __name__ == "__main__":
     292      unittest.main()