diff --git a/Doc/library/compileall.rst b/Doc/library/compileall.rst --- a/Doc/library/compileall.rst +++ b/Doc/library/compileall.rst @@ -148,16 +148,19 @@ Public functions .. versionchanged:: 3.5 *quiet* parameter was changed to a multilevel value. .. versionchanged:: 3.5 The *legacy* parameter only writes out ``.pyc`` files, not ``.pyo`` files no matter what the value of *optimize* is. + .. versionchanged:: 3.6 + Accepts a :term:`path-like object`. + .. function:: compile_file(fullname, ddir=None, force=False, rx=None, quiet=0, legacy=False, optimize=-1) Compile the file with path *fullname*. Return a true value if the file compiled successfully, and a false value otherwise. If *ddir* is given, it is prepended to the path to the file being compiled for use in compilation time tracebacks, and is also compiled in to the byte-code file, where it will be used in tracebacks and other messages in @@ -209,20 +212,23 @@ Public functions .. versionchanged:: 3.5 The *legacy* parameter only writes out ``.pyc`` files, not ``.pyo`` files no matter what the value of *optimize* is. To force a recompile of all the :file:`.py` files in the :file:`Lib/` subdirectory and all its subdirectories:: import compileall + import pathlib compileall.compile_dir('Lib/', force=True) # Perform same compilation, excluding files in .svn directories. import re compileall.compile_dir('Lib/', rx=re.compile(r'[/\\][.]svn'), force=True) + # pathlib.Path objects can also be used. + compileall.compile_dir(pathlib.Path('Lib/'), force=True) .. seealso:: Module :mod:`py_compile` Byte-compile a single source file. diff --git a/Lib/compileall.py b/Lib/compileall.py --- a/Lib/compileall.py +++ b/Lib/compileall.py @@ -100,16 +100,18 @@ def compile_file(fullname, ddir=None, fo byte-code file. force: if True, force compilation, even if timestamps are up-to-date quiet: full output with False or 0, errors only with 1, no output with 2 legacy: if True, produce legacy pyc paths instead of PEP 3147 paths optimize: optimization level or -1 for level of the interpreter """ success = True + if quiet < 2 and isinstance(fullname, os.PathLike): + fullname = os.fspath(fullname) name = os.path.basename(fullname) if ddir is not None: dfile = os.path.join(ddir, name) else: dfile = None if rx is not None: mo = rx.search(fullname) if mo: diff --git a/Lib/test/test_compileall.py b/Lib/test/test_compileall.py --- a/Lib/test/test_compileall.py +++ b/Lib/test/test_compileall.py @@ -97,16 +97,32 @@ class CompileallTests(unittest.TestCase) os.unlink(self.bc_path2) # 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)) + 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))) + self.assertEqual(stdout.getvalue(), + "Compiling '{}'...\n".format(self.source_path)) + 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)) + def test_compile_path(self): with test.test_importlib.util.import_state(path=[self.directory]): self.assertTrue(compileall.compile_path(quiet=2)) 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))