14710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm"""Routine to "compile" a .py file to a .pyc (or .pyo) file.
24710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
34710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmThis module has intimate knowledge of the format of .pyc files.
44710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm"""
54710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
64710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmimport __builtin__
74710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmimport imp
84710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmimport marshal
94710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmimport os
104710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmimport sys
114710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmimport traceback
124710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
134710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmMAGIC = imp.get_magic()
144710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
154710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm__all__ = ["compile", "main", "PyCompileError"]
164710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
174710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
184710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmclass PyCompileError(Exception):
194710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    """Exception raised when an error occurs while attempting to
204710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    compile the file.
214710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
224710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    To raise this exception, use
234710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
244710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        raise PyCompileError(exc_type,exc_value,file[,msg])
254710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
264710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    where
274710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
284710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        exc_type:   exception type to be used in error message
294710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                    type name can be accesses as class variable
304710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                    'exc_type_name'
314710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
324710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        exc_value:  exception value to be used in error message
334710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                    can be accesses as class variable 'exc_value'
344710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
354710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        file:       name of file being compiled to be used in error message
364710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                    can be accesses as class variable 'file'
374710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
384710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        msg:        string message to be written as error message
394710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                    If no value is given, a default exception message will be given,
404710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                    consistent with 'standard' py_compile output.
414710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                    message (or default) can be accesses as class variable 'msg'
424710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
434710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    """
444710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
454710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def __init__(self, exc_type, exc_value, file, msg=''):
464710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        exc_type_name = exc_type.__name__
474710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if exc_type is SyntaxError:
484710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            tbtext = ''.join(traceback.format_exception_only(exc_type, exc_value))
494710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            errmsg = tbtext.replace('File "<string>"', 'File "%s"' % file)
504710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        else:
514710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            errmsg = "Sorry: %s: %s" % (exc_type_name,exc_value)
524710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
534710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        Exception.__init__(self,msg or errmsg,exc_type_name,exc_value,file)
544710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
554710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.exc_type_name = exc_type_name
564710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.exc_value = exc_value
574710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.file = file
584710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        self.msg = msg or errmsg
594710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
604710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    def __str__(self):
614710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        return self.msg
624710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
634710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
644710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmdef wr_long(f, x):
654710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    """Internal; write a 32-bit int to a file in little-endian order."""
664710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    f.write(chr( x        & 0xff))
674710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    f.write(chr((x >> 8)  & 0xff))
684710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    f.write(chr((x >> 16) & 0xff))
694710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    f.write(chr((x >> 24) & 0xff))
704710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
714710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmdef compile(file, cfile=None, dfile=None, doraise=False):
724710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    """Byte-compile one Python source file to Python bytecode.
734710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
744710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    Arguments:
754710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
764710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    file:    source filename
774710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    cfile:   target filename; defaults to source with 'c' or 'o' appended
784710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm             ('c' normally, 'o' in optimizing mode, giving .pyc or .pyo)
794710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    dfile:   purported filename; defaults to source (this is the filename
804710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm             that will show up in error messages)
814710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    doraise: flag indicating whether or not an exception should be
824710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm             raised when a compile error is found. If an exception
834710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm             occurs and this flag is set to False, a string
844710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm             indicating the nature of the exception will be printed,
854710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm             and the function will return to the caller. If an
864710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm             exception occurs and this flag is set to True, a
874710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm             PyCompileError exception will be raised.
884710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
894710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    Note that it isn't necessary to byte-compile Python modules for
904710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    execution efficiency -- Python itself byte-compiles a module when
914710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    it is loaded, and if it can, writes out the bytecode to the
924710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    corresponding .pyc (or .pyo) file.
934710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
944710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    However, if a Python installation is shared between users, it is a
954710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    good idea to byte-compile all modules upon installation, since
964710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    other users may not be able to write in the source directories,
974710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    and thus they won't be able to write the .pyc/.pyo file, and then
984710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    they would be byte-compiling every module each time it is loaded.
994710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    This can slow down program start-up considerably.
1004710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
1014710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    See compileall.py for a script/module that uses this module to
1024710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    byte-compile all installed files (or all files in selected
1034710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    directories).
1044710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
1054710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    """
1064710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    with open(file, 'U') as f:
1074710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        try:
1084710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            timestamp = long(os.fstat(f.fileno()).st_mtime)
1094710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        except AttributeError:
1104710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            timestamp = long(os.stat(file).st_mtime)
1114710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        codestring = f.read()
1124710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    try:
1134710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        codeobject = __builtin__.compile(codestring, dfile or file,'exec')
1144710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    except Exception,err:
1154710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        py_exc = PyCompileError(err.__class__,err.args,dfile or file)
1164710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        if doraise:
1174710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            raise py_exc
1184710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        else:
1194710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            sys.stderr.write(py_exc.msg + '\n')
1204710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            return
1214710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    if cfile is None:
1224710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        cfile = file + (__debug__ and 'c' or 'o')
1234710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    with open(cfile, 'wb') as fc:
1244710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        fc.write('\0\0\0\0')
1254710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        wr_long(fc, timestamp)
1264710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        marshal.dump(codeobject, fc)
1274710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        fc.flush()
1284710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        fc.seek(0, 0)
1294710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        fc.write(MAGIC)
1304710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
1314710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmdef main(args=None):
1324710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    """Compile several source files.
1334710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
1344710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    The files named in 'args' (or on the command line, if 'args' is
1354710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    not specified) are compiled and the resulting bytecode is cached
1364710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    in the normal manner.  This function does not search a directory
1374710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    structure to locate source files; it only compiles files named
1384710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    explicitly.  If '-' is the only parameter in args, the list of
1394710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    files is taken from standard input.
1404710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
1414710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    """
1424710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    if args is None:
1434710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        args = sys.argv[1:]
1444710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    rv = 0
1454710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    if args == ['-']:
1464710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        while True:
1474710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            filename = sys.stdin.readline()
1484710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            if not filename:
1494710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                break
1504710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            filename = filename.rstrip('\n')
1514710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            try:
1524710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                compile(filename, doraise=True)
1534710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            except PyCompileError as error:
1544710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                rv = 1
1554710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                sys.stderr.write("%s\n" % error.msg)
1564710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            except IOError as error:
1574710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                rv = 1
1584710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                sys.stderr.write("%s\n" % error)
1594710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    else:
1604710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm        for filename in args:
1614710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            try:
1624710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                compile(filename, doraise=True)
1634710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm            except PyCompileError as error:
1644710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                # return value to indicate at least one failure
1654710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                rv = 1
1664710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm                sys.stderr.write(error.msg)
1674710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    return rv
1684710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm
1694710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylmif __name__ == "__main__":
1704710c53dcad1ebf3755f3efb9e80ac24bd72a9b2darylm    sys.exit(main())
171