Take 2: Rules for subclassing Any

Thanks for starting this thread @mikeshardmind. I appreciate your willingness to give this alternative approach a try.

I agree that this case is under-specified in the typing spec. I’m tracking a list of other under-specified areas that are currently causing pain for users, and that list has grown to almost 90 in length. (We have a lot of work to do! At our current pace, it will take over two years to get through this list.) From my perspective, this issue isn’t currently causing problems for users of mypy and pyright, so my inclination (purely pragmatic) would be to put this on the back burner for now and focus on other areas that are more pressing. However, if this issue is blocking you from making progress on the Intersection proposal, then it raises the priority in my mind.

A few thoughts that might help inform the discussion:

  • We have already established a precedent with type[Any] that seems applicable here. In this thread, we reached consensus that type checkers should treat type[Any] as though all known methods and attributes of type and object are present and all other (not-present) methods and attributes are treated as type Any. Theoretically, a metaclass could override one of the methods in type, but we decided to ignore that possibility. I think this is reasonable. The current behavior of mypy and pyright (which prioritize known attribute types) is consistent with the type[Any] precedent.

  • Type information is used for more than just static type checking. It’s also used for runtime type checking and for edit-time features like completion suggestions and signature help. For every Python developer who uses pyright for static type checking, there are 40x as many developers who do not use static type checking but rely on static type evaluation for language server features. These developers are more likely to be using untyped or partially-typed code, so they will more often hit the case where a class has an unknown base class. If we were to change the spec to treat all methods and attributes of such a class as Any, it would significantly harm their user experience — enough so that I don’t think I could justify making such a change in pyright regardless of what the typing spec says.

  • I don’t think this issue is important enough to justify adding another Unknown type form and all the complexity and confusion that would entail. I’d prefer if we took that off the table.

This thinking leads me to favor the first option in Mike’s list. However, we may be able to specify the behavior in a more surgical manner so as to limit the impact on the rest of the type system. We could simply state that when a type checker computes the MRO of a class, it should generate the MRO as if Any were not present. If Any is present, it should append an Any to the end of the resulting MRO list (after object). In cases where Any isn’t present, the MRO always ends with object. By specifying it this way, I think the intended behavior is clear and is consistent with the type[Any] precedent. This approach allows us to sidestep a lengthier debate about the nuanced meaning of Any, and it hopefully provides the clarity needed to unblock work on the Intersection proposal.

5 Likes