I have a decorator that creates an abstractmethod from a simple method. It works as I'd expect, however if I run mypy, it tells me this:
mypy_try.py:20: error: Missing return statement [empty-body]
mypy_try.py:20: note: If the method is meant to be abstract, use @abc.abstractmethod
Found 1 error in 1 file (checked 1 source file)
My code:
import abc
from functools import wraps
import pytest
def make_it_abstract(method_to_decorate):
@wraps(method_to_decorate)
def decorated_method(*method_args, **method_kwargs):
return method_to_decorate(*method_args, **method_kwargs)
return abc.abstractmethod(decorated_method)
class MyInterfaceClass(abc.ABC):
@make_it_abstract
# @abc.abstractmethod
def my_method(self, value: int) -> int:
...
def test_abstract_method():
class MyImplementationClass(MyInterfaceClass):
pass
with pytest.raises(
TypeError,
match="Can't instantiate abstract class MyImplementationClass with abstract method my_method"
):
MyImplementationClass()
class MyImplementationClass(MyInterfaceClass):
def my_method(self, value: int) -> float:
return value +1
assert 43 == MyImplementationClass().my_method(42)
If I use the abc.abstractmethod
decorator, it works fine.
What am I doing wrong?
You're doind everything fine, but mypy
is not smart enough to figure out that your decorator calls abc.abstractmethod
(and this is almost impossible, in fact, even if you've typed the decorator).
According to code in typeshed, abstractmethod
is a no-op for type checkers. So mypy
just detects the usage of abc.abstractmethod
as decorator directly, as can be seen here. refers_to_fullname
method expands aliases and basically checks if node name is equal to one of requested names.
So even the following raises the same error:
ab = abc.abstractmethod
class MyInterfaceClass(abc.ABC):
@ab
def my_method(self, value: int) -> int: # E: Missing return statement [empty-body]
...