From 46be0f7aa55582893feabcac6bab289a8b5c3705 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Saugat=20Pachhai=20=28=E0=A4=B8=E0=A5=8C=E0=A4=97=E0=A4=BE?= =?UTF-8?q?=E0=A4=A4=29?= Date: Wed, 21 Sep 2022 17:53:50 +0545 Subject: [PATCH] improve is_namespace check See https://stackoverflow.com/a/42962529. Let's take the following contents as an example: ```python import celery.result ``` From #1777, astroid started to use `processed_components` for namespace check. In the above case, the `modname` is `celery.result`, it first checks for `celery` and then `celery.result`. Before that PR, it'd always check for `celery.result`. But if you have imported it as `celery.result`, `sys.modules["celery"].__spec__` is going to be `None`, and hence the function will return True, and but below where we try to load get `submodule_path`/`__path__` for `celery.result` will fail as it is not a package. See https://github.com/PyCQA/astroid/blob/056d8e5fab7a167f73115d524ab92170b3ed5f9f/astroid/interpreter/_import/spec.py#L205-L207 --- The `celery.result` gets imported for me when pylint-pytest plugin tries to load fixtures, but this could happen anytime if any plugin imports packages. In that case, `find_spec("celery")` will raise ValueError since it's already in `sys.modules` and does not have a spec. I still think there's a bug for the `ExplicitNamespacePackageFinder.find_module` for namespace packages, since `modname` might be `namespace.package.module` but since `is_namespace` package goes through components, the successive `sys.modules[modname].__path__` may raise error, since modname could be not a package in a namespaced package. But I am not quite sure about that. Fixes https://github.com/PyCQA/pylint/issues/7488. --- astroid/interpreter/_import/util.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/astroid/interpreter/_import/util.py b/astroid/interpreter/_import/util.py index c9466999ab..b5b089331e 100644 --- a/astroid/interpreter/_import/util.py +++ b/astroid/interpreter/_import/util.py @@ -52,8 +52,11 @@ def is_namespace(modname: str) -> bool: # Check first fragment of modname, e.g. "astroid", not "astroid.interpreter" # because of cffi's behavior # See: https://github.com/PyCQA/astroid/issues/1776 + mod = sys.modules[processed_components[0]] return ( - sys.modules[processed_components[0]].__spec__ is None + mod.__spec__ is None + and getattr(mod, "__file__", None) is None + and hasattr(mod, "__path__") and not IS_PYPY ) except KeyError: