Allow subclassing from Any at runtime

Currently subclassing from Any gives you `TypeError: Cannot subclass typing.Any` at runtime.
You can currently work around this in .py with something like: ``` if TYPE_CHECKING: AnyBaseClass = Any else: AnyBaseClass = object
class X(AnyBaseClass): ... ```
As far as I'm aware, type checkers already support this. We inherit from Any in typeshed in a couple places. The need to support Any inheritance also arises naturally as a result of gradual typing, e.g. if your base class comes from an untyped library or the import cannot be resolved.
I've used this trick myself a couple times and have seen use cases for this a couple times on gitter.im. It's a neat way to silence many errors from classes that do super dynamic things or to allow for classes that can duck type anywhere (e.g. mocks).

On Tue, Feb 15, 2022 at 11:39:13AM +0100, Sebastian Rittau wrote:
Am 15.02.22 um 10:53 schrieb Shantanu Jain:
Currently subclassing from Any gives you `TypeError: Cannot subclass typing.Any` at runtime.
Sounds sensible to me.
Sorry Sebastian, I cannot tell if you mean that the current behaviour (TypeError) is sensible, or the proposed behaviour (no TypeError) is sensible.

Am 15.02.22 um 14:56 schrieb Steven D'Aprano:
On Tue, Feb 15, 2022 at 11:39:13AM +0100, Sebastian Rittau wrote:
Am 15.02.22 um 10:53 schrieb Shantanu Jain:
Currently subclassing from Any gives you `TypeError: Cannot subclass typing.Any` at runtime.
Sounds sensible to me.
Sorry Sebastian, I cannot tell if you mean that the current behaviour (TypeError) is sensible, or the proposed behaviour (no TypeError) is sensible.
Oops, sorry for the terrible quoting. I meant that the behavior proposed by Shantanu sounds sensible to me.
- Sebastian

On Tue, Feb 15, 2022 at 01:53:52AM -0800, Shantanu Jain wrote:
As far as I'm aware, type checkers already support this. We inherit from Any in typeshed in a couple places. The need to support Any inheritance also arises naturally as a result of gradual typing, e.g. if your base class comes from an untyped library or the import cannot be resolved.
I've used this trick myself a couple times and have seen use cases for this a couple times on gitter.im. It's a neat way to silence many errors from classes that do super dynamic things or to allow for classes that can duck type anywhere (e.g. mocks).
I don't understand why you need to subclass Any, instead of just using Any. What's the subclass do that Any doesn't?

El mar, 15 feb 2022 a las 6:31, Steven D'Aprano (steve@pearwood.info) escribió:
On Tue, Feb 15, 2022 at 01:53:52AM -0800, Shantanu Jain wrote:
As far as I'm aware, type checkers already support this. We inherit from Any in typeshed in a couple places. The need to support Any inheritance also arises naturally as a result of gradual typing, e.g. if your base class comes from an untyped library or the import cannot be resolved.
I've used this trick myself a couple times and have seen use cases for
this
a couple times on gitter.im. It's a neat way to silence many errors from classes that do super dynamic things or to allow for classes that can
duck
type anywhere (e.g. mocks).
I don't understand why you need to subclass Any, instead of just using Any. What's the subclass do that Any doesn't?
The canonical example is mock objects. In typeshed, unittest.mock.NonCallableMock is defined as (greatly simplified):
class NonCallableMock(Any): def assert_any_call(self, *args: Any, **kwargs: Any) -> None: ... def assert_has_calls(self, calls: Sequence[_Call], any_order: bool = ...) -> None: ... def mock_add_spec(self, spec: Any, spec_set: bool = ...) -> None: ... # ... many other methods
This makes it so you can pass a mock object to any function, regardless of its annotation, which is good because that's usually how you use mock objects. But it also means that the type checker knows what the signature of the `assert_has_calls` method is, and can tell you if you get it wrong.
Since this behavior is useful in stubs and we've been using it for a while, I think it makes sense to also allow it at runtime. This is a new feature, so it should go only into Python 3.11. We can then also provide a `typing_extensions.Any` that allows subclassing.
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: jelle.zijlstra@gmail.com
participants (4)
-
Jelle Zijlstra
-
Sebastian Rittau
-
Shantanu Jain
-
Steven D'Aprano