Repository: psf/black

Issue:
Internal error on a specific file
<!--
Please make sure that the bug is not already fixed either in newer versions or the
current development version. To confirm this, you have three options:

1. Update Black's version if a newer release exists: `pip install -U black`
2. Use the online formatter at <https://black.vercel.app/?version=main>, which will use
   the latest main branch.
3. Or run _Black_ on your machine:
   - create a new virtualenv (make sure it's the same Python version);
   - clone this repository;
   - run `pip install -e .[d]`;
   - run `pip install -r test_requirements.txt`
   - make sure it's sane by running `python -m pytest`; and
   - run `black` like you did last time.
-->

**Describe the bug**
`black` reports an internal error when formatting a specific file.

```
error: cannot format /home/nicpa/codes/sisl/src/sisl_toolbox/siesta/minimizer/_metric_siesta.py: INTERNAL ERROR: Black produced code that is not equivalent to the source.  Please report a bug on https://github.com/psf/black/issues.  This diff might be helpful: /tmp/blk__3mh1ucd.log
```

<!-- A clear and concise description of what the bug is. -->

**To Reproduce**

1. Download [this file](https://github.com/zerothi/sisl/blob/5a63302b57fcb38d7460507bf000f077655ac664/src/sisl_toolbox/siesta/minimizer/_metric_siesta.py)
2. Run `black` on the file. (I have done `pip install -U black` as of today!)

My pyproject.toml configuration file has this:
```toml
[tool.black]
line-length = 88
target-version = ["py38", "py39", "py310", "py311", "py312"]
```

The resulting error is:
```
error: cannot format /home/nicpa/codes/sisl/src/sisl_toolbox/siesta/minimizer/_metric_siesta.py: INTERNAL ERROR: Black produced code that is not equivalent to the source.  Please report a bug on https://github.com/psf/black/issues.  This diff might be helpful: /tmp/blk__3mh1ucd.log
```

Here is the attached diff-log file:
```diff
--- src
+++ dst
@@ -2307,16 +2307,10 @@
                   body=
                     Expr(
                       value=
                         JoinedStr(
                           values=
-                            Constant(
-                              kind=
-                                None,  # NoneType
-                              value=
-                                '',  # str
-                            )  # /Constant
                             FormattedValue(
                               conversion=
                                 -1,  # int
                               format_spec=
                                 None,  # NoneType
@@ -3263,16 +3257,10 @@
                   body=
                     Expr(
                       value=
                         JoinedStr(
                           values=
-                            Constant(
-                              kind=
-                                None,  # NoneType
-                              value=
-                                '',  # str
-                            )  # /Constant
                             FormattedValue(
                               conversion=
                                 -1,  # int
                               format_spec=
                                 None,  # NoneType
@@ -4273,16 +4261,10 @@
                   body=
                     Expr(
                       value=
                         JoinedStr(
                           values=
-                            Constant(
-                              kind=
-                                None,  # NoneType
-                              value=
-                                '',  # str
-                            )  # /Constant
                             FormattedValue(
                               conversion=
                                 -1,  # int
                               format_spec=
                                 None,  # NoneType
```

**Expected behavior**

<!-- A clear and concise description of what you expected to happen. -->

**Environment**

<!-- Please complete the following information: -->

- Black's version: <!-- e.g. [main] -->
  > black --version                                                          
  > black, 23.10.1 (compiled: yes)
  > Python (CPython) 3.11.6

- OS and Python version: Linux/debian, Python 3.11.6


Discussion:
Thanks for the report! Minimised repro:
```
def foo(energy_str):
    f""" {'.'.join(energy_str)} metric """
```
> Thanks for the report! Minimised repro:
> 
> ```
> def foo(energy_str):
>     f""" {'.'.join(energy_str)} metric """
> ```

thanks for hunting this down, that was beyond my expertise! :)

Resolving patch:
diff --git a/src/black/nodes.py b/src/black/nodes.py
--- a/src/black/nodes.py
+++ b/src/black/nodes.py
@@ -529,7 +529,7 @@ def is_docstring(leaf: Leaf) -> bool:
         return False
 
     prefix = get_string_prefix(leaf.value)
-    if "b" in prefix or "B" in prefix:
+    if set(prefix).intersection("bBfF"):
         return False
 
     if prev_siblings_are(


Test patch:
diff --git a/tests/data/cases/docstring_preview.py b/tests/data/cases/docstring_preview.py
--- a/tests/data/cases/docstring_preview.py
+++ b/tests/data/cases/docstring_preview.py
@@ -58,7 +58,8 @@ def docstring_almost_at_line_limit():
 
 
 def docstring_almost_at_line_limit_with_prefix():
-    f"""long docstring................................................................"""
+    f"""long docstring................................................................
+    """
 
 
 def mulitline_docstring_almost_at_line_limit():
diff --git a/tests/data/cases/f_docstring.py b/tests/data/cases/f_docstring.py
new file mode 100644
--- /dev/null
+++ b/tests/data/cases/f_docstring.py
@@ -0,0 +1,20 @@
+def foo(e):
+    f""" {'.'.join(e)}"""
+
+def bar(e):
+    f"{'.'.join(e)}"
+
+def baz(e):
+    F""" {'.'.join(e)}"""
+
+# output
+def foo(e):
+    f""" {'.'.join(e)}"""
+
+
+def bar(e):
+    f"{'.'.join(e)}"
+
+
+def baz(e):
+    f""" {'.'.join(e)}"""
diff --git a/tests/data/cases/docstring_preview_no_string_normalization.py b/tests/data/cases/preview_docstring_no_string_normalization.py
similarity index 100%
rename from tests/data/cases/docstring_preview_no_string_normalization.py
rename to tests/data/cases/preview_docstring_no_string_normalization.py