Skip to content

Instantly share code, notes, and snippets.

@tbrlpld
Last active May 17, 2020 02:19
Show Gist options
  • Save tbrlpld/93c25a7e21eb969311c4893ed3e0533c to your computer and use it in GitHub Desktop.
Save tbrlpld/93c25a7e21eb969311c4893ed3e0533c to your computer and use it in GitHub Desktop.
How to make sure you are only using the abstract interface of a concrete implementation.
# -*- coding: utf-8 -*-
"""
Defines the method patters the are supposed to be implemented in concrete.
There is no actual implementation here. This serves as a interface. The
consumer should use the concrete implementations only to the extend defined
here.
"""
import abc
class AbstractBase(abc.ABC):
"""Abstract class."""
@abc.abstractclassmethod
def multiplystring(self, single_string: str, muliplier: int) -> str:
"""Create long string with multiple versions of given string."""
# -*- coding: utf-8 -*-
"""
Concrete implementation of the abstract class.
"""
import abstract
class ConcreteClass(abstract.AbstractBase):
"""
Concrete implementation.
Actually implements the functionality defined by the base class but also
adds more methods. The consumer should never use any of the additional
methods. If it does, then it depends on the concrete implementation.
And that should always be avoided as much as possible.
"""
def multiplystring(self, single_string: str, multiplier: int) -> str:
return single_string * multiplier
def doublestring(self, single_string: str) -> str:
return single_string * 2
# -*- coding: utf-8 -*-
"""
Consumer of the "abstract" class.
Of course, to actually get functionality it needs to use a concrete
implementation. But, it should only use the features that are defined by the
abstract base class.
I am wondering how to get mypy to warn me if I am using functionality that
is not defined in the abstract base.
"""
import abstract
import concrete
concrete1 = concrete.ConcreteClass()
concrete1.multiplystring("something", 3)
concrete1.doublestring("something")
# No warnings so far. I have not "told" mypy that I expect to only use the
# "interface" defined by the abstract class.
# Now, lets try to define that.
concrete2: abstract.AbstractBase = concrete.ConcreteClass() # set expected type
concrete2.multiplystring("something", 3)
concrete2.doublestring("something")
# Nice. `mypy` shows the warning I was hoping for on the last line.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment