Issue #21338: Add silent mode for compileall.

quiet parameters of compile_{dir, file, path} functions now have
a multilevel value.

Also, -q option of the CLI now have a multilevel value.

Patch by Thomas Kluyver.
This commit is contained in:
Berker Peksag 2014-10-15 11:10:57 +03:00
parent 41d31967c6
commit 6554b86b1f
4 changed files with 59 additions and 25 deletions

View File

@ -42,7 +42,8 @@ compile Python sources.
.. cmdoption:: -q
Do not print the list of files compiled, print only error messages.
Do not print the list of files compiled. If passed once, error messages will
still be printed. If passed twice (``-qq``), all output is suppressed.
.. cmdoption:: -d destdir
@ -89,6 +90,9 @@ compile Python sources.
.. versionchanged:: 3.5
Added the ``-j`` and ``-r`` options.
.. versionchanged:: 3.5
``-q`` option was changed to a multilevel value.
There is no command-line option to control the optimization level used by the
:func:`compile` function, because the Python interpreter itself already
@ -97,7 +101,7 @@ provides the option: :program:`python -O -m compileall`.
Public functions
----------------
.. function:: compile_dir(dir, maxlevels=10, ddir=None, force=False, rx=None, quiet=False, legacy=False, optimize=-1, workers=1)
.. function:: compile_dir(dir, maxlevels=10, ddir=None, force=False, rx=None, quiet=0, legacy=False, optimize=-1, workers=1)
Recursively descend the directory tree named by *dir*, compiling all :file:`.py`
files along the way.
@ -118,8 +122,9 @@ Public functions
file considered for compilation, and if it returns a true value, the file
is skipped.
If *quiet* is true, nothing is printed to the standard output unless errors
occur.
If *quiet* is ``False`` or ``0`` (the default), the filenames and other
information are printed to standard out. Set to ``1``, only errors are
printed. Set to ``2``, all output is suppressed.
If *legacy* is true, byte-code files are written to their legacy locations
and names, which may overwrite byte-code files created by another version of
@ -142,8 +147,10 @@ Public functions
.. versionchanged:: 3.5
Added the *workers* parameter.
.. versionchanged:: 3.5
*quiet* parameter was changed to a multilevel value.
.. function:: compile_file(fullname, ddir=None, force=False, rx=None, quiet=False, legacy=False, optimize=-1)
.. function:: compile_file(fullname, ddir=None, force=False, rx=None, quiet=0, legacy=False, optimize=-1)
Compile the file with path *fullname*.
@ -157,8 +164,9 @@ Public functions
file being compiled, and if it returns a true value, the file is not
compiled and ``True`` is returned.
If *quiet* is true, nothing is printed to the standard output unless errors
occur.
If *quiet* is ``False`` or ``0`` (the default), the filenames and other
information are printed to standard out. Set to ``1``, only errors are
printed. Set to ``2``, all output is suppressed.
If *legacy* is true, byte-code files are written to their legacy locations
and names, which may overwrite byte-code files created by another version of
@ -171,8 +179,10 @@ Public functions
.. versionadded:: 3.2
.. versionchanged:: 3.5
*quiet* parameter was changed to a multilevel value.
.. function:: compile_path(skip_curdir=True, maxlevels=0, force=False, legacy=False, optimize=-1)
.. function:: compile_path(skip_curdir=True, maxlevels=0, force=False, quiet=0, legacy=False, optimize=-1)
Byte-compile all the :file:`.py` files found along ``sys.path``. If
*skip_curdir* is true (the default), the current directory is not included
@ -183,6 +193,8 @@ Public functions
.. versionchanged:: 3.2
Added the *legacy* and *optimize* parameter.
.. versionchanged:: 3.5
*quiet* parameter was changed to a multilevel value.
To force a recompile of all the :file:`.py` files in the :file:`Lib/`
subdirectory and all its subdirectories::

View File

@ -24,13 +24,14 @@ from functools import partial
__all__ = ["compile_dir","compile_file","compile_path"]
def _walk_dir(dir, ddir=None, maxlevels=10, quiet=False):
def _walk_dir(dir, ddir=None, maxlevels=10, quiet=0):
if not quiet:
print('Listing {!r}...'.format(dir))
try:
names = os.listdir(dir)
except OSError:
print("Can't list {!r}".format(dir))
if quiet < 2:
print("Can't list {!r}".format(dir))
names = []
names.sort()
for name in names:
@ -49,7 +50,7 @@ def _walk_dir(dir, ddir=None, maxlevels=10, quiet=False):
maxlevels=maxlevels - 1, quiet=quiet)
def compile_dir(dir, maxlevels=10, ddir=None, force=False, rx=None,
quiet=False, legacy=False, optimize=-1, workers=1):
quiet=0, legacy=False, optimize=-1, workers=1):
"""Byte-compile all modules in the given directory tree.
Arguments (only dir is required):
@ -59,7 +60,8 @@ def compile_dir(dir, maxlevels=10, ddir=None, force=False, rx=None,
ddir: the directory that will be prepended to the path to the
file as it is compiled into each byte-code file.
force: if True, force compilation, even if timestamps are up-to-date
quiet: if True, be quiet during compilation
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
workers: maximum number of parallel workers
@ -89,7 +91,7 @@ def compile_dir(dir, maxlevels=10, ddir=None, force=False, rx=None,
success = 0
return success
def compile_file(fullname, ddir=None, force=False, rx=None, quiet=False,
def compile_file(fullname, ddir=None, force=False, rx=None, quiet=0,
legacy=False, optimize=-1):
"""Byte-compile one file.
@ -99,7 +101,8 @@ def compile_file(fullname, ddir=None, force=False, rx=None, quiet=False,
ddir: if given, the directory name compiled in to the
byte-code file.
force: if True, force compilation, even if timestamps are up-to-date
quiet: if True, be quiet during compilation
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
"""
@ -142,7 +145,10 @@ def compile_file(fullname, ddir=None, force=False, rx=None, quiet=False,
ok = py_compile.compile(fullname, cfile, dfile, True,
optimize=optimize)
except py_compile.PyCompileError as err:
if quiet:
success = 0
if quiet >= 2:
return success
elif quiet:
print('*** Error compiling {!r}...'.format(fullname))
else:
print('*** ', end='')
@ -151,20 +157,21 @@ def compile_file(fullname, ddir=None, force=False, rx=None, quiet=False,
errors='backslashreplace')
msg = msg.decode(sys.stdout.encoding)
print(msg)
success = 0
except (SyntaxError, UnicodeError, OSError) as e:
if quiet:
success = 0
if quiet >= 2:
return success
elif quiet:
print('*** Error compiling {!r}...'.format(fullname))
else:
print('*** ', end='')
print(e.__class__.__name__ + ':', e)
success = 0
else:
if ok == 0:
success = 0
return success
def compile_path(skip_curdir=1, maxlevels=0, force=False, quiet=False,
def compile_path(skip_curdir=1, maxlevels=0, force=False, quiet=0,
legacy=False, optimize=-1):
"""Byte-compile all module on sys.path.
@ -173,14 +180,15 @@ def compile_path(skip_curdir=1, maxlevels=0, force=False, quiet=False,
skip_curdir: if true, skip current directory (default True)
maxlevels: max recursion level (default 0)
force: as for compile_dir() (default False)
quiet: as for compile_dir() (default False)
quiet: as for compile_dir() (default 0)
legacy: as for compile_dir() (default False)
optimize: as for compile_dir() (default -1)
"""
success = 1
for dir in sys.path:
if (not dir or dir == os.curdir) and skip_curdir:
print('Skipping current directory')
if quiet < 2:
print('Skipping current directory')
else:
success = success and compile_dir(dir, maxlevels, None,
force, quiet=quiet,
@ -203,8 +211,9 @@ def main():
'then `-r` takes precedence.'))
parser.add_argument('-f', action='store_true', dest='force',
help='force rebuild even if timestamps are up to date')
parser.add_argument('-q', action='store_true', dest='quiet',
help='output only error messages')
parser.add_argument('-q', action='count', dest='quiet', default=0,
help='output only error messages; -qq will suppress '
'the error messages as well.')
parser.add_argument('-b', action='store_true', dest='legacy',
help='use legacy (pre-PEP3147) compiled file locations')
parser.add_argument('-d', metavar='DESTDIR', dest='ddir', default=None,
@ -250,7 +259,8 @@ def main():
for line in f:
compile_dests.append(line.strip())
except OSError:
print("Error reading file list {}".format(args.flist))
if args.quiet < 2:
print("Error reading file list {}".format(args.flist))
return False
if args.workers is not None:
@ -274,7 +284,8 @@ def main():
return compile_path(legacy=args.legacy, force=args.force,
quiet=args.quiet)
except KeyboardInterrupt:
print("\n[interrupted]")
if args.quiet < 2:
print("\n[interrupted]")
return False
return True

View File

@ -347,6 +347,13 @@ class CommandLineTests(unittest.TestCase):
self.assertNotEqual(b'', noisy)
self.assertEqual(b'', quiet)
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)
def test_regexp(self):
self.assertRunOK('-q', '-x', r'ba[^\\/]*$', self.pkgdir)
self.assertNotCompiled(self.barfn)

View File

@ -175,6 +175,10 @@ Core and Builtins
Library
-------
- Issue #21338: Add silent mode for compileall. quiet parameters of
compile_{dir, file, path} functions now have a multilevel value. Also,
-q option of the CLI now have a multilevel value. Patch by Thomas Kluyver.
- Issue #20152: Convert the array and cmath modules to Argument Clinic.
- Issue #18643: Add socket.socketpair() on Windows.