Repository: koxudaxi/datamodel-code-generator

Issue:
Produces invalid Python syntax from some multi-line GraphQL comments
**Describe the bug**
datamodel-code-generator creates invalid Python syntax if multi-line comments are present on some GraphQL items.

This was discovered on the Shopify API and a minimal reproduction is below.

Workaround: Disable the default `black` formatter (which discovers the syntax error and exits) using `datamodel-codegen --formatters isort`. This allows outputting the broken Python, so then you can fix 32000 lines of code by hand.

**To Reproduce**

Example schema:
```graphql
"""
Test
What's up?
"""
type Type1 {
  id: ID!
}

"""
Test
What's up?
"""
type Type2 {
  id: ID!
}

"""
Test
What's going on?
"""
union Union1 = Type1 | Type2

```

Used commandline:
```
$ poetry run datamodel-codegen --input invalid.graphql --input-file-type graphql --output invalid.py
```

**Expected behavior**
Should produce:
```python
class Type1(BaseModel):
    """
    Test
    What's up?
    """
    id: ID
    typename__: Optional[Literal['Type1']] = Field('Type1', alias='__typename')

class Type2(BaseModel):
    """
    Test
    What's up?
    """
    id: ID
    typename__: Optional[Literal['Type2']] = Field('Type2', alias='__typename')

"""
Test
What's going on?
"""
Union1: TypeAlias = Union[
    'Type1',
    'Type2',
]
```

Actually produces:
```python
class Type1(BaseModel):
    """
    Test
    What's up?
    """
    id: ID
    typename__: Optional[Literal['Type1']] = Field('Type1', alias='__typename')

class Type2(BaseModel):
    """
    Test
    What's up?
    """
    id: ID
    typename__: Optional[Literal['Type2']] = Field('Type2', alias='__typename')

# Test
What's going on?
Union1: TypeAlias = Union[
    'Type1',
    'Type2',
]
```

**Version:**
 - OS: Arch Linux
 - Python version: 3.13
 - datamodel-code-generator version: 0.28.4



Discussion:

Patch:
diff --git a/src/datamodel_code_generator/model/template/Union.jinja2 b/src/datamodel_code_generator/model/template/Union.jinja2
index 7bfccda89..4ea482c49 100644
--- a/src/datamodel_code_generator/model/template/Union.jinja2
+++ b/src/datamodel_code_generator/model/template/Union.jinja2
@@ -1,5 +1,5 @@
 {%- if description %}
-# {{ description }}
+# {{ description | replace('\n', '\n# ') }}
 {%- endif %}
 {%- if fields|length > 1 %}
 {{ class_name }}: TypeAlias = Union[


Test patch:
diff --git a/tests/data/expected/parser/graphql/union-commented.py b/tests/data/expected/parser/graphql/union-commented.py
new file mode 100644
index 000000000..8c65f229b
--- /dev/null
+++ b/tests/data/expected/parser/graphql/union-commented.py
@@ -0,0 +1,65 @@
+# generated by datamodel-codegen:
+#   filename:  union-commented.graphql
+#   timestamp: 2019-07-26T00:00:00+00:00
+
+from __future__ import annotations
+
+from typing import Literal, Optional, TypeAlias, Union
+
+from pydantic import BaseModel, Field
+
+Boolean: TypeAlias = bool
+"""
+The `Boolean` scalar type represents `true` or `false`.
+"""
+
+
+String: TypeAlias = str
+"""
+The `String` scalar type represents textual data, represented as UTF-8 character sequences. The String type is most often used by GraphQL to represent free-form human-readable text.
+"""
+
+
+class GroupMetadata(BaseModel):
+    """
+    This is a test comment in a single line
+    """
+
+    name: String
+    typename__: Optional[Literal['GroupMetadata']] = Field(
+        'GroupMetadata', alias='__typename'
+    )
+
+
+class Resource(BaseModel):
+    metadata: UserMetadata
+    typename__: Optional[Literal['Resource']] = Field('Resource', alias='__typename')
+
+
+class UserMetadata(BaseModel):
+    """
+    This is a multiline comment,
+    with a line break,
+    and a line break
+    """
+
+    name: String
+    typename__: Optional[Literal['UserMetadata']] = Field(
+        'UserMetadata', alias='__typename'
+    )
+
+
+# This is a single line comment
+DummyMetadata: TypeAlias = Union[
+    'GroupMetadata',
+    'UserMetadata',
+]
+
+
+# This is another multiline comment,
+# with a line break,
+# and another line break
+Metadata: TypeAlias = Union[
+    'GroupMetadata',
+    'UserMetadata',
+]
diff --git a/tests/data/graphql/union-commented.graphql b/tests/data/graphql/union-commented.graphql
new file mode 100644
index 000000000..87ba4b609
--- /dev/null
+++ b/tests/data/graphql/union-commented.graphql
@@ -0,0 +1,25 @@
+"""This is a test comment in a single line"""
+type GroupMetadata {
+  name: String!
+}
+
+"""This is a multiline comment,
+with a line break,
+and a line break
+"""
+type UserMetadata {
+  name: String!
+}
+
+"""This is another multiline comment,
+with a line break,
+and another line break
+"""
+union Metadata = UserMetadata | GroupMetadata
+
+"""This is a single line comment"""
+union DummyMetadata = UserMetadata | GroupMetadata
+
+type Resource {
+  metadata: UserMetadata!
+}
diff --git a/tests/parser/test_graphql.py b/tests/parser/test_graphql.py
index f3e6df565..45dc2ea12 100644
--- a/tests/parser/test_graphql.py
+++ b/tests/parser/test_graphql.py
@@ -47,3 +47,23 @@ def test_graphql_union_aliased_bug() -> None:
         if actual != expected:
             pass
         assert actual == expected
+
+
+@freeze_time("2019-07-26")
+def test_graphql_union_commented() -> None:
+    with TemporaryDirectory() as output_dir:
+        output_file: Path = Path(output_dir) / "output.py"
+        return_code: Exit = main([
+            "--input",
+            str(GRAPHQL_DATA_PATH / "union-commented.graphql"),
+            "--output",
+            str(output_file),
+            "--input-file-type",
+            "graphql",
+        ])
+        assert return_code == Exit.OK
+        actual = output_file.read_text().rstrip()
+        expected = (EXPECTED_GRAPHQL_PATH / "union-commented.py").read_text().rstrip()
+        if actual != expected:
+            pass
+        assert actual == expected