Skip to content

Remove Python 2 logic from checkexpr #13264

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

Merged
merged 3 commits into from
Jul 28, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
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
77 changes: 11 additions & 66 deletions mypy/checkexpr.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@
)
from mypy.sametypes import is_same_type
from mypy.semanal_enum import ENUM_BASES
from mypy.subtypes import is_equivalent, is_proper_subtype, is_subtype, non_method_protocol_members
from mypy.subtypes import is_equivalent, is_subtype, non_method_protocol_members
from mypy.traverser import has_await_expression
from mypy.typeanal import (
check_for_explicit_any,
Expand Down Expand Up @@ -2561,15 +2561,7 @@ def visit_complex_expr(self, e: ComplexExpr) -> Type:

def visit_ellipsis(self, e: EllipsisExpr) -> Type:
"""Type check '...'."""
if self.chk.options.python_version[0] >= 3:
return self.named_type("builtins.ellipsis")
else:
# '...' is not valid in normal Python 2 code, but it can
# be used in stubs. The parser makes sure that we only
# get this far if we are in a stub, and we can safely
# return 'object' as ellipsis is special cased elsewhere.
# The builtins.ellipsis type does not exist in Python 2.
return self.named_type("builtins.object")
return self.named_type("builtins.ellipsis")

def visit_op_expr(self, e: OpExpr) -> Type:
"""Type check a binary operator expression."""
Expand All @@ -2596,7 +2588,7 @@ def visit_op_expr(self, e: OpExpr) -> Type:
return self.concat_tuples(proper_left_type, proper_right_type)

if e.op in operators.op_methods:
method = self.get_operator_method(e.op)
method = operators.op_methods[e.op]
result, method_type = self.check_op(method, left_type, e.right, e, allow_reverse=True)
e.method_type = method_type
return result
Expand Down Expand Up @@ -2673,7 +2665,7 @@ def visit_comparison_expr(self, e: ComparisonExpr) -> Type:
else:
self.msg.add_errors(local_errors.filtered_errors())
elif operator in operators.op_methods:
method = self.get_operator_method(operator)
method = operators.op_methods[operator]

with ErrorWatcher(self.msg.errors) as w:
sub_result, method_type = self.check_op(
Expand Down Expand Up @@ -2779,11 +2771,10 @@ def dangerous_comparison(
left = remove_optional(left)
right = remove_optional(right)
left, right = get_proper_types((left, right))
py2 = self.chk.options.python_version < (3, 0)
if (
original_container
and has_bytes_component(original_container, py2)
and has_bytes_component(left, py2)
and has_bytes_component(original_container)
and has_bytes_component(left)
):
# We need to special case bytes and bytearray, because 97 in b'abc', b'a' in b'abc',
# b'a' in bytearray(b'abc') etc. all return True (and we want to show the error only
Expand All @@ -2805,12 +2796,6 @@ def dangerous_comparison(
return False
return not is_overlapping_types(left, right, ignore_promotions=False)

def get_operator_method(self, op: str) -> str:
if op == "/" and self.chk.options.python_version[0] == 2:
return "__truediv__" if self.chk.tree.is_future_flag_set("division") else "__div__"
else:
return operators.op_methods[op]

def check_method_call_by_name(
self,
method: str,
Expand Down Expand Up @@ -2973,7 +2958,7 @@ def lookup_definer(typ: Instance, attr_name: str) -> Optional[str]:
# STEP 1:
# We start by getting the __op__ and __rop__ methods, if they exist.

rev_op_name = self.get_reverse_op_method(op_name)
rev_op_name = operators.reverse_op_methods[op_name]

left_op = lookup_operator(op_name, left_type)
right_op = lookup_operator(rev_op_name, right_type)
Expand All @@ -2986,7 +2971,6 @@ def lookup_definer(typ: Instance, attr_name: str) -> Optional[str]:
# We store the determined order inside the 'variants_raw' variable,
# which records tuples containing the method, base type, and the argument.

bias_right = is_proper_subtype(right_type, left_type)
if op_name in operators.op_methods_that_shortcut and is_same_type(left_type, right_type):
# When we do "A() + A()", for example, Python will only call the __add__ method,
# never the __radd__ method.
Expand Down Expand Up @@ -3019,22 +3003,6 @@ def lookup_definer(typ: Instance, attr_name: str) -> Optional[str]:

variants_raw = [(left_op, left_type, right_expr), (right_op, right_type, left_expr)]

# STEP 2b:
# When running Python 2, we might also try calling the __cmp__ method.

is_python_2 = self.chk.options.python_version[0] == 2
if is_python_2 and op_name in operators.ops_falling_back_to_cmp:
cmp_method = operators.comparison_fallback_method
left_cmp_op = lookup_operator(cmp_method, left_type)
right_cmp_op = lookup_operator(cmp_method, right_type)

if bias_right:
variants_raw.append((right_cmp_op, right_type, left_expr))
variants_raw.append((left_cmp_op, left_type, right_expr))
else:
variants_raw.append((left_cmp_op, left_type, right_expr))
variants_raw.append((right_cmp_op, right_type, left_expr))

# STEP 3:
# We now filter out all non-existent operators. The 'variants' list contains
# all operator methods that are actually present, in the order that Python
Expand Down Expand Up @@ -3217,12 +3185,6 @@ def check_op(
context=context,
)

def get_reverse_op_method(self, method: str) -> str:
if method == "__div__" and self.chk.options.python_version[0] == 2:
return "__rdiv__"
else:
return operators.reverse_op_methods[method]

def check_boolean_op(self, e: OpExpr, context: Context) -> Type:
"""Type check a boolean operation ('and' or 'or')."""

Expand Down Expand Up @@ -3543,8 +3505,6 @@ def visit_enum_index_expr(
self, enum_type: TypeInfo, index: Expression, context: Context
) -> Type:
string_type: Type = self.named_type("builtins.str")
if self.chk.options.python_version[0] < 3:
string_type = UnionType.make_union([string_type, self.named_type("builtins.unicode")])
self.chk.check_subtype(
self.accept(index),
string_type,
Expand Down Expand Up @@ -4176,10 +4136,7 @@ def _super_arg_types(self, e: SuperExpr) -> Union[Type, Tuple[Type, Type]]:
if not self.chk.in_checked_function():
return AnyType(TypeOfAny.unannotated)
elif len(e.call.args) == 0:
if self.chk.options.python_version[0] == 2:
self.chk.fail(message_registry.TOO_FEW_ARGS_FOR_SUPER, e)
return AnyType(TypeOfAny.from_error)
elif not e.info:
if not e.info:
# This has already been reported by the semantic analyzer.
return AnyType(TypeOfAny.from_error)
elif self.chk.scope.active_class():
Expand Down Expand Up @@ -4528,7 +4485,7 @@ def is_valid_var_arg(self, typ: Type) -> bool:

def is_valid_keyword_var_arg(self, typ: Type) -> bool:
"""Is a type valid as a **kwargs argument?"""
ret = (
return (
is_subtype(
typ,
self.chk.named_generic_type(
Expand All @@ -4544,15 +4501,6 @@ def is_valid_keyword_var_arg(self, typ: Type) -> bool:
)
or isinstance(typ, ParamSpecType)
)
if self.chk.options.python_version[0] < 3:
ret = ret or is_subtype(
typ,
self.chk.named_generic_type(
"typing.Mapping",
[self.named_type("builtins.unicode"), AnyType(TypeOfAny.special_form)],
),
)
return ret

def has_member(self, typ: Type, member: str) -> bool:
"""Does type have member with the given name?"""
Expand Down Expand Up @@ -5152,13 +5100,10 @@ def is_expr_literal_type(node: Expression) -> bool:
return False


def has_bytes_component(typ: Type, py2: bool = False) -> bool:
def has_bytes_component(typ: Type) -> bool:
"""Is this one of builtin byte types, or a union that contains it?"""
typ = get_proper_type(typ)
if py2:
byte_types = {"builtins.str", "builtins.bytearray"}
else:
byte_types = {"builtins.bytes", "builtins.bytearray"}
byte_types = {"builtins.bytes", "builtins.bytearray"}
if isinstance(typ, UnionType):
return any(has_bytes_component(t) for t in typ.items)
if isinstance(typ, Instance) and typ.type.fullname in byte_types:
Expand Down
1 change: 0 additions & 1 deletion mypy/message_registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,6 @@ def format(self, *args: object, **kwargs: object) -> "ErrorMessage":

# Super
TOO_MANY_ARGS_FOR_SUPER: Final = ErrorMessage('Too many arguments for "super"')
TOO_FEW_ARGS_FOR_SUPER: Final = ErrorMessage('Too few arguments for "super"', codes.CALL_ARG)
SUPER_WITH_SINGLE_ARG_NOT_SUPPORTED: Final = ErrorMessage(
'"super" with a single argument not supported'
)
Expand Down
1 change: 0 additions & 1 deletion mypy/operators.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@
op_methods_to_symbols: Final = {v: k for (k, v) in op_methods.items()}
op_methods_to_symbols["__div__"] = "/"

comparison_fallback_method: Final = "__cmp__"
ops_falling_back_to_cmp: Final = {"__ne__", "__eq__", "__lt__", "__le__", "__gt__", "__ge__"}


Expand Down