Skip to content

Commit

Permalink
bpo-42135: Deprecate implementations of find_module() and find_loader…
Browse files Browse the repository at this point in the history
…() (GH-25169)
  • Loading branch information
brettcannon authored Apr 6, 2021
1 parent efccff9 commit 57c6cb5
Show file tree
Hide file tree
Showing 20 changed files with 3,808 additions and 3,640 deletions.
19 changes: 16 additions & 3 deletions Doc/library/importlib.rst
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,10 @@ ABC hierarchy::
Returns ``None`` when called instead of raising
:exc:`NotImplementedError`.

.. deprecated:: 3.10
Implement :meth:`MetaPathFinder.find_spec` or
:meth:`PathEntryFinder.find_spec` instead.


.. class:: MetaPathFinder

Expand All @@ -265,6 +269,9 @@ ABC hierarchy::

.. versionadded:: 3.3

.. versionchanged:: 3.10
No longer a subclass of :class:`Finder`.

.. method:: find_spec(fullname, path, target=None)

An abstract method for finding a :term:`spec <module spec>` for
Expand Down Expand Up @@ -313,11 +320,13 @@ ABC hierarchy::
An abstract base class representing a :term:`path entry finder`. Though
it bears some similarities to :class:`MetaPathFinder`, ``PathEntryFinder``
is meant for use only within the path-based import subsystem provided
by :class:`PathFinder`. This ABC is a subclass of :class:`Finder` for
compatibility reasons only.
by :class:`importlib.machinery.PathFinder`.

.. versionadded:: 3.3

.. versionchanged:: 3.10
No longer a subclass of :class:`Finder`.

.. method:: find_spec(fullname, target=None)

An abstract method for finding a :term:`spec <module spec>` for
Expand Down Expand Up @@ -363,7 +372,8 @@ ABC hierarchy::
.. method:: invalidate_caches()

An optional method which, when called, should invalidate any internal
cache used by the finder. Used by :meth:`PathFinder.invalidate_caches`
cache used by the finder. Used by
:meth:`importlib.machinery.PathFinder.invalidate_caches`
when invalidating the caches of all cached finders.


Expand Down Expand Up @@ -1193,6 +1203,9 @@ find and load modules.

Attempt to find the loader to handle *fullname* within :attr:`path`.

.. deprecated:: 3.10
Use :meth:`find_spec` instead.

.. method:: invalidate_caches()

Clear out the internal cache.
Expand Down
33 changes: 33 additions & 0 deletions Doc/whatsnew/3.10.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1062,6 +1062,39 @@ Deprecated
:func:`importlib.util.spec_from_loader` to help in porting.
(Contributed by Brett Cannon in :issue:`43672`.)
* The various implementations of
:meth:`importlib.abc.MetaPathFinder.find_module` (
:meth:`importlib.machinery.BuiltinImporter.find_module`,
:meth:`importlib.machinery.FrozenImporter.find_module`,
:meth:`importlib.machinery.WindowsRegistryFinder.find_module`,
:meth:`importlib.machinery.PathFinder.find_module`,
:meth:`importlib.abc.MetaPathFinder.find_module`),
:meth:`importlib.abc.PathEntryFinder.find_module` (
:meth:`importlib.machinery.FileFinder.find_module`,
), and
:meth:`importlib.abc.PathEntryFinder.find_loader` (
:meth:`importlib.machinery.FileFinder.find_loader`
) now raise :exc:`DeprecationWarning` and are slated for removal in
Python 3.12 (previously they were documented as deprecated in Python 3.4).
(Contributed by Brett Cannon in :issue:`42135`.)
* :class:`importlib.abc.Finder` is deprecated (including its sole method,
:meth:`~importlib.abc.Finder.find_module`). Both
:class:`importlib.abc.MetaPathFinder` and :class:`importlib.abc.PathEntryFinder`
no longer inherit from the class. Users should inherit from one of these two
classes as appropriate instead.
(Contributed by Brett Cannon in :issue:`42135`.)
* The deprecations of :mod:`imp`, :func:`importlib.find_loader`,
:func:`importlib.util.set_package_wrapper`,
:func:`importlib.util.set_loader_wrapper`,
:func:`importlib.util.module_for_loader`,
:class:`pkgutil.ImpImporter`, and
:class:`pkgutil.ImpLoader` have all been updated to list Python 3.12 as the
slated version of removal (they began raising :exc:`DeprecationWarning` in
previous versions of Python).
(Contributed by Brett Cannon in :issue:`43720`.)
* The import system now uses the ``__spec__`` attribute on modules before
falling back on :meth:`~importlib.abc.Loader.module_repr` for a module's
``__repr__()`` method. Removal of the use of ``module_repr()`` is scheduled
Expand Down
4 changes: 2 additions & 2 deletions Lib/importlib/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,8 @@ def find_loader(name, path=None):
This function is deprecated in favor of importlib.util.find_spec().
"""
warnings.warn('Deprecated since Python 3.4. '
'Use importlib.util.find_spec() instead.',
warnings.warn('Deprecated since Python 3.4 and slated for removal in '
'Python 3.10; use importlib.util.find_spec() instead',
DeprecationWarning, stacklevel=2)
try:
loader = sys.modules[name].__loader__
Expand Down
6 changes: 6 additions & 0 deletions Lib/importlib/_bootstrap.py
Original file line number Diff line number Diff line change
Expand Up @@ -761,6 +761,9 @@ def find_module(cls, fullname, path=None):
This method is deprecated. Use find_spec() instead.
"""
_warnings.warn("BuiltinImporter.find_module() is deprecated and "
"slated for removal in Python 3.12; use find_spec() instead",
DeprecationWarning)
spec = cls.find_spec(fullname, path)
return spec.loader if spec is not None else None

Expand Down Expand Up @@ -834,6 +837,9 @@ def find_module(cls, fullname, path=None):
This method is deprecated. Use find_spec() instead.
"""
_warnings.warn("FrozenImporter.find_module() is deprecated and "
"slated for removal in Python 3.12; use find_spec() instead",
DeprecationWarning)
return cls if _imp.is_frozen(fullname) else None

@staticmethod
Expand Down
14 changes: 13 additions & 1 deletion Lib/importlib/_bootstrap_external.py
Original file line number Diff line number Diff line change
Expand Up @@ -533,6 +533,9 @@ def _find_module_shim(self, fullname):
This method is deprecated in favor of finder.find_spec().
"""
_warnings.warn("find_module() is deprecated and "
"slated for removal in Python 3.12; use find_spec() instead",
DeprecationWarning)
# Call find_loader(). If it returns a string (indicating this
# is a namespace package portion), generate a warning and
# return None.
Expand Down Expand Up @@ -801,9 +804,12 @@ def find_spec(cls, fullname, path=None, target=None):
def find_module(cls, fullname, path=None):
"""Find module named in the registry.
This method is deprecated. Use exec_module() instead.
This method is deprecated. Use find_spec() instead.
"""
_warnings.warn("WindowsRegistryFinder.find_module() is deprecated and "
"slated for removal in Python 3.12; use find_spec() instead",
DeprecationWarning)
spec = cls.find_spec(fullname, path)
if spec is not None:
return spec.loader
Expand Down Expand Up @@ -1404,6 +1410,9 @@ def find_module(cls, fullname, path=None):
This method is deprecated. Use find_spec() instead.
"""
_warnings.warn("PathFinder.find_module() is deprecated and "
"slated for removal in Python 3.12; use find_spec() instead",
DeprecationWarning)
spec = cls.find_spec(fullname, path)
if spec is None:
return None
Expand Down Expand Up @@ -1459,6 +1468,9 @@ def find_loader(self, fullname):
This method is deprecated. Use find_spec() instead.
"""
_warnings.warn("FileFinder.find_loader() is deprecated and "
"slated for removal in Python 3.12; use find_spec() instead",
DeprecationWarning)
spec = self.find_spec(fullname)
if spec is None:
return None, []
Expand Down
20 changes: 16 additions & 4 deletions Lib/importlib/abc.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,15 +41,27 @@ class Finder(metaclass=abc.ABCMeta):
Deprecated since Python 3.3
"""

def __init__(self):
warnings.warn("the Finder ABC is deprecated and "
"slated for removal in Python 3.12; use MetaPathFinder "
"or PathEntryFinder instead",
DeprecationWarning)

@abc.abstractmethod
def find_module(self, fullname, path=None):
"""An abstract method that should find a module.
The fullname is a str and the optional path is a str or None.
Returns a Loader object or None.
"""
warnings.warn("importlib.abc.Finder along with its find_module() "
"method are deprecated and "
"slated for removal in Python 3.12; use "
"MetaPathFinder.find_spec() or "
"PathEntryFinder.find_spec() instead",
DeprecationWarning)


class MetaPathFinder(Finder):
class MetaPathFinder(metaclass=abc.ABCMeta):

"""Abstract base class for import finders on sys.meta_path."""

Expand All @@ -68,8 +80,8 @@ def find_module(self, fullname, path):
"""
warnings.warn("MetaPathFinder.find_module() is deprecated since Python "
"3.4 in favor of MetaPathFinder.find_spec() "
"(available since 3.4)",
"3.4 in favor of MetaPathFinder.find_spec() and is "
"slated for removal in Python 3.12",
DeprecationWarning,
stacklevel=2)
if not hasattr(self, 'find_spec'):
Expand All @@ -86,7 +98,7 @@ def invalidate_caches(self):
machinery.PathFinder, machinery.WindowsRegistryFinder)


class PathEntryFinder(Finder):
class PathEntryFinder(metaclass=abc.ABCMeta):

"""Abstract base class for path entry finders used by PathFinder."""

Expand Down
16 changes: 12 additions & 4 deletions Lib/test/test_importlib/builtin/test_finder.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import sys
import unittest
import warnings


@unittest.skipIf(util.BUILTINS.good_name is None, 'no reasonable builtin module')
Expand Down Expand Up @@ -58,7 +59,9 @@ class FinderTests(abc.FinderTests):
def test_module(self):
# Common case.
with util.uncache(util.BUILTINS.good_name):
found = self.machinery.BuiltinImporter.find_module(util.BUILTINS.good_name)
with warnings.catch_warnings():
warnings.simplefilter("ignore", DeprecationWarning)
found = self.machinery.BuiltinImporter.find_module(util.BUILTINS.good_name)
self.assertTrue(found)
self.assertTrue(hasattr(found, 'load_module'))

Expand All @@ -70,14 +73,19 @@ def test_module(self):

def test_failure(self):
assert 'importlib' not in sys.builtin_module_names
loader = self.machinery.BuiltinImporter.find_module('importlib')
with warnings.catch_warnings():
warnings.simplefilter("ignore", DeprecationWarning)
loader = self.machinery.BuiltinImporter.find_module('importlib')
self.assertIsNone(loader)

def test_ignore_path(self):
# The value for 'path' should always trigger a failed import.
with util.uncache(util.BUILTINS.good_name):
loader = self.machinery.BuiltinImporter.find_module(util.BUILTINS.good_name,
['pkg'])
with warnings.catch_warnings():
warnings.simplefilter("ignore", DeprecationWarning)
loader = self.machinery.BuiltinImporter.find_module(
util.BUILTINS.good_name,
['pkg'])
self.assertIsNone(loader)


Expand Down
12 changes: 6 additions & 6 deletions Lib/test/test_importlib/extension/test_case_sensitivity.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,30 +12,30 @@
@util.case_insensitive_tests
class ExtensionModuleCaseSensitivityTest(util.CASEOKTestBase):

def find_module(self):
def find_spec(self):
good_name = util.EXTENSIONS.name
bad_name = good_name.upper()
assert good_name != bad_name
finder = self.machinery.FileFinder(util.EXTENSIONS.path,
(self.machinery.ExtensionFileLoader,
self.machinery.EXTENSION_SUFFIXES))
return finder.find_module(bad_name)
return finder.find_spec(bad_name)

@unittest.skipIf(sys.flags.ignore_environment, 'ignore_environment flag was set')
def test_case_sensitive(self):
with os_helper.EnvironmentVarGuard() as env:
env.unset('PYTHONCASEOK')
self.caseok_env_changed(should_exist=False)
loader = self.find_module()
self.assertIsNone(loader)
spec = self.find_spec()
self.assertIsNone(spec)

@unittest.skipIf(sys.flags.ignore_environment, 'ignore_environment flag was set')
def test_case_insensitivity(self):
with os_helper.EnvironmentVarGuard() as env:
env.set('PYTHONCASEOK', '1')
self.caseok_env_changed(should_exist=True)
loader = self.find_module()
self.assertTrue(hasattr(loader, 'load_module'))
spec = self.find_spec()
self.assertTrue(spec)


(Frozen_ExtensionCaseSensitivity,
Expand Down
11 changes: 5 additions & 6 deletions Lib/test/test_importlib/extension/test_finder.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,15 @@ class FinderTests(abc.FinderTests):

"""Test the finder for extension modules."""

def find_module(self, fullname):
def find_spec(self, fullname):
importer = self.machinery.FileFinder(util.EXTENSIONS.path,
(self.machinery.ExtensionFileLoader,
self.machinery.EXTENSION_SUFFIXES))
with warnings.catch_warnings():
warnings.simplefilter('ignore', DeprecationWarning)
return importer.find_module(fullname)

return importer.find_spec(fullname)

def test_module(self):
self.assertTrue(self.find_module(util.EXTENSIONS.name))
self.assertTrue(self.find_spec(util.EXTENSIONS.name))

# No extension module as an __init__ available for testing.
test_package = test_package_in_package = None
Expand All @@ -32,7 +31,7 @@ def test_module(self):
test_package_over_module = None

def test_failure(self):
self.assertIsNone(self.find_module('asdfjkl;'))
self.assertIsNone(self.find_spec('asdfjkl;'))


(Frozen_FinderTests,
Expand Down
5 changes: 4 additions & 1 deletion Lib/test/test_importlib/frozen/test_finder.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
machinery = util.import_importlib('importlib.machinery')

import unittest
import warnings


class FindSpecTests(abc.FinderTests):
Expand Down Expand Up @@ -49,7 +50,9 @@ class FinderTests(abc.FinderTests):

def find(self, name, path=None):
finder = self.machinery.FrozenImporter
return finder.find_module(name, path)
with warnings.catch_warnings():
warnings.simplefilter("ignore", DeprecationWarning)
return finder.find_module(name, path)

def test_module(self):
name = '__hello__'
Expand Down
2 changes: 1 addition & 1 deletion Lib/test/test_importlib/frozen/test_loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ def test_module_repr_indirect(self):
test_state_after_failure = None

def test_unloadable(self):
assert self.machinery.FrozenImporter.find_module('_not_real') is None
assert self.machinery.FrozenImporter.find_spec('_not_real') is None
with self.assertRaises(ImportError) as cm:
self.exec_module('_not_real')
self.assertEqual(cm.exception.name, '_not_real')
Expand Down
8 changes: 6 additions & 2 deletions Lib/test/test_importlib/import_/test_path.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,8 @@ def test_empty_path_hooks(self):
with util.import_state(path_importer_cache={}, path_hooks=[],
path=[path_entry]):
with warnings.catch_warnings(record=True) as w:
warnings.simplefilter('always')
warnings.simplefilter('always', ImportWarning)
warnings.simplefilter('ignore', DeprecationWarning)
self.assertIsNone(self.find('os'))
self.assertIsNone(sys.path_importer_cache[path_entry])
self.assertEqual(len(w), 1)
Expand Down Expand Up @@ -216,7 +217,9 @@ def test_invalidate_caches_clear_out_None(self):

class FindModuleTests(FinderTests):
def find(self, *args, **kwargs):
return self.machinery.PathFinder.find_module(*args, **kwargs)
with warnings.catch_warnings():
warnings.simplefilter("ignore", DeprecationWarning)
return self.machinery.PathFinder.find_module(*args, **kwargs)
def check_found(self, found, importer):
self.assertIs(found, importer)

Expand Down Expand Up @@ -278,6 +281,7 @@ def find_module(fullname):
path_hooks=[Finder]):
with warnings.catch_warnings():
warnings.simplefilter("ignore", ImportWarning)
warnings.simplefilter("ignore", DeprecationWarning)
self.machinery.PathFinder.find_module('importlib')


Expand Down
Loading

0 comments on commit 57c6cb5

Please sign in to comment.