Skip to content

fix AttributeError crash when using --import-mode=importlib #13029

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

Merged
merged 2 commits into from
Dec 7, 2024

Conversation

dongfangtianyu
Copy link
Contributor

closes #13026 .

  • Include new tests or update existing tests when applicable.
  • Allow maintainers to push and squash when merging my commits.
  • Create a new changelog file in the changelog folder.

Only parent modules with the __path__ attribute can be used by the find_spec function, and most of the standard library does not meet this condition.

https://github.com/python/cpython/blob/3.9/Lib/importlib/_bootstrap_external.py#L1223

   def _find_parent_path_names(self):
        """Returns a tuple of (parent-module-name, parent-path-attr-name)"""
        parent, dot, me = self._name.rpartition('.')
        if dot == '':
            # This is a top-level module. sys.path contains the parent path.
            return 'sys', 'path'
        # Not a top-level module. parent-module.__path__ contains the
        #  parent path.
        return parent, '__path__'

So this PR adds a condition check.

@psf-chronographer psf-chronographer bot added the bot:chronographer:provided (automation) changelog entry is part of PR label Dec 5, 2024
@dongfangtianyu dongfangtianyu changed the title fix #13026 fix AttributeError crash when using --import-mode=importlib Dec 5, 2024
Copy link
Member

@nicoddemus nicoddemus left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @dongfangtianyu for investigating it and opening a fix so quickly!

Left some small comments, can you take a look?

Copy link
Member

@nicoddemus nicoddemus left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

@nicoddemus nicoddemus merged commit 8cff128 into pytest-dev:main Dec 7, 2024
28 checks passed
Copy link

patchback bot commented Dec 7, 2024

Backport to 8.3.x: 💚 backport PR created

✅ Backport PR branch: patchback/backports/8.3.x/8cff12865691b14b4bce70e0e304524619be385d/pr-13029

Backported as #13037

🤖 @patchback
I'm built with octomachinery and
my source is open — https://github.com/sanitizers/patchback-github-app.

patchback bot pushed a commit that referenced this pull request Dec 7, 2024
Only parent modules with the `__path__` attribute can be used by the `find_spec` function, and most of the standard library does not meet this condition.

Fixes #13026 .

(cherry picked from commit 8cff128)
nicoddemus pushed a commit that referenced this pull request Dec 7, 2024
…) (#13037)

Only parent modules with the `__path__` attribute can be used by the `find_spec` function, and most of the standard library does not meet this condition.

Fixes #13026 .

(cherry picked from commit 8cff128)

Co-authored-by: dongfangtianyu <[email protected]>
@dongfangtianyu dongfangtianyu deleted the fix_13026 branch December 13, 2024 07:13
@manueljacob
Copy link
Contributor

This is very dangerous, as it will override an already imported module if it’s not a package (i.e. lacks the __path__ attribute).

For example:

pyproject.toml:

[build-system]
requires = ["setuptools"]
build-backend = "setuptools.build_meta"

[project]
name = "foo"
version = "0.0"

[tool.setuptools.packages.find]
where = ["code"]

[tool.pytest.ini_options]
addopts = [
    "--import-mode=importlib",
]

code/foo/__init__.py:

code/foo/repl.py:

def compile_command(source):
    from code import compile_command

    return compile_command(source)

code/foo/test/test_foo.py:

from foo.repl import compile_command


def test_foo():
    assert compile_command("42") is not None

Running pytest fails with:

    def test_foo():
>       assert compile_command("42") is not None
               ^^^^^^^^^^^^^^^^^^^^^

code/foo/test/test_foo.py:6: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

source = '42'

    def compile_command(source):
>       from code import compile_command
E       ImportError: cannot import name 'compile_command' from 'code' (unknown location)

code/foo/repl.py:5: ImportError

A shorter, but more artificial example:

time/test.py:

def test():
    from time import time

    assert time() > 0

Running pytest --rootdir=. --import-mode=importlib time/test.py fails with:

    def test():
>       from time import time
E       ImportError: cannot import name 'time' from 'time' (unknown location)

time/test.py:2: ImportError

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bot:chronographer:provided (automation) changelog entry is part of PR
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Upgrade pytest from 8.3.3 to 8.3.4 triggers AttributeError: module 'code' has no attribute '__path__'. Did you mean: '__all__'?
3 participants