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 treattype[Any]
as though all known methods and attributes oftype
andobject
are present and all other (not-present) methods and attributes are treated as typeAny
. Theoretically, a metaclass could override one of the methods intype
, 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 thetype[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.