Commit ea0b8239 authored by Brett Cannon's avatar Brett Cannon

Issue #14938: importlib.abc.SourceLoader.is_package() now takes the

module name into consideration when determining whether a module is a
package or not. This prevents importing a module's __init__ module
directly and having it considered a package, which can lead to
duplicate sub-modules.

Thanks to Ronan Lamy for reporting the bug.
parent 0450c9ed
...@@ -351,8 +351,10 @@ are also provided to help in implementing the core ABCs. ...@@ -351,8 +351,10 @@ are also provided to help in implementing the core ABCs.
.. method:: is_package(self, fullname) .. method:: is_package(self, fullname)
Concrete implementation of :meth:`InspectLoader.is_package`. A module Concrete implementation of :meth:`InspectLoader.is_package`. A module
is determined to be a package if its file path is a file named is determined to be a package if its file path (as provided by
``__init__`` when the file extension is removed. :meth:`ExecutionLoader.get_filename`) is a file named
``__init__`` when the file extension is removed **and** the module name
itself does not end in ``__init__``.
.. class:: PyLoader .. class:: PyLoader
......
...@@ -578,7 +578,9 @@ class _LoaderBasics: ...@@ -578,7 +578,9 @@ class _LoaderBasics:
"""Concrete implementation of InspectLoader.is_package by checking if """Concrete implementation of InspectLoader.is_package by checking if
the path returned by get_filename has a filename of '__init__.py'.""" the path returned by get_filename has a filename of '__init__.py'."""
filename = _path_split(self.get_filename(fullname))[1] filename = _path_split(self.get_filename(fullname))[1]
return filename.rsplit('.', 1)[0] == '__init__' filename_base = filename.rsplit('.', 1)[0]
tail_name = fullname.rpartition('.')[2]
return filename_base == '__init__' and tail_name != '__init__'
def _bytes_from_bytecode(self, fullname, data, bytecode_path, source_stats): def _bytes_from_bytecode(self, fullname, data, bytecode_path, source_stats):
"""Return the marshalled bytes from bytecode, verifying the magic """Return the marshalled bytes from bytecode, verifying the magic
......
...@@ -602,10 +602,11 @@ class SourceOnlyLoaderTests(SourceLoaderTestHarness): ...@@ -602,10 +602,11 @@ class SourceOnlyLoaderTests(SourceLoaderTestHarness):
def test_is_package(self): def test_is_package(self):
# Properly detect when loading a package. # Properly detect when loading a package.
self.setUp(is_package=True)
self.assertTrue(self.loader.is_package(self.name))
self.setUp(is_package=False) self.setUp(is_package=False)
self.assertFalse(self.loader.is_package(self.name)) self.assertFalse(self.loader.is_package(self.name))
self.setUp(is_package=True)
self.assertTrue(self.loader.is_package(self.name))
self.assertFalse(self.loader.is_package(self.name + '.__init__'))
def test_get_code(self): def test_get_code(self):
# Verify the code object is created. # Verify the code object is created.
......
...@@ -24,6 +24,10 @@ Core and Builtins ...@@ -24,6 +24,10 @@ Core and Builtins
Library Library
------- -------
- Issue #14938: importlib.abc.SourceLoader.is_package() will not consider a
module whose name ends in '__init__' a package (e.g. importing pkg.__init__
directly should be considered a module, not a package).
- Issue #14982: Document that pkgutil's iteration functions require the - Issue #14982: Document that pkgutil's iteration functions require the
non-standard iter_modules() method to be defined by an importer (something non-standard iter_modules() method to be defined by an importer (something
the importlib importers do not define). the importlib importers do not define).
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment