Merged revisions 82952,82954 via svnmerge from
[python/dscho.git] / Lib / py_compile.py
blobcce5ac145ac88a5b76c09b101a0db370d23a4797
1 """Routine to "compile" a .py file to a .pyc (or .pyo) file.
3 This module has intimate knowledge of the format of .pyc files.
4 """
6 import builtins
7 import imp
8 import marshal
9 import os
10 import re
11 import sys
12 import traceback
14 MAGIC = imp.get_magic()
16 __all__ = ["compile", "main", "PyCompileError"]
19 class PyCompileError(Exception):
20 """Exception raised when an error occurs while attempting to
21 compile the file.
23 To raise this exception, use
25 raise PyCompileError(exc_type,exc_value,file[,msg])
27 where
29 exc_type: exception type to be used in error message
30 type name can be accesses as class variable
31 'exc_type_name'
33 exc_value: exception value to be used in error message
34 can be accesses as class variable 'exc_value'
36 file: name of file being compiled to be used in error message
37 can be accesses as class variable 'file'
39 msg: string message to be written as error message
40 If no value is given, a default exception message will be given,
41 consistent with 'standard' py_compile output.
42 message (or default) can be accesses as class variable 'msg'
44 """
46 def __init__(self, exc_type, exc_value, file, msg=''):
47 exc_type_name = exc_type.__name__
48 if exc_type is SyntaxError:
49 tbtext = ''.join(traceback.format_exception_only(exc_type, exc_value))
50 errmsg = tbtext.replace('File "<string>"', 'File "%s"' % file)
51 else:
52 errmsg = "Sorry: %s: %s" % (exc_type_name,exc_value)
54 Exception.__init__(self,msg or errmsg,exc_type_name,exc_value,file)
56 self.exc_type_name = exc_type_name
57 self.exc_value = exc_value
58 self.file = file
59 self.msg = msg or errmsg
61 def __str__(self):
62 return self.msg
65 # Define an internal helper according to the platform
66 if os.name == "mac":
67 import MacOS
68 def set_creator_type(file):
69 MacOS.SetCreatorAndType(file, 'Pyth', 'PYC ')
70 else:
71 def set_creator_type(file):
72 pass
74 def wr_long(f, x):
75 """Internal; write a 32-bit int to a file in little-endian order."""
76 f.write(bytes([x & 0xff,
77 (x >> 8) & 0xff,
78 (x >> 16) & 0xff,
79 (x >> 24) & 0xff]))
81 def read_encoding(file, default):
82 """Read the first two lines of the file looking for coding: xyzzy."""
83 f = open(file, "rb")
84 try:
85 for i in range(2):
86 line = f.readline()
87 if not line:
88 break
89 m = re.match(br".*\bcoding:\s*(\S+)\b", line)
90 if m:
91 return m.group(1).decode("ascii")
92 return default
93 finally:
94 f.close()
96 def compile(file, cfile=None, dfile=None, doraise=False):
97 """Byte-compile one Python source file to Python bytecode.
99 Arguments:
101 file: source filename
102 cfile: target filename; defaults to source with 'c' or 'o' appended
103 ('c' normally, 'o' in optimizing mode, giving .pyc or .pyo)
104 dfile: purported filename; defaults to source (this is the filename
105 that will show up in error messages)
106 doraise: flag indicating whether or not an exception should be
107 raised when a compile error is found. If an exception
108 occurs and this flag is set to False, a string
109 indicating the nature of the exception will be printed,
110 and the function will return to the caller. If an
111 exception occurs and this flag is set to True, a
112 PyCompileError exception will be raised.
114 Note that it isn't necessary to byte-compile Python modules for
115 execution efficiency -- Python itself byte-compiles a module when
116 it is loaded, and if it can, writes out the bytecode to the
117 corresponding .pyc (or .pyo) file.
119 However, if a Python installation is shared between users, it is a
120 good idea to byte-compile all modules upon installation, since
121 other users may not be able to write in the source directories,
122 and thus they won't be able to write the .pyc/.pyo file, and then
123 they would be byte-compiling every module each time it is loaded.
124 This can slow down program start-up considerably.
126 See compileall.py for a script/module that uses this module to
127 byte-compile all installed files (or all files in selected
128 directories).
131 encoding = read_encoding(file, "utf-8")
132 f = open(file, 'U', encoding=encoding)
133 try:
134 timestamp = int(os.fstat(f.fileno()).st_mtime)
135 except AttributeError:
136 timestamp = int(os.stat(file).st_mtime)
137 codestring = f.read()
138 f.close()
139 if codestring and codestring[-1] != '\n':
140 codestring = codestring + '\n'
141 try:
142 codeobject = builtins.compile(codestring, dfile or file,'exec')
143 except Exception as err:
144 py_exc = PyCompileError(err.__class__, err, dfile or file)
145 if doraise:
146 raise py_exc
147 else:
148 sys.stderr.write(py_exc.msg + '\n')
149 return
150 if cfile is None:
151 cfile = file + (__debug__ and 'c' or 'o')
152 fc = open(cfile, 'wb')
153 fc.write(b'\0\0\0\0')
154 wr_long(fc, timestamp)
155 marshal.dump(codeobject, fc)
156 fc.flush()
157 fc.seek(0, 0)
158 fc.write(MAGIC)
159 fc.close()
160 set_creator_type(cfile)
162 def main(args=None):
163 """Compile several source files.
165 The files named in 'args' (or on the command line, if 'args' is
166 not specified) are compiled and the resulting bytecode is cached
167 in the normal manner. This function does not search a directory
168 structure to locate source files; it only compiles files named
169 explicitly.
172 if args is None:
173 args = sys.argv[1:]
174 rv = 0
175 for filename in args:
176 try:
177 compile(filename, doraise=True)
178 except PyCompileError as err:
179 # return value to indicate at least one failure
180 rv = 1
181 sys.stderr.write(err.msg)
182 return rv
184 if __name__ == "__main__":
185 sys.exit(main())