15r10nk / inline-snapshot

create and update inline snapshots in your python tests
https://15r10nk.github.io/inline-snapshot/
MIT License
343 stars 10 forks source link

Allow formatting with ruff instead of black #58

Open alexmojaki opened 3 months ago

alexmojaki commented 3 months ago

Every time I fix snapshots, I immediately have to press a few more keys to reformat with ruff. It'd be lovely if it was already formatted 'correctly'.

15r10nk commented 3 months ago

It preserves currently the black formatting (https://15r10nk.github.io/inline-snapshot/#code-generation point 5).

I think the problem is that it does not detect that your code is formatted with "black style". black and ruff should format the code in the similar 99% like way.

Maybe your files are part of this 1%, or there are some black or ruff options which are responsible for the different formatting.

Maybe this info helps you.

The other option for me would be to preform the same check and formatting with ruff, which I do with black.

alexmojaki commented 3 months ago
  1. Something is wrong with the black formatting and indentation.
  2. We use single quotes for strings, so ruff and black format differently.
$ cat /home/alex/.config/JetBrains/PyCharm2023.3/scratches/scratch_1906.py                         
from inline_snapshot import snapshot

def test_example():
    foo = [dict.fromkeys(range(6), 'abcdefghijkl') for _ in range(2)]
    assert foo == snapshot(0)

$ pytest --inline-snapshot=fix /home/alex/.config/JetBrains/PyCharm2023.3/scratches/scratch_1906.py
=============================================================================== test session starts ===============================================================================
platform linux -- Python 3.12.1, pytest-7.4.3, pluggy-1.3.0
rootdir: /home/alex
plugins: xdist-3.5.0, anyio-4.2.0, base-url-2.0.0, hypothesis-6.92.2, logfire-0.22.0, playwright-0.4.3, devtools-0.12.2, inline-snapshot-0.7.0, django-4.8.0, timeout-2.2.0, requests-mock-1.11.0, pretty-1.2.0
collected 1 item                                                                                                                                                                  

../../.config/JetBrains/PyCharm2023.3/scratches/scratch_1906.py .                                                                                                           [100%]
================================================================================= inline snapshot =================================================================================
fixed 1 snapshots
Results (0.15s):
         1 passed

$ cat /home/alex/.config/JetBrains/PyCharm2023.3/scratches/scratch_1906.py
from inline_snapshot import snapshot

def test_example():
    foo = [dict.fromkeys(range(6), 'abcdefghijkl') for _ in range(2)]
    assert foo == snapshot([
    {
        0: "abcdefghijkl",
        1: "abcdefghijkl",
        2: "abcdefghijkl",
        3: "abcdefghijkl",
        4: "abcdefghijkl",
        5: "abcdefghijkl",
    },
    {
        0: "abcdefghijkl",
        1: "abcdefghijkl",
        2: "abcdefghijkl",
        3: "abcdefghijkl",
        4: "abcdefghijkl",
        5: "abcdefghijkl",
    },
])

$ black /home/alex/.config/JetBrains/PyCharm2023.3/scratches/scratch_1906.py
reformatted /home/alex/.config/JetBrains/PyCharm2023.3/scratches/scratch_1906.py

All done! ✨ 🍰 ✨
1 file reformatted.

$ cat /home/alex/.config/JetBrains/PyCharm2023.3/scratches/scratch_1906.py
from inline_snapshot import snapshot

def test_example():
    foo = [dict.fromkeys(range(6), "abcdefghijkl") for _ in range(2)]
    assert foo == snapshot(
        [
            {
                0: "abcdefghijkl",
                1: "abcdefghijkl",
                2: "abcdefghijkl",
                3: "abcdefghijkl",
                4: "abcdefghijkl",
                5: "abcdefghijkl",
            },
            {
                0: "abcdefghijkl",
                1: "abcdefghijkl",
                2: "abcdefghijkl",
                3: "abcdefghijkl",
                4: "abcdefghijkl",
                5: "abcdefghijkl",
            },
        ]
    )

$ ruff format /home/alex/.config/JetBrains/PyCharm2023.3/scratches/scratch_1906.py
1 file reformatted

$ cat /home/alex/.config/JetBrains/PyCharm2023.3/scratches/scratch_1906.py
from inline_snapshot import snapshot

def test_example():
    foo = [dict.fromkeys(range(6), 'abcdefghijkl') for _ in range(2)]
    assert foo == snapshot(
        [
            {
                0: 'abcdefghijkl',
                1: 'abcdefghijkl',
                2: 'abcdefghijkl',
                3: 'abcdefghijkl',
                4: 'abcdefghijkl',
                5: 'abcdefghijkl',
            },
            {
                0: 'abcdefghijkl',
                1: 'abcdefghijkl',
                2: 'abcdefghijkl',
                3: 'abcdefghijkl',
                4: 'abcdefghijkl',
                5: 'abcdefghijkl',
            },
        ]
    )
15r10nk commented 3 months ago

The problem is that ruff formats differently than black in your case (double/single quotes).

inline-snapshot looks at the source before it changes anything and thinks "this is not formatted with black". It re-formats only the snapshots with black (this causes the double quotes in your snapshots) and skips the reformatting of the whole file.

I think you have


[tool.ruff.format]
quote-style = "single"

in your config.

I will think about a way to handle the formatting better. Maybe a enforce-format="black|ruff" option, maybe integrating ruff in the same way I integrated black.

What would you prefer?

alexmojaki commented 3 months ago

OK, I see now how this happens:

def test_example():
    foo = [dict.fromkeys(range(6), 'abcdefghijkl') for _ in range(2)]
    assert foo == snapshot([
    {
        0: "abcdefghijkl",
        1: "abcdefghijkl",
        2: "abcdefghijkl",
        3: "abcdefghijkl",
        4: "abcdefghijkl",
        5: "abcdefghijkl",
    },
    {
        0: "abcdefghijkl",
        1: "abcdefghijkl",
        2: "abcdefghijkl",
        3: "abcdefghijkl",
        4: "abcdefghijkl",
        5: "abcdefghijkl",
    },
])

as a result of only formatting the snapshot. I do think that could be improved so that the result is still nice for people who are not using any kind of formatter. Specifically, I think you should black/ruff-format the snapshot call node (not just the contents) and then indent that by the amount of indentation in the containin statement (4 spaces in this case).

I will think about a way to handle the formatting better. Maybe a enforce-format="black|ruff" option, maybe integrating ruff in the same way I integrated black.

What would you prefer?

I'm not sure I understand the difference between the two options. If both are integrated, there will probably need to be a config option to pick one. You could maybe default to using the one that seems closest to the file's current formatting. If it's a tie, I would suggest choosing ruff just because it's faster.

15r10nk commented 3 months ago

Formating source ranges is not supported by black https://github.com/psf/black/issues/134#issuecomment-381726788

I can fix the indentation and make it look acceptable if you are not using black for your whole file. But the indentation affects the formatting and this solution will probably never be perfect.

Detecting if a file is formatted and preserving this formatting is easier. This is what inline-snapshot currently does with black.

You can format your example with black before you fix your snapshot. Inline-snapshot will format the whole file after the fix and preserve the formatting.

I think I can implement the same behavior for ruff formatted files.

alexmojaki commented 3 months ago

To be clear, for the case of formatting just the snapshot, I'm only talking about it for the sake of interest and because I imagine you'd like it to work better for users not using black/ruff. I'm happy with just formatting the whole file with ruff.

I'm not suggesting formatting source ranges. I'm saying instead of using black to format [{...}], format snapshot([{...}]), and then indent that.