Back to index

python3.2  3.2.2
compileall.py
Go to the documentation of this file.
00001 """Module/script to byte-compile all .py files to .pyc (or .pyo) files.
00002 
00003 When called as a script with arguments, this compiles the directories
00004 given as arguments recursively; the -l option prevents it from
00005 recursing into directories.
00006 
00007 Without arguments, if compiles all modules on sys.path, without
00008 recursing into subdirectories.  (Even though it should do so for
00009 packages -- for now, you'll have to deal with packages separately.)
00010 
00011 See module py_compile for details of the actual byte-compilation.
00012 """
00013 import os
00014 import sys
00015 import errno
00016 import imp
00017 import py_compile
00018 import struct
00019 
00020 __all__ = ["compile_dir","compile_file","compile_path"]
00021 
00022 def compile_dir(dir, maxlevels=10, ddir=None, force=False, rx=None,
00023                 quiet=False, legacy=False, optimize=-1):
00024     """Byte-compile all modules in the given directory tree.
00025 
00026     Arguments (only dir is required):
00027 
00028     dir:       the directory to byte-compile
00029     maxlevels: maximum recursion level (default 10)
00030     ddir:      the directory that will be prepended to the path to the
00031                file as it is compiled into each byte-code file.
00032     force:     if True, force compilation, even if timestamps are up-to-date
00033     quiet:     if True, be quiet during compilation
00034     legacy:    if True, produce legacy pyc paths instead of PEP 3147 paths
00035     optimize:  optimization level or -1 for level of the interpreter
00036     """
00037     if not quiet:
00038         print('Listing {!r}...'.format(dir))
00039     try:
00040         names = os.listdir(dir)
00041     except os.error:
00042         print("Can't list {!r}".format(dir))
00043         names = []
00044     names.sort()
00045     success = 1
00046     for name in names:
00047         if name == '__pycache__':
00048             continue
00049         fullname = os.path.join(dir, name)
00050         if ddir is not None:
00051             dfile = os.path.join(ddir, name)
00052         else:
00053             dfile = None
00054         if not os.path.isdir(fullname):
00055             if not compile_file(fullname, ddir, force, rx, quiet,
00056                                 legacy, optimize):
00057                 success = 0
00058         elif (maxlevels > 0 and name != os.curdir and name != os.pardir and
00059               os.path.isdir(fullname) and not os.path.islink(fullname)):
00060             if not compile_dir(fullname, maxlevels - 1, dfile, force, rx,
00061                                quiet, legacy, optimize):
00062                 success = 0
00063     return success
00064 
00065 def compile_file(fullname, ddir=None, force=False, rx=None, quiet=False,
00066                  legacy=False, optimize=-1):
00067     """Byte-compile one file.
00068 
00069     Arguments (only fullname is required):
00070 
00071     fullname:  the file to byte-compile
00072     ddir:      if given, the directory name compiled in to the
00073                byte-code file.
00074     force:     if True, force compilation, even if timestamps are up-to-date
00075     quiet:     if True, be quiet during compilation
00076     legacy:    if True, produce legacy pyc paths instead of PEP 3147 paths
00077     optimize:  optimization level or -1 for level of the interpreter
00078     """
00079     success = 1
00080     name = os.path.basename(fullname)
00081     if ddir is not None:
00082         dfile = os.path.join(ddir, name)
00083     else:
00084         dfile = None
00085     if rx is not None:
00086         mo = rx.search(fullname)
00087         if mo:
00088             return success
00089     if os.path.isfile(fullname):
00090         if legacy:
00091             cfile = fullname + ('c' if __debug__ else 'o')
00092         else:
00093             if optimize >= 0:
00094                 cfile = imp.cache_from_source(fullname,
00095                                               debug_override=not optimize)
00096             else:
00097                 cfile = imp.cache_from_source(fullname)
00098             cache_dir = os.path.dirname(cfile)
00099         head, tail = name[:-3], name[-3:]
00100         if tail == '.py':
00101             if not force:
00102                 try:
00103                     mtime = int(os.stat(fullname).st_mtime)
00104                     expect = struct.pack('<4sl', imp.get_magic(), mtime)
00105                     with open(cfile, 'rb') as chandle:
00106                         actual = chandle.read(8)
00107                     if expect == actual:
00108                         return success
00109                 except IOError:
00110                     pass
00111             if not quiet:
00112                 print('Compiling {!r}...'.format(fullname))
00113             try:
00114                 ok = py_compile.compile(fullname, cfile, dfile, True,
00115                                         optimize=optimize)
00116             except py_compile.PyCompileError as err:
00117                 if quiet:
00118                     print('*** Error compiling {!r}...'.format(fullname))
00119                 else:
00120                     print('*** ', end='')
00121                 # escape non-printable characters in msg
00122                 msg = err.msg.encode(sys.stdout.encoding,
00123                                      errors='backslashreplace')
00124                 msg = msg.decode(sys.stdout.encoding)
00125                 print(msg)
00126                 success = 0
00127             except (SyntaxError, UnicodeError, IOError) as e:
00128                 if quiet:
00129                     print('*** Error compiling {!r}...'.format(fullname))
00130                 else:
00131                     print('*** ', end='')
00132                 print(e.__class__.__name__ + ':', e)
00133                 success = 0
00134             else:
00135                 if ok == 0:
00136                     success = 0
00137     return success
00138 
00139 def compile_path(skip_curdir=1, maxlevels=0, force=False, quiet=False,
00140                  legacy=False, optimize=-1):
00141     """Byte-compile all module on sys.path.
00142 
00143     Arguments (all optional):
00144 
00145     skip_curdir: if true, skip current directory (default true)
00146     maxlevels:   max recursion level (default 0)
00147     force: as for compile_dir() (default False)
00148     quiet: as for compile_dir() (default False)
00149     legacy: as for compile_dir() (default False)
00150     optimize: as for compile_dir() (default -1)
00151     """
00152     success = 1
00153     for dir in sys.path:
00154         if (not dir or dir == os.curdir) and skip_curdir:
00155             print('Skipping current directory')
00156         else:
00157             success = success and compile_dir(dir, maxlevels, None,
00158                                               force, quiet=quiet,
00159                                               legacy=legacy, optimize=optimize)
00160     return success
00161 
00162 
00163 def main():
00164     """Script main program."""
00165     import argparse
00166 
00167     parser = argparse.ArgumentParser(
00168         description='Utilities to support installing Python libraries.')
00169     parser.add_argument('-l', action='store_const', const=0,
00170                         default=10, dest='maxlevels',
00171                         help="don't recurse into subdirectories")
00172     parser.add_argument('-f', action='store_true', dest='force',
00173                         help='force rebuild even if timestamps are up to date')
00174     parser.add_argument('-q', action='store_true', dest='quiet',
00175                         help='output only error messages')
00176     parser.add_argument('-b', action='store_true', dest='legacy',
00177                         help='use legacy (pre-PEP3147) compiled file locations')
00178     parser.add_argument('-d', metavar='DESTDIR',  dest='ddir', default=None,
00179                         help=('directory to prepend to file paths for use in '
00180                               'compile time tracebacks and in runtime '
00181                               'tracebacks in cases where the source file is '
00182                               'unavailable'))
00183     parser.add_argument('-x', metavar='REGEXP', dest='rx', default=None,
00184                         help=('skip files matching the regular expression. '
00185                               'The regexp is searched for in the full path '
00186                               'to each file considered for compilation.'))
00187     parser.add_argument('-i', metavar='FILE', dest='flist',
00188                         help=('add all the files and directories listed in '
00189                               'FILE to the list considered for compilation. '
00190                               'If "-", names are read from stdin.'))
00191     parser.add_argument('compile_dest', metavar='FILE|DIR', nargs='*',
00192                         help=('zero or more file and directory names '
00193                               'to compile; if no arguments given, defaults '
00194                               'to the equivalent of -l sys.path'))
00195     args = parser.parse_args()
00196 
00197     compile_dests = args.compile_dest
00198 
00199     if (args.ddir and (len(compile_dests) != 1
00200             or not os.path.isdir(compile_dests[0]))):
00201         parser.exit('-d destdir requires exactly one directory argument')
00202     if args.rx:
00203         import re
00204         args.rx = re.compile(args.rx)
00205 
00206     # if flist is provided then load it
00207     if args.flist:
00208         try:
00209             with (sys.stdin if args.flist=='-' else open(args.flist)) as f:
00210                 for line in f:
00211                     compile_dests.append(line.strip())
00212         except EnvironmentError:
00213             print("Error reading file list {}".format(args.flist))
00214             return False
00215 
00216     success = True
00217     try:
00218         if compile_dests:
00219             for dest in compile_dests:
00220                 if os.path.isfile(dest):
00221                     if not compile_file(dest, args.ddir, args.force, args.rx,
00222                                         args.quiet, args.legacy):
00223                         success = False
00224                 else:
00225                     if not compile_dir(dest, args.maxlevels, args.ddir,
00226                                        args.force, args.rx, args.quiet,
00227                                        args.legacy):
00228                         success = False
00229             return success
00230         else:
00231             return compile_path(legacy=args.legacy)
00232     except KeyboardInterrupt:
00233         print("\n[interrupted]")
00234         return False
00235     return True
00236 
00237 
00238 if __name__ == '__main__':
00239     exit_status = int(not main())
00240     sys.exit(exit_status)