<div dir="ltr"><div>Hi,</div><div><br></div><div>PEP 544 specifies this address as "Discussions-To" so I hope I'm at the right address.</div><div><br></div><div>I think protocols as defined in the PEP are a very interesting idea and I'm thinking of ways of applying them. The first use case is in the context of attrs.</div><div><br></div><div>attrs has a number of functions that work only on attrs classes; asdict for example (turns an attrs class into a dictionary recursively). We can't really type hint this properly using nominal subtyping since attrs classes don't have an exclusive ancestor. But it sounds like we could use structural subtyping!</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><font color="rgb(212, 212, 212)"><br></font></div><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><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. (The second error implies the attrs plugin doesn't handle wrapping attr.s, which is unfortunate but a different issue.)</font></div><div><font color="rgb(212, 212, 212)"><br></font></div><div><font color="rgb(212, 212, 212)">I have found this pattern of decorating classes and enriching them with additional methods at run-time really powerful, especially when used with run-time parsing of type information (that often gets a bad rep on this list, I've noticed :) The structural typing subsystem seems like a good fit for use cases like this, and I think it'd be a shame if we couldn't make it work somehow.</font><br></div></div>