Skip to content

Commit 9ffe85e

Browse files
committed
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.
1 parent 1256f1f commit 9ffe85e

4 files changed

Lines changed: 66 additions & 15 deletions

File tree

Doc/library/importlib.rst

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,17 @@ ABC hierarchy::
349349
.. versionchanged:: 3.4
350350
Raises :exc:`ImportError` instead of :exc:`NotImplementedError`.
351351

352+
.. method:: source_to_code(data, path='<string>')
353+
354+
Create a code object from Python source.
355+
356+
The *data* argument can be whatever the :func:`compile` function
357+
supports (i.e. string or bytes). The *path* argument should be
358+
the "path" to where the source code originated from, which can be an
359+
abstract concept (e.g. location in a zip file).
360+
361+
.. versionadded:: 3.4
362+
352363

353364
.. class:: ExecutionLoader
354365

@@ -466,17 +477,6 @@ ABC hierarchy::
466477
.. versionchanged:: 3.4
467478
No longer raises :exc:`NotImplementedError` when called.
468479

469-
.. method:: source_to_code(data, path)
470-
471-
Create a code object from Python source.
472-
473-
The *data* argument can be whatever the :func:`compile` function
474-
supports (i.e. string or bytes). The *path* argument should be
475-
the "path" to where the source code originated from, which can be an
476-
abstract concept (e.g. location in a zip file).
477-
478-
.. versionadded:: 3.4
479-
480480
.. method:: get_code(fullname)
481481

482482
Concrete implementation of :meth:`InspectLoader.get_code`.

Lib/importlib/abc.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,13 @@ def get_source(self, fullname):
165165
"""
166166
raise ImportError
167167

168+
def source_to_code(self, data, path='<string>'):
169+
"""Compile 'data' into a code object.
170+
171+
The 'data' argument can be anything that compile() can handle. The'path'
172+
argument should be where the data was retrieved (when applicable)."""
173+
return compile(data, path, 'exec', dont_inherit=True)
174+
168175
_register(InspectLoader, machinery.BuiltinImporter, machinery.FrozenImporter,
169176
machinery.ExtensionFileLoader)
170177

Lib/test/test_importlib/test_abc.py

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
from . import util
1414

15-
##### Inheritance
15+
##### Inheritance ##############################################################
1616
class InheritanceTests:
1717

1818
"""Test that the specified class is a subclass/superclass of the expected
@@ -81,7 +81,7 @@ class SourceLoader(InheritanceTests, unittest.TestCase):
8181
subclasses = [machinery.SourceFileLoader]
8282

8383

84-
##### Default semantics
84+
##### Default return values ####################################################
8585
class MetaPathFinderSubclass(abc.MetaPathFinder):
8686

8787
def find_module(self, fullname, path):
@@ -205,7 +205,50 @@ def test_get_filename(self):
205205
self.ins.get_filename('blah')
206206

207207

208-
##### SourceLoader
208+
##### InspectLoader concrete methods ###########################################
209+
class InspectLoaderConcreteMethodTests(unittest.TestCase):
210+
211+
def source_to_module(self, data, path=None):
212+
"""Help with source_to_code() tests."""
213+
module = imp.new_module('blah')
214+
loader = InspectLoaderSubclass()
215+
if path is None:
216+
code = loader.source_to_code(data)
217+
else:
218+
code = loader.source_to_code(data, path)
219+
exec(code, module.__dict__)
220+
return module
221+
222+
def test_source_to_code_source(self):
223+
# Since compile() can handle strings, so should source_to_code().
224+
source = 'attr = 42'
225+
module = self.source_to_module(source)
226+
self.assertTrue(hasattr(module, 'attr'))
227+
self.assertEqual(module.attr, 42)
228+
229+
def test_source_to_code_bytes(self):
230+
# Since compile() can handle bytes, so should source_to_code().
231+
source = b'attr = 42'
232+
module = self.source_to_module(source)
233+
self.assertTrue(hasattr(module, 'attr'))
234+
self.assertEqual(module.attr, 42)
235+
236+
def test_source_to_code_path(self):
237+
# Specifying a path should set it for the code object.
238+
path = 'path/to/somewhere'
239+
loader = InspectLoaderSubclass()
240+
code = loader.source_to_code('', path)
241+
self.assertEqual(code.co_filename, path)
242+
243+
def test_source_to_code_no_path(self):
244+
# Not setting a path should still work and be set to <string> since that
245+
# is a pre-existing practice as a default to compile().
246+
loader = InspectLoaderSubclass()
247+
code = loader.source_to_code('')
248+
self.assertEqual(code.co_filename, '<string>')
249+
250+
251+
##### SourceLoader concrete methods ############################################
209252
class SourceOnlyLoaderMock(abc.SourceLoader):
210253

211254
# Globals that should be defined for all modules.
@@ -498,5 +541,6 @@ def test_universal_newlines(self):
498541
self.assertEqual(mock.get_source(name), expect)
499542

500543

544+
501545
if __name__ == '__main__':
502546
unittest.main()

Misc/NEWS

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1111,7 +1111,7 @@ Library
11111111

11121112
- Issue #16522: added FAIL_FAST flag to doctest.
11131113

1114-
- Issue #15627: Add the importlib.abc.SourceLoader.source_to_code() method.
1114+
- Issue #15627: Add the importlib.abc.InspectLoader.source_to_code() method.
11151115

11161116
- Issue #16408: Fix file descriptors not being closed in error conditions
11171117
in the zipfile module. Patch by Serhiy Storchaka.

0 commit comments

Comments
 (0)