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