Skip to content

Decide Type[C] vs. Callable #1670

Closed
Closed

Description

@gvanrossum

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?

Activity

  1. rwbarton commented on Jun 9, 2016

    @rwbarton
    Contributor

    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'
  2. rwbarton commented on Jun 9, 2016

    @rwbarton
    Contributor

    (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?)

  3. gvanrossum commented on Jun 9, 2016

    @gvanrossum
    MemberAuthor
  4. JukkaL commented on Jun 9, 2016

    @JukkaL
    Collaborator

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

  5. pkch commented on Apr 7, 2017

    @pkch
    Contributor

    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.

  6. ilevkivskyi commented on May 18, 2018

    @ilevkivskyi
    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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions