Mypy: Semantic analyzer confuses a function with a module

Created on 13 Jul 2019  路  6Comments  路  Source: python/mypy

The following code creates a package structure that demonstrates what I think is a bug in mypy's new semantic analyzer.

from os import makedirs

makedirs('src/core')
makedirs('src/stuff')

open('src/__init__.py', 'w').write("""
# nothing here
""")

open('src/core/__init__.py', 'w').write("""
from .run import config
""")

open('src/core/run.py', 'w').write("""
from ..stuff import somefunction

config = None

somefunction()
""")

open('src/stuff/__init__.py', 'w').write("""
from .somemore import more
from .somefunction import somefunction
""")

open('src/stuff/somefunction.py', 'w').write("""
def somefunction():
    print("Hello, World!")
""")

open('src/stuff/somemore.py', 'w').write("""
def more():
    from ..core import config
""")

When checking the created src package with mypy 0.720 and Python 3.7, I get the following error:

src/core/run.py:6: error: Module not callable

What mypy thinks is a module is actually a function. It just has the same name as its containing module.

When I check with --no-new-semantic-analyzer, I do not get this error.

bug false-positive new-semantic-analyzer priority-0-high

Most helpful comment

I confirm this. Also I was able to find a much simpler repro. Here is a test case:

[case testSubModuleVsFunction]
# cmd: mypy -m test stuff stuff.somefunction

[file test.py]
from stuff import somefunction
somefunction()

[file stuff/__init__.py]
import test
from stuff.somefunction import somefunction

[file stuff/somefunction.py]
def somefunction(): ...

[builtins fixtures/module.pyi]

All 6 comments

I confirm this. Also I was able to find a much simpler repro. Here is a test case:

[case testSubModuleVsFunction]
# cmd: mypy -m test stuff stuff.somefunction

[file test.py]
from stuff import somefunction
somefunction()

[file stuff/__init__.py]
import test
from stuff.somefunction import somefunction

[file stuff/somefunction.py]
def somefunction(): ...

[builtins fixtures/module.pyi]

cc @JukkaL

Note there is a similar issue modulo function -> class, see #7172:

[case testCycleRenames]
# flags: --new-semantic-analyzer
import MyX

[file MyX.py]
from inner import MyBase

class MyX:
    x: MyBase = None

[file inner/__init__.py]
from inner.MyBase import MyBase as MyBase

[file inner/MyBase.py]
from MyX import MyX

class MyBase:
    pass

[file inner/MyDerived1.py]
from inner.MyBase import MyBase

class MyDerived1(MyBase):
    pass

Hi, is there any known workaround for this issue?

A possible workaround is to move the definition of the function to __init__.py, instead of importing it from a submodule.

pkg/__init__.py:

def submodule() -> int:
     return 4

pkg/submodule.py:

# no definition of submodule() here

Alternatively, maybe you can define the function in both pkg/__init__.py and pkg/submodule.py. To avoid code duplication, both of them can just wrap a call to another function such as _submodule().

Does this help? The underlying issue may be hard to fix.

I ended up importing as

from package.Object import Object

instead of

from package import Object

Note that my specific case was classes, not functions, but I guess it's the same issue.

Was this page helpful?
0 / 5 - 0 ratings