psf / black

The uncompromising Python code formatter
https://black.readthedocs.io/en/stable/
MIT License
39.21k stars 2.48k forks source link

INTERNAL ERROR: Black produced code that is not equivalent to the source #4481

Open ryank231231 opened 1 month ago

ryank231231 commented 1 month ago

Describe the bug

Formatting the code snippet below produces an internal error.

To Reproduce

Format the following code snippet:

foo(
    bar()    # fmt: skip
  + bar()    # fmt: skip
)

The resulting error is:

INTERNAL ERROR: Black 24.10.1.dev3+g9995bff on Python (CPython) 3.12.5 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_vu09qp1g.log

Log:

INTERNAL ERROR: Black 24.10.1.dev3+g9995bff on Python (CPython) 3.12.5 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_vu09qp1g.log

--- src
+++ dst
@@ -2,41 +2,22 @@
     body=
     Expr(
         value=
         Call(
             args=
-            BinOp(
-                left=
-                Call(
-                    args=
-                    func=
-                    Name(
-                        ctx=
-                        Load(
-                        )  # /Load
-                        id=
-                        'bar',  # str
-                    )  # /Name
-                    keywords=
-                )  # /Call
-                op=
-                Add(
-                )  # /Add
-                right=
-                Call(
-                    args=
-                    func=
-                    Name(
-                        ctx=
-                        Load(
-                        )  # /Load
-                        id=
-                        'bar',  # str
-                    )  # /Name
-                    keywords=
-                )  # /Call
-            )  # /BinOp
+            Call(
+                args=
+                func=
+                Name(
+                    ctx=
+                    Load(
+                    )  # /Load
+                    id=
+                    'bar',  # str
+                )  # /Name
+                keywords=
+            )  # /Call
             func=
             Name(
                 ctx=
                 Load(
                 )  # /Load

Expected behavior

The lines should be left untouched.

Environment

Can also be reproduced on https://black.vercel.app/?version=main&state=_Td6WFoAAATm1rRGAgAhARYAAAB0L-Wj4ADHAGZdAD2IimZxl1N_WlkOE2WKO8q04xFLHliTY02DINaThb5NmXp3LURBIVoPx1hVWZbZyQHzS7z4xwAjKn-Mq7mHuHTZTmuDTTSwvMjOpdZEDkCdYF5Qlw2K-7fLc9LT6VrsBXcNyBZpZgAAANIVr-TpMphLAAGCAcgBAABg5HYfscRn-wIAAAAABFla .

hauntsaninja commented 1 month ago

Black 24.10.1.dev3+g9995bff

Hmm, we should ban running on 3.12.5 as of https://github.com/psf/black/pull/4447 , because an upstream memory safety issue can cause AST safety checks to fail. Do you have a local repro, if so, could you figure out why the check against 3.12.5 isn't working for you?

Looks like the issue is unrelated to that, though. Probably a duplicate of https://github.com/psf/black/issues/3608

ryank231231 commented 1 month ago

Hmm, we should ban running on 3.12.5 as of #4447, because an upstream memory safety issue can cause AST safety checks to fail.

Just to clarify, Python 3.12.5 was being used in the playground, and I was able to reproduce the same issue locally as well. My local setup uses Black 24.10.0 and CPython 3.11.2.

Do you have a local repro? If so, could you figure out why the check against 3.12.5 isn't working for you?

Yes, I was able to reproduce the issue locally with Black 24.10.0 and CPython 3.11.2. The same internal error occurs with the provided code snippet.

ssbarnea commented 2 hours ago

Another piece of code that seems to hit this bug (happens at least with 3.11-3.13):

Mode(target_versions={<TargetVersion.PY310: 10>}, line_length=88, string_normalization=True, is_pyi=False, is_ipynb=False, skip_source_first_line=False, magic_trailing_comma=True, python_cell_magics=set(), preview=False, unstable=False, enabled_features=set())
--- source
+++ first pass
@@ -261,12 +261,13 @@
 def set_collections_basedir(basedir: Path) -> None:
     """Set the playbook directory as playbook_paths for the collection loader."""
     # Ansible expects only absolute paths inside `playbook_paths` and will
     # produce weird errors if we use a relative one.
     # pyright: ignore[reportAttributeAccessIssue]
-    AnsibleCollectionConfig.playbook_paths = (  # pyright: ignore[reportAttributeAccessIssue]
-        str(basedir.resolve()))
+    AnsibleCollectionConfig.playbook_paths = (
+        str(basedir.resolve())  # pyright: ignore[reportAttributeAccessIssue]
+    )

 def template(
     basedir: Path,
     value: Any,
@@ -959,19 +960,23 @@
                         raise RuntimeError(msg)
     else:
         yield from each_entry(data, position)

-def add_action_type(actions: AnsibleBaseYAMLObject, action_type: str) -> AnsibleSequence:
+def add_action_type(
+    actions: AnsibleBaseYAMLObject, action_type: str
+) -> AnsibleSequence:
     """Add action markers to task objects."""
     results = AnsibleSequence()
     if isinstance(actions, Iterable):
         for action in actions:
             # ignore empty task
             if not action:
                 continue
-            action["__ansible_action_type__"] = BLOCK_NAME_TO_ACTION_TYPE_MAP[action_type]
+            action["__ansible_action_type__"] = BLOCK_NAME_TO_ACTION_TYPE_MAP[
+                action_type
+            ]
             results.append(action)
     return results

 @cache
@@ -998,11 +1003,13 @@
     # signature of AnsibleConstructor.construct_mapping
     def construct_mapping(
         node: yaml.MappingNode,
         deep: bool = False,  # noqa: FBT002
     ) -> AnsibleMapping:
-        mapping = AnsibleConstructor.construct_mapping(loader, node, deep=deep)  # pyright: ignore[reportArgumentType]
+        mapping = AnsibleConstructor.construct_mapping(
+            loader, node, deep=deep
+        )  # pyright: ignore[reportArgumentType]
         if hasattr(node, LINE_NUMBER_KEY):
             mapping[LINE_NUMBER_KEY] = getattr(node, LINE_NUMBER_KEY)
         else:
             mapping[LINE_NUMBER_KEY] = mapping._line_number  # noqa: SLF001
         mapping[FILENAME_KEY] = lintable.path
--- first pass
+++ second pass
@@ -261,13 +261,13 @@
 def set_collections_basedir(basedir: Path) -> None:
     """Set the playbook directory as playbook_paths for the collection loader."""
     # Ansible expects only absolute paths inside `playbook_paths` and will
     # produce weird errors if we use a relative one.
     # pyright: ignore[reportAttributeAccessIssue]
-    AnsibleCollectionConfig.playbook_paths = (
-        str(basedir.resolve())  # pyright: ignore[reportAttributeAccessIssue]
-    )
+    AnsibleCollectionConfig.playbook_paths = str(
+        basedir.resolve()
+    )  # pyright: ignore[reportAttributeAccessIssue]

 def template(
     basedir: Path,
     value: Any,

Still, in my case, I have no case of using no fmt comments.