Skip to content
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

IDEA: inheriting from 'Any' should create a new 'Any' type #567

Closed
gvanrossum opened this issue Jun 22, 2018 · 8 comments
Closed

IDEA: inheriting from 'Any' should create a new 'Any' type #567

gvanrossum opened this issue Jun 22, 2018 · 8 comments
Labels
topic: feature Discussions about new features for Python's type annotations

Comments

@gvanrossum
Copy link
Member

Currently, in mypy, class M(Any): is an error, because Any is not an actual class. (Note that this is different from class M(A): where A has type Any. I'm talking about explicitly inheriting from Any only here.)

In mypy, if we ignore the error, we obtain a class that is a subclass of all other classes. (At runtime it still fails, but in a stub file this will work.) Example:

class M(Any):  # type: ignore
    pass
class A: pass
def foo(a: A): pass
foo(M())  # No error

However, M does not have the other important property of Any, that it is a superclass of all other classes. If we add the following to the above example, we get an error:

def bar(a: M): pass
bar(A())  # error: Argument 1 to "bar" has incompatible type "A"; expected "M"

As a feature, it might be nice to allow explicitly subclassing Any and make the resulting class behave more like Any, so that the bar(A()) call above actually works.

Why would this be useful? Why not just make M an object with type Any? Because we can still endow M with specific methods, which will be type-checked as usual:

class M(Any):  # type: ignore
    def m(self) -> None: pass
M().m()  # OK
M().m(12)  # error: Too many arguments for "m" of "M"

If M were just an object with type Any, we wouldn't get that error.

Real-world use case: Mock; see the thread started at python/typeshed#2173 (comment).

@srittau
Copy link
Collaborator

srittau commented Jun 22, 2018

In our internal test code we are often using "fakes" (a bit like mocks, only with canned values and behaviour and no dynamic behaviour) in place of real domain objects that are difficult to set up:

# Implementation:
class Foo:  # domain class with side effects
    ...

def do_interesting_stuff(foo: Foo): ...

# Test
class FakeFoo:  # simple class
    ...

foo = FakeFoo()
do_interesting_stuff(cast(Foo, foo))

If I understand the proposal correctly, this would allow us to instead derive FakeFoo from Any and do away with the cast? This would be helpful.

@gvanrossum
Copy link
Member Author

gvanrossum commented Jun 22, 2018 via email

@ilevkivskyi
Copy link
Member

I like this idea. We can preserve the current behaviour for unimported bases etc., while have the both-ways-Any for an explicit Any base. Also I actually think it is OK this fails at runtime, we can just reserve this feature for stubs (it is only useful for highly magic classes, that are anyway hard to annotate inline).

@JukkaL what do you think?

@JukkaL
Copy link
Contributor

JukkaL commented Jun 25, 2018

I'm not sure if this is important enough for the extra complexity to work on at least in the near term. This would add a new kind of type, and from experience we know that these sorts of changes tend to result in all kinds of unanticipated corner cases that may take a lot of work to get right, even for supposedly simple features. They also complicate the internals of all type checking tools, all/most of which are still lagging behind PEP 484 in supported features (including mypy).

For non-stub code, the fact that the Any base class would fail at runtime makes this pretty hard to use, and the current way of using casts may not be significantly worse.

@ilevkivskyi
Copy link
Member

This would add a new kind of type

On one hand this may be just Any, but with an attribute like fallback_instance, which will be used only by checkmember. But on other hand I agree we have more important things to do.

@Kentzo
Copy link

Kentzo commented Jun 27, 2018

What is a particular use case when this is useful?

@srittau
Copy link
Collaborator

srittau commented Nov 4, 2021

Is this still relevant? In typeshed we derive NonCallableMock from Any and it seems to work fine.

@srittau srittau added topic: feature Discussions about new features for Python's type annotations and removed enhancement labels Nov 4, 2021
@emmatyping
Copy link
Contributor

As nice as this would be, I fear it may break too much code. I think this is definitely something that should be experimented on in a type checker first.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
topic: feature Discussions about new features for Python's type annotations
Projects
None yet
Development

No branches or pull requests

6 participants