Skip to content
Closed
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
14 changes: 7 additions & 7 deletions mypy/checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
ARG_POS, ARG_STAR, LITERAL_TYPE, LDEF, MDEF, GDEF,
CONTRAVARIANT, COVARIANT, INVARIANT, TypeVarExpr, AssignmentExpr,
is_final_node,
ARG_NAMED)
ARG_NAMED, BinOp)
from mypy import nodes
from mypy import operators
from mypy.literals import literal, literal_hash, Key
Expand Down Expand Up @@ -2076,7 +2076,7 @@ def visit_assignment_stmt(self, s: AssignmentStmt) -> None:
self.fail(message_registry.DEPENDENT_FINAL_IN_CLASS_BODY, s)

def check_type_alias_rvalue(self, s: AssignmentStmt) -> None:
if not (self.is_stub and isinstance(s.rvalue, OpExpr) and s.rvalue.op == '|'):
if not (self.is_stub and isinstance(s.rvalue, OpExpr) and s.rvalue.op == BinOp.BitOr):
# We do this mostly for compatibility with old semantic analyzer.
# TODO: should we get rid of this?
alias_type = self.expr_checker.accept(s.rvalue)
Expand All @@ -2086,7 +2086,7 @@ def check_type_alias_rvalue(self, s: AssignmentStmt) -> None:
alias_type = AnyType(TypeOfAny.special_form)

def accept_items(e: Expression) -> None:
if isinstance(e, OpExpr) and e.op == '|':
if isinstance(e, OpExpr) and e.op == BinOp.BitOr:
accept_items(e.left)
accept_items(e.right)
else:
Expand Down Expand Up @@ -4491,15 +4491,15 @@ def has_no_custom_eq_checks(t: Type) -> bool:
(None if if_assignment_map is None or if_condition_map is None else if_map),
(None if else_assignment_map is None or else_condition_map is None else else_map),
)
elif isinstance(node, OpExpr) and node.op == 'and':
elif isinstance(node, OpExpr) and node.op == BinOp.And:
left_if_vars, left_else_vars = self.find_isinstance_check(node.left)
right_if_vars, right_else_vars = self.find_isinstance_check(node.right)

# (e1 and e2) is true if both e1 and e2 are true,
# and false if at least one of e1 and e2 is false.
return (and_conditional_maps(left_if_vars, right_if_vars),
or_conditional_maps(left_else_vars, right_else_vars))
elif isinstance(node, OpExpr) and node.op == 'or':
elif isinstance(node, OpExpr) and node.op == BinOp.Or:
left_if_vars, left_else_vars = self.find_isinstance_check(node.left)
right_if_vars, right_else_vars = self.find_isinstance_check(node.right)

Expand Down Expand Up @@ -5563,7 +5563,7 @@ def flatten_types(t: Type) -> List[Type]:

def get_isinstance_type(expr: Expression,
type_map: Dict[Expression, Type]) -> Optional[List[TypeRange]]:
if isinstance(expr, OpExpr) and expr.op == '|':
if isinstance(expr, OpExpr) and expr.op == BinOp.BitOr:
left = get_isinstance_type(expr.left, type_map)
right = get_isinstance_type(expr.right, type_map)
if left is None or right is None:
Expand Down Expand Up @@ -5762,7 +5762,7 @@ def is_same_arg_prefix(t: CallableType, s: CallableType) -> bool:
ignore_pos_arg_names=True)


def infer_operator_assignment_method(typ: Type, operator: str) -> Tuple[bool, str]:
def infer_operator_assignment_method(typ: Type, operator: BinOp) -> Tuple[bool, str]:
"""Determine if operator assignment on given value type is in-place, and the method name.

For example, if operator is '+', return (True, '__iadd__') or (False, '__add__')
Expand Down
24 changes: 12 additions & 12 deletions mypy/checkexpr.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
DictionaryComprehension, ComplexExpr, EllipsisExpr, StarExpr, AwaitExpr, YieldExpr,
YieldFromExpr, TypedDictExpr, PromoteExpr, NewTypeExpr, NamedTupleExpr, TypeVarExpr,
TypeAliasExpr, BackquoteExpr, EnumCallExpr, TypeAlias, SymbolNode, PlaceholderNode,
ParamSpecExpr,
ParamSpecExpr, BinOp,
ArgKind, ARG_POS, ARG_NAMED, ARG_STAR, ARG_STAR2, LITERAL_TYPE, REVEAL_TYPE,
)
from mypy.literals import literal
Expand Down Expand Up @@ -2156,12 +2156,12 @@ def visit_ellipsis(self, e: EllipsisExpr) -> Type:

def visit_op_expr(self, e: OpExpr) -> Type:
"""Type check a binary operator expression."""
if e.op == 'and' or e.op == 'or':
if e.op.is_boolean():
return self.check_boolean_op(e, e)
if e.op == '*' and isinstance(e.left, ListExpr):
if e.op == BinOp.Mul and isinstance(e.left, ListExpr):
# Expressions of form [...] * e get special type inference.
return self.check_list_multiply(e)
if e.op == '%':
if e.op == BinOp.Mod:
pyversion = self.chk.options.python_version
if pyversion[0] == 3:
if isinstance(e.left, BytesExpr) and pyversion[1] >= 5:
Expand All @@ -2174,7 +2174,7 @@ def visit_op_expr(self, e: OpExpr) -> Type:
left_type = self.accept(e.left)

proper_left_type = get_proper_type(left_type)
if isinstance(proper_left_type, TupleType) and e.op == '+':
if isinstance(proper_left_type, TupleType) and e.op == BinOp.Add:
left_add_method = proper_left_type.partial_fallback.type.get('__add__')
if left_add_method and left_add_method.fullname == 'builtins.tuple.__add__':
proper_right_type = get_proper_type(self.accept(e.right))
Expand Down Expand Up @@ -2371,8 +2371,8 @@ def dangerous_comparison(self, left: Type, right: Type,
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:
def get_operator_method(self, op: BinOp) -> str:
if op == BinOp.Div and self.chk.options.python_version[0] == 2:
# TODO also check for "from __future__ import division"
return '__div__'
else:
Expand Down Expand Up @@ -2788,15 +2788,15 @@ def check_boolean_op(self, e: OpExpr, context: Context) -> Type:
ctx = self.type_context[-1]
left_type = self.accept(e.left, ctx)

assert e.op in ('and', 'or') # Checked by visit_op_expr
assert e.op.is_boolean() # Checked by visit_op_expr

if e.right_always:
left_map, right_map = None, {} # type: mypy.checker.TypeMap, mypy.checker.TypeMap
elif e.right_unreachable:
left_map, right_map = {}, None
elif e.op == 'and':
elif e.op == BinOp.And:
right_map, left_map = self.chk.find_isinstance_check(e.left)
elif e.op == 'or':
elif e.op == BinOp.Or:
left_map, right_map = self.chk.find_isinstance_check(e.left)

# If left_map is None then we know mypy considers the left expression
Expand Down Expand Up @@ -2832,10 +2832,10 @@ def check_boolean_op(self, e: OpExpr, context: Context) -> Type:
assert right_map is not None # find_isinstance_check guarantees this
return right_type

if e.op == 'and':
if e.op == BinOp.And:
restricted_left_type = false_only(left_type)
result_is_left = not left_type.can_be_true
elif e.op == 'or':
elif e.op == BinOp.Or:
restricted_left_type = true_only(left_type)
result_is_left = not left_type.can_be_false

Expand Down
3 changes: 2 additions & 1 deletion mypy/exprtotype.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
RawExpressionType, ProperType, UnionType
)
from mypy.options import Options
from mypy.operators import BinOp


class TypeTranslationError(Exception):
Expand Down Expand Up @@ -86,7 +87,7 @@ def expr_to_unanalyzed_type(expr: Expression,
else:
raise TypeTranslationError()
elif (isinstance(expr, OpExpr)
and expr.op == '|'
and expr.op == BinOp.BitOr
and ((options and options.python_version >= (3, 10)) or allow_new_syntax)):
return UnionType([expr_to_unanalyzed_type(expr.left, options, allow_new_syntax),
expr_to_unanalyzed_type(expr.right, options, allow_new_syntax)])
Expand Down
15 changes: 8 additions & 7 deletions mypy/fastparse.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
from mypy import message_registry, errorcodes as codes
from mypy.errors import Errors
from mypy.options import Options
from mypy.operators import BinOp
from mypy.reachability import mark_block_unreachable

try:
Expand Down Expand Up @@ -402,12 +403,12 @@ def translate_type_comment(self,
ast3.FloorDiv: '//'
}

def from_operator(self, op: ast3.operator) -> str:
def from_operator(self, op: ast3.operator) -> BinOp:
op_name = ASTConverter.op_map.get(type(op))
if op_name is None:
raise RuntimeError('Unknown operator ' + str(type(op)))
else:
return op_name
return BinOp(op_name)

comp_op_map: Final[Dict[typing.Type[AST], str]] = {
ast3.Gt: '>',
Expand All @@ -422,12 +423,12 @@ def from_operator(self, op: ast3.operator) -> str:
ast3.NotIn: 'not in'
}

def from_comp_operator(self, op: ast3.cmpop) -> str:
def from_comp_operator(self, op: ast3.cmpop) -> BinOp:
op_name = ASTConverter.comp_op_map.get(type(op))
if op_name is None:
raise RuntimeError('Unknown comparison operator ' + str(type(op)))
else:
return op_name
return BinOp(op_name)

def as_block(self, stmts: List[ast3.stmt], lineno: int) -> Optional[Block]:
b = None
Expand Down Expand Up @@ -966,9 +967,9 @@ def visit_BoolOp(self, n: ast3.BoolOp) -> OpExpr:

def group(self, op: str, vals: List[Expression], n: ast3.expr) -> OpExpr:
if len(vals) == 2:
e = OpExpr(op, vals[0], vals[1])
e = OpExpr(BinOp(op), vals[0], vals[1])
else:
e = OpExpr(op, vals[0], self.group(op, vals[1:], n))
e = OpExpr(BinOp(op), vals[0], self.group(op, vals[1:], n))
return self.set_line(e, n)

# BinOp(expr left, operator op, expr right)
Expand All @@ -978,7 +979,7 @@ def visit_BinOp(self, n: ast3.BinOp) -> OpExpr:
if op is None:
raise RuntimeError('cannot translate BinOp ' + str(type(n.op)))

e = OpExpr(op, self.visit(n.left), self.visit(n.right))
e = OpExpr(BinOp(op), self.visit(n.left), self.visit(n.right))
return self.set_line(e, n)

# UnaryOp(unaryop op, expr operand)
Expand Down
15 changes: 8 additions & 7 deletions mypy/fastparse2.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
TYPE_IGNORE_PATTERN, INVALID_TYPE_IGNORE
)
from mypy.options import Options
from mypy.operators import BinOp
from mypy.reachability import mark_block_unreachable

try:
Expand Down Expand Up @@ -258,14 +259,14 @@ def translate_type_comment(self, n: ast27.stmt,
ast27.FloorDiv: '//'
}

def from_operator(self, op: ast27.operator) -> str:
def from_operator(self, op: ast27.operator) -> BinOp:
op_name = ASTConverter.op_map.get(type(op))
if op_name is None:
raise RuntimeError('Unknown operator ' + str(type(op)))
elif op_name == '@':
raise RuntimeError('mypy does not support the MatMult operator')
else:
return op_name
return BinOp(op_name)

comp_op_map: Final[Dict[typing.Type[AST], str]] = {
ast27.Gt: '>',
Expand All @@ -280,12 +281,12 @@ def from_operator(self, op: ast27.operator) -> str:
ast27.NotIn: 'not in'
}

def from_comp_operator(self, op: ast27.cmpop) -> str:
def from_comp_operator(self, op: ast27.cmpop) -> BinOp:
op_name = ASTConverter.comp_op_map.get(type(op))
if op_name is None:
raise RuntimeError('Unknown comparison operator ' + str(type(op)))
else:
return op_name
return BinOp(op_name)

def as_block(self, stmts: List[ast27.stmt], lineno: int) -> Optional[Block]:
b = None
Expand Down Expand Up @@ -813,9 +814,9 @@ def visit_BoolOp(self, n: ast27.BoolOp) -> OpExpr:

def group(self, vals: List[Expression], op: str) -> OpExpr:
if len(vals) == 2:
return OpExpr(op, vals[0], vals[1])
return OpExpr(BinOp(op), vals[0], vals[1])
else:
return OpExpr(op, vals[0], self.group(vals[1:], op))
return OpExpr(BinOp(op), vals[0], self.group(vals[1:], op))

# BinOp(expr left, operator op, expr right)
def visit_BinOp(self, n: ast27.BinOp) -> OpExpr:
Expand All @@ -824,7 +825,7 @@ def visit_BinOp(self, n: ast27.BinOp) -> OpExpr:
if op is None:
raise RuntimeError('cannot translate BinOp ' + str(type(n.op)))

e = OpExpr(op, self.visit(n.left), self.visit(n.right))
e = OpExpr(BinOp(op), self.visit(n.left), self.visit(n.right))
return self.set_line(e, n)

# UnaryOp(unaryop op, expr operand)
Expand Down
6 changes: 3 additions & 3 deletions mypy/messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
ReturnStmt, NameExpr, Var, CONTRAVARIANT, COVARIANT, SymbolNode,
CallExpr, IndexExpr, StrExpr, SymbolTable, TempNode, SYMBOL_FUNCBASE_TYPES
)
from mypy.operators import op_methods, op_methods_to_symbols
from mypy.operators import BinOp, op_methods, op_methods_to_symbols
from mypy.subtypes import (
is_subtype, find_member, get_member_flags,
IS_SETTABLE, IS_CLASSVAR, IS_CLASS_OR_STATIC,
Expand Down Expand Up @@ -437,10 +437,10 @@ def incompatible_argument(self,
base = extract_type(name)

for method, op in op_methods_to_symbols.items():
for variant in method, '__r' + method[2:]:
for variant in (method, '__r' + method[2:]):
# FIX: do not rely on textual formatting
if name.startswith('"{}" of'.format(variant)):
if op == 'in' or variant != method:
if op == BinOp.In or variant != method:
# Reversed order of base/argument.
self.unsupported_operand_types(op, arg_type, base,
context, code=codes.OPERATOR)
Expand Down
16 changes: 8 additions & 8 deletions mypy/nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
import mypy.strconv
from mypy.util import short_type
from mypy.visitor import NodeVisitor, StatementVisitor, ExpressionVisitor

from mypy.operators import BinOp
from mypy.bogus_type import Bogus


Expand Down Expand Up @@ -1134,11 +1134,11 @@ class OperatorAssignmentStmt(Statement):

__slots__ = ('op', 'lvalue', 'rvalue')

op: str # TODO: Enum?
op: BinOp
lvalue: Lvalue
rvalue: Expression

def __init__(self, op: str, lvalue: Lvalue, rvalue: Expression) -> None:
def __init__(self, op: BinOp, lvalue: Lvalue, rvalue: Expression) -> None:
super().__init__()
self.op = op
self.lvalue = lvalue
Expand Down Expand Up @@ -1796,7 +1796,7 @@ class OpExpr(Expression):
__slots__ = ('op', 'left', 'right',
'method_type', 'right_always', 'right_unreachable')

op: str # TODO: Enum?
op: BinOp
left: Expression
right: Expression
# Inferred type for the operator method type (when relevant).
Expand All @@ -1806,7 +1806,7 @@ class OpExpr(Expression):
# Per static analysis only: Is the right side unreachable?
right_unreachable: bool

def __init__(self, op: str, left: Expression, right: Expression) -> None:
def __init__(self, op: BinOp, left: Expression, right: Expression) -> None:
super().__init__()
self.op = op
self.left = left
Expand All @@ -1824,18 +1824,18 @@ class ComparisonExpr(Expression):

__slots__ = ('operators', 'operands', 'method_types')

operators: List[str]
operators: List[BinOp]
operands: List[Expression]
# Inferred type for the operator methods (when relevant; None for 'is').
method_types: List[Optional["mypy.types.Type"]]

def __init__(self, operators: List[str], operands: List[Expression]) -> None:
def __init__(self, operators: List[BinOp], operands: List[Expression]) -> None:
super().__init__()
self.operators = operators
self.operands = operands
self.method_types = []

def pairwise(self) -> Iterator[Tuple[str, Expression, Expression]]:
def pairwise(self) -> Iterator[Tuple[BinOp, Expression, Expression]]:
"""If this comparison expr is "a < b is c == d", yields the sequence
("<", a, b), ("is", b, c), ("==", c, d)
"""
Expand Down
Loading