Skip to content
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

Inferencing type reference of abstract class inferred from concrete type values #8050

Closed
achimnol opened this issue Dec 2, 2019 · 3 comments · Fixed by #8096
Closed

Inferencing type reference of abstract class inferred from concrete type values #8050

achimnol opened this issue Dec 2, 2019 · 3 comments · Fixed by #8096
Labels
bug mypy got something wrong false-positive mypy gave an error on correct code priority-1-normal

Comments

@achimnol
Copy link
Contributor

achimnol commented Dec 2, 2019

import abc

class MyAbstractType(metaclass=abc.ABCMeta):
  @abc.abstractmethod
  def do(self): pass

class MyConcreteA(MyAbstractType):
  def do(self):
    print('A')

class MyConcreteB(MyAbstractType):
  def do(self):
    print('B')

my_types = {
  'A': MyConcreteA,
  'B': MyConcreteB,
}
reveal_type(my_types)  # Revealed type is 'builtins.dict[builtins.str*, def () -> test-abc.MyAbstractType]'

a = my_types['A']()  # Cannot instantiate abstract class 'MyAbstractType' with abstract attribute 'do'
a.do()
b = my_types['B']()  # Cannot instantiate abstract class 'MyAbstractType' with abstract attribute 'do'
b.do()

The above example does not pass the type check as-is.
I need to change it to pass the type check as follows:

import abc
from typing import Mapping, Type

class MyAbstractType(metaclass=abc.ABCMeta):
  @abc.abstractmethod
  def do(self): pass

class MyConcreteA(MyAbstractType):
  def do(self):
    print('A')

class MyConcreteB(MyAbstractType):
  def do(self):
    print('B')

my_types: Mapping[str, Type[MyAbstractType]] = {  # explicit annotation
  'A': MyConcreteA,
  'B': MyConcreteB,
}
reveal_type(my_types)  # Revealed type is 'typing.Mapping[builtins.str, Type[test-abc.MyAbstractType]]'

a = my_types['A']()
a.do()
b = my_types['B']()
b.do()

Since mypy is able to infer MyAbstractType from the dictionary in the first snippet, I believe that it should be also able to infer Type[MyAbstractType] since the dictionary values are not instances but class names.

My setup: Python 3.8.0 + mypy 0.750

Related issues/PRs:

@JukkaL
Copy link
Collaborator

JukkaL commented Dec 2, 2019

There are two potential ways to improve the situation:

  1. Infer Type[X] as the value type instead of a callable type.
  2. Make it possible to instantiate an abstract type object represented as a callable type.

The first option is harder to implement and is not backward compatible, so my preference would be option 2.

A potential way to implement 2 would be to only flag as errors direct calls to an ABC type object (e.g. MyAbstractType()). If the callee is any other kind of expression or a variable, mypy wouldn't complain, since it could actually be a reference to a concrete derived class at runtime.

@ilevkivskyi
Copy link
Member

More details about ways to implement option 2. There may be a really simple way: set the from_type_type flag to True, when computing join and meet of callables (probably unless some/all items represent an abstract class object).

ilevkivskyi pushed a commit that referenced this issue Dec 7, 2019
…ype values (#8096)

Resolves #8050, following the purposed solution 2 in  #8050 (comment) and using the implementation purposed in #8050 (comment)
@scottfurry
Copy link

This assumes both abstract and all concrete implementations are in the same model.

Has someone tried appending to the mapping?
Each concrete implementation would append itself to the mapping?

Seems like a lot of contortions to silence this error.
Note - it took a handful of searches to end up here.
Stopped at along the way... : bugs #1843, #3048, #5135, and #6586.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug mypy got something wrong false-positive mypy gave an error on correct code priority-1-normal
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants