Skip to content

Commit

Permalink
pythongh-104212: Add importlib.util.load_source_path() function
Browse files Browse the repository at this point in the history
  • Loading branch information
vstinner committed Jun 16, 2023
1 parent 0bffe1a commit d426783
Show file tree
Hide file tree
Showing 5 changed files with 54 additions and 0 deletions.
7 changes: 7 additions & 0 deletions Doc/library/importlib.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1240,6 +1240,13 @@ an :term:`importer`.
.. versionchanged:: 3.6
Accepts a :term:`path-like object`.

.. function:: load_source_path(module_name, filename)

Load a module from a filename: execute the module and add it to
:data:`sys.modules`.

.. versionadded:: 3.12

.. function:: source_hash(source_bytes)

Return the hash of *source_bytes* as bytes. A hash-based ``.pyc`` file embeds
Expand Down
9 changes: 9 additions & 0 deletions Doc/whatsnew/3.12.rst
Original file line number Diff line number Diff line change
Expand Up @@ -570,6 +570,12 @@ fractions
* Objects of type :class:`fractions.Fraction` now support float-style
formatting. (Contributed by Mark Dickinson in :gh:`100161`.)

importlib
---------

* Add :func:`importlib.util.load_source_path` to load a module from a filename.
(Contributed by Victor Stinner in :gh:`104212`.)

inspect
-------

Expand Down Expand Up @@ -1371,6 +1377,9 @@ Removed

* Replace ``imp.new_module(name)`` with ``types.ModuleType(name)``.

* Replace ``imp.load_source(module_name, filename)``
with ``importlib.util.load_source_path(module_name, filename)``.

* Removed the ``suspicious`` rule from the documentation Makefile, and
removed ``Doc/tools/rstlint.py``, both in favor of `sphinx-lint
<https://github.com/sphinx-contrib/sphinx-lint>`_.
Expand Down
11 changes: 11 additions & 0 deletions Lib/importlib/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from ._bootstrap_external import decode_source
from ._bootstrap_external import source_from_cache
from ._bootstrap_external import spec_from_file_location
from ._bootstrap_external import SourceFileLoader

import _imp
import sys
Expand Down Expand Up @@ -246,3 +247,13 @@ def exec_module(self, module):
loader_state['__class__'] = module.__class__
module.__spec__.loader_state = loader_state
module.__class__ = _LazyModule


def load_source_path(module_name, filename):
"""Load a module from a filename."""
loader = SourceFileLoader(module_name, filename)
module = types.ModuleType(loader.name)
module.__file__ = filename
sys.modules[module.__name__] = module
loader.exec_module(module)
return module
25 changes: 25 additions & 0 deletions Lib/test/test_importlib/test_util.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from test.test_importlib import fixtures
from test.test_importlib import util

abc = util.import_importlib('importlib.abc')
Expand All @@ -12,6 +13,7 @@
import string
import sys
from test import support
from test.support import import_helper, os_helper
import textwrap
import types
import unittest
Expand Down Expand Up @@ -758,5 +760,28 @@ def test_complete_multi_phase_init_module(self):
self.run_with_own_gil(script)


class LoadSourceTests(unittest.TestCase):
def test_load_source_path(self):
modname = 'test_load_source_path_mod'
filename = 'load_source_path_filename'

self.assertNotIn(modname, sys.modules)
self.addCleanup(import_helper.unload, modname)

# Use a temporary directory to remove __pycache__/ subdirectory
with fixtures.tempdir_as_cwd():
with open(filename, "w", encoding="utf8") as fp:
print("attr = 'load_source_path_attr'", file=fp)

mod = importlib.util.load_source_path(modname, filename)

self.assertIsInstance(mod, types.ModuleType)
self.assertEqual(mod.__name__, modname)
self.assertEqual(mod.__file__, filename)
self.assertIn(modname, sys.modules)
self.assertIs(sys.modules[modname], mod)
self.assertEqual(mod.attr, 'load_source_path_attr')


if __name__ == '__main__':
unittest.main()
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Add :func:`importlib.util.load_source_path` to load a module from a filename.
Patch by Victor Stinner.

0 comments on commit d426783

Please sign in to comment.