Commit | Line | Data |
---|---|---|
920dae64 AT |
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) | |
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 | for filename in args: | |
158 | try: | |
159 | compile(filename, doraise=True) | |
160 | except PyCompileError,err: | |
161 | sys.stderr.write(err.msg) | |
162 | ||
163 | if __name__ == "__main__": | |
164 | main() |