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