|
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 # Define an internal helper according to the platform |
|
65 if os.name == "mac": |
|
66 import MacOS |
|
67 def set_creator_type(file): |
|
68 MacOS.SetCreatorAndType(file, 'Pyth', 'PYC ') |
|
69 else: |
|
70 def set_creator_type(file): |
|
71 pass |
|
72 |
|
73 def wr_long(f, x): |
|
74 """Internal; write a 32-bit int to a file in little-endian order.""" |
|
75 f.write(chr( x & 0xff)) |
|
76 f.write(chr((x >> 8) & 0xff)) |
|
77 f.write(chr((x >> 16) & 0xff)) |
|
78 f.write(chr((x >> 24) & 0xff)) |
|
79 |
|
80 def compile(file, cfile=None, dfile=None, doraise=False): |
|
81 """Byte-compile one Python source file to Python bytecode. |
|
82 |
|
83 Arguments: |
|
84 |
|
85 file: source filename |
|
86 cfile: target filename; defaults to source with 'c' or 'o' appended |
|
87 ('c' normally, 'o' in optimizing mode, giving .pyc or .pyo) |
|
88 dfile: purported filename; defaults to source (this is the filename |
|
89 that will show up in error messages) |
|
90 doraise: flag indicating whether or not an exception should be |
|
91 raised when a compile error is found. If an exception |
|
92 occurs and this flag is set to False, a string |
|
93 indicating the nature of the exception will be printed, |
|
94 and the function will return to the caller. If an |
|
95 exception occurs and this flag is set to True, a |
|
96 PyCompileError exception will be raised. |
|
97 |
|
98 Note that it isn't necessary to byte-compile Python modules for |
|
99 execution efficiency -- Python itself byte-compiles a module when |
|
100 it is loaded, and if it can, writes out the bytecode to the |
|
101 corresponding .pyc (or .pyo) file. |
|
102 |
|
103 However, if a Python installation is shared between users, it is a |
|
104 good idea to byte-compile all modules upon installation, since |
|
105 other users may not be able to write in the source directories, |
|
106 and thus they won't be able to write the .pyc/.pyo file, and then |
|
107 they would be byte-compiling every module each time it is loaded. |
|
108 This can slow down program start-up considerably. |
|
109 |
|
110 See compileall.py for a script/module that uses this module to |
|
111 byte-compile all installed files (or all files in selected |
|
112 directories). |
|
113 |
|
114 """ |
|
115 f = open(file, 'U') |
|
116 try: |
|
117 timestamp = long(os.fstat(f.fileno()).st_mtime) |
|
118 except AttributeError: |
|
119 timestamp = long(os.stat(file).st_mtime) |
|
120 codestring = f.read() |
|
121 f.close() |
|
122 if codestring and codestring[-1] != '\n': |
|
123 codestring = codestring + '\n' |
|
124 try: |
|
125 codeobject = __builtin__.compile(codestring, dfile or file,'exec') |
|
126 except Exception,err: |
|
127 py_exc = PyCompileError(err.__class__,err.args,dfile or file) |
|
128 if doraise: |
|
129 raise py_exc |
|
130 else: |
|
131 sys.stderr.write(py_exc.msg + '\n') |
|
132 return |
|
133 if cfile is None: |
|
134 cfile = file + (__debug__ and 'c' or 'o') |
|
135 fc = open(cfile, 'wb') |
|
136 fc.write('\0\0\0\0') |
|
137 wr_long(fc, timestamp) |
|
138 marshal.dump(codeobject, fc) |
|
139 fc.flush() |
|
140 fc.seek(0, 0) |
|
141 fc.write(MAGIC) |
|
142 fc.close() |
|
143 set_creator_type(cfile) |
|
144 |
|
145 def main(args=None): |
|
146 """Compile several source files. |
|
147 |
|
148 The files named in 'args' (or on the command line, if 'args' is |
|
149 not specified) are compiled and the resulting bytecode is cached |
|
150 in the normal manner. This function does not search a directory |
|
151 structure to locate source files; it only compiles files named |
|
152 explicitly. |
|
153 |
|
154 """ |
|
155 if args is None: |
|
156 args = sys.argv[1:] |
|
157 rv = 0 |
|
158 for filename in args: |
|
159 try: |
|
160 compile(filename, doraise=True) |
|
161 except PyCompileError, err: |
|
162 # return value to indicate at least one failure |
|
163 rv = 1 |
|
164 sys.stderr.write(err.msg) |
|
165 return rv |
|
166 |
|
167 if __name__ == "__main__": |
|
168 sys.exit(main()) |