bpo-38731: Add --quiet option to py_compile CLI (GH-17134)

https://github.com/python/cpython/commit/daff39070e7ea71b0ba49d9150ac7a210a1... commit: daff39070e7ea71b0ba49d9150ac7a210a125682 branch: master author: Gregory Schevchenko <3405066+gvsheva@users.noreply.github.com> committer: GitHub <noreply@github.com> date: 2020-07-25T22:58:45+03:00 summary: bpo-38731: Add --quiet option to py_compile CLI (GH-17134) files: A Misc/NEWS.d/next/Library/2019-11-13-07-37-11.bpo-38731.9qmcSx.rst M Doc/library/py_compile.rst M Doc/whatsnew/3.10.rst M Lib/py_compile.py M Lib/test/test_py_compile.py M Misc/ACKS diff --git a/Doc/library/py_compile.rst b/Doc/library/py_compile.rst index a12a5bb0b1aa2..9b5c8ee645a38 100644 --- a/Doc/library/py_compile.rst +++ b/Doc/library/py_compile.rst @@ -125,21 +125,33 @@ byte-code cache files in the directory containing the source code. system external to Python like a build system. -.. function:: main(args=None) +Command-Line Interface +---------------------- - Compile several source files. The files named in *args* (or on the command - line, if *args* is ``None``) are compiled and the resulting byte-code is - cached in the normal manner. This function does not search a directory - structure to locate source files; it only compiles files named explicitly. - If ``'-'`` is the only parameter in args, the list of files is taken from - standard input. +This module can be invoked as a script to compile several source +files. The files named in *filenames* are compiled and the resulting +bytecode is cached in the normal manner. This program does not search +a directory structure to locate source files; it only compiles files +named explicitly. The exit status is nonzero if one of the files could +not be compiled. - .. versionchanged:: 3.2 - Added support for ``'-'``. +.. program:: python -m py_compile + +.. cmdoption:: <file> ... <fileN> + - + + Positional arguments are files to compile. If ``-`` is the only + parameter, the list of files is taken from standard input. + +.. cmdoption:: -q, --quiet + + Suppress errors output. + +.. versionchanged:: 3.2 + Added support for ``-``. -When this module is run as a script, the :func:`main` is used to compile all the -files named on the command line. The exit status is nonzero if one of the files -could not be compiled. +.. versionchanged:: 3.10 + Added support for :option:`-q`. .. seealso:: diff --git a/Doc/whatsnew/3.10.rst b/Doc/whatsnew/3.10.rst index e4beb600b8d51..1865fa227534a 100644 --- a/Doc/whatsnew/3.10.rst +++ b/Doc/whatsnew/3.10.rst @@ -110,6 +110,12 @@ Added the *root_dir* and *dir_fd* parameters in :func:`~glob.glob` and :func:`~glob.iglob` which allow to specify the root directory for searching. (Contributed by Serhiy Storchaka in :issue:`38144`.) +py_compile +---------- + +Added ``--quiet`` option to command-line interface of :mod:`py_compile`. +(Contributed by Gregory Schevchenko in :issue:`38731`.) + sys --- diff --git a/Lib/py_compile.py b/Lib/py_compile.py index 21736896afc21..0f9b59025cee3 100644 --- a/Lib/py_compile.py +++ b/Lib/py_compile.py @@ -173,46 +173,40 @@ def compile(file, cfile=None, dfile=None, doraise=False, optimize=-1, return cfile -def main(args=None): - """Compile several source files. - - The files named in 'args' (or on the command line, if 'args' is - not specified) are compiled and the resulting bytecode is cached - in the normal manner. This function does not search a directory - structure to locate source files; it only compiles files named - explicitly. If '-' is the only parameter in args, the list of - files is taken from standard input. - - """ - if args is None: - args = sys.argv[1:] - rv = 0 - if args == ['-']: - while True: - filename = sys.stdin.readline() - if not filename: - break - filename = filename.rstrip('\n') - try: - compile(filename, doraise=True) - except PyCompileError as error: - rv = 1 - if quiet < 2: - sys.stderr.write("%s\n" % error.msg) - except OSError as error: - rv = 1 - if quiet < 2: - sys.stderr.write("%s\n" % error) +def main(): + import argparse + + description = 'A simple command-line interface for py_compile module.' + parser = argparse.ArgumentParser(description=description) + parser.add_argument( + '-q', '--quiet', + action='store_true', + help='Suppress error output', + ) + parser.add_argument( + 'filenames', + nargs='+', + help='Files to compile', + ) + args = parser.parse_args() + if args.filenames == ['-']: + filenames = sys.stdin.readlines() else: - for filename in args: - try: - compile(filename, doraise=True) - except PyCompileError as error: - # return value to indicate at least one failure - rv = 1 - if quiet < 2: - sys.stderr.write("%s\n" % error.msg) - return rv + filenames = args.filenames + for filename in filenames: + try: + compile(filename, doraise=True) + except PyCompileError as error: + if args.quiet: + parser.exit(1) + else: + parser.exit(1, error.msg) + except OSError as error: + if args.quiet: + parser.exit(1) + else: + parser.exit(1, str(error)) + if __name__ == "__main__": - sys.exit(main()) + main() diff --git a/Lib/test/test_py_compile.py b/Lib/test/test_py_compile.py index d8ba009ea8482..009645689f423 100644 --- a/Lib/test/test_py_compile.py +++ b/Lib/test/test_py_compile.py @@ -4,12 +4,13 @@ import py_compile import shutil import stat +import subprocess import sys import tempfile import unittest from test import support -from test.support import os_helper +from test.support import os_helper, script_helper def without_source_date_epoch(fxn): @@ -217,5 +218,73 @@ class PyCompileTestsWithoutSourceEpoch(PyCompileTestsBase, pass +class PyCompileCLITestCase(unittest.TestCase): + + def setUp(self): + self.directory = tempfile.mkdtemp() + self.source_path = os.path.join(self.directory, '_test.py') + self.cache_path = importlib.util.cache_from_source(self.source_path) + with open(self.source_path, 'w') as file: + file.write('x = 123\n') + + def tearDown(self): + support.rmtree(self.directory) + + def pycompilecmd(self, *args, **kwargs): + # assert_python_* helpers don't return proc object. We'll just use + # subprocess.run() instead of spawn_python() and its friends to test + # stdin support of the CLI. + if args and args[0] == '-' and 'input' in kwargs: + return subprocess.run([sys.executable, '-m', 'py_compile', '-'], + input=kwargs['input'].encode(), + capture_output=True) + return script_helper.assert_python_ok('-m', 'py_compile', *args, **kwargs) + + def pycompilecmd_failure(self, *args): + return script_helper.assert_python_failure('-m', 'py_compile', *args) + + def test_stdin(self): + result = self.pycompilecmd('-', input=self.source_path) + self.assertEqual(result.returncode, 0) + self.assertEqual(result.stdout, b'') + self.assertEqual(result.stderr, b'') + self.assertTrue(os.path.exists(self.cache_path)) + + def test_with_files(self): + rc, stdout, stderr = self.pycompilecmd(self.source_path, self.source_path) + self.assertEqual(rc, 0) + self.assertEqual(stdout, b'') + self.assertEqual(stderr, b'') + self.assertTrue(os.path.exists(self.cache_path)) + + def test_bad_syntax(self): + bad_syntax = os.path.join(os.path.dirname(__file__), 'badsyntax_3131.py') + rc, stdout, stderr = self.pycompilecmd_failure(bad_syntax) + self.assertEqual(rc, 1) + self.assertEqual(stdout, b'') + self.assertIn(b'SyntaxError', stderr) + + def test_bad_syntax_with_quiet(self): + bad_syntax = os.path.join(os.path.dirname(__file__), 'badsyntax_3131.py') + rc, stdout, stderr = self.pycompilecmd_failure('-q', bad_syntax) + self.assertEqual(rc, 1) + self.assertEqual(stdout, b'') + self.assertEqual(stderr, b'') + + def test_file_not_exists(self): + should_not_exists = os.path.join(os.path.dirname(__file__), 'should_not_exists.py') + rc, stdout, stderr = self.pycompilecmd_failure(self.source_path, should_not_exists) + self.assertEqual(rc, 1) + self.assertEqual(stdout, b'') + self.assertIn(b'No such file or directory', stderr) + + def test_file_not_exists_with_quiet(self): + should_not_exists = os.path.join(os.path.dirname(__file__), 'should_not_exists.py') + rc, stdout, stderr = self.pycompilecmd_failure('-q', self.source_path, should_not_exists) + self.assertEqual(rc, 1) + self.assertEqual(stdout, b'') + self.assertEqual(stderr, b'') + + if __name__ == "__main__": unittest.main() diff --git a/Misc/ACKS b/Misc/ACKS index b585769608f4e..f5e9459276c86 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -1565,6 +1565,7 @@ Justin Sheehy Akash Shende Charlie Shepherd Bruce Sherwood +Gregory Shevchenko Alexander Shigin Pete Shinners Michael Shiplett diff --git a/Misc/NEWS.d/next/Library/2019-11-13-07-37-11.bpo-38731.9qmcSx.rst b/Misc/NEWS.d/next/Library/2019-11-13-07-37-11.bpo-38731.9qmcSx.rst new file mode 100644 index 0000000000000..ba9e522ecfcbf --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-11-13-07-37-11.bpo-38731.9qmcSx.rst @@ -0,0 +1,2 @@ +Add ``--quiet`` option to command-line interface of :mod:`py_compile`. +Patch by Gregory Schevchenko.
participants (1)
-
Gregory Schevchenko