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("%s\n" % error.msg) |
---|
167 | return rv |
---|
168 | |
---|
169 | if __name__ == "__main__": |
---|
170 | sys.exit(main()) |
---|