Skip to content

Allow @overload outside stubs #1136

Description

@bdarnell

Mypy does not consider the stub file for the module it is currently type-checking (and doing so was apparently considered and rejected recently). However, @overload as defined in the PEP is only allowed to appear in stubs, which implies a requirement for type checkers to consider a module's own stubs when type checking it. Otherwise, the ability to type check a module which defines an overloaded function is limited.

My motivating example:

@overload
def utf8(s: None) -> None: ...
@overload
def utf8(s: AnyStr) -> bytes: ...

This could be expressed as def utf8(s: Optional[AnyStr]) -> Optional[bytes], but I'm trying to capture the fact that if the argument is not None then the result is also guaranteed not to be None, so it doesn't introduce unnecessary Optional-ness. (Is there a way to express this with a TypeVar? I don't see one). I can put the overloads in a stub for the benefit of external callers of this module, but then I need to put all the type information for this module in the stub and can't type check it internally (I could duplicate all the non-overload type info in the module itself and get partial coverage). I could also put overloaded functions in their own tiny modules to minimize the impact, but that just feels silly.

Activity

  1. gvanrossum commented on Jan 18, 2016

    @gvanrossum
    Member
  2. JukkaL commented on Jan 18, 2016

    @JukkaL
    Collaborator

    I'm sure there's a better way to support @overload than having a separate .pyi file (such as the approach discussed in python/typing#72). Separate .pyi files will be painful to maintain, and they don't provide most of the readability gains of inline annotations.

  3. bdarnell commented on Jan 19, 2016

    @bdarnell
    ContributorAuthor

    I don't see how OptAnyStr would help. Note that there are two different unions involved: (Union[bytes, unicode, None]) -> Union[bytes, None]. I want to constrain the return type as a function of the input type, but it can't just pick the same type on both sides because of the (unicode) -> bytes case.

    I'd be happy with a non-stub way to express overloads (and would prefer it to a requirement for separate stub files to use this feature). The # type: comment syntax could support this relatively easily and wouldn't carry the implications of runtime dispatch like a decorator would.

    Overloads that can't be satisfied with TypeVars may be rare, but when they come up they can be significant (that's why the feature is in the PEP). In this case, tornado.escape.utf8 is used all over Tornado, and I'd have to add dozens of explicit casts if I had to use the union declaration instead of the overloaded version.

    The interaction between stub files and source files is generally awkward. I just realized that the type annotations I've been adding to Tornado source files aren't actually used outside those files because there are stubs for Tornado in typeshed (mypy --verbose should report where it's loading type information from). Assuming I release a version of Tornado with inline type annotations I'm going to need to be able to make sure that those types take precedence over the versions in typeshed.

  4. gvanrossum commented on Jan 22, 2016

    @gvanrossum
    Member
  5. gvanrossum commented on Jan 26, 2016

    @gvanrossum
    Member
  6. gvanrossum commented on Mar 21, 2016

    @gvanrossum
    Member

    The PEP is now updated. This issue should stay open until it's been implemented in mypy.

  7. changed the title [-]Combine stubs and source (for overloads)[/-] [+]Allow @overload outside stubs[/+] on Apr 7, 2016
  8. added this to the milestone on Apr 7, 2016
  9. rwbarton commented on Jun 24, 2016

    @rwbarton
    Contributor

    What's the idea for how this would actually work in a user program? After all @overload doesn't (and can't I think, especially with Python 2-style type comments) do runtime type-based dispatch.

    I think there needs to be just one function body, that mypy will type check with each of the overloaded types. Straw-man syntax in a minimal example:

    @overload
    def f(x: str) -> str: ...    # literal ...
    @overload
    def f(x: int) -> int:
        return x

    At runtime @overload would be just the identity function and the second definition of f would overwrite the first one. (Maybe for Python 3 @overload could collect the type attributes and store them in a property of the returned function.)

  10. rwbarton commented on Jun 24, 2016

    @rwbarton
    Contributor

    Oh interesting, there's actually an example of the expected (but not yet implemented) syntax in the docstring for @overload, which is a bit different.

          @overload
          def utf8(value: None) -> None: ...
          @overload
          def utf8(value: bytes) -> bytes: ...
          @overload
          def utf8(value: str) -> bytes: ...
          def utf8(value):
              # implementation goes here
  11. gvanrossum commented on Jun 24, 2016

    @gvanrossum
    Member

    Yeah, that's how it should work. The overloads are for the type checker, the final non-overloaded function is for the interpreter. If the latter has annotations it should be checked as a regular function; separately mypy might want to check that it can handle all the overloads; when type-checking a call, only the overloads should be considered.

  12. cpennington commented on Oct 18, 2016

    @cpennington
    Contributor

    I was looking into implementing this. From what I can see, the example above (utf8) don't typecheck in mypy as of v0.4.5, with the following errors:

    $ mypy test_overloads.py
    test_overloads.py:4: error: Overloaded function signatures 1 and 2 overlap with incompatible return types
    test_overloads.py:4: error: Overloaded function signatures 1 and 3 overlap with incompatible return types
    test_overloads.py:9: error: Name 'utf8' already defined
    

    The error on line 9 can be fixed by adding an @overload to the final definition of utf8, but it's not clear to me if that's allowed by the spec or not.

    The errors about overlapping signatures are because None inhabits all types, unless --strict-optional is used (and with --strict-optional, the errors go away).

    Is there a reason for not putting the implementation in the final @overloaded definition? Also, without --strict-optional, should we ever expect an overload involving None to typecheck?

  13. gvanrossum commented on Oct 18, 2016

    @gvanrossum
    Member
  14. gvanrossum commented on Dec 7, 2016

    @gvanrossum
    Member

    @sixolet Adding you to this issue for coordination purposes.

  15. added a commit that references this issue on Mar 27, 2017
    e674e25
  16. douglas-treadwell commented on Jun 30, 2017

    @douglas-treadwell

    I don't understand how this solved #2305.

    I'm using MyPy 0.511 and still having the issue described in #2305.

  17. ilevkivskyi commented on Jun 30, 2017

    @ilevkivskyi
    Member

    It doesn't. Guido was referring to python/typeshed#1136 (on typeshed tracker)

  18. douglas-treadwell commented on Jun 30, 2017

    @douglas-treadwell

    Although 0.520-dev (GitHub master) does seem to handle #2305...

    Maybe that's because installing MyPy from GitHub master requires preparing the latest typeshed?

  19. douglas-treadwell commented on Jun 30, 2017

    @douglas-treadwell

    @ilevkivskyi can you clarify?

  20. ilevkivskyi commented on Jun 30, 2017

    @ilevkivskyi
    Member

    mypy master is synced with typeshed every two-three days. PR python/typeshed#1136 was merged more than two months ago.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions