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

Ensures overloads are ordered from narrow to broad #2138

Merged
merged 4 commits into from
May 17, 2018

Conversation

Michael0x2a
Copy link
Contributor

This commit reorders any overloads where the first overload was "shadowing" the second, preventing it from ever being matched by type checkers that work by selecting the first matching overload alternative.

For example, the first overload alternative below is strictly broader then the second, preventing it from ever being selected:

class Parent: pass
class Child(Parent): pass

@overload
def foo(x: *int) -> Parent: ...
@overload
def foo(x: int, y: int) -> Child: ...

The correct thing to do is to either delete the second overload or rearrange them to look like this:

@overload
def foo(x: int, y: int) -> Child: ...
@overload
def foo(x: *int) -> Parent: ...

Rationale: I'm currently working on a proposal that would amend PEP 484 to (a) mandate type checkers check overloads in order and (b) prohibit overloads where an earlier alternative completely shadows a later one.

This would prohibit overloads that look like the example below, where the first alternative completely shadows the second.

I figured it would be a good idea to make these changes ahead of time: if my proposal is accepted, it'd make the transition smoother. If not, this is hopefully a relatively harmless change.

Note: I think some of these overloads could be simplified (e.g. reversed(...)), but I mostly stuck with rearranging them in case I was wrong. The only overload I actually changed was hmac.compare_digest -- I believe the Python 2 version actually accepts unicode.

This commit reorders any overloads where the first overload was
"shadowing" the second, preventing it from ever being matched by type
checkers that work by selecting the first matching overload alternative.

For example, the first overload alternative below is strictly broader
then the second, preventing it from ever being selected:

    class Parent: pass
    class Child(Parent): pass

    @overload
    def foo(x: *int) -> Parent: ...
    @overload
    def foo(x: int, y: int) -> Child: ...

The correct thing to do is to either delete the second overload or
rearrange them to look like this:

    @overload
    def foo(x: int, y: int) -> Child: ...
    @overload
    def foo(x: *int) -> Parent: ...

Rationale: I'm currently [working on a proposal][0] that would amend
PEP 484 to (a) mandate type checkers check overloads in order and
(b) prohibit overloads where an earlier alternative completely shadows
a later one.

  [0]: python/typing#253 (comment)

This would prohibit overloads that look like the example below, where
the first alternative completely shadows the second.

I figured it would be a good idea to make these changes ahead of time:
if my proposal is accepted, it'd make the transition smoother. If not,
this is hopefully a relatively harmless change.

Note: I think some of these overloads could be simplified (e.g.
`reversed(...)`), but I mostly stuck with rearranging them in case I was
wrong. The only overload I actually changed was `hmac.compare_digest` --
I believe the Python 2 version actually accepts unicode.
@JelleZijlstra
Copy link
Member

I assume you mean "overloads", not "commits" :)

def stat_float_times() -> bool: ...
@overload
def stat_float_times(newvalue: bool = ...) -> None: ...
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't we just remove the = ... here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@Michael0x2a Michael0x2a changed the title Ensures commits are ordered from narrow to broad Ensures overloads are ordered from narrow to broad May 16, 2018
@Michael0x2a
Copy link
Contributor Author

lol, good point -- fixed the title

Michael0x2a added a commit to Michael0x2a/mypy that referenced this pull request May 16, 2018
This pull request relaxes the overlapping overload checks so that if the
parameter types of an alternative are a proper subtype of another, the
return type needs to only be a regular subtype, not a proper subtype.

The net effect is that the return type of the first alternative is
allowed to be 'Any'.

This *does* make overloads slightly less safe, but 'Any' was always
meant to be an escape hatch, so I'm figuring this is fine.

**Rationale**: My [proposed changes on overhauling overloads][0] require
a few changes to typeshed -- [relevant pull request here][1]. Rather then
trying to change both at the same time, I want to get typeshed working
first.

This was pretty easy to do, apart from the attrs stubs. The issue
basically boils down to this:

    # Alternative 1
    @overload
    def attrib(x: Optional[T]) -> T: ...

    # Alternative 2
    @overload
    def attrib(x: None = None) -> Any: ...

This code typechecks under the current system because alternative 1
completely shadows alternative 2. It fails to typecheck under the new
system for the exact same reason.

If we swap the two alternatives, it fails under the current system
because 'Any' is not a proper subtype of 'T'.

It *is*, however, regular subtype of 'T' -- hence this change.

  [0]: python/typing#253 (comment)

  [1]: python/typeshed#2138
@Michael0x2a
Copy link
Contributor Author

Michael0x2a commented May 16, 2018

I reverted the changes to the attrs stubs for now -- it looks like the attrs stubs are currently impossible to type in a way that's compatible with both the current and proposed overload semantics.

I submitted a pull request to slightly relax mypy's current overload semantics so that this change can go through. Once my other pull request is accepted, I'll unrevert my revert (unless you decide to accept this pull request as-is -- if so, I can just make another pull request for attrs).

ilevkivskyi pushed a commit to python/mypy that referenced this pull request May 16, 2018
…5064)

This pull request relaxes the overlapping overload checks so that if the
parameter types of an alternative are a proper subtype of another, the
return type needs to only be a regular subtype, not a proper subtype.

The net effect is that the return type of the first alternative is
allowed to be 'Any'.

This *does* make overloads slightly less safe, but 'Any' was always
meant to be an escape hatch, so I'm figuring this is fine.

**Rationale**: My [proposed changes on overhauling overloads][0] require
a few changes to typeshed -- [relevant pull request here][1]. Rather then
trying to change both at the same time, I want to get typeshed working
first.

