test_compileall.py 24.8 KB
Newer Older
1
import sys
2
import compileall
3
import importlib.util
4
import test.test_importlib.util
5
import os
6
import pathlib
7 8 9 10
import py_compile
import shutil
import struct
import tempfile
11
import time
12
import unittest
13
import io
14

15 16 17 18 19 20 21
from unittest import mock, skipUnless
try:
    from concurrent.futures import ProcessPoolExecutor
    _have_multiprocessing = True
except ImportError:
    _have_multiprocessing = False

22 23
from test import support
from test.support import script_helper
24

25 26 27 28 29
from .test_py_compile import without_source_date_epoch
from .test_py_compile import SourceDateEpochTestMeta


class CompileallTestsBase:
30 31 32 33

    def setUp(self):
        self.directory = tempfile.mkdtemp()
        self.source_path = os.path.join(self.directory, '_test.py')
34
        self.bc_path = importlib.util.cache_from_source(self.source_path)
35 36
        with open(self.source_path, 'w') as file:
            file.write('x = 123\n')
37
        self.source_path2 = os.path.join(self.directory, '_test2.py')
38
        self.bc_path2 = importlib.util.cache_from_source(self.source_path2)
39
        shutil.copyfile(self.source_path, self.source_path2)
40 41 42 43
        self.subdirectory = os.path.join(self.directory, '_subdir')
        os.mkdir(self.subdirectory)
        self.source_path3 = os.path.join(self.subdirectory, '_test3.py')
        shutil.copyfile(self.source_path, self.source_path3)
44 45 46 47

    def tearDown(self):
        shutil.rmtree(self.directory)

48 49 50 51 52
    def add_bad_source_file(self):
        self.bad_source_path = os.path.join(self.directory, '_test_bad.py')
        with open(self.bad_source_path, 'w') as file:
            file.write('x (\n')

53
    def timestamp_metadata(self):
54
        with open(self.bc_path, 'rb') as file:
55
            data = file.read(12)
56
        mtime = int(os.stat(self.source_path).st_mtime)
57
        compare = struct.pack('<4sll', importlib.util.MAGIC_NUMBER, 0, mtime)
58 59 60 61 62
        return data, compare

    def recreation_check(self, metadata):
        """Check that compileall recreates bytecode when the new metadata is
        used."""
63 64
        if os.environ.get('SOURCE_DATE_EPOCH'):
            raise unittest.SkipTest('SOURCE_DATE_EPOCH is set')
65
        py_compile.compile(self.source_path)
66
        self.assertEqual(*self.timestamp_metadata())
67 68 69 70 71
        with open(self.bc_path, 'rb') as file:
            bc = file.read()[len(metadata):]
        with open(self.bc_path, 'wb') as file:
            file.write(metadata)
            file.write(bc)
72
        self.assertNotEqual(*self.timestamp_metadata())
73
        compileall.compile_dir(self.directory, force=False, quiet=True)
74
        self.assertTrue(*self.timestamp_metadata())
75 76 77

    def test_mtime(self):
        # Test a change in mtime leads to a new .pyc.
78 79
        self.recreation_check(struct.pack('<4sll', importlib.util.MAGIC_NUMBER,
                                          0, 1))
80 81 82 83 84

    def test_magic_number(self):
        # Test a change in mtime leads to a new .pyc.
        self.recreation_check(b'\0\0\0\0')

85 86 87 88 89 90 91
    def test_compile_files(self):
        # Test compiling a single file, and complete directory
        for fn in (self.bc_path, self.bc_path2):
            try:
                os.unlink(fn)
            except:
                pass
92 93
        self.assertTrue(compileall.compile_file(self.source_path,
                                                force=False, quiet=True))
Barry Warsaw's avatar
Barry Warsaw committed
94 95
        self.assertTrue(os.path.isfile(self.bc_path) and
                        not os.path.isfile(self.bc_path2))
96
        os.unlink(self.bc_path)
97 98
        self.assertTrue(compileall.compile_dir(self.directory, force=False,
                                               quiet=True))
Barry Warsaw's avatar
Barry Warsaw committed
99 100
        self.assertTrue(os.path.isfile(self.bc_path) and
                        os.path.isfile(self.bc_path2))
101 102
        os.unlink(self.bc_path)
        os.unlink(self.bc_path2)
103 104 105 106 107 108 109
        # Test against bad files
        self.add_bad_source_file()
        self.assertFalse(compileall.compile_file(self.bad_source_path,
                                                 force=False, quiet=2))
        self.assertFalse(compileall.compile_dir(self.directory,
                                                force=False, quiet=2))

110 111 112 113 114
    def test_compile_file_pathlike(self):
        self.assertFalse(os.path.isfile(self.bc_path))
        # we should also test the output
        with support.captured_stdout() as stdout:
            self.assertTrue(compileall.compile_file(pathlib.Path(self.source_path)))
115
        self.assertRegex(stdout.getvalue(), r'Compiling ([^WindowsPath|PosixPath].*)')
116 117 118 119 120 121 122 123 124
        self.assertTrue(os.path.isfile(self.bc_path))

    def test_compile_file_pathlike_ddir(self):
        self.assertFalse(os.path.isfile(self.bc_path))
        self.assertTrue(compileall.compile_file(pathlib.Path(self.source_path),
                                                ddir=pathlib.Path('ddir_path'),
                                                quiet=2))
        self.assertTrue(os.path.isfile(self.bc_path))

125
    def test_compile_path(self):
126 127
        with test.test_importlib.util.import_state(path=[self.directory]):
            self.assertTrue(compileall.compile_path(quiet=2))
128 129 130 131 132

        with test.test_importlib.util.import_state(path=[self.directory]):
            self.add_bad_source_file()
            self.assertFalse(compileall.compile_path(skip_curdir=False,
                                                     force=True, quiet=2))
133

134 135 136 137 138 139 140 141 142 143 144 145
    def test_no_pycache_in_non_package(self):
        # Bug 8563 reported that __pycache__ directories got created by
        # compile_file() for non-.py files.
        data_dir = os.path.join(self.directory, 'data')
        data_file = os.path.join(data_dir, 'file')
        os.mkdir(data_dir)
        # touch data/file
        with open(data_file, 'w'):
            pass
        compileall.compile_file(data_file)
        self.assertFalse(os.path.exists(os.path.join(data_dir, '__pycache__')))

146 147 148
    def test_optimize(self):
        # make sure compiling with different optimization settings than the
        # interpreter's creates the correct file names
149
        optimize, opt = (1, 1) if __debug__ else (0, '')
150
        compileall.compile_dir(self.directory, quiet=True, optimize=optimize)
151
        cached = importlib.util.cache_from_source(self.source_path,
152
                                                  optimization=opt)
153
        self.assertTrue(os.path.isfile(cached))
154
        cached2 = importlib.util.cache_from_source(self.source_path2,
155
                                                   optimization=opt)
156
        self.assertTrue(os.path.isfile(cached2))
157
        cached3 = importlib.util.cache_from_source(self.source_path3,
158
                                                   optimization=opt)
159
        self.assertTrue(os.path.isfile(cached3))
160

161 162 163 164
    def test_compile_dir_pathlike(self):
        self.assertFalse(os.path.isfile(self.bc_path))
        with support.captured_stdout() as stdout:
            compileall.compile_dir(pathlib.Path(self.directory))
165 166
        line = stdout.getvalue().splitlines()[0]
        self.assertRegex(line, r'Listing ([^WindowsPath|PosixPath].*)')
167 168
        self.assertTrue(os.path.isfile(self.bc_path))

169
    @mock.patch('concurrent.futures.ProcessPoolExecutor')
170 171 172 173 174 175 176 177 178
    def test_compile_pool_called(self, pool_mock):
        compileall.compile_dir(self.directory, quiet=True, workers=5)
        self.assertTrue(pool_mock.called)

    def test_compile_workers_non_positive(self):
        with self.assertRaisesRegex(ValueError,
                                    "workers must be greater or equal to 0"):
            compileall.compile_dir(self.directory, workers=-1)

179
    @mock.patch('concurrent.futures.ProcessPoolExecutor')
180 181 182 183
    def test_compile_workers_cpu_count(self, pool_mock):
        compileall.compile_dir(self.directory, quiet=True, workers=0)
        self.assertEqual(pool_mock.call_args[1]['max_workers'], None)

184
    @mock.patch('concurrent.futures.ProcessPoolExecutor')
185 186 187 188 189 190
    @mock.patch('compileall.compile_file')
    def test_compile_one_worker(self, compile_file_mock, pool_mock):
        compileall.compile_dir(self.directory, quiet=True)
        self.assertFalse(pool_mock.called)
        self.assertTrue(compile_file_mock.called)

191
    @mock.patch('concurrent.futures.ProcessPoolExecutor', new=None)
192 193 194 195
    @mock.patch('compileall.compile_file')
    def test_compile_missing_multiprocessing(self, compile_file_mock):
        compileall.compile_dir(self.directory, quiet=True, workers=5)
        self.assertTrue(compile_file_mock.called)
Barry Warsaw's avatar
Barry Warsaw committed
196

197 198 199 200 201 202 203 204 205 206 207 208 209 210 211

class CompileallTestsWithSourceEpoch(CompileallTestsBase,
                                     unittest.TestCase,
                                     metaclass=SourceDateEpochTestMeta,
                                     source_date_epoch=True):
    pass


class CompileallTestsWithoutSourceEpoch(CompileallTestsBase,
                                        unittest.TestCase,
                                        metaclass=SourceDateEpochTestMeta,
                                        source_date_epoch=False):
    pass


212
class EncodingTest(unittest.TestCase):
Barry Warsaw's avatar
Barry Warsaw committed
213 214
    """Issue 6716: compileall should escape source code when printing errors
    to stdout."""
215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233

    def setUp(self):
        self.directory = tempfile.mkdtemp()
        self.source_path = os.path.join(self.directory, '_test.py')
        with open(self.source_path, 'w', encoding='utf-8') as file:
            file.write('# -*- coding: utf-8 -*-\n')
            file.write('print u"\u20ac"\n')

    def tearDown(self):
        shutil.rmtree(self.directory)

    def test_error(self):
        try:
            orig_stdout = sys.stdout
            sys.stdout = io.TextIOWrapper(io.BytesIO(),encoding='ascii')
            compileall.compile_dir(self.directory)
        finally:
            sys.stdout = orig_stdout

234

235
class CommandLineTestsBase:
236
    """Test compileall's CLI."""
Barry Warsaw's avatar
Barry Warsaw committed
237

238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264
    @classmethod
    def setUpClass(cls):
        for path in filter(os.path.isdir, sys.path):
            directory_created = False
            directory = pathlib.Path(path) / '__pycache__'
            path = directory / 'test.try'
            try:
                if not directory.is_dir():
                    directory.mkdir()
                    directory_created = True
                with path.open('w') as file:
                    file.write('# for test_compileall')
            except OSError:
                sys_path_writable = False
                break
            finally:
                support.unlink(str(path))
                if directory_created:
                    directory.rmdir()
        else:
            sys_path_writable = True
        cls._sys_path_writable = sys_path_writable

    def _skip_if_sys_path_not_writable(self):
        if not self._sys_path_writable:
            raise unittest.SkipTest('not all entries on sys.path are writable')

265
    def _get_run_args(self, args):
266 267 268
        return [*support.optim_args_from_interpreter_flags(),
                '-S', '-m', 'compileall',
                *args]
269

270 271
    def assertRunOK(self, *args, **env_vars):
        rc, out, err = script_helper.assert_python_ok(
272
                         *self._get_run_args(args), **env_vars)
273 274 275
        self.assertEqual(b'', err)
        return out

276
    def assertRunNotOK(self, *args, **env_vars):
277
        rc, out, err = script_helper.assert_python_failure(
278
                        *self._get_run_args(args), **env_vars)
279 280 281
        return rc, out, err

    def assertCompiled(self, fn):
282 283
        path = importlib.util.cache_from_source(fn)
        self.assertTrue(os.path.exists(path))
284 285

    def assertNotCompiled(self, fn):
286 287
        path = importlib.util.cache_from_source(fn)
        self.assertFalse(os.path.exists(path))
288

Barry Warsaw's avatar
Barry Warsaw committed
289 290
    def setUp(self):
        self.directory = tempfile.mkdtemp()
291
        self.addCleanup(support.rmtree, self.directory)
Barry Warsaw's avatar
Barry Warsaw committed
292 293
        self.pkgdir = os.path.join(self.directory, 'foo')
        os.mkdir(self.pkgdir)
294 295 296 297
        self.pkgdir_cachedir = os.path.join(self.pkgdir, '__pycache__')
        # Create the __init__.py and a package module.
        self.initfn = script_helper.make_script(self.pkgdir, '__init__', '')
        self.barfn = script_helper.make_script(self.pkgdir, 'bar', '')
Barry Warsaw's avatar
Barry Warsaw committed
298

299 300
    def test_no_args_compiles_path(self):
        # Note that -l is implied for the no args case.
301
        self._skip_if_sys_path_not_writable()
302 303 304 305 306
        bazfn = script_helper.make_script(self.directory, 'baz', '')
        self.assertRunOK(PYTHONPATH=self.directory)
        self.assertCompiled(bazfn)
        self.assertNotCompiled(self.initfn)
        self.assertNotCompiled(self.barfn)
Barry Warsaw's avatar
Barry Warsaw committed
307

308
    @without_source_date_epoch  # timestamp invalidation test
309
    def test_no_args_respects_force_flag(self):
310
        self._skip_if_sys_path_not_writable()
311 312
        bazfn = script_helper.make_script(self.directory, 'baz', '')
        self.assertRunOK(PYTHONPATH=self.directory)
313
        pycpath = importlib.util.cache_from_source(bazfn)
314 315 316 317 318 319 320 321 322 323 324 325 326
        # Set atime/mtime backward to avoid file timestamp resolution issues
        os.utime(pycpath, (time.time()-60,)*2)
        mtime = os.stat(pycpath).st_mtime
        # Without force, no recompilation
        self.assertRunOK(PYTHONPATH=self.directory)
        mtime2 = os.stat(pycpath).st_mtime
        self.assertEqual(mtime, mtime2)
        # Now force it.
        self.assertRunOK('-f', PYTHONPATH=self.directory)
        mtime2 = os.stat(pycpath).st_mtime
        self.assertNotEqual(mtime, mtime2)

    def test_no_args_respects_quiet_flag(self):
327
        self._skip_if_sys_path_not_writable()
328 329 330 331 332 333
        script_helper.make_script(self.directory, 'baz', '')
        noisy = self.assertRunOK(PYTHONPATH=self.directory)
        self.assertIn(b'Listing ', noisy)
        quiet = self.assertRunOK('-q', PYTHONPATH=self.directory)
        self.assertNotIn(b'Listing ', quiet)

334
    # Ensure that the default behavior of compileall's CLI is to create
335
    # PEP 3147/PEP 488 pyc files.
336 337
    for name, ext, switch in [
        ('normal', 'pyc', []),
338 339
        ('optimize', 'opt-1.pyc', ['-O']),
        ('doubleoptimize', 'opt-2.pyc', ['-OO']),
340 341
    ]:
        def f(self, ext=ext, switch=switch):
342 343
            script_helper.assert_python_ok(*(switch +
                ['-m', 'compileall', '-q', self.pkgdir]))
344
            # Verify the __pycache__ directory contents.
345
            self.assertTrue(os.path.exists(self.pkgdir_cachedir))
346 347
            expected = sorted(base.format(sys.implementation.cache_tag, ext)
                              for base in ('__init__.{}.{}', 'bar.{}.{}'))
348
            self.assertEqual(sorted(os.listdir(self.pkgdir_cachedir)), expected)
349
            # Make sure there are no .pyc files in the source directory.
350 351
            self.assertFalse([fn for fn in os.listdir(self.pkgdir)
                              if fn.endswith(ext)])
352
        locals()['test_pep3147_paths_' + name] = f
Barry Warsaw's avatar
Barry Warsaw committed
353 354 355

    def test_legacy_paths(self):
        # Ensure that with the proper switch, compileall leaves legacy
356
        # pyc files, and no __pycache__ directory.
357
        self.assertRunOK('-b', '-q', self.pkgdir)
Barry Warsaw's avatar
Barry Warsaw committed
358
        # Verify the __pycache__ directory contents.
359
        self.assertFalse(os.path.exists(self.pkgdir_cachedir))
360 361
        expected = sorted(['__init__.py', '__init__.pyc', 'bar.py',
                           'bar.pyc'])
Barry Warsaw's avatar
Barry Warsaw committed
362 363
        self.assertEqual(sorted(os.listdir(self.pkgdir)), expected)

364 365 366
    def test_multiple_runs(self):
        # Bug 8527 reported that multiple calls produced empty
        # __pycache__/__pycache__ directories.
367
        self.assertRunOK('-q', self.pkgdir)
368
        # Verify the __pycache__ directory contents.
369 370
        self.assertTrue(os.path.exists(self.pkgdir_cachedir))
        cachecachedir = os.path.join(self.pkgdir_cachedir, '__pycache__')
371 372
        self.assertFalse(os.path.exists(cachecachedir))
        # Call compileall again.
373 374
        self.assertRunOK('-q', self.pkgdir)
        self.assertTrue(os.path.exists(self.pkgdir_cachedir))
375 376
        self.assertFalse(os.path.exists(cachecachedir))

377
    @without_source_date_epoch  # timestamp invalidation test
378
    def test_force(self):
379
        self.assertRunOK('-q', self.pkgdir)
380
        pycpath = importlib.util.cache_from_source(self.barfn)
381 382
        # set atime/mtime backward to avoid file timestamp resolution issues
        os.utime(pycpath, (time.time()-60,)*2)
383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403
        mtime = os.stat(pycpath).st_mtime
        # without force, no recompilation
        self.assertRunOK('-q', self.pkgdir)
        mtime2 = os.stat(pycpath).st_mtime
        self.assertEqual(mtime, mtime2)
        # now force it.
        self.assertRunOK('-q', '-f', self.pkgdir)
        mtime2 = os.stat(pycpath).st_mtime
        self.assertNotEqual(mtime, mtime2)

    def test_recursion_control(self):
        subpackage = os.path.join(self.pkgdir, 'spam')
        os.mkdir(subpackage)
        subinitfn = script_helper.make_script(subpackage, '__init__', '')
        hamfn = script_helper.make_script(subpackage, 'ham', '')
        self.assertRunOK('-q', '-l', self.pkgdir)
        self.assertNotCompiled(subinitfn)
        self.assertFalse(os.path.exists(os.path.join(subpackage, '__pycache__')))
        self.assertRunOK('-q', self.pkgdir)
        self.assertCompiled(subinitfn)
        self.assertCompiled(hamfn)
404

405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438
    def test_recursion_limit(self):
        subpackage = os.path.join(self.pkgdir, 'spam')
        subpackage2 = os.path.join(subpackage, 'ham')
        subpackage3 = os.path.join(subpackage2, 'eggs')
        for pkg in (subpackage, subpackage2, subpackage3):
            script_helper.make_pkg(pkg)

        subinitfn = os.path.join(subpackage, '__init__.py')
        hamfn = script_helper.make_script(subpackage, 'ham', '')
        spamfn = script_helper.make_script(subpackage2, 'spam', '')
        eggfn = script_helper.make_script(subpackage3, 'egg', '')

        self.assertRunOK('-q', '-r 0', self.pkgdir)
        self.assertNotCompiled(subinitfn)
        self.assertFalse(
            os.path.exists(os.path.join(subpackage, '__pycache__')))

        self.assertRunOK('-q', '-r 1', self.pkgdir)
        self.assertCompiled(subinitfn)
        self.assertCompiled(hamfn)
        self.assertNotCompiled(spamfn)

        self.assertRunOK('-q', '-r 2', self.pkgdir)
        self.assertCompiled(subinitfn)
        self.assertCompiled(hamfn)
        self.assertCompiled(spamfn)
        self.assertNotCompiled(eggfn)

        self.assertRunOK('-q', '-r 5', self.pkgdir)
        self.assertCompiled(subinitfn)
        self.assertCompiled(hamfn)
        self.assertCompiled(spamfn)
        self.assertCompiled(eggfn)

439
    def test_quiet(self):
440 441 442 443
        noisy = self.assertRunOK(self.pkgdir)
        quiet = self.assertRunOK('-q', self.pkgdir)
        self.assertNotEqual(b'', noisy)
        self.assertEqual(b'', quiet)
444

445 446 447 448 449 450 451
    def test_silent(self):
        script_helper.make_script(self.pkgdir, 'crunchyfrog', 'bad(syntax')
        _, quiet, _ = self.assertRunNotOK('-q', self.pkgdir)
        _, silent, _ = self.assertRunNotOK('-qq', self.pkgdir)
        self.assertNotEqual(b'', quiet)
        self.assertEqual(b'', silent)

452
    def test_regexp(self):
453
        self.assertRunOK('-q', '-x', r'ba[^\\/]*$', self.pkgdir)
454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476
        self.assertNotCompiled(self.barfn)
        self.assertCompiled(self.initfn)

    def test_multiple_dirs(self):
        pkgdir2 = os.path.join(self.directory, 'foo2')
        os.mkdir(pkgdir2)
        init2fn = script_helper.make_script(pkgdir2, '__init__', '')
        bar2fn = script_helper.make_script(pkgdir2, 'bar2', '')
        self.assertRunOK('-q', self.pkgdir, pkgdir2)
        self.assertCompiled(self.initfn)
        self.assertCompiled(self.barfn)
        self.assertCompiled(init2fn)
        self.assertCompiled(bar2fn)

    def test_d_compile_error(self):
        script_helper.make_script(self.pkgdir, 'crunchyfrog', 'bad(syntax')
        rc, out, err = self.assertRunNotOK('-q', '-d', 'dinsdale', self.pkgdir)
        self.assertRegex(out, b'File "dinsdale')

    def test_d_runtime_error(self):
        bazfn = script_helper.make_script(self.pkgdir, 'baz', 'raise Exception')
        self.assertRunOK('-q', '-d', 'dinsdale', self.pkgdir)
        fn = script_helper.make_script(self.pkgdir, 'bing', 'import baz')
477
        pyc = importlib.util.cache_from_source(bazfn)
478 479
        os.rename(pyc, os.path.join(self.pkgdir, 'baz.pyc'))
        os.remove(bazfn)
480
        rc, out, err = script_helper.assert_python_failure(fn, __isolated=False)
481 482 483 484 485 486 487
        self.assertRegex(err, b'File "dinsdale')

    def test_include_bad_file(self):
        rc, out, err = self.assertRunNotOK(
            '-i', os.path.join(self.directory, 'nosuchfile'), self.pkgdir)
        self.assertRegex(out, b'rror.*nosuchfile')
        self.assertNotRegex(err, b'Traceback')
488
        self.assertFalse(os.path.exists(importlib.util.cache_from_source(
489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522
                                            self.pkgdir_cachedir)))

    def test_include_file_with_arg(self):
        f1 = script_helper.make_script(self.pkgdir, 'f1', '')
        f2 = script_helper.make_script(self.pkgdir, 'f2', '')
        f3 = script_helper.make_script(self.pkgdir, 'f3', '')
        f4 = script_helper.make_script(self.pkgdir, 'f4', '')
        with open(os.path.join(self.directory, 'l1'), 'w') as l1:
            l1.write(os.path.join(self.pkgdir, 'f1.py')+os.linesep)
            l1.write(os.path.join(self.pkgdir, 'f2.py')+os.linesep)
        self.assertRunOK('-i', os.path.join(self.directory, 'l1'), f4)
        self.assertCompiled(f1)
        self.assertCompiled(f2)
        self.assertNotCompiled(f3)
        self.assertCompiled(f4)

    def test_include_file_no_arg(self):
        f1 = script_helper.make_script(self.pkgdir, 'f1', '')
        f2 = script_helper.make_script(self.pkgdir, 'f2', '')
        f3 = script_helper.make_script(self.pkgdir, 'f3', '')
        f4 = script_helper.make_script(self.pkgdir, 'f4', '')
        with open(os.path.join(self.directory, 'l1'), 'w') as l1:
            l1.write(os.path.join(self.pkgdir, 'f2.py')+os.linesep)
        self.assertRunOK('-i', os.path.join(self.directory, 'l1'))
        self.assertNotCompiled(f1)
        self.assertCompiled(f2)
        self.assertNotCompiled(f3)
        self.assertNotCompiled(f4)

    def test_include_on_stdin(self):
        f1 = script_helper.make_script(self.pkgdir, 'f1', '')
        f2 = script_helper.make_script(self.pkgdir, 'f2', '')
        f3 = script_helper.make_script(self.pkgdir, 'f3', '')
        f4 = script_helper.make_script(self.pkgdir, 'f4', '')
523
        p = script_helper.spawn_python(*(self._get_run_args(()) + ['-i', '-']))
524 525 526 527 528 529 530 531 532 533 534
        p.stdin.write((f3+os.linesep).encode('ascii'))
        script_helper.kill_python(p)
        self.assertNotCompiled(f1)
        self.assertNotCompiled(f2)
        self.assertCompiled(f3)
        self.assertNotCompiled(f4)

    def test_compiles_as_much_as_possible(self):
        bingfn = script_helper.make_script(self.pkgdir, 'bing', 'syntax(error')
        rc, out, err = self.assertRunNotOK('nosuchfile', self.initfn,
                                           bingfn, self.barfn)
535
        self.assertRegex(out, b'rror')
536 537 538 539
        self.assertNotCompiled(bingfn)
        self.assertCompiled(self.initfn)
        self.assertCompiled(self.barfn)

540 541
    def test_invalid_arg_produces_message(self):
        out = self.assertRunOK('badfilename')
542
        self.assertRegex(out, b"Can't list 'badfilename'")
543

544 545 546 547 548 549 550 551 552 553 554 555 556
    def test_pyc_invalidation_mode(self):
        script_helper.make_script(self.pkgdir, 'f1', '')
        pyc = importlib.util.cache_from_source(
            os.path.join(self.pkgdir, 'f1.py'))
        self.assertRunOK('--invalidation-mode=checked-hash', self.pkgdir)
        with open(pyc, 'rb') as fp:
            data = fp.read()
        self.assertEqual(int.from_bytes(data[4:8], 'little'), 0b11)
        self.assertRunOK('--invalidation-mode=unchecked-hash', self.pkgdir)
        with open(pyc, 'rb') as fp:
            data = fp.read()
        self.assertEqual(int.from_bytes(data[4:8], 'little'), 0b01)

557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579
    @skipUnless(_have_multiprocessing, "requires multiprocessing")
    def test_workers(self):
        bar2fn = script_helper.make_script(self.directory, 'bar2', '')
        files = []
        for suffix in range(5):
            pkgdir = os.path.join(self.directory, 'foo{}'.format(suffix))
            os.mkdir(pkgdir)
            fn = script_helper.make_script(pkgdir, '__init__', '')
            files.append(script_helper.make_script(pkgdir, 'bar2', ''))

        self.assertRunOK(self.directory, '-j', '0')
        self.assertCompiled(bar2fn)
        for file in files:
            self.assertCompiled(file)

    @mock.patch('compileall.compile_dir')
    def test_workers_available_cores(self, compile_dir):
        with mock.patch("sys.argv",
                        new=[sys.executable, self.directory, "-j0"]):
            compileall.main()
            self.assertTrue(compile_dir.called)
            self.assertEqual(compile_dir.call_args[-1]['workers'], None)

Barry Warsaw's avatar
Barry Warsaw committed
580

581 582 583 584 585 586 587 588 589 590 591 592 593 594 595
class CommmandLineTestsWithSourceEpoch(CommandLineTestsBase,
                                       unittest.TestCase,
                                       metaclass=SourceDateEpochTestMeta,
                                       source_date_epoch=True):
    pass


class CommmandLineTestsNoSourceEpoch(CommandLineTestsBase,
                                     unittest.TestCase,
                                     metaclass=SourceDateEpochTestMeta,
                                     source_date_epoch=False):
    pass



596
if __name__ == "__main__":
597
    unittest.main()