Home | History | Annotate | Download | only in python2.7
      1 """Routine to "compile" a .py file to a .pyc (or .pyo) file.
      2 
      3 This module has intimate knowledge of the format of .pyc files.
      4 """
      5 
      6 import __builtin__
      7 import imp
      8 import marshal
      9 import os
     10 import sys
     11 import traceback
     12 
     13 MAGIC = imp.get_magic()
     14 
     15 __all__ = ["compile", "main", "PyCompileError"]
     16 
     17 
     18 class PyCompileError(Exception):
     19     """Exception raised when an error occurs while attempting to
     20     compile the file.
     21 
     22     To raise this exception, use
     23 
     24         raise PyCompileError(exc_type,exc_value,file[,msg])
     25 
     26     where
     27 
     28         exc_type:   exception type to be used in error message
     29                     type name can be accesses as class variable
     30                     'exc_type_name'
     31 
     32         exc_value:  exception value to be used in error message
     33                     can be accesses as class variable 'exc_value'
     34 
     35         file:       name of file being compiled to be used in error message
     36                     can be accesses as class variable 'file'
     37 
     38         msg:        string message to be written as error message
     39                     If no value is given, a default exception message will be given,
     40                     consistent with 'standard' py_compile output.
     41                     message (or default) can be accesses as class variable 'msg'
     42 
     43     """
     44 
     45     def __init__(self, exc_type, exc_value, file, msg=''):
     46         exc_type_name = exc_type.__name__
     47         if exc_type is SyntaxError:
     48             tbtext = ''.join(traceback.format_exception_only(exc_type, exc_value))
     49             errmsg = tbtext.replace('File "<string>"', 'File "%s"' % file)
     50         else:
     51             errmsg = "Sorry: %s: %s" % (exc_type_name,exc_value)
     52 
     53         Exception.__init__(self,msg or errmsg,exc_type_name,exc_value,file)
     54 
     55         self.exc_type_name = exc_type_name
     56         self.exc_value = exc_value
     57         self.file = file
     58         self.msg = msg or errmsg
     59 
     60     def __str__(self):
     61         return self.msg
     62 
     63 
     64 def wr_long(f, x):
     65     """Internal; write a 32-bit int to a file in little-endian order."""
     66     f.write(chr( x        & 0xff))
     67     f.write(chr((x >> 8)  & 0xff))
     68     f.write(chr((x >> 16) & 0xff))
     69     f.write(chr((x >> 24) & 0xff))
     70 
     71 def compile(file, cfile=None, dfile=None, doraise=False):
     72     """Byte-compile one Python source file to Python bytecode.
     73 
     74     Arguments:
     75 
     76     file:    source filename
     77     cfile:   target filename; defaults to source with 'c' or 'o' appended
     78              ('c' normally, 'o' in optimizing mode, giving .pyc or .pyo)
     79     dfile:   purported filename; defaults to source (this is the filename
     80              that will show up in error messages)
     81     doraise: flag indicating whether or not an exception should be
     82              raised when a compile error is found. If an exception
     83              occurs and this flag is set to False, a string
     84              indicating the nature of the exception will be printed,
     85              and the function will return to the caller. If an
     86              exception occurs and this flag is set to True, a
     87              PyCompileError exception will be raised.
     88 
     89     Note that it isn't necessary to byte-compile Python modules for
     90     execution efficiency -- Python itself byte-compiles a module when
     91     it is loaded, and if it can, writes out the bytecode to the
     92     corresponding .pyc (or .pyo) file.
     93 
     94     However, if a Python installation is shared between users, it is a
     95     good idea to byte-compile all modules upon installation, since
     96     other users may not be able to write in the source directories,
     97     and thus they won't be able to write the .pyc/.pyo file, and then
     98     they would be byte-compiling every module each time it is loaded.
     99     This can slow down program start-up considerably.
    100 
    101     See compileall.py for a script/module that uses this module to
    102     byte-compile all installed files (or all files in selected
    103     directories).
    104 
    105     """
    106     with open(file, 'U') as f:
    107         try:
    108             timestamp = long(os.fstat(f.fileno()).st_mtime)
    109         except AttributeError:
    110             timestamp = long(os.stat(file).st_mtime)
    111         codestring = f.read()
    112     try:
    113         codeobject = __builtin__.compile(codestring, dfile or file,'exec')
    114     except Exception,err:
    115         py_exc = PyCompileError(err.__class__, err, dfile or file)
    116         if doraise:
    117             raise py_exc
    118         else:
    119             sys.stderr.write(py_exc.msg + '\n')
    120             return
    121     if cfile is None:
    122         cfile = file + (__debug__ and 'c' or 'o')
    123     with open(cfile, 'wb') as fc:
    124         fc.write('\0\0\0\0')
    125         wr_long(fc, timestamp)
    126         marshal.dump(codeobject, fc)
    127         fc.flush()
    128         fc.seek(0, 0)
    129         fc.write(MAGIC)
    130 
    131 def main(args=None):
    132     """Compile several source files.
    133 
    134     The files named in 'args' (or on the command line, if 'args' is
    135     not specified) are compiled and the resulting bytecode is cached
    136     in the normal manner.  This function does not search a directory
    137     structure to locate source files; it only compiles files named
    138     explicitly.  If '-' is the only parameter in args, the list of
    139     files is taken from standard input.
    140 
    141     """
    142     if args is None:
    143         args = sys.argv[1:]
    144     rv = 0
    145     if args == ['-']:
    146         while True:
    147             filename = sys.stdin.readline()
    148             if not filename:
    149                 break
    150             filename = filename.rstrip('\n')
    151             try:
    152                 compile(filename, doraise=True)
    153             except PyCompileError as error:
    154                 rv = 1
    155                 sys.stderr.write("%s\n" % error.msg)
    156             except IOError as error:
    157                 rv = 1
    158                 sys.stderr.write("%s\n" % error)
    159     else:
    160         for filename in args:
    161             try:
    162                 compile(filename, doraise=True)
    163             except PyCompileError as error:
    164                 # return value to indicate at least one failure
    165                 rv = 1
    166                 sys.stderr.write(error.msg)
    167     return rv
    168 
    169 if __name__ == "__main__":
    170     sys.exit(main())
    171