Commit 14581d5d authored by Brett Cannon's avatar Brett Cannon

Port py_compile over to importlib

parent 80512de4
...@@ -689,6 +689,15 @@ def _compile_bytecode(data, name=None, bytecode_path=None, source_path=None): ...@@ -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): ...@@ -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): ...@@ -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)
......
...@@ -3,17 +3,13 @@ ...@@ -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): ...@@ -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): ...@@ -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): ...@@ -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):
......
...@@ -223,6 +223,8 @@ Core and Builtins ...@@ -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.
......
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment