<div dir="ltr"><div class="gmail_extra"><div class="gmail_quote">On 30 June 2018 at 23:54, Tin Tvrtković <span dir="ltr"><<a href="mailto:tinchester@gmail.com" target="_blank">tinchester@gmail.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><div>[...]</div><div><br></div><div>An attrs class has a special class-level field, __attrs_attrs__, which holds the attribute definitions. So maybe we can define a protocol:</div><div><br></div><div>class AttrsClass(Protocol):<br>    __attrs_attrs__: ClassVar[Tuple[Attribute, ...]]</div><div><br></div><div>then we could define asdict as (simplified):</div><div><br></div><div><font color="rgb(212, 212, 212)">def asdict(inst: AttrsClass) -> Dict[str, Any]:</font></div><div><font color="rgb(212, 212, 212)">    ...<br></font></div><div><font color="rgb(212, 212, 212)"><br></font></div><div><font color="rgb(212, 212, 212)">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.</font></div></div></blockquote><div><br></div><div>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.</div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><div><font color="rgb(212, 212, 212)">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.</font></div><div><br></div><div><font color="rgb(212, 212, 212)">class Serializable(Protocol):<br>    def __serialize__(self) -> int:<br>        ...<br><br>def make_serializable(cl: Type) -> Type:<br>    cl = attr.s(cl)<br>    cl.__serialize__ = lambda self: 1<br>    return cl<br><br><br>@make_serializable<br>class A:<br>    a: int = attr.ib()<br><br><br>def serialize(inst: Serializable) -> int:<br>    return inst.__serialize__()<br><br><br>serialize(A(1))</font></div><div><font color="rgb(212, 212, 212)"><br></font></div><div><font color="rgb(212, 212, 212)">error: Argument 1 to "serialize" has incompatible type "A"; expected "Serializable"<br>error: Too many arguments for "A"</font></div><div><font color="rgb(212, 212, 212)"><br></font></div><div><font color="rgb(212, 212, 212)">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.</font></div></div></blockquote><div><br></div><div>A proper solution for this would be to introduce intersection types, and type your decorator as following:</div><div><br></div><div>T = TypeVar('T')</div><div>def make_serializable(cls: Type[T]) -> Type[Intersection[T, Serializable]]: ...</div><div><br></div><div>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.</div><div> <br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><div><font color="rgb(212, 212, 212)">(The second error implies the attrs plugin doesn't handle wrapping attr.s, which is unfortunate but a different issue.)</font></div></div></blockquote><div><br></div><div> 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.</div><div><br></div><div>--</div><div>Ivan</div><div><br></div><div><br></div></div></div></div>