Skip to content

internal error while formatting with-statement with tuple and none tuple argument #4642

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
15r10nk opened this issue Apr 2, 2025 · 18 comments · Fixed by #4646 · May be fixed by #4650
Open

internal error while formatting with-statement with tuple and none tuple argument #4642

15r10nk opened this issue Apr 2, 2025 · 18 comments · Fixed by #4646 · May be fixed by #4650
Labels
T: bug Something isn't working

Comments

@15r10nk
Copy link

15r10nk commented Apr 2, 2025

Describe the bug

The following code can not be parsed/formatted by black:

with (name_5, name_4), name_5:
    pass

(playground)

black reported the following error:

> black -l 100 -C -t py313 bug.py
error: cannot format bug.py: INTERNAL ERROR: Black 25.1.1.dev21+g944a38e.d20250317 on Python (CPython) 3.13.1 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_sxhcobwt.log

Oh no! 💥 💔 💥
1 file failed to reformat.

the reported diff in /tmp/blk_sxhcobwt.log is:

--- src
+++ dst
@@ -5,30 +5,29 @@
         Pass(
         )  # /Pass
         items=
         withitem(
             context_expr=
-            Tuple(
+            Name(
                 ctx=
                 Load(
                 )  # /Load
-                elts=
-                Name(
-                    ctx=
-                    Load(
-                    )  # /Load
-                    id=
-                    'name_5',  # str
-                )  # /Name
-                Name(
-                    ctx=
-                    Load(
-                    )  # /Load
-                    id=
-                    'name_4',  # str
-                )  # /Name
-            )  # /Tuple
+                id=
+                'name_5',  # str
+            )  # /Name
+            optional_vars=
+            None,  # NoneType
+        )  # /withitem
+        withitem(
+            context_expr=
+            Name(
+                ctx=
+                Load(
+                )  # /Load
+                id=
+                'name_4',  # str
+            )  # /Name
             optional_vars=
             None,  # NoneType
         )  # /withitem
         withitem(
             context_expr=

but it can be parsed by cpython:

from ast import parse
parse(
    'with (name_5, name_4), name_5:\n'
    '    pass\n'
)

The code can be formatted with black -l 100 -C -t py313 bug.py --fast:

with name_5, name_4, name_5:
    pass

Environment

  • Black's version: current main (950ec38)
  • OS and Python version: Linux/Python 3.13.1 (main, Jan 14 2025, 22:47:38) [Clang 19.1.6 ]

Additional context

The bug was found by pysource-codegen (see #3908)
The above problem description was created from a script,
let me know if you think it can be improved.

@15r10nk 15r10nk added the T: bug Something isn't working label Apr 2, 2025
@Pedro-Muller29
Copy link
Contributor

Pedro-Muller29 commented Apr 8, 2025

I believe this doesn't need a fix since this code will always fail at runtime, as tuples don't support the context manager protocol.

In fact, running a code such as:

with (open("a.txt", "r"), open("b.txt", "r")), open("c.txt", "r"):
    pass

Will throw the following error:

Traceback (most recent call last):
  File "/anon_path/with_tuple.py", line 1, in <module>
    with (open("a.txt", "r"), open("b.txt", "r")), open("c.txt", "r"):
         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
TypeError: 'tuple' object does not support the context manager protocol

@15r10nk
Copy link
Author

15r10nk commented Apr 8, 2025

To be honest, I don't like to create these kind of pedantic issues. But this are the things which are found by pysource-codegen.

I can understand when you do not want to fix it, but I have a example where this code can be useful.

def test_tuple_as_contextmanager():
    from contextlib import nullcontext

    try:
        with (nullcontext(),nullcontext()),nullcontext():
            pass
    except TypeError: 
        # test passed
        pass
    else:
        # this should be a type error
        assert False

Code like this could be used in tests of an python implementation. I have no real world example, but this is the only use case I can think of where this syntax is "useful".

@Pedro-Muller29
Copy link
Contributor

Fair point, I guess there is some niche where this would be useful. Working on a PR.

@JelleZijlstra
Copy link
Collaborator

I do think we should fix this sort of thing even if the code always fails—if nothing else so that we can continue fuzzing, because fuzzing is a good way to find bugs!

Thanks @15r10nk for doing this fuzzing and thanks @Pedro-Muller29 for the fix.

@15r10nk
Copy link
Author

15r10nk commented Apr 9, 2025

@Pedro-Muller29 I think your fix solves only 50% of the cases.

This is fixed:

with (a, b), c:
    pass

This not:

with c,(a, b):
    pass

I have looked at your merge request, but I can't see the problem.

@Pedro-Muller29
Copy link
Contributor

@Pedro-Muller29 I think your fix solves only 50% of the cases.

This is fixed:

with (a, b), c:
    pass

This not:

with c,(a, b):
    pass

I have looked at your merge request, but I can't see the problem.

Nice catch! Yeah, it's weird that my PR didn't fix that since it looks for commas both ways. I can try to fix it this weekend.

@JelleZijlstra JelleZijlstra reopened this Apr 10, 2025
@Pedro-Muller29
Copy link
Contributor

I believe the initial PR I opened already handles both cases correctly.

To confirm this, I’m opening a follow-up PR that adds some additional test cases, including one for c, (a, b).

@15r10nk
Copy link
Author

15r10nk commented Apr 12, 2025

@15r10nk
Copy link
Author

15r10nk commented Apr 12, 2025

I test it locally with python -m black example.py all inside a fresh venv with black installed.

example.py

with c, (a, b): 
    pass

@15r10nk
Copy link
Author

15r10nk commented Apr 12, 2025

This is interresting:

❯ pytest
=========================== test session starts ===========================
platform linux -- Python 3.12.6, pytest-8.3.5, pluggy-1.5.0
rootdir: /home/frank/projects/black
configfile: pyproject.toml
plugins: cov-6.1.1, xdist-3.6.1
collected 390 items / 1 skipped                                           

tests/test_black.py ............................................... [ 12%]
................................................................... [ 29%]
...............................                                     [ 37%]
tests/test_blackd.py ....................                           [ 42%]
tests/test_docs.py .                                                [ 42%]
tests/test_format.py .............................................. [ 54%]
................................................................... [ 71%]
................................................................... [ 88%]
.......                                                             [ 90%]
tests/test_no_ipynb.py ss                                           [ 91%]
tests/test_ranges.py ...............................                [ 98%]
tests/test_schema.py .                                              [ 99%]
tests/test_tokenize.py ..                                           [ 99%]
tests/test_trans.py .                                               [100%]

===================== 388 passed, 3 skipped in 57.48s =====================

black on  test-cases-tuple-with-stmt [?] via 🐍 v3.12.6 (black) took 57s 
❯ python -m black tests/data/cases/tuple_with_stmt.py
error: cannot format tests/data/cases/tuple_with_stmt.py: INTERNAL ERROR: B
lack 25.1.1.dev28+gaf53269 on Python (CPython) 3.12.6 produced code that is
 not equivalent to the source.  Please report a bug on https://github.com/p
sf/black/issues.  This diff might be helpful: /tmp/blk_p2hno1g7.log

Oh no! 💥 💔 💥
1 file failed to reformat.

The tests pass but I can not format the test file which you have created @Pedro-Muller29

@Pedro-Muller29
Copy link
Contributor

Pedro-Muller29 commented Apr 12, 2025

sorry, but I tested it again on my main and it did not work.

I think this playground link uses the current main.

https://black.vercel.app/?version=main&state=_Td6WFoAAATm1rRGAgAhARYAAAB0L-Wj4ACgAGRdAD2IimZxl1N_WmufDs0Loq3TUzq5FqllsBRV4UAXQkscnsrAoYdPVTl2gXdaxC3BLyx0rgHGpoUGUVahow_D3BrCuhNmnSe56aCmDhKpAWzM70HZtFjdwniaZzpqcYmOh6OgnwAACIDdvK-640cAAYABoQEAAOL9kBSxxGf7AgAAAAAEWVo=

I can not explain why it works in CI.

I think this playground link is using the stable version (which don't have my changes yet). I tried switching it to main and it worked.

Here’s the link to the working version

@15r10nk
Copy link
Author

15r10nk commented Apr 12, 2025

Does python -m black tests/data/cases/tuple_with_stmt.py also work for you?

@Pedro-Muller29
Copy link
Contributor

I test it locally with python -m black example.py all inside a fresh venv with black installed.

example.py

with c, (a, b):
pass

This is interresting:

❯ pytest
=========================== test session starts ===========================
platform linux -- Python 3.12.6, pytest-8.3.5, pluggy-1.5.0
rootdir: /home/frank/projects/black
configfile: pyproject.toml
plugins: cov-6.1.1, xdist-3.6.1
collected 390 items / 1 skipped                                           

tests/test_black.py ............................................... [ 12%]
................................................................... [ 29%]
...............................                                     [ 37%]
tests/test_blackd.py ....................                           [ 42%]
tests/test_docs.py .                                                [ 42%]
tests/test_format.py .............................................. [ 54%]
................................................................... [ 71%]
................................................................... [ 88%]
.......                                                             [ 90%]
tests/test_no_ipynb.py ss                                           [ 91%]
tests/test_ranges.py ...............................                [ 98%]
tests/test_schema.py .                                              [ 99%]
tests/test_tokenize.py ..                                           [ 99%]
tests/test_trans.py .                                               [100%]

===================== 388 passed, 3 skipped in 57.48s =====================

black on  test-cases-tuple-with-stmt [?] via 🐍 v3.12.6 (black) took 57s 
❯ python -m black tests/data/cases/tuple_with_stmt.py
error: cannot format tests/data/cases/tuple_with_stmt.py: INTERNAL ERROR: B
lack 25.1.1.dev28+gaf53269 on Python (CPython) 3.12.6 produced code that is
 not equivalent to the source.  Please report a bug on https://github.com/p
sf/black/issues.  This diff might be helpful: /tmp/blk_p2hno1g7.log

Oh no! 💥 💔 💥
1 file failed to reformat.

The tests pass but I can not format the test file which you have created @Pedro-Muller29

Yeah, this is really strange. It’s almost like pytest isn’t treating a formatting failure as a test failure.

I was planning to create a test file that intentionally fails formatting just to check if pytest was behaving correctly. But then I switched back to the same venv I was using earlier and tried formatting the tuple_with_stmt.py file—and it failed. No idea why, especially since it worked just an hour ago in this exact same environment.

@Pedro-Muller29
Copy link
Contributor

Does python -m black tests/data/cases/tuple_with_stmt.py also work for you?

It was working when I opened my PR but now it didn't in the same venv, and I have no idea why

@15r10nk
Copy link
Author

15r10nk commented Apr 12, 2025

I also have no idea what is going on here. But thank you for verifying that I'm not crazy.

@JelleZijlstra
Copy link
Collaborator

Is it possible that we don't check for AST equivalence in the test suite?

@Pedro-Muller29
Copy link
Contributor

Maybe, but that still wouldn't explain the playground link I sent working

@15r10nk
Copy link
Author

15r10nk commented Apr 13, 2025

The link stops working when you deselect 3.8 https://black.vercel.app/?version=main&state=_Td6WFoAAATm1rRGAgAhARYAAAB0L-Wj4ADFAHBdAD2IimZxl1N_WmufDs0Loq3S0OujgtCoa6AGvvQPuvBGmbyr-KSEqr0X_j0sQDbLR8QE1fGl0ja2t5zbln7xaJ-krx95VW_XNCZly-Coo8Klf9vqRtHuK6UufVN_zebQwK4Y4axfwmji8lXDui_kF2AAFrLd_oOX-YEAAYwBxgEAACPyo8WxxGf7AgAAAAAEWVo=

The formatting fails when the target version is > 3.8

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
T: bug Something isn't working
Projects
None yet
3 participants