Left: | ||
Right: |
OLD | NEW |
---|---|
1 """Routine to "compile" a .py file to a .pyc (or .pyo) file. | 1 """Routine to "compile" a .py file to a .pyc (or .pyo) file. |
2 | 2 |
3 This module has intimate knowledge of the format of .pyc files. | 3 This module has intimate knowledge of the format of .pyc files. |
4 """ | 4 """ |
5 | 5 |
6 import builtins | 6 import builtins |
7 import errno | |
7 import imp | 8 import imp |
8 import marshal | 9 import marshal |
9 import os | 10 import os |
10 import sys | 11 import sys |
11 import tokenize | 12 import tokenize |
12 import traceback | 13 import traceback |
13 | 14 |
15 from imp import cache_from_source | |
Benjamin
2010/04/07 02:50:04
What's the benefit on having this in the module na
barry
2010/04/08 12:53:47
Done.
| |
16 | |
14 MAGIC = imp.get_magic() | 17 MAGIC = imp.get_magic() |
15 | 18 |
16 __all__ = ["compile", "main", "PyCompileError"] | 19 __all__ = ["compile", "main", "PyCompileError"] |
17 | 20 |
18 | 21 |
19 class PyCompileError(Exception): | 22 class PyCompileError(Exception): |
20 """Exception raised when an error occurs while attempting to | 23 """Exception raised when an error occurs while attempting to |
21 compile the file. | 24 compile the file. |
22 | 25 |
23 To raise this exception, use | 26 To raise this exception, use |
24 | 27 |
25 raise PyCompileError(exc_type,exc_value,file[,msg]) | 28 raise PyCompileError(exc_type,exc_value,file[,msg]) |
26 | 29 |
27 where | 30 where |
28 | 31 |
29 exc_type: exception type to be used in error message | 32 exc_type: exception type to be used in error message |
30 type name can be accesses as class variable | 33 type name can be accesses as class variable |
31 'exc_type_name' | 34 'exc_type_name' |
32 | 35 |
33 exc_value: exception value to be used in error message | 36 exc_value: exception value to be used in error message |
34 can be accesses as class variable 'exc_value' | 37 can be accesses as class variable 'exc_value' |
35 | 38 |
36 file: name of file being compiled to be used in error message | 39 file: name of file being compiled to be used in error message |
37 can be accesses as class variable 'file' | 40 can be accesses as class variable 'file' |
38 | 41 |
39 msg: string message to be written as error message | 42 msg: string message to be written as error message |
40 If no value is given, a default exception message will be gi ven, | 43 If no value is given, a default exception message will be |
41 consistent with 'standard' py_compile output. | 44 given, consistent with 'standard' py_compile output. |
42 message (or default) can be accesses as class variable 'msg' | 45 message (or default) can be accesses as class variable |
46 'msg' | |
43 | 47 |
44 """ | 48 """ |
45 | 49 |
46 def __init__(self, exc_type, exc_value, file, msg=''): | 50 def __init__(self, exc_type, exc_value, file, msg=''): |
47 exc_type_name = exc_type.__name__ | 51 exc_type_name = exc_type.__name__ |
48 if exc_type is SyntaxError: | 52 if exc_type is SyntaxError: |
49 tbtext = ''.join(traceback.format_exception_only(exc_type, exc_value )) | 53 tbtext = ''.join(traceback.format_exception_only( |
54 exc_type, exc_value)) | |
50 errmsg = tbtext.replace('File "<string>"', 'File "%s"' % file) | 55 errmsg = tbtext.replace('File "<string>"', 'File "%s"' % file) |
51 else: | 56 else: |
52 errmsg = "Sorry: %s: %s" % (exc_type_name,exc_value) | 57 errmsg = "Sorry: %s: %s" % (exc_type_name,exc_value) |
53 | 58 |
54 Exception.__init__(self,msg or errmsg,exc_type_name,exc_value,file) | 59 Exception.__init__(self,msg or errmsg,exc_type_name,exc_value,file) |
55 | 60 |
56 self.exc_type_name = exc_type_name | 61 self.exc_type_name = exc_type_name |
57 self.exc_value = exc_value | 62 self.exc_value = exc_value |
58 self.file = file | 63 self.file = file |
59 self.msg = msg or errmsg | 64 self.msg = msg or errmsg |
60 | 65 |
61 def __str__(self): | 66 def __str__(self): |
62 return self.msg | 67 return self.msg |
63 | 68 |
64 | 69 |
65 def wr_long(f, x): | 70 def wr_long(f, x): |
66 """Internal; write a 32-bit int to a file in little-endian order.""" | 71 """Internal; write a 32-bit int to a file in little-endian order.""" |
67 f.write(bytes([x & 0xff, | 72 f.write(bytes([x & 0xff, |
68 (x >> 8) & 0xff, | 73 (x >> 8) & 0xff, |
69 (x >> 16) & 0xff, | 74 (x >> 16) & 0xff, |
70 (x >> 24) & 0xff])) | 75 (x >> 24) & 0xff])) |
71 | 76 |
72 def compile(file, cfile=None, dfile=None, doraise=False): | 77 def compile(file, cfile=None, dfile=None, doraise=False): |
73 """Byte-compile one Python source file to Python bytecode. | 78 """Byte-compile one Python source file to Python bytecode. |
74 | 79 |
75 Arguments: | 80 :param file: The source file name. |
Benjamin
2010/04/07 02:50:04
Doc/library/py_compile.rst should be updated, too.
barry
2010/04/08 12:53:47
I've been putting off updating the docs, but in th
| |
76 | 81 :param cfile: The target byte compiled file name. When not given, this |
77 file: source filename | 82 defaults to the PEP 3147 location. |
78 cfile: target filename; defaults to source with 'c' or 'o' appended | 83 :param dfile: Purported file name, i.e. the file name that shows up in |
79 ('c' normally, 'o' in optimizing mode, giving .pyc or .pyo) | 84 error messages. Defaults to the source file name. |
80 dfile: purported filename; defaults to source (this is the filename | 85 :param doraise: Flag indicating whether or not an exception should be |
81 that will show up in error messages) | 86 raised when a compile error is found. If an exception occurs and this |
82 doraise: flag indicating whether or not an exception should be | 87 flag is set to False, a string indicating the nature of the exception |
83 raised when a compile error is found. If an exception | 88 will be printed, and the function will return to the caller. If an |
84 occurs and this flag is set to False, a string | 89 exception occurs and this flag is set to True, a PyCompileError |
85 indicating the nature of the exception will be printed, | 90 exception will be raised. |
86 and the function will return to the caller. If an | 91 :return: Path to the resulting byte compiled file. |
87 exception occurs and this flag is set to True, a | |
88 PyCompileError exception will be raised. | |
89 | 92 |
90 Note that it isn't necessary to byte-compile Python modules for | 93 Note that it isn't necessary to byte-compile Python modules for |
91 execution efficiency -- Python itself byte-compiles a module when | 94 execution efficiency -- Python itself byte-compiles a module when |
92 it is loaded, and if it can, writes out the bytecode to the | 95 it is loaded, and if it can, writes out the bytecode to the |
93 corresponding .pyc (or .pyo) file. | 96 corresponding .pyc (or .pyo) file. |
94 | 97 |
95 However, if a Python installation is shared between users, it is a | 98 However, if a Python installation is shared between users, it is a |
96 good idea to byte-compile all modules upon installation, since | 99 good idea to byte-compile all modules upon installation, since |
97 other users may not be able to write in the source directories, | 100 other users may not be able to write in the source directories, |
98 and thus they won't be able to write the .pyc/.pyo file, and then | 101 and thus they won't be able to write the .pyc/.pyo file, and then |
99 they would be byte-compiling every module each time it is loaded. | 102 they would be byte-compiling every module each time it is loaded. |
100 This can slow down program start-up considerably. | 103 This can slow down program start-up considerably. |
101 | 104 |
102 See compileall.py for a script/module that uses this module to | 105 See compileall.py for a script/module that uses this module to |
103 byte-compile all installed files (or all files in selected | 106 byte-compile all installed files (or all files in selected |
104 directories). | 107 directories). |
105 | |
106 """ | 108 """ |
107 with open(file, "rb") as f: | 109 with open(file, "rb") as f: |
108 encoding = tokenize.detect_encoding(f.readline)[0] | 110 encoding = tokenize.detect_encoding(f.readline)[0] |
109 with open(file, encoding=encoding) as f: | 111 with open(file, encoding=encoding) as f: |
110 try: | 112 try: |
111 timestamp = int(os.fstat(f.fileno()).st_mtime) | 113 timestamp = int(os.fstat(f.fileno()).st_mtime) |
112 except AttributeError: | 114 except AttributeError: |
113 timestamp = int(os.stat(file).st_mtime) | 115 timestamp = int(os.stat(file).st_mtime) |
114 codestring = f.read() | 116 codestring = f.read() |
115 try: | 117 try: |
116 codeobject = builtins.compile(codestring, dfile or file,'exec') | 118 codeobject = builtins.compile(codestring, dfile or file,'exec') |
117 except Exception as err: | 119 except Exception as err: |
118 py_exc = PyCompileError(err.__class__, err, dfile or file) | 120 py_exc = PyCompileError(err.__class__, err, dfile or file) |
119 if doraise: | 121 if doraise: |
120 raise py_exc | 122 raise py_exc |
121 else: | 123 else: |
122 sys.stderr.write(py_exc.msg + '\n') | 124 sys.stderr.write(py_exc.msg + '\n') |
123 return | 125 return |
124 if cfile is None: | 126 if cfile is None: |
125 cfile = file + (__debug__ and 'c' or 'o') | 127 cfile = cache_from_source(file) |
128 try: | |
129 os.mkdir(os.path.dirname(cfile)) | |
130 except OSError as error: | |
131 if error.errno != errno.EEXIST: | |
132 raise | |
126 with open(cfile, 'wb') as fc: | 133 with open(cfile, 'wb') as fc: |
127 fc.write(b'\0\0\0\0') | 134 fc.write(b'\0\0\0\0') |
128 wr_long(fc, timestamp) | 135 wr_long(fc, timestamp) |
129 marshal.dump(codeobject, fc) | 136 marshal.dump(codeobject, fc) |
130 fc.flush() | 137 fc.flush() |
131 fc.seek(0, 0) | 138 fc.seek(0, 0) |
132 fc.write(MAGIC) | 139 fc.write(MAGIC) |
140 return cfile | |
133 | 141 |
134 def main(args=None): | 142 def main(args=None): |
135 """Compile several source files. | 143 """Compile several source files. |
136 | 144 |
137 The files named in 'args' (or on the command line, if 'args' is | 145 The files named in 'args' (or on the command line, if 'args' is |
138 not specified) are compiled and the resulting bytecode is cached | 146 not specified) are compiled and the resulting bytecode is cached |
139 in the normal manner. This function does not search a directory | 147 in the normal manner. This function does not search a directory |
140 structure to locate source files; it only compiles files named | 148 structure to locate source files; it only compiles files named |
141 explicitly. If '-' is the only parameter in args, the list of | 149 explicitly. If '-' is the only parameter in args, the list of |
142 files is taken from standard input. | 150 files is taken from standard input. |
(...skipping 21 matching lines...) Expand all Loading... | |
164 try: | 172 try: |
165 compile(filename, doraise=True) | 173 compile(filename, doraise=True) |
166 except PyCompileError as err: | 174 except PyCompileError as err: |
167 # return value to indicate at least one failure | 175 # return value to indicate at least one failure |
168 rv = 1 | 176 rv = 1 |
169 sys.stderr.write(error.msg) | 177 sys.stderr.write(error.msg) |
170 return rv | 178 return rv |
171 | 179 |
172 if __name__ == "__main__": | 180 if __name__ == "__main__": |
173 sys.exit(main()) | 181 sys.exit(main()) |
OLD | NEW |