Skip to content

Commit 9d10a3e

Browse files
TH3CHARLieilevkivskyi
authored andcommitted
Inferencing type reference of abstract class inferred from concrete type values (python#8096)
Resolves python#8050, following the purposed solution 2 in python#8050 (comment) and using the implementation purposed in python#8050 (comment)
1 parent a2a5147 commit 9d10a3e

File tree

3 files changed

+58
-0
lines changed

3 files changed

+58
-0
lines changed

mypy/join.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,11 @@ def visit_callable_type(self, t: CallableType) -> ProperType:
187187
if is_equivalent(t, self.s):
188188
return combine_similar_callables(t, self.s)
189189
result = join_similar_callables(t, self.s)
190+
# We set the from_type_type flag to suppress error when a collection of
191+
# concrete class objects gets inferred as their common abstract superclass.
192+
if not ((t.is_type_obj() and t.type_object().is_abstract) or
193+
(self.s.is_type_obj() and self.s.type_object().is_abstract)):
194+
result.from_type_type = True
190195
if any(isinstance(tp, (NoneType, UninhabitedType))
191196
for tp in get_proper_types(result.arg_types)):
192197
# We don't want to return unusable Callable, attempt fallback instead.

mypy/meet.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -533,6 +533,11 @@ def visit_callable_type(self, t: CallableType) -> ProperType:
533533
if is_equivalent(t, self.s):
534534
return combine_similar_callables(t, self.s)
535535
result = meet_similar_callables(t, self.s)
536+
# We set the from_type_type flag to suppress error when a collection of
537+
# concrete class objects gets inferred as their common abstract superclass.
538+
if not ((t.is_type_obj() and t.type_object().is_abstract) or
539+
(self.s.is_type_obj() and self.s.type_object().is_abstract)):
540+
result.from_type_type = True
536541
if isinstance(get_proper_type(result.ret_type), UninhabitedType):
537542
# Return a plain None or <uninhabited> instead of a weird function.
538543
return self.default(self.s)

test-data/unit/check-abstract.test

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -951,3 +951,51 @@ default = Config({'cannot': 'modify'}) # OK
951951
default[1] = 2 # E: Unsupported target for indexed assignment
952952
[builtins fixtures/dict.pyi]
953953
[typing fixtures/typing-full.pyi]
954+
955+
[case testSubclassOfABCFromDictionary]
956+
from abc import abstractmethod, ABCMeta
957+
958+
class MyAbstractType(metaclass=ABCMeta):
959+
@abstractmethod
960+
def do(self): pass
961+
962+
class MyConcreteA(MyAbstractType):
963+
def do(self):
964+
print('A')
965+
966+
class MyConcreteB(MyAbstractType):
967+
def do(self):
968+
print('B')
969+
970+
class MyAbstractA(MyAbstractType):
971+
@abstractmethod
972+
def do(self): pass
973+
974+
class MyAbstractB(MyAbstractType):
975+
@abstractmethod
976+
def do(self): pass
977+
978+
my_concrete_types = {
979+
'A': MyConcreteA,
980+
'B': MyConcreteB,
981+
}
982+
983+
my_abstract_types = {
984+
'A': MyAbstractA,
985+
'B': MyAbstractB,
986+
}
987+
988+
reveal_type(my_concrete_types) # N: Revealed type is 'builtins.dict[builtins.str*, def () -> __main__.MyAbstractType]'
989+
reveal_type(my_abstract_types) # N: Revealed type is 'builtins.dict[builtins.str*, def () -> __main__.MyAbstractType]'
990+
991+
a = my_concrete_types['A']()
992+
a.do()
993+
b = my_concrete_types['B']()
994+
b.do()
995+
996+
c = my_abstract_types['A']() # E: Cannot instantiate abstract class 'MyAbstractType' with abstract attribute 'do'
997+
c.do()
998+
d = my_abstract_types['B']() # E: Cannot instantiate abstract class 'MyAbstractType' with abstract attribute 'do'
999+
d.do()
1000+
1001+
[builtins fixtures/dict.pyi]

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy