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