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

Allow fancy self-types #7860

Merged
merged 25 commits into from
Nov 5, 2019
Merged

Conversation

ilevkivskyi
Copy link
Member

@ilevkivskyi ilevkivskyi commented Nov 2, 2019

Fixes #3625
Fixes #5305
Fixes #5320
Fixes #5868
Fixes #7191
Fixes #7778
Fixes python/typing#680

So, lately I was noticing many issues that would be fixed by (partially) moving the check for self-type from definition site to call site.

This morning I found that we actually have such function check_self_arg() that is applied at call site, but it is almost not used. After more reading of the code I found that all the patterns for self-types that I wanted to support should either already work, or work with minimal modifications. Finally, I discovered that the root cause of many of the problems is the fact that bind_self() uses wrong direction for type inference! All these years it expected actual argument type to be supertype of the formal one.

After fixing this bug, it turned out it was easy to support following patterns for explicit self-types:

  • Structured match on generic self-types
  • Restricted methods in generic classes (methods that one is allowed to call only for some values or type arguments)
  • Methods overloaded on self-type
  • (Important case of the above) overloaded __init__ for generic classes
  • Mixin classes (using protocols)
  • Private class-level decorators (a bit hacky)
  • Precise types for alternative constructors (mostly already worked)

This PR cuts few corners, but it is ready for review (I left some TODOs). Note I also add some docs, I am not sure this is really needed, but probably good to have.

@ilevkivskyi
Copy link
Member Author

ilevkivskyi commented Nov 2, 2019

Few extra comments:

  • I just updated the PR description with a list of issues this will close, I didn't do a thorough search however.
  • Potentially this PR can be split in few parts, if needed (I would prefer to keep it single however).
  • I am going to push couple more tests that mimic tricky examples in one issue (just to be sure they are fixed). If you think it is too much I can remove them.

@ilevkivskyi
Copy link
Member Author

(Also note that we can count #3625 as fixed only if remove all checks at the definition site).

Copy link
Collaborator

@Michael0x2a Michael0x2a left a comment

Choose a reason for hiding this comment

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

Maybe you'll want to get a second opinion from Jukka/Sully, but FWIW these changes all look fine to me: the overall approach seems sound and nicely minimal, and my comments were mostly nits and smaller suggestions.

docs/source/more_types.rst Outdated Show resolved Hide resolved
docs/source/more_types.rst Outdated Show resolved Hide resolved
docs/source/more_types.rst Outdated Show resolved Hide resolved
docs/source/more_types.rst Outdated Show resolved Hide resolved
mypy/checker.py Outdated Show resolved Hide resolved
mypy/typeops.py Outdated Show resolved Hide resolved
mypy/typeops.py Show resolved Hide resolved
mypy/typeops.py Outdated Show resolved Hide resolved
test-data/unit/check-selftype.test Show resolved Hide resolved
test-data/unit/check-selftype.test Show resolved Hide resolved
@ilevkivskyi
Copy link
Member Author

(Also note that we can count #3625 as fixed only if remove all checks at the definition site).

I changed my mind about this. With a tiny extra fix this issue will be now also closed by this PR.

Copy link
Collaborator

@msullivan msullivan left a comment

Choose a reason for hiding this comment

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

This is a really exciting improvement. I love when so much falls out of one fix.

@@ -561,6 +561,140 @@ with ``Union[int, slice]`` and ``Union[T, Sequence]``.
to returning ``Any`` only if the input arguments also contain ``Any``.


.. _advanced_self:

Advanced uses of self-types
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is it worth documenting the use in __init__?

Copy link
Member Author

Choose a reason for hiding this comment

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

I noticed it is mostly useful for typeshed stubs, but didn't find this patter in user code, I will add a short sentence at the end, where I discuss overloads.

mypy/typeops.py Outdated
# (info). For example
# We first need to record all non-trivial (explicit) self types in __init__,
# since they will not be available after we bind them. Note, we use explicit
# self-types only in the defining class, similar to __new__ (see below).
Copy link
Collaborator

Choose a reason for hiding this comment

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

__new__ can use explicit returns in subclasses, but kind of as a technicality, since it only works using bound type vars, I think.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, I will make the comment more clear.

"""
if isinstance(typ, TypeType):
return supported_self_type(typ.item)
return (isinstance(typ, TypeVarType) or
Copy link
Collaborator

Choose a reason for hiding this comment

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

This doesn't seem to check that there is an instance upper bound? Should there be?

Also my inclination would be to throw parens around the and clause.

Copy link
Member Author

Choose a reason for hiding this comment

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

This doesn't seem to check that there is an instance upper bound? Should there be?

I would say it is safe to keep it as is. We can restrict this later if we will find problems.

Also my inclination would be to throw parens around the and clause.

OK.

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