[2] | 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 | """
|
---|
[391] | 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()
|
---|
[2] | 112 | try:
|
---|
| 113 | codeobject = __builtin__.compile(codestring, dfile or file,'exec')
|
---|
| 114 | except Exception,err:
|
---|
[391] | 115 | py_exc = PyCompileError(err.__class__, err, dfile or file)
|
---|
[2] | 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')
|
---|
[391] | 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)
|
---|
[2] | 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
|
---|
[391] | 138 | explicitly. If '-' is the only parameter in args, the list of
|
---|
| 139 | files is taken from standard input.
|
---|
[2] | 140 |
|
---|
| 141 | """
|
---|
| 142 | if args is None:
|
---|
| 143 | args = sys.argv[1:]
|
---|
| 144 | rv = 0
|
---|
[391] | 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)
|
---|
[2] | 167 | return rv
|
---|
| 168 |
|
---|
| 169 | if __name__ == "__main__":
|
---|
| 170 | sys.exit(main())
|
---|