Issue #15627: Add the compile_source() method to

importlib.abc.SourceLoader.

This provides an easy hook into the import system to allow for source
transformations, AST optimizations, etc.
This commit is contained in:
Brett Cannon 2012-11-18 10:03:31 -05:00
parent 195ad6ce05
commit 5650e4f41c
5 changed files with 2404 additions and 2360 deletions

View File

@ -364,10 +364,12 @@ ABC hierarchy::
* :meth:`ResourceLoader.get_data` * :meth:`ResourceLoader.get_data`
* :meth:`ExecutionLoader.get_filename` * :meth:`ExecutionLoader.get_filename`
Should only return the path to the source file; sourceless Should only return the path to the source file; sourceless
loading is not supported. loading is not supported (see :class:`SourcelessLoader` if that
functionality is required)
The abstract methods defined by this class are to add optional bytecode The abstract methods defined by this class are to add optional bytecode
file support. Not implementing these optional methods causes the loader to file support. Not implementing these optional methods (or causing them to
raise :exc:`NotImplementedError`) causes the loader to
only work with source code. Implementing the methods allows the loader to only work with source code. Implementing the methods allows the loader to
work with source *and* bytecode files; it does not allow for *sourceless* work with source *and* bytecode files; it does not allow for *sourceless*
loading where only bytecode is provided. Bytecode files are an loading where only bytecode is provided. Bytecode files are an
@ -407,6 +409,17 @@ ABC hierarchy::
When writing to the path fails because the path is read-only When writing to the path fails because the path is read-only
(:attr:`errno.EACCES`), do not propagate the exception. (:attr:`errno.EACCES`), do not propagate the exception.
.. method:: compile_source(data, path)
Create a code object from Python source.
The *data* argument can be whatever the :func:`compile` function
supports (i.e. string or bytes). The *path* argument should be
the "path" to where the source code originated from, which can be an
abstract concept (e.g. location in a zip file).
.. versionadded:: 3.4
.. method:: get_code(fullname) .. method:: get_code(fullname)
Concrete implementation of :meth:`InspectLoader.get_code`. Concrete implementation of :meth:`InspectLoader.get_code`.

View File

@ -931,6 +931,14 @@ 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 compile_source(self, data, path):
"""Return the code object compiled from source.
The 'data' argument can be any object type that compile() supports.
"""
return _call_with_frames_removed(compile, data, path, 'exec',
dont_inherit=True)
def get_code(self, fullname): def get_code(self, fullname):
"""Concrete implementation of InspectLoader.get_code. """Concrete implementation of InspectLoader.get_code.
@ -976,9 +984,7 @@ class SourceLoader(_LoaderBasics):
raise ImportError(msg.format(bytecode_path), raise ImportError(msg.format(bytecode_path),
name=fullname, path=bytecode_path) name=fullname, path=bytecode_path)
source_bytes = self.get_data(source_path) source_bytes = self.get_data(source_path)
code_object = _call_with_frames_removed(compile, code_object = self.compile_source(source_bytes, source_path)
source_bytes, source_path, 'exec',
dont_inherit=True)
_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):

View File

@ -148,6 +148,11 @@ class SourceOnlyLoaderTests(SourceLoaderTestHarness):
code_object = self.loader.get_code(self.name) code_object = self.loader.get_code(self.name)
self.verify_code(code_object) self.verify_code(code_object)
def test_compile_source(self):
# Verify the compiled code object.
code = self.loader.compile_source(self.loader.source, self.path)
self.verify_code(code)
def test_load_module(self): def test_load_module(self):
# Loading a module should set __name__, __loader__, __package__, # Loading a module should set __name__, __loader__, __package__,
# __path__ (for packages), __file__, and __cached__. # __path__ (for packages), __file__, and __cached__.
@ -395,12 +400,10 @@ class AbstractMethodImplTests(unittest.TestCase):
def test_main(): def test_main():
from test.support import run_unittest from test.support import run_unittest
run_unittest(SkipWritingBytecodeTests, RegeneratedBytecodeTests, run_unittest(SourceOnlyLoaderTests,
BadBytecodeFailureTests, MissingPathsTests, SourceLoaderBytecodeTests,
SourceOnlyLoaderTests, SourceLoaderGetSourceTests,
SourceLoaderBytecodeTests, AbstractMethodImplTests)
SourceLoaderGetSourceTests,
AbstractMethodImplTests)
if __name__ == '__main__': if __name__ == '__main__':

View File

@ -133,6 +133,8 @@ Core and Builtins
Library Library
------- -------
- Issue #15627: Add the importlib.abc.SourceLoader.compile_source() method.
- Issue #16408: Fix file descriptors not being closed in error conditions - Issue #16408: Fix file descriptors not being closed in error conditions
in the zipfile module. Patch by Serhiy Storchaka. in the zipfile module. Patch by Serhiy Storchaka.

File diff suppressed because it is too large Load Diff