blob: 8e9dd57a5440172fc1c6d6156fdf180cb5f25385 [file] [log] [blame]
Brett Cannonf299abd2015-04-13 14:21:02 -04001"""Routine to "compile" a .py file to a .pyc file.
Guido van Rossum63566e21998-01-19 04:01:26 +00002
3This module has intimate knowledge of the format of .pyc files.
4"""
Guido van Rossum3bb54481994-08-29 10:52:58 +00005
Benjamin Peterson42aa93b2017-12-09 10:26:52 -08006import enum
Eric Snow32439d62015-05-02 19:15:18 -06007import importlib._bootstrap_external
Brett Cannon14581d52013-01-26 08:48:36 -05008import importlib.machinery
Brett Cannondf960682013-06-15 14:07:21 -04009import importlib.util
Fred Drakea96f1a32002-08-21 20:23:22 +000010import os
Brett Cannon33915eb2013-06-14 18:33:00 -040011import os.path
Fred Drakea96f1a32002-08-21 20:23:22 +000012import sys
13import traceback
14
Benjamin Peterson42aa93b2017-12-09 10:26:52 -080015__all__ = ["compile", "main", "PyCompileError", "PycInvalidationMode"]
Martin v. Löwis0c6774d2003-01-15 11:51:06 +000016
17
18class 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
Tim Peters2c60f7a2003-01-29 03:49:43 +000027
Martin v. Löwis0c6774d2003-01-15 11:51:06 +000028 exc_type: exception type to be used in error message
29 type name can be accesses as class variable
30 'exc_type_name'
Tim Peters2c60f7a2003-01-29 03:49:43 +000031
Martin v. Löwis0c6774d2003-01-15 11:51:06 +000032 exc_value: exception value to be used in error message
33 can be accesses as class variable 'exc_value'
Tim Peters2c60f7a2003-01-29 03:49:43 +000034
Martin v. Löwis0c6774d2003-01-15 11:51:06 +000035 file: name of file being compiled to be used in error message
36 can be accesses as class variable 'file'
Tim Peters2c60f7a2003-01-29 03:49:43 +000037
Martin v. Löwis0c6774d2003-01-15 11:51:06 +000038 msg: string message to be written as error message
Barry Warsaw28a691b2010-04-17 00:19:56 +000039 If no value is given, a default exception message will be
40 given, consistent with 'standard' py_compile output.
41 message (or default) can be accesses as class variable
42 'msg'
Tim Peters2c60f7a2003-01-29 03:49:43 +000043
Martin v. Löwis0c6774d2003-01-15 11:51:06 +000044 """
Tim Peters2c60f7a2003-01-29 03:49:43 +000045
Martin v. Löwis0c6774d2003-01-15 11:51:06 +000046 def __init__(self, exc_type, exc_value, file, msg=''):
47 exc_type_name = exc_type.__name__
48 if exc_type is SyntaxError:
Barry Warsaw28a691b2010-04-17 00:19:56 +000049 tbtext = ''.join(traceback.format_exception_only(
50 exc_type, exc_value))
Martin v. Löwis0c6774d2003-01-15 11:51:06 +000051 errmsg = tbtext.replace('File "<string>"', 'File "%s"' % file)
52 else:
53 errmsg = "Sorry: %s: %s" % (exc_type_name,exc_value)
Tim Peters2c60f7a2003-01-29 03:49:43 +000054
Martin v. Löwis0c6774d2003-01-15 11:51:06 +000055 Exception.__init__(self,msg or errmsg,exc_type_name,exc_value,file)
56
57 self.exc_type_name = exc_type_name
58 self.exc_value = exc_value
59 self.file = file
60 self.msg = msg or errmsg
61
62 def __str__(self):
63 return self.msg
64
Skip Montanaroc62c81e2001-02-12 02:00:42 +000065
Benjamin Peterson42aa93b2017-12-09 10:26:52 -080066class PycInvalidationMode(enum.Enum):
67 TIMESTAMP = 1
68 CHECKED_HASH = 2
69 UNCHECKED_HASH = 3
70
71
Elvis Pranskevichusa6b3ec52018-10-10 12:43:14 -040072def _get_default_invalidation_mode():
73 if os.environ.get('SOURCE_DATE_EPOCH'):
74 return PycInvalidationMode.CHECKED_HASH
75 else:
76 return PycInvalidationMode.TIMESTAMP
77
78
Benjamin Peterson42aa93b2017-12-09 10:26:52 -080079def compile(file, cfile=None, dfile=None, doraise=False, optimize=-1,
Elvis Pranskevichusa6b3ec52018-10-10 12:43:14 -040080 invalidation_mode=None):
Guido van Rossum63566e21998-01-19 04:01:26 +000081 """Byte-compile one Python source file to Python bytecode.
82
Barry Warsaw28a691b2010-04-17 00:19:56 +000083 :param file: The source file name.
84 :param cfile: The target byte compiled file name. When not given, this
Brett Cannonf299abd2015-04-13 14:21:02 -040085 defaults to the PEP 3147/PEP 488 location.
Barry Warsaw28a691b2010-04-17 00:19:56 +000086 :param dfile: Purported file name, i.e. the file name that shows up in
87 error messages. Defaults to the source file name.
88 :param doraise: Flag indicating whether or not an exception should be
89 raised when a compile error is found. If an exception occurs and this
90 flag is set to False, a string indicating the nature of the exception
91 will be printed, and the function will return to the caller. If an
92 exception occurs and this flag is set to True, a PyCompileError
93 exception will be raised.
Georg Brandl8334fd92010-12-04 10:26:46 +000094 :param optimize: The optimization level for the compiler. Valid values
95 are -1, 0, 1 and 2. A value of -1 means to use the optimization
96 level of the current interpreter, as given by -O command line options.
Benjamin Peterson42aa93b2017-12-09 10:26:52 -080097 :param invalidation_mode:
Georg Brandl8334fd92010-12-04 10:26:46 +000098
Barry Warsaw28a691b2010-04-17 00:19:56 +000099 :return: Path to the resulting byte compiled file.
Tim Peters2c60f7a2003-01-29 03:49:43 +0000100
Guido van Rossum63566e21998-01-19 04:01:26 +0000101 Note that it isn't necessary to byte-compile Python modules for
102 execution efficiency -- Python itself byte-compiles a module when
103 it is loaded, and if it can, writes out the bytecode to the
Brett Cannonf299abd2015-04-13 14:21:02 -0400104 corresponding .pyc file.
Guido van Rossum63566e21998-01-19 04:01:26 +0000105
106 However, if a Python installation is shared between users, it is a
107 good idea to byte-compile all modules upon installation, since
108 other users may not be able to write in the source directories,
Brett Cannonf299abd2015-04-13 14:21:02 -0400109 and thus they won't be able to write the .pyc file, and then
Guido van Rossum63566e21998-01-19 04:01:26 +0000110 they would be byte-compiling every module each time it is loaded.
111 This can slow down program start-up considerably.
112
113 See compileall.py for a script/module that uses this module to
114 byte-compile all installed files (or all files in selected
115 directories).
Brett Cannon33915eb2013-06-14 18:33:00 -0400116
117 Do note that FileExistsError is raised if cfile ends up pointing at a
118 non-regular file or symlink. Because the compilation uses a file renaming,
119 the resulting file would be regular and thus not the same type of file as
120 it was previously.
Guido van Rossum63566e21998-01-19 04:01:26 +0000121 """
Elvis Pranskevichusa6b3ec52018-10-10 12:43:14 -0400122 if invalidation_mode is None:
123 invalidation_mode = _get_default_invalidation_mode()
Brett Cannon14581d52013-01-26 08:48:36 -0500124 if cfile is None:
125 if optimize >= 0:
Brett Cannonf299abd2015-04-13 14:21:02 -0400126 optimization = optimize if optimize >= 1 else ''
Brett Cannondf960682013-06-15 14:07:21 -0400127 cfile = importlib.util.cache_from_source(file,
Brett Cannonf299abd2015-04-13 14:21:02 -0400128 optimization=optimization)
Brett Cannon14581d52013-01-26 08:48:36 -0500129 else:
Brett Cannondf960682013-06-15 14:07:21 -0400130 cfile = importlib.util.cache_from_source(file)
Brett Cannon33915eb2013-06-14 18:33:00 -0400131 if os.path.islink(cfile):
132 msg = ('{} is a symlink and will be changed into a regular file if '
133 'import writes a byte-compiled file to it')
Brett Cannon9674bd02013-06-17 17:48:30 -0400134 raise FileExistsError(msg.format(cfile))
Brett Cannon33915eb2013-06-14 18:33:00 -0400135 elif os.path.exists(cfile) and not os.path.isfile(cfile):
136 msg = ('{} is a non-regular file and will be changed into a regular '
137 'one if import writes a byte-compiled file to it')
Brett Cannon9674bd02013-06-17 17:48:30 -0400138 raise FileExistsError(msg.format(cfile))
Brett Cannon14581d52013-01-26 08:48:36 -0500139 loader = importlib.machinery.SourceFileLoader('<py_compile>', file)
140 source_bytes = loader.get_data(file)
Guido van Rossumf984a651998-09-29 15:57:42 +0000141 try:
Brett Cannon14581d52013-01-26 08:48:36 -0500142 code = loader.source_to_code(source_bytes, dfile or file,
Brett Cannonedfd6ae2013-04-14 12:48:15 -0400143 _optimize=optimize)
Guido van Rossumb940e112007-01-10 16:19:56 +0000144 except Exception as err:
Guido van Rossumbd4a63e2007-08-10 17:36:34 +0000145 py_exc = PyCompileError(err.__class__, err, dfile or file)
Martin v. Löwis0c6774d2003-01-15 11:51:06 +0000146 if doraise:
147 raise py_exc
148 else:
Georg Brandle537d6e2005-06-10 17:15:18 +0000149 sys.stderr.write(py_exc.msg + '\n')
Martin v. Löwis0c6774d2003-01-15 11:51:06 +0000150 return
Benjamin Peterson25216ba2010-05-08 19:52:21 +0000151 try:
Meador Inge22b9b372011-11-28 09:27:32 -0600152 dirname = os.path.dirname(cfile)
153 if dirname:
154 os.makedirs(dirname)
Brett Cannon14581d52013-01-26 08:48:36 -0500155 except FileExistsError:
156 pass
Benjamin Peterson42aa93b2017-12-09 10:26:52 -0800157 if invalidation_mode == PycInvalidationMode.TIMESTAMP:
158 source_stats = loader.path_stats(file)
159 bytecode = importlib._bootstrap_external._code_to_timestamp_pyc(
Brett Cannonedfd6ae2013-04-14 12:48:15 -0400160 code, source_stats['mtime'], source_stats['size'])
Benjamin Peterson42aa93b2017-12-09 10:26:52 -0800161 else:
162 source_hash = importlib.util.source_hash(source_bytes)
163 bytecode = importlib._bootstrap_external._code_to_hash_pyc(
164 code,
165 source_hash,
166 (invalidation_mode == PycInvalidationMode.CHECKED_HASH),
167 )
Eric Snow32439d62015-05-02 19:15:18 -0600168 mode = importlib._bootstrap_external._calc_mode(file)
169 importlib._bootstrap_external._write_atomic(cfile, bytecode, mode)
Barry Warsaw28a691b2010-04-17 00:19:56 +0000170 return cfile
Fred Drake61cf4402002-08-21 20:56:21 +0000171
Brett Cannonedfd6ae2013-04-14 12:48:15 -0400172
Fred Drake61cf4402002-08-21 20:56:21 +0000173def main(args=None):
174 """Compile several source files.
175
176 The files named in 'args' (or on the command line, if 'args' is
177 not specified) are compiled and the resulting bytecode is cached
178 in the normal manner. This function does not search a directory
179 structure to locate source files; it only compiles files named
Barry Warsawd5f9bf52010-03-31 21:36:22 +0000180 explicitly. If '-' is the only parameter in args, the list of
181 files is taken from standard input.
Fred Drake61cf4402002-08-21 20:56:21 +0000182
183 """
184 if args is None:
185 args = sys.argv[1:]
Christian Heimesdd15f6c2008-03-16 00:07:10 +0000186 rv = 0
Barry Warsawd5f9bf52010-03-31 21:36:22 +0000187 if args == ['-']:
188 while True:
189 filename = sys.stdin.readline()
190 if not filename:
191 break
192 filename = filename.rstrip('\n')
193 try:
194 compile(filename, doraise=True)
195 except PyCompileError as error:
196 rv = 1
197 sys.stderr.write("%s\n" % error.msg)
Andrew Svetlovf7a17b42012-12-25 16:47:37 +0200198 except OSError as error:
Barry Warsawd5f9bf52010-03-31 21:36:22 +0000199 rv = 1
200 sys.stderr.write("%s\n" % error)
201 else:
202 for filename in args:
203 try:
204 compile(filename, doraise=True)
Matthias Klose1c994732010-04-20 19:48:04 +0000205 except PyCompileError as error:
Barry Warsawd5f9bf52010-03-31 21:36:22 +0000206 # return value to indicate at least one failure
207 rv = 1
Berker Peksag34c9be72015-04-14 18:57:55 +0300208 sys.stderr.write("%s\n" % error.msg)
Christian Heimesdd15f6c2008-03-16 00:07:10 +0000209 return rv
Tim Peters2c60f7a2003-01-29 03:49:43 +0000210
Fred Drake61cf4402002-08-21 20:56:21 +0000211if __name__ == "__main__":
Christian Heimesdd15f6c2008-03-16 00:07:10 +0000212 sys.exit(main())