microsoft / pyright

Static Type Checker for Python
Other
13.12k stars 1.4k forks source link

Type of a callable submodule is inferred incorrectly #8830

Closed dimaqq closed 2 weeks ago

dimaqq commented 2 weeks ago

Given following ops/__init__.py:

from types import ModuleType

from . import main as _main

class _CallableMainModule(ModuleType):
    def __call__(self):
        return 42

main = _CallableMainModule("ops.main", "some doc")

__all__ = ["main"]

And this the test code below, pyright doesn't agree with Python run time:

  def test_top_level_import():
      import ops

      reveal_type(ops.main) # "_CallableMainModule"
      assert ops.main() == 42

  def test_submodule_import():
      import ops.main

      reveal_type(ops.main) # "Module("ops.main")"
E     assert ops.main() == 42 # E: Module is not callable

  def test_function_import():
      from ops import main

      reveal_type(main) # "_CallableMainModule"
      assert main() == 42

In fact, all 3 imports+call styles work OK at run time.

Full MRE: https://github.com/dimaqq/MRE-pyright-submodule-import Tested: pyright 1.1.377

Note that if from . import main as _main is removed, pyright's interpretation of submodule import becomes correct, but then pyright doesn't catch the function import.

erictraut commented 2 weeks ago

Your code is relying on side effects of the import loader that pyright does not model. This is by design, so I don't consider this a bug.

You'll need to change your code if you want it to work with pyright.

dimaqq commented 2 weeks ago

Thank you for quick response.

Maybe I can get some advice how to best achieve what I'm really trying to do:

In the past we have ops/main.py: def main(..): ...:

We can't afford a breaking change, but we want:

In our case, the new way of calling main was introduced before the package got typed.

P.S. there seem to some kind of spam going around on GitHub, this issue got a comment from spammer, right above. We're reporting and deleting these comments in our repos, but it seems that only maintainers can do so.