-
-
Notifications
You must be signed in to change notification settings - Fork 373
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Protocol(s) for attrs classes? #952
Comments
To add a bit of context, this stemmed from code that basically did:
While (most of) the other cases can be represented in the type signature ( |
I started some work on this already: #890 My plan was to go even further: since Mypy now knows the exact type of But I do think this would be a good idea, naturally. |
The problem is that As much as I love them, I don't think that a I think what would make more sense here is something like: class AttrsClass(ABC):
pass and then call Opinions? |
Even if you consider @hynek Your proposed solution doesn't work for static checking, right? |
I don’t think there’s a solution that does it right statically without writing plugins? Given that I’m afraid that pyright is the future thanks to funding, I’m not sure any other is viable. Unless I misunderstood the problem — I thought they just wanted to be able to determine at runtime via typing that something is an attrs class? |
The plugin is already written :p. The protocol from the OP should actually work in Mypy now. |
Not quite. The desire is for static analysis to be able to know a type is an attrs class and address them.
This is probably mostly true (iiuc, mypy and pytype both have custom code for attrs). But such type-checker specific extensions just allow a specific type checker to track, via it's own implementation-specific mechanisms, that an object is of type "Attrs generated class". The key part missing is a canonical static-analysis-time symbol that says "this is an attrs generated class", and it'll work best if that symbol name is provided/owned by attrs itself instead of some third party. Such a symbol allows arbitrary code to say "I accept an attrs generated class" and not have to worry about a specific type checker's way to represent them. Without such a symbol, user code would have to do e.g.
TBH, I wouldn't worry about that. Type annotations aren't really a great way to enforce visibility controls. Users can just cast, disable type checker warnings, and etc to bypass the "wrong" errors when the type checker says an attribute doesn't exist when it actually does. An alternative would be to introduce an attribute whose only purpose is to act as a marker. e.g. re: Personally, I don't really like protocols so this more nominal typing approach is more appealing to me. I don't think the register() part is strictly necessary. Just note that it would allow a second way to test for an attrs class in addition to TBH, my gut says keeping the static-analysis-support simple like this is prudent -- the type language is capable of expressing the basics of "the class/object looks-like/inherits-from X", but isn't yet capable of expressing the set of transformations attrs does to a class, so trying to fit anything more than "yep, its an attrs generated thing" would be hard and not worth it. A simple empty marker class like this would probably provide the basics for what's necessary, though. Type checkers can internally tag that |
The issue I see with an empty marker class is that every type checker would still have to special-case it, and if we're special-casing attrs classes anyway, why not just support I like the idea of using a protocol. If the concern is about |
To be clear, this is indeed what I was intending to propose: |
Having a type checker recognize the type-implications that attrs.has() performs is somewhat a different issue. The goal is make code like this type-safe insofar as static analysis is concerned:
A type checker might internally know Foo is attrs-generated, but users have no way of saying that themselves. Because they can't say that, they are forced to, essentially, disable type checking -- they have to resort to putting Any or object, which defeats any more specific types they put in. A type checker supporting attrs.has() would make the "attrs.asdict()" branch Just Work for users, but it's not a necessity. Users could still manually cast when they know.
I don't see how this is possible using Protocol? Protocol is for structural typing, so there has to be something about the object structure that causes an object to match. Is an empty Protocol special? |
I'm suggesting that The main constraint this introduces on |
Alright, Paul & I spoke at PyCon and I'll try to shepherd #890 into main. Currently some higher CI forces are fighting us. |
Fixed by #890 |
During a conversation today, it came up that some people might find it useful to have a static type that is equivalent to
attrs.has
. I noticed, for example, that the signature forattrs.asdict
is:But if, for example, we had a protocol like this:
It would make it so that many of the attrs-class-specific functions could be statically typed. End-users could create this protocol themselves, but it seems like this might be relying on implementation details, and the
attrs
functions aren't annotated to require classes match this protocol anyway, so this seems like something that maybe should be exposed directly inattrs
.I'm not sure if there are any finer-grained protocols that it would be worth defining — e.g. are there any
attrs
classes whereasdict
orastuple
orevolve
would fail on? If so, it might make sense to defineAsDictable
,AsTupleAble
,MutableAttrsClass
or whatever.The text was updated successfully, but these errors were encountered: