Repository: mirumee/ariadne-codegen (python)

Issue:
black.parsing.InvalidInput error when enum value is a reserved name
Hi! I'm trying to create a client for Graphprotocol network subgraph. However, the client build fails on `_generate_enums`

```
[tool.ariadne-codegen]
remote_schema_url = "https://api.thegraph.com/subgraphs/name/graphprotocol/graph-network-mainnet"
queries_path = "queries/network_subgraph"
```

The error message is:

```
Generating enums into 'enums.py'.
Generating inputs into 'input_types.py'.
Generating fragments into 'fragments.py'.
Including comments.
Converting fields and arguments name to snake case.
Generating async client.
No files to copy.
No plugin is being used.
Traceback (most recent call last):
  File "/Users/sergeykozlov/Documents/gl/graphprotocol-graphql-clients/venv/bin/ariadne-codegen", line 8, in <module>
    sys.exit(main())
  File "/Users/sergeykozlov/Documents/gl/graphprotocol-graphql-clients/venv/lib/python3.9/site-packages/click/core.py", line 1157, in __call__
    return self.main(*args, **kwargs)
  File "/Users/sergeykozlov/Documents/gl/graphprotocol-graphql-clients/venv/lib/python3.9/site-packages/click/core.py", line 1078, in main
    rv = self.invoke(ctx)
  File "/Users/sergeykozlov/Documents/gl/graphprotocol-graphql-clients/venv/lib/python3.9/site-packages/click/core.py", line 1434, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "/Users/sergeykozlov/Documents/gl/graphprotocol-graphql-clients/venv/lib/python3.9/site-packages/click/core.py", line 783, in invoke
    return __callback(*args, **kwargs)
  File "/Users/sergeykozlov/Documents/gl/graphprotocol-graphql-clients/venv/lib/python3.9/site-packages/ariadne_codegen/main.py", line 34, in main
    client(config_dict)
  File "/Users/sergeykozlov/Documents/gl/graphprotocol-graphql-clients/venv/lib/python3.9/site-packages/ariadne_codegen/main.py", line 91, in client
    generated_files = package_generator.generate()
  File "/Users/sergeykozlov/Documents/gl/graphprotocol-graphql-clients/venv/lib/python3.9/site-packages/ariadne_codegen/client_generators/package.py", line 172, in generate
    self._generate_enums()
  File "/Users/sergeykozlov/Documents/gl/graphprotocol-graphql-clients/venv/lib/python3.9/site-packages/ariadne_codegen/client_generators/package.py", line 283, in _generate_enums
    code = self._proccess_generated_code(ast_to_str(module), self.schema_source)
  File "/Users/sergeykozlov/Documents/gl/graphprotocol-graphql-clients/venv/lib/python3.9/site-packages/ariadne_codegen/utils.py", line 31, in ast_to_str
    return format_str(isort.code(code), mode=Mode())
  File "src/black/__init__.py", line 1079, in format_str
  File "src/black/__init__.py", line 1089, in _format_str_once
  File "src/black/parsing.py", line 100, in lib2to3_parse
black.parsing.InvalidInput: Cannot parse: 256:9:     from = 'from'
```

In the schema.py generated with `ariadne-codegen graphqlschema` i see that some Enums are using reserved python names in values and I think that ariadne does not properly sanitizes them.
```
"from": GraphQLEnumValue(
                value="from", description=None, deprecation_reason=None
)
```,

Pull request description:
Escape enum values which are python keywords
resolves #220 

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b364bde..d0e63a8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,7 @@
 - Changed `str_to_snake_case` utility to correctly handle capitalized words.
 - Digits in Python names are now preceded by an underscore (breaking change).
 - Fixed parsing of unions and interfaces to always add `__typename` to generated result models.
+- Added escaping of enum values which are Python keywords by appending `_` to them.
 
 
 ## 0.9.0 (2023-09-11)
diff --git a/ariadne_codegen/client_generators/enums.py b/ariadne_codegen/client_generators/enums.py
index 53d83da..56a19e9 100644
--- a/ariadne_codegen/client_generators/enums.py
+++ b/ariadne_codegen/client_generators/enums.py
@@ -1,4 +1,5 @@
 import ast
+from keyword import iskeyword
 from typing import List, Optional, cast
 
 from graphql import GraphQLEnumType, GraphQLSchema
@@ -48,12 +49,14 @@ class EnumsGenerator:
         ]
 
     def _parse_enum_definition(self, definition: GraphQLEnumType) -> ast.ClassDef:
-        fields = [
-            generate_assign([val_name], generate_constant(val_def.value), lineno)
-            for lineno, (val_name, val_def) in enumerate(
-                definition.values.items(), start=1
+        fields: List[ast.stmt] = []
+        for lineno, (val_name, val_def) in enumerate(
+            definition.values.items(), start=1
+        ):
+            name = val_name if not iskeyword(val_name) else val_name + "_"
+            fields.append(
+                generate_assign([name], generate_constant(val_def.value), lineno)
             )
-        ]
 
         class_def = generate_class_def(
             name=definition.name,


Test patch:
diff --git a/tests/client_generators/test_enums_generator.py b/tests/client_generators/test_enums_generator.py
index 61e035d..bdcbf83 100644
--- a/tests/client_generators/test_enums_generator.py
+++ b/tests/client_generators/test_enums_generator.py
@@ -31,6 +31,8 @@ def test_generate_returns_module_with_enum_class_definition():
     enum CustomEnum {
         VALUE1
         VALUE2
+        from
+        import
     }
     """
     expected_class_def = ast.ClassDef(
@@ -45,6 +47,12 @@ def test_generate_returns_module_with_enum_class_definition():
             ast.Assign(
                 targets=[ast.Name(id="VALUE2")], value=ast.Constant(value="VALUE2")
             ),
+            ast.Assign(
+                targets=[ast.Name(id="from_")], value=ast.Constant(value="from")
+            ),
+            ast.Assign(
+                targets=[ast.Name(id="import_")], value=ast.Constant(value="import")
+            ),
         ],
     )
     generator = EnumsGenerator(schema=build_ast_schema(parse(schema_str)))