Port py_compile over to importlib

This commit is contained in:
Brett Cannon 2013-01-26 08:48:36 -05:00
parent 80512de43b
commit 14581d5dc4
4 changed files with 4375 additions and 4362 deletions

View File

@ -689,6 +689,15 @@ def _compile_bytecode(data, name=None, bytecode_path=None, source_path=None):
raise ImportError("Non-code object in {!r}".format(bytecode_path), raise ImportError("Non-code object in {!r}".format(bytecode_path),
name=name, path=bytecode_path) name=name, path=bytecode_path)
def _code_to_bytecode(code, mtime=0, source_size=0):
"""Compile a code object into bytecode for writing out to a byte-compiled
file."""
data = bytearray(_MAGIC_BYTES)
data.extend(_w_long(mtime))
data.extend(_w_long(source_size))
data.extend(marshal.dumps(code))
return data
# Loaders ##################################################################### # Loaders #####################################################################
@ -951,13 +960,13 @@ class SourceLoader(_LoaderBasics):
raise ImportError("Failed to decode source file", raise ImportError("Failed to decode source file",
name=fullname) from exc name=fullname) from exc
def source_to_code(self, data, path): def source_to_code(self, data, path, *, _optimize=-1):
"""Return the code object compiled from source. """Return the code object compiled from source.
The 'data' argument can be any object type that compile() supports. The 'data' argument can be any object type that compile() supports.
""" """
return _call_with_frames_removed(compile, data, path, 'exec', return _call_with_frames_removed(compile, data, path, 'exec',
dont_inherit=True) dont_inherit=True, optimize=_optimize)
def get_code(self, fullname): def get_code(self, fullname):
"""Concrete implementation of InspectLoader.get_code. """Concrete implementation of InspectLoader.get_code.
@ -1000,11 +1009,9 @@ class SourceLoader(_LoaderBasics):
code_object = self.source_to_code(source_bytes, source_path) code_object = self.source_to_code(source_bytes, source_path)
_verbose_message('code object from {}', source_path) _verbose_message('code object from {}', source_path)
if (not sys.dont_write_bytecode and bytecode_path is not None and if (not sys.dont_write_bytecode and bytecode_path is not None and
source_mtime is not None): source_mtime is not None):
data = bytearray(_MAGIC_BYTES) data = _code_to_bytecode(code_object, source_mtime,
data.extend(_w_long(source_mtime)) len(source_bytes))
data.extend(_w_long(len(source_bytes)))
data.extend(marshal.dumps(code_object))
try: try:
self._cache_bytecode(source_path, bytecode_path, data) self._cache_bytecode(source_path, bytecode_path, data)
_verbose_message('wrote {!r}', bytecode_path) _verbose_message('wrote {!r}', bytecode_path)

View File

@ -3,17 +3,13 @@
This module has intimate knowledge of the format of .pyc files. This module has intimate knowledge of the format of .pyc files.
""" """
import builtins
import errno
import imp import imp
import marshal import importlib._bootstrap
import importlib.machinery
import os import os
import sys import sys
import tokenize
import traceback import traceback
MAGIC = imp.get_magic()
__all__ = ["compile", "main", "PyCompileError"] __all__ = ["compile", "main", "PyCompileError"]
@ -65,13 +61,6 @@ class PyCompileError(Exception):
return self.msg return self.msg
def wr_long(f, x):
"""Internal; write a 32-bit int to a file in little-endian order."""
f.write(bytes([x & 0xff,
(x >> 8) & 0xff,
(x >> 16) & 0xff,
(x >> 24) & 0xff]))
def compile(file, cfile=None, dfile=None, doraise=False, optimize=-1): def compile(file, cfile=None, dfile=None, doraise=False, optimize=-1):
"""Byte-compile one Python source file to Python bytecode. """Byte-compile one Python source file to Python bytecode.
@ -108,17 +97,16 @@ def compile(file, cfile=None, dfile=None, doraise=False, optimize=-1):
byte-compile all installed files (or all files in selected byte-compile all installed files (or all files in selected
directories). directories).
""" """
with tokenize.open(file) as f: if cfile is None:
try: if optimize >= 0:
st = os.fstat(f.fileno()) cfile = imp.cache_from_source(file, debug_override=not optimize)
except AttributeError: else:
st = os.stat(file) cfile = imp.cache_from_source(file)
timestamp = int(st.st_mtime) loader = importlib.machinery.SourceFileLoader('<py_compile>', file)
size = st.st_size & 0xFFFFFFFF source_bytes = loader.get_data(file)
codestring = f.read()
try: try:
codeobject = builtins.compile(codestring, dfile or file, 'exec', code = loader.source_to_code(source_bytes, dfile or file,
optimize=optimize) _optimize=optimize)
except Exception as err: except Exception as err:
py_exc = PyCompileError(err.__class__, err, dfile or file) py_exc = PyCompileError(err.__class__, err, dfile or file)
if doraise: if doraise:
@ -126,26 +114,16 @@ def compile(file, cfile=None, dfile=None, doraise=False, optimize=-1):
else: else:
sys.stderr.write(py_exc.msg + '\n') sys.stderr.write(py_exc.msg + '\n')
return return
if cfile is None:
if optimize >= 0:
cfile = imp.cache_from_source(file, debug_override=not optimize)
else:
cfile = imp.cache_from_source(file)
try: try:
dirname = os.path.dirname(cfile) dirname = os.path.dirname(cfile)
if dirname: if dirname:
os.makedirs(dirname) os.makedirs(dirname)
except OSError as error: except FileExistsError:
if error.errno != errno.EEXIST: pass
raise source_stats = loader.path_stats(file)
with open(cfile, 'wb') as fc: bytecode = importlib._bootstrap._code_to_bytecode(code,
fc.write(b'\0\0\0\0') source_stats['mtime'], len(source_bytes))
wr_long(fc, timestamp) loader._cache_bytecode(file, cfile, bytecode)
wr_long(fc, size)
marshal.dump(codeobject, fc)
fc.flush()
fc.seek(0, 0)
fc.write(MAGIC)
return cfile return cfile
def main(args=None): def main(args=None):

View File

@ -223,6 +223,8 @@ Core and Builtins
Library Library
------- -------
Have py_compile use importlib as much as possible to avoid code duplication.
- Issue #180022: Have site.addpackage() consider already known paths even when - Issue #180022: Have site.addpackage() consider already known paths even when
none are explicitly passed in. Bug report and fix by Kirill. none are explicitly passed in. Bug report and fix by Kirill.

File diff suppressed because it is too large Load Diff