This was pretty easy to do, apart from the attrs stubs. The issue
basically boils down to this:

    # Alternative 1
    @overload
    def attrib(x: Optional[T]) -> T: ...

    # Alternative 2
    @overload
    def attrib(x: None = None) -> Any: ...

This code typechecks under the current system because alternative 1
completely shadows alternative 2. It fails to typecheck under the new
system for the exact same reason.

If we swap the two alternatives, it fails under the current system
because 'Any' is not a proper subtype of 'T'.

It *is*, however, regular subtype of 'T' -- hence this change.

  [0]: python/typing#253 (comment)

  [1]: python/typeshed#2138
@Michael0x2a
Copy link
Contributor Author

@JelleZijlstra -- Ok, looks like third time was the charm. I think this is ready for a second look whenever you have time.

@JelleZijlstra JelleZijlstra merged commit 97d9f2e into python:master May 17, 2018
@Michael0x2a Michael0x2a deleted the rearrange-overloads branch May 17, 2018 13:47
Michael0x2a added a commit to Michael0x2a/mypy that referenced this pull request May 17, 2018
Syncs typeshed, mostly to pull in some [tweaks I made][0] that makes
typeshed compatible with both the current and [proposed][1] overload
semantics.

  [0]: python/typeshed#2138
  [1]: python/typing#253 (comment)
ilevkivskyi pushed a commit to python/mypy that referenced this pull request May 17, 2018
Syncs typeshed, mostly to pull in some [tweaks I made][0] that makes
typeshed compatible with both the current and [proposed][1] overload
semantics.

  [0]: python/typeshed#2138
  [1]: python/typing#253 (comment)
Michael0x2a added a commit to Michael0x2a/typeshed that referenced this pull request May 18, 2018
Basically, the same thing as [my previous pull request][0], except the
fixes are now focusing on functions with overlapping argument counts.

  [0]: python#2138
JelleZijlstra pushed a commit that referenced this pull request May 19, 2018
Basically, the same thing as [my previous pull request][0], except the
fixes are now focusing on functions with overlapping argument counts.

  [0]: #2138
gwk pushed a commit to gwk/typeshed that referenced this pull request May 29, 2018
This commit reorders any overloads where the first overload was
"shadowing" the second, preventing it from ever being matched by type
checkers that work by selecting the first matching overload alternative.

For example, the first overload alternative below is strictly broader
then the second, preventing it from ever being selected:

    class Parent: pass
    class Child(Parent): pass

    @overload
    def foo(x: *int) -> Parent: ...
    @overload
    def foo(x: int, y: int) -> Child: ...

The correct thing to do is to either delete the second overload or
rearrange them to look like this:

    @overload
    def foo(x: int, y: int) -> Child: ...
    @overload
    def foo(x: *int) -> Parent: ...

Rationale: I'm currently [working on a proposal][0] that would amend
PEP 484 to (a) mandate type checkers check overloads in order and
(b) prohibit overloads where an earlier alternative completely shadows
a later one.

  [0]: python/typing#253 (comment)

This would prohibit overloads that look like the example below, where
the first alternative completely shadows the second.

I figured it would be a good idea to make these changes ahead of time:
if my proposal is accepted, it'd make the transition smoother. If not,
this is hopefully a relatively harmless change.

Note: I think some of these overloads could be simplified (e.g.
`reversed(...)`), but I mostly stuck with rearranging them in case I was
wrong. The only overload I actually changed was `hmac.compare_digest` --
I believe the Python 2 version actually accepts unicode.
gwk pushed a commit to gwk/typeshed that referenced this pull request May 29, 2018
Basically, the same thing as [my previous pull request][0], except the
fixes are now focusing on functions with overlapping argument counts.

  [0]: python#2138
yedpodtrzitko pushed a commit to yedpodtrzitko/typeshed that referenced this pull request Jan 23, 2019
This commit reorders any overloads where the first overload was
"shadowing" the second, preventing it from ever being matched by type
checkers that work by selecting the first matching overload alternative.

For example, the first overload alternative below is strictly broader
then the second, preventing it from ever being selected:

    class Parent: pass
    class Child(Parent): pass

    @overload
    def foo(x: *int) -> Parent: ...
    @overload
    def foo(x: int, y: int) -> Child: ...

The correct thing to do is to either delete the second overload or
rearrange them to look like this:

    @overload
    def foo(x: int, y: int) -> Child: ...
    @overload
    def foo(x: *int) -> Parent: ...

Rationale: I'm currently [working on a proposal][0] that would amend
PEP 484 to (a) mandate type checkers check overloads in order and
(b) prohibit overloads where an earlier alternative completely shadows
a later one.

  [0]: python/typing#253 (comment)

This would prohibit overloads that look like the example below, where
the first alternative completely shadows the second.

I figured it would be a good idea to make these changes ahead of time:
if my proposal is accepted, it'd make the transition smoother. If not,
this is hopefully a relatively harmless change.

Note: I think some of these overloads could be simplified (e.g.
`reversed(...)`), but I mostly stuck with rearranging them in case I was
wrong. The only overload I actually changed was `hmac.compare_digest` --
I believe the Python 2 version actually accepts unicode.
yedpodtrzitko pushed a commit to yedpodtrzitko/typeshed that referenced this pull request Jan 23, 2019
Basically, the same thing as [my previous pull request][0], except the
fixes are now focusing on functions with overlapping argument counts.

  [0]: python#2138
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

Successfully merging this pull request may close these issues.

2 participants