python / python/mypy

Issue inferring type with circular imports/future annotations

Open
#12,874 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Python
Stars
20.6k
Forks
3.3k
PR merge metrics
PR metrics pending

Description

Hey! I ran into something which I think is a bug, although its a very small use case and can be fixed pretty easily. This is my first time reporting an issue so let me know if I need to supply anything more info, or if this isn't actually a bug

Bug Report

using future annotations with this circular import seems to cause issues with recognizing types

To reproduce:

create the following structure:

src
 ├──my_directory
     ├── __init__.py
     ├──a.py
 ├──__init__.py
 ├──b.py

with the following code in the files:

src/my_directory/init.py:

from .a import OBJECT_FACTORY, Object

src/my_directory/a.py

from .. import b

class Object():
    pass

class ObjectFactory:
    def make_object(self) -> Object:
        pass

OBJECT_FACTORY = ObjectFactory()

src/init.py

# empty file

src/b.py

from __future__ import annotations
from . import my_directory

def func(bar: Bar):
    print(bar.object)

class Bar:
    def __init__( 
        self,
        _: str, # if you delete this type annotation there will no longer be an error
    ):
        self.object = my_directory.OBJECT_FACTORY.make_object()

then run:
mypy src

Expected Behavior:

Should be able to infer that the type of bar.object is Object from the type annotation of the make_object method.

Actual Behavior

src/b.py:5: error: Cannot determine type of "object"
Found 1 error in 1 file (checked 4 source files)

Some interesting behaviors:

  • if you delete the str type annotation in the Bar class, mypy passes with no issues
  • defining func after Bar class passes with no issues
  • deleting the 'from .. import b' in a.py passes with no issues
  • using a static function make_object with the same annotation instead of in OBJECT_FACTORY will pass with no issues
  • running mypy src/b.py instead of mypy src will pass with no issues
    • subsequently, now running mypy src will actually pass without issues (likely due to .mypy_cache, deleting the cache will re-instate the bug)
  • you can fix this issue if you add the type annotation my_directory.Object to self.object in class Bar
My Environment:

mypy --version

mypy 0.960 (compiled: yes)

python --version

Python 3.8.6

  • Mypy command-line flags: no flags
  • OS: Windows 10

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Reproduce the report using the files under src/my_directory/a.py, src/b.py, and the package init.py files, then run mypy src with the cache removed. Compare the behavior when the constructor annotation, circular import, or future annotations are changed. Done means the reported example infers bar.object as Object without adding a redundant annotation and still reports no errors.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
tooling
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.