Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Fix test case failures
  • Loading branch information
JukkaL committed Mar 24, 2017
commit ca511d8632e8652336109e66056e6b004758e982
77 changes: 39 additions & 38 deletions mypy/checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
Type, AnyType, CallableType, FunctionLike, Overloaded, TupleType, TypedDictType,
Instance, NoneTyp, ErrorType, strip_type, TypeType,
UnionType, TypeVarId, TypeVarType, PartialType, DeletedType, UninhabitedType, TypeVarDef,
true_only, false_only, function_type, is_named_instance
true_only, false_only, function_type, is_named_instance, union_items
)
from mypy.sametypes import is_same_type, is_same_types
from mypy.messages import MessageBuilder
Expand Down Expand Up @@ -768,44 +768,45 @@ def check_overlapping_op_methods(self,
# of x in __radd__ would not be A, the methods could be
# non-overlapping.

if isinstance(forward_type, CallableType):
# TODO check argument kinds
if len(forward_type.arg_types) < 1:
# Not a valid operator method -- can't succeed anyway.
return
for forward_item in union_items(forward_type):
if isinstance(forward_item, CallableType):
# TODO check argument kinds
if len(forward_item.arg_types) < 1:
# Not a valid operator method -- can't succeed anyway.
return

# Construct normalized function signatures corresponding to the
# operator methods. The first argument is the left operand and the
# second operand is the right argument -- we switch the order of
# the arguments of the reverse method.
forward_tweaked = CallableType(
[forward_base, forward_type.arg_types[0]],
[nodes.ARG_POS] * 2,
[None] * 2,
forward_type.ret_type,
forward_type.fallback,
name=forward_type.name)
reverse_args = reverse_type.arg_types
reverse_tweaked = CallableType(
[reverse_args[1], reverse_args[0]],
[nodes.ARG_POS] * 2,
[None] * 2,
reverse_type.ret_type,
fallback=self.named_type('builtins.function'),
name=reverse_type.name)

if is_unsafe_overlapping_signatures(forward_tweaked,
reverse_tweaked):
self.msg.operator_method_signatures_overlap(
reverse_class.name(), reverse_name,
forward_base.type.name(), forward_name, context)
elif isinstance(forward_type, Overloaded):
for item in forward_type.items():
self.check_overlapping_op_methods(
reverse_type, reverse_name, reverse_class,
item, forward_name, forward_base, context)
elif not isinstance(forward_type, AnyType):
self.msg.forward_operator_not_callable(forward_name, context)
# Construct normalized function signatures corresponding to the
# operator methods. The first argument is the left operand and the
# second operand is the right argument -- we switch the order of
# the arguments of the reverse method.
forward_tweaked = CallableType(
[forward_base, forward_item.arg_types[0]],
[nodes.ARG_POS] * 2,
[None] * 2,
forward_item.ret_type,
forward_item.fallback,
name=forward_item.name)
reverse_args = reverse_type.arg_types
reverse_tweaked = CallableType(
[reverse_args[1], reverse_args[0]],
[nodes.ARG_POS] * 2,
[None] * 2,
reverse_type.ret_type,
fallback=self.named_type('builtins.function'),
name=reverse_type.name)

if is_unsafe_overlapping_signatures(forward_tweaked,
reverse_tweaked):
self.msg.operator_method_signatures_overlap(
reverse_class.name(), reverse_name,
forward_base.type.name(), forward_name, context)
elif isinstance(forward_item, Overloaded):
for item in forward_item.items():
self.check_overlapping_op_methods(
reverse_type, reverse_name, reverse_class,
item, forward_name, forward_base, context)
elif not isinstance(forward_item, AnyType):
self.msg.forward_operator_not_callable(forward_name, context)

def check_inplace_operator_method(self, defn: FuncBase) -> None:
"""Check an inplace operator method such as __iadd__.
Expand Down
9 changes: 5 additions & 4 deletions mypy/subtypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
ARG_POS, ARG_OPT, ARG_NAMED, ARG_NAMED_OPT, ARG_STAR, ARG_STAR2,
)
from mypy.maptype import map_instance_to_supertype
from mypy.sametypes import is_same_type

from mypy import experiments

Expand Down Expand Up @@ -373,7 +374,7 @@ def is_callable_subtype(left: CallableType, right: CallableType,
right_by_position = right.argument_by_position(j)
assert right_by_position is not None
if not are_args_compatible(left_by_position, right_by_position,
ignore_pos_arg_names):
ignore_pos_arg_names, use_proper_subtype):
return False
j += 1
continue
Expand All @@ -396,7 +397,7 @@ def is_callable_subtype(left: CallableType, right: CallableType,
right_by_name = right.argument_by_name(name)
assert right_by_name is not None
if not are_args_compatible(left_by_name, right_by_name,
ignore_pos_arg_names):
ignore_pos_arg_names, use_proper_subtype):
return False
continue

Expand Down Expand Up @@ -655,8 +656,8 @@ def visit_tuple_type(self, left: TupleType) -> bool:
def visit_typeddict_type(self, left: TypedDictType) -> bool:
right = self.right
if isinstance(right, TypedDictType):
for name, typ in left.items():
if name not in right or not is_same_type(typ, right.items[name]):
for name, typ in left.items.items():
if name not in right.items or not is_same_type(typ, right.items[name]):
return False
return True
return is_proper_subtype(left.fallback, right)
Expand Down
14 changes: 14 additions & 0 deletions mypy/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -1751,6 +1751,20 @@ def get_type_vars(typ: Type) -> List[TypeVarType]:
return tvars


def union_items(typ: Type) -> List[Type]:
"""Return the flattened items of a union type.

For non-union types, return a list containing just the argument.
"""
if isinstance(typ, UnionType):
items = []
for item in typ.items:
items.extend(union_items(item))
return items
else:
return [typ]


deserialize_map = {
key: obj.deserialize # type: ignore
for key, obj in globals().items()
Expand Down
9 changes: 9 additions & 0 deletions test-data/unit/check-unions.test
Original file line number Diff line number Diff line change
Expand Up @@ -417,3 +417,12 @@ reveal_type(u(D_C, D_D)) # E: Revealed type is 'def (__main__.D) -> __main__.C'
reveal_type(u(D_D, D_C)) # E: Revealed type is 'def (__main__.D) -> __main__.C'

reveal_type(u(D_C, i_C)) # E: Revealed type is 'Union[def (builtins.int) -> __main__.C, def (__main__.D) -> __main__.C]'

[case testUnionOperatorMethodSpecialCase]
from typing import Union
class C:
def __le__(self, x: 'C') -> int: ...
class D:
def __le__(self, other) -> int: ...
class E:
def __ge__(self, other: Union[C, D]) -> int: ...