Skip to content

Decide Type[C] vs. Callable #1670

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

Closed
gvanrossum opened this issue Jun 7, 2016 · 6 comments
Closed

Decide Type[C] vs. Callable #1670

gvanrossum opened this issue Jun 7, 2016 · 6 comments

Comments

@gvanrossum
Copy link
Member

From #1569 (comment):

  • Should Type[C] be a subtype of Callable[...] in case the signature is compatible?
  • If yes, we should also infer constraints for a Callable template and TypeType actual.
  • If yes, join and meet will likely also be affected.

The current code seems to answer the first bullet with Yes:

class C: pass
T = Type[C]
def f(a: Callable[[], C]) -> None: pass
f(C)
f(T)

has no errors. But what does that imply for the remaining two questions?

@gvanrossum gvanrossum mentioned this issue Jun 7, 2016
@rwbarton
Copy link
Contributor

rwbarton commented Jun 9, 2016

Actually the test would be

class C: pass
C1 = C  # type: Type[C]
def f(a: Callable[[], C]) -> None: pass
f(C1)  # E: Argument 1 to "f" has incompatible type Type[C]; expected Callable[[], C]

and that currently errors. I do think it should work, though, since

reveal_type(C1)  # E: Revealed type is 'Type[T1670.C]'
reveal_type(C1())  # E: Revealed type is 'T1670.C'

@rwbarton
Copy link
Contributor

rwbarton commented Jun 9, 2016

(Interestingly

T = Type[C]
reveal_type(T)  # Revealed type is 'Any'

but it's not specific to Type[C]; the same happens for List and so on. This comes from TypeAlias in typing.pyi. Is there any valid runtime use for Type[C] or List[int]? Maybe the stub should give these a more specific type?)

@gvanrossum
Copy link
Member Author

gvanrossum commented Jun 9, 2016 via email

@JukkaL
Copy link
Collaborator

JukkaL commented Jun 9, 2016

There is a related issue about type aliases having Any types: #329

@pkch
Copy link
Contributor

pkch commented Apr 7, 2017

The code that was shown by @rwbarton earlier in this issue now passes type check. A simplified variation:

class C: pass
C1: Type[C] = C
a: Callable[[], C]
reveal_type(C) # def () -> test.C
a = C # ok
reveal_type(C1) # Type[test.C]
a = C1 # ok

Presumably some recent PR fixed this issue and made Type[C] a subtype of Callable[[], C]). However, this is not sound: a constructor of subclass of C may have an incompatible signature.

It's ok to violate soundness, but in this case I think it can be avoided. My comment in #241 suggests an approach that I think would restore soundness. If that happens, Type[C] would store the signature of the C.__init__, and then we can stop using Callable[[], C] to represent class object C, and switch to Type[C]. This would make things cleaner, IMO.

Note that Callable[[], C] cannot be made a subtype of Type[C] because Type[C] has __mro__ and other things that a Callable isn't guaranteed to have. So if we want to avoid the Type vs Callable split, we have to standardize on Type, not on Callable.

@ilevkivskyi
Copy link
Member

I think this issue is now fixed:

class C: pass
C1 = C  # type: Type[C]
def f(a: Callable[[], C]) -> None: pass
f(C1)  # Correctly passes
def g(a: Callable[[], int]) -> None: pass
g(C1)  # Correctly fails

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants