diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 15a32b3..d25c1c5 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -14,7 +14,7 @@ jobs: - uses: actions/setup-python@v4 - run: pip install -r requirements.txt - uses: pre-commit/action@v3.0.0 - build: + tests: runs-on: ubuntu-latest strategy: matrix: diff --git a/CHANGELOG.md b/CHANGELOG.md index 8f0d36f..5009b44 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,13 @@ Changelog ========= +## 0.1.1 +*** +**🔧 Fixes** +* Fix complaining about necessary parentheses in multi-line unpacking + assignments + ([#16](https://github.com/robsdedude/flake8-picky-parentheses/pull/16)). + ## 0.1.0 *** **🎉 Initial release** diff --git a/flake8_picky_parentheses/_redundant_parentheses.py b/flake8_picky_parentheses/_redundant_parentheses.py index 033fa18..00fdd35 100644 --- a/flake8_picky_parentheses/_redundant_parentheses.py +++ b/flake8_picky_parentheses/_redundant_parentheses.py @@ -81,9 +81,7 @@ def _check_parens_is_tuple(node, parens_coords): ) def checked_parentheses(self, coords) -> bool: - if coords in self.exceptions or coords[0] in self.problems: - return True - return False + return coords in self.exceptions or coords[0] in self.problems def check(self) -> None: msg = "PAR001: Too many parentheses" @@ -93,7 +91,7 @@ def check(self) -> None: ast.BinOp, ast.BoolOp, ast.UnaryOp, ast.Compare, ast.Await ) for node in ast.walk(self.tree): - breaker = None + breaker = False if isinstance(node, ast.Slice): for child in ast.iter_child_nodes(node): for coords in self.parens_coords: @@ -103,11 +101,11 @@ def check(self) -> None: == (child.lineno, child.col_offset) and isinstance(child, special_ops_pair_exceptions)): - breaker = 1 + breaker = True self.exceptions.append(coords) if breaker: break - if isinstance(node, special_ops_pair_exceptions): + elif isinstance(node, special_ops_pair_exceptions): for child in ast.iter_child_nodes(node): if not isinstance(child, special_ops_pair_exceptions): continue @@ -117,45 +115,45 @@ def check(self) -> None: continue if self._node_in_parens(child, coords): self.exceptions.append(coords) - breaker = 1 + breaker = True break if breaker: break - if isinstance(node, ast.Assign): + elif isinstance(node, ast.Assign): for target in node.targets: if not isinstance(target, ast.Tuple): continue - for elts in target.elts: - tuple_coords = (target.lineno, target.col_offset) - elts_coords = (elts.lineno, elts.col_offset) - if tuple_coords > elts_coords: + if not target.elts: + continue + elt = target.elts[0] + elt_coords = (elt.lineno, elt.col_offset) + matching_parens = None + for coords in self.parens_coords: + if self.checked_parentheses(coords): continue - for coords in self.parens_coords: - if self.checked_parentheses(coords): - continue - if (coords[0][1] <= tuple_coords[1] - and coords[0][0] == tuple_coords[0]): - self.exceptions.append(coords) - breaker = 1 - break - if not any( - self.file_tokens_nn[token].start == elts_coords - and self.file_tokens_nn[token - 1].string - == "(" - for token in range(len(self.file_tokens_nn)) - ): + if self._node_in_parens(elt, coords): + matching_parens = coords break - self.problems.append(( - node.lineno, node.col_offset, - "PAR002: Dont use parentheses for " - "unpacking" - )) - break - if breaker: + if not matching_parens: + continue + if not any( + self.file_tokens_nn[token].start == elt_coords + and self.file_tokens_nn[token - 1].string + == "(" + for token in range(len(self.file_tokens_nn)) + ): break - - if isinstance(node, ast.Tuple): + self.problems.append(( + node.lineno, node.col_offset, + "PAR002: Dont use parentheses for " + "unpacking" + )) + # no need to treat them again later + self.exceptions.append(matching_parens) + break + + elif isinstance(node, ast.Tuple): for coords in self.parens_coords: if self.checked_parentheses(coords): continue @@ -163,7 +161,7 @@ def check(self) -> None: self.exceptions.append(coords) break - if isinstance(node, ast.comprehension): + elif isinstance(node, ast.comprehension): for coords in self.parens_coords: if self.checked_parentheses(coords): continue @@ -172,7 +170,7 @@ def check(self) -> None: break if coords.open_[0] != coords.close[0]: self.exceptions.append(coords) - breaker = 1 + breaker = True break if breaker: break diff --git a/tests/test_redundant_parentheses.py b/tests/test_redundant_parentheses.py index d7d2fcb..ca9f26f 100644 --- a/tests/test_redundant_parentheses.py +++ b/tests/test_redundant_parentheses.py @@ -157,6 +157,22 @@ def test_ugly_multiline_unpacking(plugin): assert len(plugin(s)) == 1 +# GOOD (unpacking with line break) +def test_multiline_unpacking_implicit_tuple_literal(plugin): + s = """( +a, b +) = 1, 2""" + assert not plugin(s) + + +# GOOD (unpacking with line break) +def test_multiline_unpacking_explicit_tuple_literal(plugin): + s = """( +a, b +) = (1, 2)""" + assert not plugin(s) + + # GOOD (parentheses for tuple literal are optional) def test_tuple_literal_unpacking_in_if(plugin): s = """if foo: