Hi everyone,


Very happy to see the thread being alive again. Thank you all for sharing your thoughts.


I agree with Eric that deprecating existing functionality should not be done without a compelling reason. However, from my point of view, the same principle should apply to *all* changes and not just deprecations. So, I think it is important to specify the static behavior of typing.Any subclasses and accept the extra complexity it brings, if deprecation isn't an option.


For example, we could agree that


> typing.Any subclasses are allowed at runtime, but they are not part of the static Python type system and thus static type checkers are free to reject such code.


or that


> typing.Any subclasses should be treated in the same way as classes with a base which fails to resolve


as suggested by multiple people on this thread. Note that this option requires us to also describe 



At the moment, the static behavior is completely unspecified, and if I were to implement a new type checker, it is unclear how it should treat typing.Any subclasses. I could of course copy whatever mypy or pyright is doing, but I would much rather have this documented either in the typing module docs or in the form of a PEP (if it is possible to write one retroactively).


On why this is not a purely runtime change: in my experience people often use runtime behavior to build intuition about the static type system. Thus, by allowing typing.Any to be subclassed at runtime we hint to users that this could also be accepted by static type checkers. Moreover, if this works at runtime, people *will* write the code subclassing typing.Any, and type checkers *will* need to make sense of it.


Sergei



On Sun, Aug 13, 2023 at 8:17 PM Shantanu Jain <hauntsaninja@gmail.com> wrote:
There isn't consensus. I just haven't chimed in because Eric has said everything I would want to say.

On use cases:
We've used subclassing of Any as a way to type Mock's in typeshed for basically forever. This is mentioned in Python 3.11's What's New, the original typing-sig thread and discussed at PyCon US 2022. I've used this for a very dynamic parametrisation situation, where I had specific objects that I wanted to be able to duck type as anything (while ideally preserving its extra methods). I've also used it as a way to more easily duck type through an incorrectly strictly typed interface that I couldn't change. I remember recommending this to people on gitter.im / stackoverflow, but I don't remember the specifics of their use cases, possibly something something metaclass.

Paul, I'll also say that when proposing a revert, coming at the question with the assumption that "neither of these can be reasonably answered" is maybe not the best approach. It feels very Cunningham's Law.


On the process to make the original change:
Given that all major type checkers already supported it, typeshed already used it since antiquity, no concerns were raised on the typing-sig thread, it made syntax in pyi usable in py (without hacks), this wasn't a controversial change.


On interpretation:
Eric's already mentioned how it should be interpreted, but since you asked again: type checkers should treat subclassing of Any the same way they would subclassing of an unknown symbol. This typically looks something like the example below, but semantics here haven't been standardised, so a type checker could choose to approach this differently:
```
from unknown import anybaseclass

class X(anybaseclass):
    def foo(self) -> int: ...

reveal_type(X().foo())  # reveals int
reveal_type(X().bar())  # reveals Any

class A: ...

def takes_A(a: A): ...
takes_A(X())  # okay
```


On how to revert:
The onus is on you to prove why this should be reverted (the default position is that features do not get reverted). Arguments that I think could lead to a revert are showing why this breaks the type system or is an unreasonable lift for type checkers. I think it will be hard to make those arguments, since this was already part of our gradual type system, and all type checkers already had to have behaviour to handle it.



On Sun, 13 Aug 2023 at 11:09, Alex Waygood <alex.waygood@gmail.com> wrote:
I agree with everything Eric Traut has said so far in this thread.

In particular, "it doesn't feel right" isn't a strong enough justification for a deprecation period that could force people to change existing, working code just to avoid deprecation warnings at runtime. Since this is now established behaviour in Python 3.11 (and it's much too late to change it in Python 3.12), you don't just need to explain why you disagree with the previous change — you need to justify this change in behaviour independently, as a change in its own right. See PEP 387 for the full details on CPython's backwards-compatibility policy at runtime.

I also still don't understand why "I have quibbles with the fact that static type checkers allow `Any` to be subclassed" has really any bearing on whether we allow it to be subclassed at runtime. We made the runtime change because all major static type checkers allow `Any` to be subclassed at "typing time", and it's best to avoid inconsistencies between runtime and typing time where possible. But that doesn't force any type checker to permit `Any` to be subclassed at typing time. Most major type checkers still provide an option which, when enabled, means they will emit an error when `Any` is subclassed. It's best for the runtime behaviour to be no more restrictive than static type checkers, but the reverse doesn't hold — there are many situations in which things are allowed at runtime but banned by type checkers, and that's perfectly fine.

Lastly — yes, strictly speaking, behaviour specified in a PEP should usually not be changed unless a new PEP has been written and accepted, and the new PEP specifies that the behaviour should be changed. If you like, I _suppose_ you could bring this to the attention of the Python Steering Council, who have the power to demand that the change be reverted if they feel like the proper process wasn't followed. However, this honestly seems like a pretty minor detail of PEP 484, and the proposed change was advertised on this mailing list before being made, in order to check whether anybody disagreed with the change. (Nobody did.) I think it's unlikely that the Steering Council would be sympathetic to your case, especially since it's been a while since the change was made.

Best,
Alex

-------- Original message --------
From: Paul Bryan <pbryan@anode.ca>
Date: 13/08/2023 18:57 (GMT+00:00)
Subject: [Typing-sig] Re: typing.Any subclasses

I had posed the questions:

1. What does subclassing `Any` even mean, given everything type matches `Any`?
2. Is there a valid use case for a subclass of `Any`?

If neither of these can be reasonably answered, I certainly wouldn't feel bad about breaking backward compatibility with a nonsensical construct.

On Sun, Aug 13, 2023, at 10:36, Eric Traut wrote:
I don't think there is consensus. I don't think there has been a compelling argument made here. Just because something "doesn't feel right" to a few people doesn't mean that it should be changed. Deprecating functionality and breaking backward compatibility requires a solid justification, and I don't think anyone has presented one yet.

As I pointed out above, type checkers already need to deal with the case where a base class is unknown (and therefore has an `Any` type). Disallowing `Any` as an explicit base class won't change that.

If you feel that there's a need to standardize the behavior of type checkers when the type of a base class is unknown, then let's talk about what form that specification might take (e.g. PEP or official Python documentation). I personally don't think there's a strong need here because I don't recall any issue arising from this in the pyright or mypy issue trackers. If you are aware of any real problems caused by inconsistencies between type checkers in this regard, please let us know.
_______________________________________________
Typing-sig mailing list -- typing-sig@python.org
To unsubscribe send an email to typing-sig-leave@python.org
Member address: pbryan@anode.ca


_______________________________________________
Typing-sig mailing list -- typing-sig@python.org
To unsubscribe send an email to typing-sig-leave@python.org
https://mail.python.org/mailman3/lists/typing-sig.python.org/
Member address: hauntsaninja@gmail.com
_______________________________________________
Typing-sig mailing list -- typing-sig@python.org
To unsubscribe send an email to typing-sig-leave@python.org
https://mail.python.org/mailman3/lists/typing-sig.python.org/
Member address: sergei.a.lebedev@gmail.com