mirror of
https://github.com/python/cpython.git
synced 2024-12-19 14:53:39 +08:00
Issue #18072: Implement get_code() for importlib.abc.InspectLoader and
ExecutionLoader.
This commit is contained in:
parent
acfa291af9
commit
3b62ca88e4
@ -318,16 +318,20 @@ ABC hierarchy::
|
||||
|
||||
.. method:: get_code(fullname)
|
||||
|
||||
An abstract method to return the :class:`code` object for a module.
|
||||
``None`` is returned if the module does not have a code object
|
||||
Return the code object for a module.
|
||||
``None`` should be returned if the module does not have a code object
|
||||
(e.g. built-in module). :exc:`ImportError` is raised if loader cannot
|
||||
find the requested module.
|
||||
|
||||
.. note::
|
||||
While the method has a default implementation, it is suggested that
|
||||
it be overridden if possible for performance.
|
||||
|
||||
.. index::
|
||||
single: universal newlines; importlib.abc.InspectLoader.get_source method
|
||||
|
||||
.. versionchanged:: 3.4
|
||||
Raises :exc:`ImportError` instead of :exc:`NotImplementedError`.
|
||||
No longer abstract and a concrete implementation is provided.
|
||||
|
||||
.. method:: get_source(fullname)
|
||||
|
||||
@ -410,7 +414,7 @@ ABC hierarchy::
|
||||
|
||||
.. method:: get_data(path)
|
||||
|
||||
Returns the open, binary file for *path*.
|
||||
Reads *path* as a binary file and returns the bytes from it.
|
||||
|
||||
|
||||
.. class:: SourceLoader
|
||||
|
@ -147,14 +147,18 @@ class InspectLoader(Loader):
|
||||
"""
|
||||
raise ImportError
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_code(self, fullname):
|
||||
"""Abstract method which when implemented should return the code object
|
||||
for the module. The fullname is a str. Returns a types.CodeType.
|
||||
"""Method which returns the code object for the module.
|
||||
|
||||
Raises ImportError if the module cannot be found.
|
||||
The fullname is a str. Returns a types.CodeType if possible, else
|
||||
returns None if a code object does not make sense
|
||||
(e.g. built-in module). Raises ImportError if the module cannot be
|
||||
found.
|
||||
"""
|
||||
raise ImportError
|
||||
source = self.get_source(fullname)
|
||||
if source is None:
|
||||
return None
|
||||
return self.source_to_code(source)
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_source(self, fullname):
|
||||
@ -194,6 +198,22 @@ class ExecutionLoader(InspectLoader):
|
||||
"""
|
||||
raise ImportError
|
||||
|
||||
def get_code(self, fullname):
|
||||
"""Method to return the code object for fullname.
|
||||
|
||||
Should return None if not applicable (e.g. built-in module).
|
||||
Raise ImportError if the module cannot be found.
|
||||
"""
|
||||
source = self.get_source(fullname)
|
||||
if source is None:
|
||||
return None
|
||||
try:
|
||||
path = self.get_filename(fullname)
|
||||
except ImportError:
|
||||
return self.source_to_code(source)
|
||||
else:
|
||||
return self.source_to_code(source, path)
|
||||
|
||||
|
||||
class FileLoader(_bootstrap.FileLoader, ResourceLoader, ExecutionLoader):
|
||||
|
||||
|
@ -9,6 +9,7 @@ import marshal
|
||||
import os
|
||||
import sys
|
||||
import unittest
|
||||
from unittest import mock
|
||||
|
||||
from . import util
|
||||
|
||||
@ -166,9 +167,6 @@ class InspectLoaderSubclass(LoaderSubclass, abc.InspectLoader):
|
||||
def is_package(self, fullname):
|
||||
return super().is_package(fullname)
|
||||
|
||||
def get_code(self, fullname):
|
||||
return super().get_code(fullname)
|
||||
|
||||
def get_source(self, fullname):
|
||||
return super().get_source(fullname)
|
||||
|
||||
@ -181,10 +179,6 @@ class InspectLoaderDefaultsTests(unittest.TestCase):
|
||||
with self.assertRaises(ImportError):
|
||||
self.ins.is_package('blah')
|
||||
|
||||
def test_get_code(self):
|
||||
with self.assertRaises(ImportError):
|
||||
self.ins.get_code('blah')
|
||||
|
||||
def test_get_source(self):
|
||||
with self.assertRaises(ImportError):
|
||||
self.ins.get_source('blah')
|
||||
@ -206,7 +200,7 @@ class ExecutionLoaderDefaultsTests(unittest.TestCase):
|
||||
|
||||
|
||||
##### InspectLoader concrete methods ###########################################
|
||||
class InspectLoaderConcreteMethodTests(unittest.TestCase):
|
||||
class InspectLoaderSourceToCodeTests(unittest.TestCase):
|
||||
|
||||
def source_to_module(self, data, path=None):
|
||||
"""Help with source_to_code() tests."""
|
||||
@ -248,6 +242,92 @@ class InspectLoaderConcreteMethodTests(unittest.TestCase):
|
||||
self.assertEqual(code.co_filename, '<string>')
|
||||
|
||||
|
||||
class InspectLoaderGetCodeTests(unittest.TestCase):
|
||||
|
||||
def test_get_code(self):
|
||||
# Test success.
|
||||
module = imp.new_module('blah')
|
||||
with mock.patch.object(InspectLoaderSubclass, 'get_source') as mocked:
|
||||
mocked.return_value = 'attr = 42'
|
||||
loader = InspectLoaderSubclass()
|
||||
code = loader.get_code('blah')
|
||||
exec(code, module.__dict__)
|
||||
self.assertEqual(module.attr, 42)
|
||||
|
||||
def test_get_code_source_is_None(self):
|
||||
# If get_source() is None then this should be None.
|
||||
with mock.patch.object(InspectLoaderSubclass, 'get_source') as mocked:
|
||||
mocked.return_value = None
|
||||
loader = InspectLoaderSubclass()
|
||||
code = loader.get_code('blah')
|
||||
self.assertIsNone(code)
|
||||
|
||||
def test_get_code_source_not_found(self):
|
||||
# If there is no source then there is no code object.
|
||||
loader = InspectLoaderSubclass()
|
||||
with self.assertRaises(ImportError):
|
||||
loader.get_code('blah')
|
||||
|
||||
|
||||
##### ExecutionLoader concrete methods #########################################
|
||||
class ExecutionLoaderGetCodeTests(unittest.TestCase):
|
||||
|
||||
def mock_methods(self, *, get_source=False, get_filename=False):
|
||||
source_mock_context, filename_mock_context = None, None
|
||||
if get_source:
|
||||
source_mock_context = mock.patch.object(ExecutionLoaderSubclass,
|
||||
'get_source')
|
||||
if get_filename:
|
||||
filename_mock_context = mock.patch.object(ExecutionLoaderSubclass,
|
||||
'get_filename')
|
||||
return source_mock_context, filename_mock_context
|
||||
|
||||
def test_get_code(self):
|
||||
path = 'blah.py'
|
||||
source_mock_context, filename_mock_context = self.mock_methods(
|
||||
get_source=True, get_filename=True)
|
||||
with source_mock_context as source_mock, filename_mock_context as name_mock:
|
||||
source_mock.return_value = 'attr = 42'
|
||||
name_mock.return_value = path
|
||||
loader = ExecutionLoaderSubclass()
|
||||
code = loader.get_code('blah')
|
||||
self.assertEqual(code.co_filename, path)
|
||||
module = imp.new_module('blah')
|
||||
exec(code, module.__dict__)
|
||||
self.assertEqual(module.attr, 42)
|
||||
|
||||
def test_get_code_source_is_None(self):
|
||||
# If get_source() is None then this should be None.
|
||||
source_mock_context, _ = self.mock_methods(get_source=True)
|
||||
with source_mock_context as mocked:
|
||||
mocked.return_value = None
|
||||
loader = ExecutionLoaderSubclass()
|
||||
code = loader.get_code('blah')
|
||||
self.assertIsNone(code)
|
||||
|
||||
def test_get_code_source_not_found(self):
|
||||
# If there is no source then there is no code object.
|
||||
loader = ExecutionLoaderSubclass()
|
||||
with self.assertRaises(ImportError):
|
||||
loader.get_code('blah')
|
||||
|
||||
def test_get_code_no_path(self):
|
||||
# If get_filename() raises ImportError then simply skip setting the path
|
||||
# on the code object.
|
||||
source_mock_context, filename_mock_context = self.mock_methods(
|
||||
get_source=True, get_filename=True)
|
||||
with source_mock_context as source_mock, filename_mock_context as name_mock:
|
||||
source_mock.return_value = 'attr = 42'
|
||||
name_mock.side_effect = ImportError
|
||||
loader = ExecutionLoaderSubclass()
|
||||
code = loader.get_code('blah')
|
||||
self.assertEqual(code.co_filename, '<string>')
|
||||
module = imp.new_module('blah')
|
||||
exec(code, module.__dict__)
|
||||
self.assertEqual(module.attr, 42)
|
||||
|
||||
|
||||
|
||||
##### SourceLoader concrete methods ############################################
|
||||
class SourceOnlyLoaderMock(abc.SourceLoader):
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user