Search code examples
pythonsyntax-errormypyf-stringpython-3.12

Mypy throws syntax error for multi-line f-strings, despite code running without error


I'm working with Python 3.12 and recently added mypy type-checking to my project. I've encountered an odd issue where mypy throws a syntax error for certain f-strings in my code, specifically those with newline characters in the middle of the f-string. The curious thing is that the Python interpreter doesn’t complain at all and runs the code just fine.

Here’s a simplified example of the kind of f-string that mypy flags as a syntax error:

name = "Alice"
message = f"Hello, {name
}, welcome!"

Mypy error:

mypy minimal_reproducible_example.py
src/loculus_preprocessing/alice.py:2: error: unterminated string literal (detected at line 2)  [syntax]
Found 1 error in 1 file (errors prevented further checking)

Even adding --python-version 3.12 doesn't fix it.

I understand that using triple quotes (""") for multi-line strings is recommended, but in this case, the code works in the interpreter without issue, while mypy consistently fails with a syntax error.

My questions:

  1. Why does mypy consider this a syntax error, even though Python 3.12 accepts it?
  2. Is this a limitation of mypy, or am I overlooking something in Python's syntax that could lead to issues?

Solution

  • mypy uses the standard library's ast module to parse Python code, so mypy can't parse Python 3.12 code (including multiline f-strings) if it's installed on Python 3.11 or below. As for why the command-line options don't help the issue, none of them actually changes the Python version used by mypy:

    • --python-version=3.12: This is relevant to sys.version_info guards so that mypy knows the correct blocks of code to analyse for code written for multiple Python versions:

      import sys
      
      if sys.version_info >= (3, 12):
          # PEP 698 - only available in the standard library's `typing` module
          # from Python 3.12 onwards. This block of code is only reached for analysis if:
          #  1. you're actually running mypy under Python >= 3.12, or
          #  2. you've added `--python-version=3.12` when running mypy on Python 3.11 and below.
          from typing import override
      else:
          # mypy will warn you of errors here if you *don't* add
          # `--python-version=3.12` and you're running mypy on Python 3.11 or below.
          from typing import override  # ✘ needs import from `typing_extensions`
      
    • --python-executable: This is for discovering the virtual environment, and thus the 3rd-party packages that are available for importing when type-checking your code.

      By default, mypy uses the same virtual environment as it is installed on, so things usually "just work" if you pip install mypy.

      Pointing --python-executable to a Python 3.12 executable with a mypy installed on Python 3.11 will make mypy attempt to use Python 3.11's ast to parse 3rd party libraries installed on the Python 3.12's virtual environment. If any of the packages have multiline f-strings, then the analysis will fail with a syntax error.