def visit_BinOp(self, node: ast.BinOp):
node = self.generic_visit(node)
if self._is_numeric_pow(node):
left, right = node.left, node.right
degree = ( right.n if isinstance(right, ast.Num)
else -right.operand.n if isinstance(right.op, ast.USub)
else right.operand.n )
degree = int(degree)
if abs(degree) == 0:
node = ast.copy_location(ast.Num(n = 1), node)
elif abs(degree) == 1:
node = node.left
elif 2 <= abs(degree) <= self.MAX_DEGREE:
for _ in range(1, abs(degree)):
new_node = ast.BinOp\
( left = left
, op = ast.Mult()
, right = copy(node.left)
)
left = new_node = ast.copy_location(new_node, node)
node = new_node
else:
return node
if degree < 0:
new_node = ast.BinOp\
( left = ast.Num(n = 1)
, op = ast.Div()
, right = node
)
node = ast.copy_location(new_node, node)
return node
python类BinOp()的实例源码
def listNotEmpty(a):
"""Determines that the iterable is NOT empty, if we can know that"""
"""Used for For objects"""
if not isinstance(a, ast.AST):
return False
if type(a) == ast.Call:
if type(a.func) == ast.Name and a.func.id in ["range"]:
if len(a.args) == 1: # range(x)
return type(a.args[0]) == ast.Num and type(a.args[0].n) != complex and a.args[0].n > 0
elif len(a.args) == 2: # range(start, x)
if type(a.args[0]) == ast.Num and type(a.args[1]) == ast.Num and \
type(a.args[0].n) != complex and type(a.args[1].n) != complex and \
a.args[0].n < a.args[1].n:
return True
elif type(a.args[1]) == ast.BinOp and type(a.args[1].op) == ast.Add:
if type(a.args[1].right) == ast.Num and type(a.args[1].right) != complex and a.args[1].right.n > 0 and \
compareASTs(a.args[0], a.args[1].left, checkEquality=True) == 0:
return True
elif type(a.args[1].left) == ast.Num and type(a.args[1].left) != complex and a.args[1].left.n > 0 and \
compareASTs(a.args[0], a.args[1].right, checkEquality=True) == 0:
return True
elif type(a) in [ast.List, ast.Tuple]:
return len(a.elts) > 0
elif type(a) == ast.Str:
return len(a.s) > 0
return False
def cleanupTypes(a):
"""Remove any unneccessary type mappings"""
if not isinstance(a, ast.AST):
return a
# No need to cast something if it'll be changed anyway by a binary operation
if type(a) == ast.BinOp:
a.left = cleanupTypes(a.left)
a.right = cleanupTypes(a.right)
# Ints become floats naturally
if eventualType(a.left) == eventualType(a.right) == float:
if type(a.right) == ast.Call and type(a.right.func) == ast.Name and \
a.right.func.id == "float" and len(a.right.args) == 1 and len(a.right.keywords) == 0 and \
eventualType(a.right.args[0]) in [int, float]:
a.right = a.right.args[0]
elif type(a.left) == ast.Call and type(a.left.func) == ast.Name and \
a.left.func.id == "float" and len(a.left.args) == 1 and len(a.left.keywords) == 0 and \
eventualType(a.left.args[0]) in [int, float]:
a.left = a.left.args[0]
return a
elif type(a) == ast.Call and type(a.func) == ast.Name and len(a.args) == 1 and len(a.keywords) == 0:
a.func = cleanupTypes(a.func)
a.args = [cleanupTypes(a.args[0])]
# If the type already matches, no need to cast it
funName = a.func.id
argType = eventualType(a.args[0])
if type(a.func) == ast.Name:
if (funName == "float" and argType == float) or \
(funName == "int" and argType == int) or \
(funName == "bool" and argType == bool) or \
(funName == "str" and argType == str):
return a.args[0]
return applyToChildren(a, cleanupTypes)
def _translate_augassign(self, target, op, value, location):
return self._translate_assign([target], ast.BinOp(target, op, value), location)
def _binop(self, other, *, _opnode=opnode, _sym=sym):
othername, other, constants = _normalize_arg(
other,
self._constants,
)
return __class__(
'%s %s %s' % (self._pname, _sym, othername),
ast.BinOp(
left=self._tree,
op=_opnode(),
right=other,
),
merge(constants, getattr(other, '_constants', {})),
)
def visit_AugAssign(self, node):
if node.op.__class__ != ast.FloorDiv:
return node
dummy_op = ast.BinOp(left=node.target, op=ast.Div(), right=node.value)
dummy_int = ast.Name(id="int", ctx=ast.Load())
dummy_call = ast.Call(func=dummy_int, args=[dummy_op], keywords=[], starargs=None, kwargs=None)
return ast.Assign(targets=[node.target], value=dummy_call)
def visit_BinOp(self, node):
if node.op.__class__ != ast.FloorDiv:
return node
dummy_op = ast.BinOp(left=node.left, op=ast.Div(), right=node.right)
dummy_int = ast.Name(id="int", ctx=ast.Load())
return ast.Call(func=dummy_int, args=[dummy_op], keywords=[], starargs=None, kwargs=None)
def test_copy_location(self):
src = ast.parse('1 + 1', mode='eval')
src.body.right = ast.copy_location(ast.Num(2), src.body.right)
self.assertEqual(ast.dump(src, include_attributes=True),
'Expression(body=BinOp(left=Num(n=1, lineno=1, col_offset=0), '
'op=Add(), right=Num(n=2, lineno=1, col_offset=4), lineno=1, '
'col_offset=0))'
)
def get_type(self, node):
if isinstance(node, ast.BinOp):
left_type = self.get_type(node.left)
right_type = self.get_type(node.right)
if isinstance(node.op, ast.Add):
if left_type.is_number and right_type.is_number:
return Type.NUMBER
else:
return Type.STRING
elif left_type.is_number and right_type.is_number:
return Type.NUMBER
else:
raise CompileError("Can not '%s' operator with string." % node.op.__class__.__name__)
elif isinstance(node, ast.UnaryOp):
if isinstance(operand, ast.Num):
return Type.NUMBER
else:
raise SyntaxNotSupportError("Not support unary operator except number.")
elif isinstance(node, ast.Num):
return Type.NUMBER
elif isinstance(node, ast.Str):
return Type.STRING
elif isinstance(node, ast.List):
return Type.LIST
elif isinstance(node, ast.Call):
args_type = [self.get_type(arg) for arg in node.args]
return self.get_function_return_type(node.func.id, args_type)
elif isinstance(node, ast.Name):
return self.variables[node.id].var_type
def test_copy_location(self):
src = ast.parse('1 + 1', mode='eval')
src.body.right = ast.copy_location(ast.Num(2), src.body.right)
self.assertEqual(ast.dump(src, include_attributes=True),
'Expression(body=BinOp(left=Num(n=1, lineno=1, col_offset=0), '
'op=Add(), right=Num(n=2, lineno=1, col_offset=4), lineno=1, '
'col_offset=0))'
)
def test_copy_location(self):
src = ast.parse('1 + 1', mode='eval')
src.body.right = ast.copy_location(ast.Num(2), src.body.right)
self.assertEqual(ast.dump(src, include_attributes=True),
'Expression(body=BinOp(left=Num(n=1, lineno=1, col_offset=0), '
'op=Add(), right=Num(n=2, lineno=1, col_offset=4), lineno=1, '
'col_offset=0))'
)
def eval_(node):
"""Calculate a mathematical expression."""
if isinstance(node, ast.Num): # <number>
return node.n
elif isinstance(node, ast.BinOp): # <left> <operator> <right>
return operators[type(node.op)](eval_(node.left), eval_(node.right))
elif isinstance(node, ast.UnaryOp): # <operator> <operand> e.g., -1
return operators[type(node.op)](eval_(node.operand))
else:
raise TypeError(node)
def _is_range_op(self, node):
return isinstance(node, ast.BinOp) and isinstance(node.op, self.range_op)
def visit_BinOp(self, binop):
symbol = binop_map[binop.op.__class__]
left_expr, left_expl = self.visit(binop.left)
right_expr, right_expl = self.visit(binop.right)
explanation = "(%s %s %s)" % (left_expl, symbol, right_expl)
res = self.assign(ast.BinOp(left_expr, binop.op, right_expr))
return res, explanation
def concat_string(node, stop=None):
'''Builds a string from a ast.BinOp chain.
This will build a string from a series of ast.Str nodes wrapped in
ast.BinOp nodes. Something like "a" + "b" + "c" or "a %s" % val etc.
The provided node can be any participant in the BinOp chain.
:param node: (ast.Str or ast.BinOp) The node to process
:param stop: (ast.Str or ast.BinOp) Optional base node to stop at
:returns: (Tuple) the root node of the expression, the string value
'''
def _get(node, bits, stop=None):
if node != stop:
bits.append(
_get(node.left, bits, stop)
if isinstance(node.left, ast.BinOp)
else node.left)
bits.append(
_get(node.right, bits, stop)
if isinstance(node.right, ast.BinOp)
else node.right)
bits = [node]
while isinstance(node.parent, ast.BinOp):
node = node.parent
if isinstance(node, ast.BinOp):
_get(node, bits, stop)
return (node, " ".join([x.s for x in bits if isinstance(x, ast.Str)]))
def raw_str_sql_expressions(context):
str_node = context.node
if isinstance(str_node.parent, ast.BinOp):
# avoid duplicates findings with B9101
return
return _check_string_for_sql(context.node, confidence=bandit.LOW)
def test_copy_location(self):
src = ast.parse('1 + 1', mode='eval')
src.body.right = ast.copy_location(ast.Num(2), src.body.right)
self.assertEqual(ast.dump(src, include_attributes=True),
'Expression(body=BinOp(left=Num(n=1, lineno=1, col_offset=0), '
'op=Add(), right=Num(n=2, lineno=1, col_offset=4), lineno=1, '
'col_offset=0))'
)
def visit_BinOp(self, binop):
symbol = binop_map[binop.op.__class__]
left_expr, left_expl = self.visit(binop.left)
right_expr, right_expl = self.visit(binop.right)
explanation = "(%s %s %s)" % (left_expl, symbol, right_expl)
res = self.assign(ast.BinOp(left_expr, binop.op, right_expr))
return res, explanation
def test_shift_error(self):
self.check_dont_optimize_func("1 << -3",
ast.BinOp(left=ast.Num(n=1), op=ast.LShift(), right=ast.Num(-3)))
self.check_dont_optimize_func("1 >> -3",
ast.BinOp(left=ast.Num(n=1), op=ast.RShift(), right=ast.Num(-3)))
def test_pow(self):
self.check_optimize_func("2 ** 3", "8")
self.check_optimize_func("2.0 ** 3.0", "8.0")
# complex
self.check_dont_optimize_func("2.0j ** 3.0")
self.check_dont_optimize_func("2.0 ** 3.0j")
# 0 ** -1
self.check_dont_optimize_func("0 ** -1",
ast.BinOp(left=ast.Num(n=0), op=ast.Pow(), right=ast.Num(-1)))
self.check_dont_optimize_func("0.0 ** -1",
ast.BinOp(left=ast.Num(n=0.0), op=ast.Pow(), right=ast.Num(-1)))