mypy-zope icon indicating copy to clipboard operation
mypy-zope copied to clipboard

A way to express "A type which implements these N interfaces"

Open DMRobertson opened this issue 3 years ago • 3 comments

Let's say I have two interfaces IA and IB. They're completely independent of each other. I implement both in some type C.

from zope.interface import Interface, implementer

class IA(Interface):
    def do_a(): ...

class IB(Interface):
    def do_b(): ...


@implementer(IA, IB)
class C:
    def do_a(): ...
    def do_b(): ...

I can assign a C instance to a variable marked against either interface.

a: IA = C  # ok
b: IB = C  # ok

I can also pass a C instance to a function requiring either interface.

def needs_IA(a: IA): ...
def needs_IB(b: IB): ...

needs_IA(c)  # ok
needs_IB(c)  # ok

Suppose I have another function which requires both interfaces.

def needs_IA_and_IB(arg): ...
needs_IA_and_IB(c)         # want this to be checked as ok
needs_IA_and_IB(object())  # want this to be checked as not okay

If I know that I'm only going to use Cs, I can mark arg: C and call it a day. But this doesn't work if I have a second type D that implements both interfaces.


@implementer(IA, IB)
class D:
    def do_a(): ...
    def do_b(): ...
    
    
d = D()
needs_IA_and_IB(d)  # want this to also be okay

How should I annotate arg? What I'd like is a way to describe "a type that implements each of these interfaces". Is there a way to do this at present? There's no notion of an intersection type; maybe there'd need to be a new Type spelt Implements[IA, IB, ...] for this purpose?

Example

To give a concrete example: Twisted's HostnameEndpoint.__init__ expects a reactor argument which should be

provider of IReactorTCP, IReactorTime and either IReactorPluggableNameResolver or IReactorPluggableResolver.

I don't think that can be currently expressed in the type system. I'd want to spell it as something like

Implements[
    IReactorTCP,
    IReactorTime,
    Union[IReactorPluggableNameResolver, IReactorPluggableResolver]
]

The inner Union might make things harder; even being able to express one of the two choices like

Implements[IReactorTCP, IReactorTime, IReactorPluggableNameResolver]

would be helpful!


I don't know if this is something in the remit of mypy-zope as opposed to the zope project itself. I'm hopingthis is a good to ask fo suggestions or feedback though!

DMRobertson avatar Oct 28 '21 10:10 DMRobertson