5

Greatings, consider the following code.

from abc import ABC, abstractmethod


class Interface(ABC):
    @abstractmethod
    def method(self) -> None:
        pass


class A(Interface):
    def method(self) -> None:
        pass


class B(Interface):
    def method(self) -> None:
        pass


mapping = {'A': A, 'B': B}


# does NOT pass mypy checks
def create_map(param: str) -> Interface:
    if param in mapping:
        return mapping[param]()
    else:
        raise NotImplementedError()

# passes mypy checks
def create_if(param: str) -> Interface:
    if param == 'A':
        return A()
    elif param == 'B':
        return B()
    else:
        raise NotImplementedError()

For some reason, create_if passes all mypy type checking, but create_map does not. The reveal_type for both functions is 'def (param: builtins.str) -> test.Interface'.

The error I get is the same as if I were trying to instantiate an abstract class directly, which is weird considering this reference for mypy.

error: Cannot instantiate abstract class 'Interface' with abstract attribute 'method'

Also, if I make mapping = {'A': A} (i.e. remove 'B': B) now create_map also passes.

Can someone shed some light on this?

Daniel Severo
  • 1,768
  • 2
  • 15
  • 22
  • 2
    If you type your `mapping` (`mapping: Mapping[str, Type[Interface]] = {'A': A, 'B': B}`), things appear to work correctly. Credit to mypy issues [1843](https://github.com/python/mypy/issues/1843) and [3048](https://github.com/python/mypy/issues/3048) for showing this syntax. (Why `mapping = {'A': A}` works without this extends beyond my mypy knowledge, however.) – jonafato Jan 17 '19 at 18:48
  • Not sure exactly how I missed issue 3048 when searching for a solution, it is almost exactly what I asked. Thanks! – Daniel Severo Jan 17 '19 at 18:53
  • "(Why mapping = {'A': A} works without this extends beyond my mypy knowledge, however.)" <- I would really like to know the answer to this :) I had the same problem over at https://stackoverflow.com/questions/74741125/why-does-mypy-throw-cannot-instantiate-abstract-class-when-it-is-not-being-ins/74741969#74741969 – jamiet Dec 09 '22 at 13:34

1 Answers1

13

Apparently all I was missing was the type annotation for the mapping.

mapping: Mapping[str, Type[Interface]] = {'A': A, 'B': B}

Thanks @jonafato

Credit to mypy issues 1843 and 3048 for showing this syntax.

Daniel Severo
  • 1,768
  • 2
  • 15
  • 22