Skip to content

Instantly share code, notes, and snippets.

@pauleveritt
Created April 18, 2020 15:37
Show Gist options
  • Save pauleveritt/6cf726b72422e89d4e6e5a3fc1036e12 to your computer and use it in GitHub Desktop.
Save pauleveritt/6cf726b72422e89d4e6e5a3fc1036e12 to your computer and use it in GitHub Desktop.
Protocols and dataclass-based components
"""
https://github.com/python/mypy/issues/4717#issuecomment-454609539
Hi Glyph, thanks for reading. This is going to take a while.
I'm interested in pluggable systems. Specifically a better Sphinx rendering
layer, which I've spent a lot of time on. Recently I've worked on Michael
Merickel's wired:
https://wired.readthedocs.io/
Specifically I wrote the dataclass-based injector, which uses dataclass
field metadata to give instructions to the injector.
wired makes it easy to register multiple implementations for a class or
interface. But classes kind of suck, because the "interface" is an
implementation (class). However interfaces still have poor tooling
support, even with mypy-zope.
(I won't belabor the point about wired, DI, and the React-style component
stuff I'm working on. Not necessary to this.)
I'm interested in PEP 544 protocols because they have a chance to be more
adopted than interfaces. In the example
"""
from dataclasses import dataclass
# This starts the part that is in framework space, so civilians don't
# have to see it, their tooling, e.g. IDE autocomplete, just starts
# working.
from typing import Callable, Type, TypeVar
from typing_extensions import Protocol
protocol = TypeVar("protocol")
# In wired, this decorator also handles a registration in the registry,
# as well as a constructor which sniffs the dataclass fields to do the
# DI.
def component(c: Callable[[], protocol]) -> Callable[[Type[protocol]], Type[protocol]]:
def decor(input_value: Type[protocol]) -> Type[protocol]:
return input_value
return decor
# /end framework space
# This is done by the creator of some sexy library of ready-to-go
# components. The average consumer doesn't see this.
class Heading(Protocol):
x: int
@component(Heading)
@dataclass
class GoodHeading:
x: int
other: str
@component(Heading)
@dataclass
class BadHeading:
y: int
# /end component library author consumer
# I will leave out what the end-user will see, as it is specific to
# the work I'm doing.
if __name__ == '__main__':
good = GoodHeading(x=3, other='xyz')
bad = BadHeading(y=3)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment