Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

gh-97850: remove find_loader and get_loader from pkgutil #119656

Merged
merged 17 commits into from
Nov 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Doc/deprecations/pending-removal-in-3.14.rst
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ Pending removal in Python 3.14
:meth:`~pathlib.PurePath.relative_to`: passing additional arguments is
deprecated.

* :mod:`pkgutil`: :func:`~pkgutil.find_loader` and :func:`~pkgutil.get_loader`
* :mod:`pkgutil`: :func:`!pkgutil.find_loader` and :func:!pkgutil.get_loader`
now raise :exc:`DeprecationWarning`;
use :func:`importlib.util.find_spec` instead.
(Contributed by Nikita Sobolev in :gh:`97850`.)
Expand Down
40 changes: 0 additions & 40 deletions Doc/library/pkgutil.rst
Original file line number Diff line number Diff line change
Expand Up @@ -49,25 +49,6 @@ support.
this function to raise an exception (in line with :func:`os.path.isdir`
behavior).

.. function:: find_loader(fullname)

Retrieve a module :term:`loader` for the given *fullname*.

This is a backwards compatibility wrapper around
:func:`importlib.util.find_spec` that converts most failures to
:exc:`ImportError` and only returns the loader rather than the full
:class:`importlib.machinery.ModuleSpec`.

.. versionchanged:: 3.3
Updated to be based directly on :mod:`importlib` rather than relying
on the package internal :pep:`302` import emulation.

.. versionchanged:: 3.4
Updated to be based on :pep:`451`

.. deprecated-removed:: 3.12 3.14
Use :func:`importlib.util.find_spec` instead.


.. function:: get_importer(path_item)

Expand All @@ -84,27 +65,6 @@ support.
on the package internal :pep:`302` import emulation.


.. function:: get_loader(module_or_name)

Get a :term:`loader` object for *module_or_name*.

If the module or package is accessible via the normal import mechanism, a
wrapper around the relevant part of that machinery is returned. Returns
``None`` if the module cannot be found or imported. If the named module is
not already imported, its containing package (if any) is imported, in order
to establish the package ``__path__``.

.. versionchanged:: 3.3
Updated to be based directly on :mod:`importlib` rather than relying
on the package internal :pep:`302` import emulation.

.. versionchanged:: 3.4
Updated to be based on :pep:`451`

.. deprecated-removed:: 3.12 3.14
Use :func:`importlib.util.find_spec` instead.


.. function:: iter_importers(fullname='')

Yield :term:`finder` objects for the given module name.
Expand Down
2 changes: 1 addition & 1 deletion Doc/whatsnew/3.12.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1229,7 +1229,7 @@ Deprecated
your code *requires* ``'fork'``. See :ref:`contexts and start methods
<multiprocessing-start-methods>`.

* :mod:`pkgutil`: :func:`pkgutil.find_loader` and :func:`pkgutil.get_loader`
* :mod:`pkgutil`: :func:`!pkgutil.find_loader` and :func:`!pkgutil.get_loader`
are deprecated and will be removed in Python 3.14;
use :func:`importlib.util.find_spec` instead.
(Contributed by Nikita Sobolev in :gh:`97850`.)
Expand Down
7 changes: 7 additions & 0 deletions Doc/whatsnew/3.14.rst
Original file line number Diff line number Diff line change
Expand Up @@ -615,6 +615,13 @@ pathlib
:meth:`~pathlib.PurePath.is_relative_to`. In previous versions, any such
arguments are joined onto *other*.

pkgutil
-------

* Remove deprecated :func:`!pkgutil.get_loader` and :func:`!pkgutil.find_loader`.
These had previously raised a :exc:`DeprecationWarning` since Python 3.12.
(Contributed by Bénédikt Tran in :gh:`97850`.)

pty
---

Expand Down
55 changes: 1 addition & 54 deletions Lib/pkgutil.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
import warnings

__all__ = [
'get_importer', 'iter_importers', 'get_loader', 'find_loader',
'get_importer', 'iter_importers',
'walk_packages', 'iter_modules', 'get_data',
'read_code', 'extend_path',
'ModuleInfo',
Expand Down Expand Up @@ -263,59 +263,6 @@ def iter_importers(fullname=""):
yield get_importer(item)


def get_loader(module_or_name):
"""Get a "loader" object for module_or_name

Returns None if the module cannot be found or imported.
If the named module is not already imported, its containing package
(if any) is imported, in order to establish the package __path__.
"""
warnings._deprecated("pkgutil.get_loader",
f"{warnings._DEPRECATED_MSG}; "
"use importlib.util.find_spec() instead",
remove=(3, 14))
if module_or_name in sys.modules:
module_or_name = sys.modules[module_or_name]
if module_or_name is None:
return None
if isinstance(module_or_name, ModuleType):
module = module_or_name
loader = getattr(module, '__loader__', None)
if loader is not None:
return loader
if getattr(module, '__spec__', None) is None:
return None
fullname = module.__name__
else:
fullname = module_or_name
return find_loader(fullname)


def find_loader(fullname):
"""Find a "loader" object for fullname

This is a backwards compatibility wrapper around
importlib.util.find_spec that converts most failures to ImportError
and only returns the loader rather than the full spec
"""
warnings._deprecated("pkgutil.find_loader",
f"{warnings._DEPRECATED_MSG}; "
"use importlib.util.find_spec() instead",
remove=(3, 14))
if fullname.startswith('.'):
msg = "Relative module name {!r} not supported".format(fullname)
raise ImportError(msg)
try:
spec = importlib.util.find_spec(fullname)
except (ImportError, AttributeError, TypeError, ValueError) as ex:
# This hack fixes an impedance mismatch between pkgutil and
# importlib, where the latter raises other errors for cases where
# pkgutil previously raised ImportError
msg = "Error while finding loader for {!r} ({}: {})"
raise ImportError(msg.format(fullname, type(ex), ex)) from ex
return spec.loader if spec is not None else None


def extend_path(path, name):
"""Extend a package's path.

Expand Down
19 changes: 0 additions & 19 deletions Lib/test/test_doctest/test_doctest.py
Original file line number Diff line number Diff line change
Expand Up @@ -2410,25 +2410,6 @@ def test_DocFileSuite():
>>> suite.run(unittest.TestResult())
<unittest.result.TestResult run=3 errors=0 failures=2>

Support for using a package's __loader__.get_data() is also
provided.

>>> import unittest, pkgutil, test
>>> added_loader = False
>>> if not hasattr(test, '__loader__'):
... test.__loader__ = pkgutil.get_loader(test)
... added_loader = True
>>> try:
... suite = doctest.DocFileSuite('test_doctest.txt',
... 'test_doctest2.txt',
... 'test_doctest4.txt',
... package='test.test_doctest')
... suite.run(unittest.TestResult())
... finally:
... if added_loader:
... del test.__loader__
<unittest.result.TestResult run=3 errors=0 failures=2>

'/' should be used as a path separator. It will be converted
to a native separator at run time:

Expand Down
67 changes: 0 additions & 67 deletions Lib/test/test_pkgutil.py
Original file line number Diff line number Diff line change
Expand Up @@ -607,73 +607,6 @@ class ImportlibMigrationTests(unittest.TestCase):
# PEP 302 emulation in this module is in the process of being
# deprecated in favour of importlib proper

@unittest.skipIf(__name__ == '__main__', 'not compatible with __main__')
@ignore_warnings(category=DeprecationWarning)
def test_get_loader_handles_missing_loader_attribute(self):
global __loader__
this_loader = __loader__
del __loader__
try:
self.assertIsNotNone(pkgutil.get_loader(__name__))
finally:
__loader__ = this_loader

@ignore_warnings(category=DeprecationWarning)
def test_get_loader_handles_missing_spec_attribute(self):
name = 'spam'
mod = type(sys)(name)
del mod.__spec__
with CleanImport(name):
try:
sys.modules[name] = mod
loader = pkgutil.get_loader(name)
finally:
sys.modules.pop(name, None)
self.assertIsNone(loader)

@ignore_warnings(category=DeprecationWarning)
def test_get_loader_handles_spec_attribute_none(self):
name = 'spam'
mod = type(sys)(name)
mod.__spec__ = None
with CleanImport(name):
try:
sys.modules[name] = mod
loader = pkgutil.get_loader(name)
finally:
sys.modules.pop(name, None)
self.assertIsNone(loader)

@ignore_warnings(category=DeprecationWarning)
def test_get_loader_None_in_sys_modules(self):
name = 'totally bogus'
sys.modules[name] = None
try:
loader = pkgutil.get_loader(name)
finally:
del sys.modules[name]
self.assertIsNone(loader)

def test_get_loader_is_deprecated(self):
with check_warnings(
(r".*\bpkgutil.get_loader\b.*", DeprecationWarning),
):
res = pkgutil.get_loader("sys")
self.assertIsNotNone(res)

def test_find_loader_is_deprecated(self):
with check_warnings(
(r".*\bpkgutil.find_loader\b.*", DeprecationWarning),
):
res = pkgutil.find_loader("sys")
self.assertIsNotNone(res)

@ignore_warnings(category=DeprecationWarning)
def test_find_loader_missing_module(self):
name = 'totally bogus'
loader = pkgutil.find_loader(name)
self.assertIsNone(loader)

def test_get_importer_avoids_emulation(self):
# We use an illegal path so *none* of the path hooks should fire
with check_warnings() as w:
Expand Down
2 changes: 1 addition & 1 deletion Misc/NEWS.d/3.12.0b1.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1750,7 +1750,7 @@ Remove the long-deprecated ``imp`` module.
.. nonce: N46coo
.. section: Library

Deprecate :func:`pkgutil.find_loader` and :func:`pkgutil.get_loader` in
Deprecate :func:`!pkgutil.find_loader` and :func:`!pkgutil.get_loader` in
favor of :func:`importlib.util.find_spec`.

..
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Remove deprecated :func:`!pkgutil.get_loader` and :func:`!pkgutil.find_loader`.
Loading