[Python-Dev] PEP 544 (Protocols): adding a protocol to a class post-hoc
On 30 June 2018 at 23:54, Tin Tvrtkovi? <tinchester at gmail.com> wrote:
> An attrs class has a special class-level field, __attrs_attrs__, which
> holds the attribute definitions. So maybe we can define a protocol:
> class AttrsClass(Protocol):
> __attrs_attrs__: ClassVar[Tuple[Attribute, ...]]
> then we could define asdict as (simplified):
> def asdict(inst: AttrsClass) -> Dict[str, Any]:
> and it should work out. My question is how to actually add this protocol
> to attrs classes. Now, we currently have an attrs plugin in mypy so maybe
> some magic in there could make it happen in this particular case.
Just add a Var with an appropriate name and type to the TypeInfo. This is
literary a dozen lines of code, you can ask on mypy tracker or typing
Gitter chat for more help.
> My second use case is a small library I've developed for work, which
> basically wraps attrs and generates and sticks methods on a class for
> serialization/deserialization. Consider the following short program, which
> does not typecheck on the current mypy.
> class Serializable(Protocol):
> def __serialize__(self) -> int:
> def make_serializable(cl: Type) -> Type:
> cl = attr.s(cl)
> cl.__serialize__ = lambda self: 1
> return cl
> class A:
> a: int = attr.ib()
> def serialize(inst: Serializable) -> int:
> return inst.__serialize__()
> error: Argument 1 to "serialize" has incompatible type "A"; expected
> error: Too many arguments for "A"
> I have no desire to write a mypy plugin for this library. So I guess what
> is needed is a way to annotate the class decorator, telling mypy it's
> adding a protocol to a class. It seems to have trouble getting to this
> conclusion by itself.
A proper solution for this would be to introduce intersection types, and
type your decorator as following:
T = TypeVar('T')
def make_serializable(cls: Type[T]) -> Type[Intersection[T, Serializable]]:
However, intersection types are unlikely to appear in mypy this year. In
best case they could appear around mid-2019, so you are better with writing
a plugin for now.
> (The second error implies the attrs plugin doesn't handle wrapping attr.s,
> which is unfortunate but a different issue.)
Your decorator is typed as (Type) -> Type, thats it, the function is a
black box for mypy (with few special exceptions), if some effect of a
function is not declared in its signature, then it is lost forever.
-------------- next part --------------
An HTML attachment was scrubbed...