From 37c9f605259c686daeb9c00f33df886cd83b3d1c Mon Sep 17 00:00:00 2001
From: Anthony Sottile <asottile@umich.edu>
Date: Sun, 4 Aug 2024 16:16:13 -0400
Subject: [PATCH] adjust logical line for FSTRING_MIDDLE brace escaping

---
 pycodestyle.py            |  5 ++++-
 tests/test_pycodestyle.py | 19 +++++++++++++++++++
 2 files changed, 23 insertions(+), 1 deletion(-)

diff --git a/pycodestyle.py b/pycodestyle.py
index 19f88c2a..4df30f62 100755
--- a/pycodestyle.py
+++ b/pycodestyle.py
@@ -1949,7 +1949,10 @@ def build_tokens_line(self):
             if token_type == tokenize.STRING:
                 text = mute_string(text)
             elif token_type == FSTRING_MIDDLE:  # pragma: >=3.12 cover
-                text = 'x' * len(text)
+                # fstring tokens are "unescaped" braces -- re-escape!
+                brace_count = text.count('{') + text.count('}')
+                text = 'x' * (len(text) + brace_count)
+                end = (end[0], end[1] + brace_count)
             if prev_row:
                 (start_row, start_col) = start
                 if prev_row != start_row:    # different row
diff --git a/tests/test_pycodestyle.py b/tests/test_pycodestyle.py
index 8885f0d8..444d59f0 100644
--- a/tests/test_pycodestyle.py
+++ b/tests/test_pycodestyle.py
@@ -1,5 +1,10 @@
+import io
+import sys
+import tokenize
+
 import pytest
 
+from pycodestyle import Checker
 from pycodestyle import expand_indent
 from pycodestyle import mute_string
 
@@ -27,3 +32,17 @@ def test_expand_indent(s, expected):
 )
 def test_mute_string(s, expected):
     assert mute_string(s) == expected
+
+
+def test_fstring_logical_line():
+    src = '''\
+f'hello {{ {thing} }} world'
+'''
+    checker = Checker(lines=src.splitlines())
+    checker.tokens = list(tokenize.generate_tokens(io.StringIO(src).readline))
+    checker.build_tokens_line()
+
+    if sys.version_info >= (3, 12):  # pragma: >3.12 cover
+        assert checker.logical_line == "f'xxxxxxxxx{thing}xxxxxxxxx'"
+    else:
+        assert checker.logical_line == "f'xxxxxxxxxxxxxxxxxxxxxxxxx'"