From 821a03013f89415019bdded20728bdaa5096bf14 Mon Sep 17 00:00:00 2001 From: Erik Wright Date: Mon, 5 Jun 2017 14:15:25 -0400 Subject: [PATCH 1/2] Add bitwise unary operators, also resilience against unrecognized operators. --- shopify_python/google_styleguide.py | 11 +++++++---- tests/shopify_python/test_google_styleguide.py | 8 ++++++-- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/shopify_python/google_styleguide.py b/shopify_python/google_styleguide.py index ce3a284..14cbcec 100644 --- a/shopify_python/google_styleguide.py +++ b/shopify_python/google_styleguide.py @@ -131,7 +131,10 @@ class GoogleStyleGuideChecker(checkers.BaseChecker): "==": "eq", "!=": "ne", ">=": "ge", - ">": "gt" + ">": "gt", + "&": "and_", + "|": "or_", + "^": "xor", } def visit_assign(self, node): # type: (astroid.Assign) -> None @@ -285,7 +288,7 @@ def __lambda_func(self, node): # type: (astroid.Lambda) -> None """Prefer Operator Function to Lambda Functions""" if isinstance(node.body, astroid.UnaryOp): - operator = self.UNARY_OPERATORS[node.body.op] + operator = self.UNARY_OPERATORS.get(node.body.op, None) argname = node.args.args[0].name if operator and not isinstance(node.body.operand, astroid.BinOp) and argname is node.body.operand.name: varname = node.body.operand.name @@ -295,7 +298,7 @@ def __lambda_func(self, node): # type: (astroid.Lambda) -> None elif isinstance(node.body, astroid.BinOp): if shopify_python.ast.count_tree_size(node.body) == 3 and len(node.args.args) == 2: node = node.body - operator = self.BINARY_OPERATORS[node.op] + operator = self.BINARY_OPERATORS.get(node.op, None) if operator: left = str(node.left.value) if node.left.name == 'int' else node.left.name right = str(node.right.value) if node.right.name == 'int' else node.right.name @@ -305,7 +308,7 @@ def __lambda_func(self, node): # type: (astroid.Lambda) -> None elif isinstance(node.body, astroid.Compare): if shopify_python.ast.count_tree_size(node.body) == 3 and len(node.args.args) == 2: node = node.body - operator = self.BINARY_OPERATORS[node.ops[0][0]] + operator = self.BINARY_OPERATORS.get(node.ops[0][0], None) if operator: left = str(node.left.value) if node.left.name == 'int' else node.left.name right = node.ops[0][1].name diff --git a/tests/shopify_python/test_google_styleguide.py b/tests/shopify_python/test_google_styleguide.py index c90f7b5..5848869 100644 --- a/tests/shopify_python/test_google_styleguide.py +++ b/tests/shopify_python/test_google_styleguide.py @@ -267,7 +267,10 @@ def unaryfnc(): ('x == y', 'eq'), ('x != y', 'ne'), ('x >= y', 'ge'), - ('x > y', 'gt') + ('x > y', 'gt'), + ('x & y', 'and_'), + ('x | y', 'or_'), + ('x ^ y', 'xor'), ]) def test_binary_lambda_func(self, test_case): (expression, op_name) = test_case @@ -286,7 +289,8 @@ def binaryfnc(): @pytest.mark.parametrize('expression', [ 'map(lambda x: x + 3, [1, 2, 3, 4])', - 'reduce(lambda x, y, z: x * y + z, [1, 2, 3])' + 'reduce(lambda x, y, z: x * y + z, [1, 2, 3])', + 'reduce(lambda x, y: x in y, [1, 2, 3])' ]) def test_allowed_binary_operation(self, expression): binary_root = astroid.builder.parse(""" From ce7d30ffd679d1061a9261d7d144d2906d3796c2 Mon Sep 17 00:00:00 2001 From: Erik Wright Date: Mon, 5 Jun 2017 15:34:13 -0400 Subject: [PATCH 2/2] Improve tests and fix revealed bug. (#73) --- shopify_python/google_styleguide.py | 10 +++ .../shopify_python/test_google_styleguide.py | 71 ++++++++++++++----- 2 files changed, 65 insertions(+), 16 deletions(-) diff --git a/shopify_python/google_styleguide.py b/shopify_python/google_styleguide.py index 059f00e..ce3a284 100644 --- a/shopify_python/google_styleguide.py +++ b/shopify_python/google_styleguide.py @@ -302,3 +302,13 @@ def __lambda_func(self, node): # type: (astroid.Lambda) -> None lambda_fun = "lambda " + left + ', ' + right + ": " + " ".join([left, node.op, right]) op_fun = "operator." + operator self.add_message('lambda-func', node=node, args={'op': op_fun, 'lambda_fun': lambda_fun}) + elif isinstance(node.body, astroid.Compare): + if shopify_python.ast.count_tree_size(node.body) == 3 and len(node.args.args) == 2: + node = node.body + operator = self.BINARY_OPERATORS[node.ops[0][0]] + if operator: + left = str(node.left.value) if node.left.name == 'int' else node.left.name + right = node.ops[0][1].name + lambda_fun = "lambda " + left + ', ' + right + ": " + " ".join([left, node.ops[0][0], right]) + op_fun = "operator." + operator + self.add_message('lambda-func', node=node, args={'op': op_fun, 'lambda_fun': lambda_fun}) diff --git a/tests/shopify_python/test_google_styleguide.py b/tests/shopify_python/test_google_styleguide.py index d1b5327..c90f7b5 100644 --- a/tests/shopify_python/test_google_styleguide.py +++ b/tests/shopify_python/test_google_styleguide.py @@ -226,33 +226,72 @@ def test_use_cond_exprs(self): with self.assertNoMessages(): self.walk(root_not_msg) - def test_unary_lambda_func(self): + def test_unary_lambda_func_with_complex_operand_allowed(self): unary_root = astroid.builder.parse(""" def unaryfnc(): unary_pass = map(lambda x: not (x+3), [1, 2, 3, 4]) - unary_fail = map(lambda x: -x, [1, 2, 3, 4]) """) + with self.assertNoMessages(): + self.walk(unary_root) + + @pytest.mark.parametrize('test_case', [ + ('- x', 'neg'), + ('~ x', 'invert'), + ('not x', 'not_'), + ('+ x', 'pos') + ]) + def test_unary_lambda_func(self, test_case): + (expression, op_name) = test_case + unary_root = astroid.builder.parse(""" + def unaryfnc(): + unary_fail = map(lambda x: {}, [1, 2, 3, 4]) + """.format(expression)) - ulam_fail = unary_root.body[0].body[1].value.args[0] + ulam_fail = unary_root.body[0].body[0].value.args[0] unary_message = pylint.testutils.Message('lambda-func', node=ulam_fail, args={ - 'op': 'operator.neg', - 'lambda_fun': 'lambda x: - x' + 'op': 'operator.{}'.format(op_name), + 'lambda_fun': 'lambda x: {}'.format(expression) }) with self.assertAddsMessages(unary_message): self.walk(unary_root) - def test_binary_lambda_func(self): + @pytest.mark.parametrize('test_case', [ + ('x + y', 'add'), + ('x - y', 'sub'), + ('x * y', 'mul'), + ('x / y', 'truediv'), + ('x ** y', 'pow'), + ('x % y', 'modulo'), + ('x < y', 'lt'), + ('x <= y', 'le'), + ('x == y', 'eq'), + ('x != y', 'ne'), + ('x >= y', 'ge'), + ('x > y', 'gt') + ]) + def test_binary_lambda_func(self, test_case): + (expression, op_name) = test_case binary_root = astroid.builder.parse(""" def binaryfnc(): - binary_pass = reduce(lambda x, y, z: x * y + z, [1, 2, 3]) - binary_pass2 = map(lambda x: x + 3, [1, 2, 3, 4]) - binary_fail = reduce(lambda x, y: x * y, [1, 2, 3, 4]) - """) + binary_fail = reduce(lambda x, y: {}, [1, 2, 3, 4]) + """.format(expression)) - binary_fail = binary_root.body[0].body[2].value.args[0] - bin_message = pylint.testutils.Message('lambda-func', node=binary_fail.body, args={ - 'op': 'operator.mul', - 'lambda_fun': 'lambda x, y: x * y' - }) - with self.assertAddsMessages(bin_message): + binary_fail = binary_root.body[0].body[0].value.args[0] + binary_message = pylint.testutils.Message('lambda-func', node=binary_fail.body, args={ + 'op': 'operator.{}'.format(op_name), + 'lambda_fun': 'lambda x, y: {}'.format(expression) + }) + with self.assertAddsMessages(binary_message): + self.walk(binary_root) + + @pytest.mark.parametrize('expression', [ + 'map(lambda x: x + 3, [1, 2, 3, 4])', + 'reduce(lambda x, y, z: x * y + z, [1, 2, 3])' + ]) + def test_allowed_binary_operation(self, expression): + binary_root = astroid.builder.parse(""" + def binaryfnc(): + binary_pass = {} + """.format(expression)) + with self.assertNoMessages(): self.walk(binary_root)