-
-
Notifications
You must be signed in to change notification settings - Fork 2.9k
Implement basic subtyping & inferrence for variadic classes. #13105
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
Conversation
@@ -702,7 +753,7 @@ def visit_tuple_type(self, template: TupleType) -> List[Constraint]: | |||
isinstance(actual, Instance) | |||
and actual.type.fullname == "builtins.tuple" | |||
) | |||
unpack_index = find_unpack_in_tuple(template) | |||
unpack_index = find_unpack_in_list(template.items) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wanted to migrate this to use the other helpers, but couldn't figure out how to cleanly do it.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
Ts = TypeVarTuple("Ts") | ||
T = TypeVar("T") | ||
|
||
class Variadic(Generic[T, Ts]): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test fails if I add a suffix type variable, with a weird error Argument 1 to "foo" has incompatible type "Variadic[int, str, bool, float]"; expected "Variadic[int, str, bool, float]" (diff)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I left a comment below about what seems to be going on.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not a full review, but I think that I've figured out why suffix doesn't work.
mypy/subtypes.py
Outdated
assert unpack_index is not None | ||
type_params = zip( | ||
left_prefix + right_prefix, | ||
left_suffix + right_suffix, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These are not correct. It should be left_prefix + left_suffix
and right_prefix + right_suffix
, I think.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ya that does seem like a bad problem.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good, I only have a few nits.
mypy/constraints.py
Outdated
template_unpack.type.fullname == "builtins.tuple" | ||
): | ||
# TODO: check homogenous tuple case | ||
assert NotImplementedError |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe add a TODO about fixed-length tuple here?
) | ||
modified_actual = actual.copy_modified( | ||
items=list(actual_items) | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we also infer constraints for the prefix and suffix? I guess they may contain regular type variable types?
mypy/expandtype.py
Outdated
@@ -26,8 +27,27 @@ def expand_type_by_instance(typ: Type, instance: Instance) -> Type: | |||
return typ | |||
else: | |||
variables: Dict[TypeVarId, Type] = {} | |||
for binder, arg in zip(instance.type.defn.type_vars, instance.args): | |||
if instance.type.has_type_var_tuple_type: | |||
import mypy.constraints |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this import unused?
mypy/typevartuples.py
Outdated
|
||
from mypy.types import Instance, UnpackType, ProperType, get_proper_type, Type | ||
|
||
"""Helpers for interacting with type var tuples.""" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Style nit: move docstring to top of file.
This comment has been minimized.
This comment has been minimized.
Diff from mypy_primer, showing the effect of this PR on open source code: dragonchain (https://github.com/dragonchain/dragonchain)
- dragonchain/lib/database/redis.py:399:36: error: Argument 2 to "zadd" of "SortedSetCommands" has incompatible type "Dict[str, int]"; expected "Mapping[Union[str, bytes], Union[bytes, float, int, str]]"
+ dragonchain/lib/database/redis.py:399:36: error: Argument 2 to "zadd" of "SortedSetCommands" has incompatible type "Dict[str, int]"; expected "Mapping[Union[str, bytes], Union[bytes, float, str]]"
- dragonchain/lib/database/redis_utest.py:218:9: error: "Callable[[Union[str, bytes], Mapping[Union[str, bytes], Union[bytes, float, int, str]], bool, bool, bool, bool, Optional[Any], Optional[Any]], int]" has no attribute "assert_called_once_with"
+ dragonchain/lib/database/redis_utest.py:218:9: error: "Callable[[Union[str, bytes], Mapping[Union[str, bytes], Union[bytes, float, str]], bool, bool, bool, bool, Optional[Any], Optional[Any]], int]" has no attribute "assert_called_once_with"
|
left_items = t.args[:right.type.type_var_tuple_prefix] | ||
right_items = right.args[:right.type.type_var_tuple_prefix] | ||
if right.type.type_var_tuple_suffix: | ||
left_items += t.args[-right.type.type_var_tuple_suffix:] | ||
right_items += right.args[-right.type.type_var_tuple_suffix:] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jhance maybe I'm just totally missing something, but I think these aren't actually used anywhere? The slicing means we're not mutating the original t.args
and right.args
This makes several basic testcases for using classes with variadic generics pass. Some pieces of this are left as TODOs to flesh out various edge cases to avoid the diff growing in complexity.