From 808754af18f1cea2444c6facf63f3a86952c5d44 Mon Sep 17 00:00:00 2001
From: =?utf8?q?=C5=81ukasz=20Langa?= <lukasz@langa.pl>
Date: Mon, 21 May 2018 15:20:19 -0700
Subject: [PATCH] Fix optional parentheses being removed within `# fmt: off`
 sections

Fixes #224
---
 README.md         |  2 ++
 black.py          |  7 ++++++-
 tests/fmtonoff.py | 17 +++++++++++++++--
 3 files changed, 23 insertions(+), 3 deletions(-)

diff --git a/README.md b/README.md
index b11f7e0..9b42f20 100644
--- a/README.md
+++ b/README.md
@@ -659,6 +659,8 @@ More details can be found in [CONTRIBUTING](CONTRIBUTING.md).
 * fixed invalid code produced when standalone comments were present in a trailer
   that was omitted from line splitting on a large expression (#237)
 
+* fixed optional parentheses being removed within `# fmt: off` sections (#224)
+
 
 ### 18.5b0
 
diff --git a/black.py b/black.py
index 8d55164..9fbacc1 100644
--- a/black.py
+++ b/black.py
@@ -1820,7 +1820,7 @@ def is_split_before_delimiter(leaf: Leaf, previous: Leaf = None) -> int:
     return 0
 
 
-def generate_comments(leaf: Leaf) -> Iterator[Leaf]:
+def generate_comments(leaf: LN) -> Iterator[Leaf]:
     """Clean the prefix of the `leaf` and generate comments from it, if any.
 
     Comments in lib2to3 are shoved into the whitespace prefix.  This happens
@@ -2337,6 +2337,11 @@ def normalize_invisible_parens(node: Node, parens_after: Set[str]) -> None:
     Standardizes on visible parentheses for single-element tuples, and keeps
     existing visible parentheses for other tuples and generator expressions.
     """
+    try:
+        list(generate_comments(node))
+    except FormatOff:
+        return  # This `node` has a prefix with `# fmt: off`, don't mess with parens.
+
     check_lpar = False
     for child in list(node.children):
         if check_lpar:
diff --git a/tests/fmtonoff.py b/tests/fmtonoff.py
index 0ff6672..e76e6a7 100644
--- a/tests/fmtonoff.py
+++ b/tests/fmtonoff.py
@@ -50,6 +50,11 @@ def long_lines():
         typedargslist.extend(
             gen_annotated_params(ast_args.kwonlyargs, ast_args.kw_defaults, parameters, implicit_default=True)
         )
+        # fmt: off
+        a = (
+            unnecessary_bracket()
+        )
+        # fmt: on
     _type_comment_re = re.compile(
         r"""
         ^
@@ -69,8 +74,10 @@ def long_lines():
             \n?
         )
         $
-        """, re.MULTILINE | re.VERBOSE
+        """, # fmt: off
+        re.MULTILINE | re.VERBOSE
     )
+    # fmt: on
 def single_literal_yapf_disable():
     """Black does not support this."""
     BAZ = {
@@ -163,6 +170,11 @@ def long_lines():
                 implicit_default=True,
             )
         )
+        # fmt: off
+        a = (
+            unnecessary_bracket()
+        )
+        # fmt: on
     _type_comment_re = re.compile(
         r"""
         ^
@@ -182,9 +194,10 @@ def long_lines():
             \n?
         )
         $
-        """,
+        """,  # fmt: off
         re.MULTILINE | re.VERBOSE,
     )
+    # fmt: on
 
 
 def single_literal_yapf_disable():
-- 
2.39.5