Brett Cannon avatar Brett Cannon committed 0a7d237

Move importlib.abc.SourceLoader.source_to_code() to InspectLoader.

While the previous location was fine, it makes more sense to have the
method higher up in the inheritance chain, especially at a point where
get_source() is defined which is the earliest source_to_code() could
programmatically be used in the inheritance tree in importlib.abc.

Comments (0)

Files changed (4)

Doc/library/importlib.rst

         .. versionchanged:: 3.4
            Raises :exc:`ImportError` instead of :exc:`NotImplementedError`.
 
+    .. method:: source_to_code(data, path='<string>')
+
+        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
+
 
 .. class:: ExecutionLoader
 
         .. versionchanged:: 3.4
            No longer raises :exc:`NotImplementedError` when called.
 
-    .. method:: source_to_code(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)
 
         Concrete implementation of :meth:`InspectLoader.get_code`.

Lib/importlib/abc.py

         """
         raise ImportError
 
+    def source_to_code(self, data, path='<string>'):
+        """Compile 'data' into a code object.
+
+        The 'data' argument can be anything that compile() can handle. The'path'
+        argument should be where the data was retrieved (when applicable)."""
+        return compile(data, path, 'exec', dont_inherit=True)
+
 _register(InspectLoader, machinery.BuiltinImporter, machinery.FrozenImporter,
             machinery.ExtensionFileLoader)
 

Lib/test/test_importlib/test_abc.py

 
 from . import util
 
-##### Inheritance
+##### Inheritance ##############################################################
 class InheritanceTests:
 
     """Test that the specified class is a subclass/superclass of the expected
     subclasses = [machinery.SourceFileLoader]
 
 
-##### Default semantics
+##### Default return values ####################################################
 class MetaPathFinderSubclass(abc.MetaPathFinder):
 
     def find_module(self, fullname, path):
             self.ins.get_filename('blah')
 
 
-##### SourceLoader
+##### InspectLoader concrete methods ###########################################
+class InspectLoaderConcreteMethodTests(unittest.TestCase):
+
+    def source_to_module(self, data, path=None):
+        """Help with source_to_code() tests."""
+        module = imp.new_module('blah')
+        loader = InspectLoaderSubclass()
+        if path is None:
+            code = loader.source_to_code(data)
+        else:
+            code = loader.source_to_code(data, path)
+        exec(code, module.__dict__)
+        return module
+
+    def test_source_to_code_source(self):
+        # Since compile() can handle strings, so should source_to_code().
+        source = 'attr = 42'
+        module = self.source_to_module(source)
+        self.assertTrue(hasattr(module, 'attr'))
+        self.assertEqual(module.attr, 42)
+
+    def test_source_to_code_bytes(self):
+        # Since compile() can handle bytes, so should source_to_code().
+        source = b'attr = 42'
+        module = self.source_to_module(source)
+        self.assertTrue(hasattr(module, 'attr'))
+        self.assertEqual(module.attr, 42)
+
+    def test_source_to_code_path(self):
+        # Specifying a path should set it for the code object.
+        path = 'path/to/somewhere'
+        loader = InspectLoaderSubclass()
+        code = loader.source_to_code('', path)
+        self.assertEqual(code.co_filename, path)
+
+    def test_source_to_code_no_path(self):
+        # Not setting a path should still work and be set to <string> since that
+        # is a pre-existing practice as a default to compile().
+        loader = InspectLoaderSubclass()
+        code = loader.source_to_code('')
+        self.assertEqual(code.co_filename, '<string>')
+
+
+##### SourceLoader concrete methods ############################################
 class SourceOnlyLoaderMock(abc.SourceLoader):
 
     # Globals that should be defined for all modules.
         self.assertEqual(mock.get_source(name), expect)
 
 
+
 if __name__ == '__main__':
     unittest.main()
 
 - Issue #16522: added FAIL_FAST flag to doctest.
 
-- Issue #15627: Add the importlib.abc.SourceLoader.source_to_code() method.
+- Issue #15627: Add the importlib.abc.InspectLoader.source_to_code() method.
 
 - Issue #16408: Fix file descriptors not being closed in error conditions
   in the zipfile module.  Patch by Serhiy Storchaka.
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.