Repository: mirumee/ariadne-codegen
Pull request #148 (2023-05-17 06:30:36)

fix: Don't prefix `typename` with underscore
Rename `__typename__` so it doesn't start with an underscore. This is because fields with such prefix are marked as private in pydantic.

Ref #144


Patch:
diff --git a/ariadne_codegen/client_generators/input_types.py b/ariadne_codegen/client_generators/input_types.py
index 781ffca..c2d4a42 100644
--- a/ariadne_codegen/client_generators/input_types.py
+++ b/ariadne_codegen/client_generators/input_types.py
@@ -114,6 +114,7 @@ class InputTypesGenerator:
                 convert_to_snake_case=self.convert_to_snake_case,
                 plugin_manager=self.plugin_manager,
                 node=field,
+                trim_leading_underscore=True,
             )
             annotation, field_type = parse_input_field_type(
                 field.type, custom_scalars=self.custom_scalars
diff --git a/ariadne_codegen/client_generators/result_types.py b/ariadne_codegen/client_generators/result_types.py
index cd3cc54..b3fd2eb 100644
--- a/ariadne_codegen/client_generators/result_types.py
+++ b/ariadne_codegen/client_generators/result_types.py
@@ -300,12 +300,13 @@ class ResultTypesGenerator:
 
     def _process_field_name(self, name: str, field: FieldNode) -> str:
         if self.convert_to_snake_case and name == TYPENAME_FIELD_NAME:
-            return "__typename__"
+            return "typename__"
         return process_name(
             name,
             convert_to_snake_case=self.convert_to_snake_case,
             plugin_manager=self.plugin_manager,
             node=field,
+            trim_leading_underscore=True,
         )
 
     def _get_field_from_schema(self, type_name: str, field_name: str) -> GraphQLField:
diff --git a/ariadne_codegen/codegen.py b/ariadne_codegen/codegen.py
index cddaeb8..2583324 100644
--- a/ariadne_codegen/codegen.py
+++ b/ariadne_codegen/codegen.py
@@ -283,7 +283,7 @@ def generate_field_with_alias(name):
 
 def generate_typename_field_definition():
     return generate_ann_assign(
-        target="__typename__",
+        target="typename__",
         annotation=generate_name("str"),
         value=generate_field_with_alias("__typename"),
     )
diff --git a/ariadne_codegen/utils.py b/ariadne_codegen/utils.py
index e5c7719..5d01dbb 100644
--- a/ariadne_codegen/utils.py
+++ b/ariadne_codegen/utils.py
@@ -82,6 +82,7 @@ def process_name(
     convert_to_snake_case: bool,
     plugin_manager: Optional[PluginManager] = None,
     node: Optional[Node] = None,
+    trim_leading_underscore: bool = False,
 ) -> str:
     """Processes the GraphQL name to remove keywords
     and optionally convert to snake_case."""
@@ -90,6 +91,8 @@ def process_name(
         processed_name = str_to_snake_case(processed_name)
     if iskeyword(processed_name):
         processed_name += "_"
+    if trim_leading_underscore:
+        processed_name = processed_name.lstrip("_")
     if plugin_manager:
         processed_name = plugin_manager.process_name(processed_name, node=node)
     return processed_name


Test patch:
diff --git a/tests/client_generators/input_types_generator/test_names.py b/tests/client_generators/input_types_generator/test_names.py
index c009e26..35214d0 100644
--- a/tests/client_generators/input_types_generator/test_names.py
+++ b/tests/client_generators/input_types_generator/test_names.py
@@ -146,6 +146,9 @@ def test_generate_returns_module_with_valid_field_names():
         in: String!
         from: String!
         and: String!
+        _foo: String!
+        _Bar: String!
+        ____baz_: String!
     }
     """
 
@@ -160,4 +163,4 @@ def test_generate_returns_module_with_valid_field_names():
     )  # Round trip because invalid identifiers get picked up in parse
     class_def = get_class_def(parsed)
     field_names = get_assignment_target_names(class_def)
-    assert field_names == {"in_", "from_", "and_"}
+    assert field_names == {"in_", "from_", "and_", "foo", "bar", "baz_"}
diff --git a/tests/client_generators/result_types_generator/schema.py b/tests/client_generators/result_types_generator/schema.py
index ad9a83e..c5068e1 100644
--- a/tests/client_generators/result_types_generator/schema.py
+++ b/tests/client_generators/result_types_generator/schema.py
@@ -28,6 +28,8 @@ type CustomType {
     field1: CustomType1!
     field2: CustomType2
     field3: CustomEnum!
+    _field4: String!
+    _Field5: String!
     scalarField: SCALARA
 }
 
diff --git a/tests/client_generators/result_types_generator/test_names.py b/tests/client_generators/result_types_generator/test_names.py
index 5a2dddd..0f65465 100644
--- a/tests/client_generators/result_types_generator/test_names.py
+++ b/tests/client_generators/result_types_generator/test_names.py
@@ -112,6 +112,8 @@ def test_generate_returns_module_with_valid_field_names():
     query CustomQuery {
         camelCaseQuery {
             in: id
+            _field4
+            _Field5
         }
     }
     """
@@ -131,4 +133,4 @@ def test_generate_returns_module_with_valid_field_names():
     )  # Round trip because invalid identifiers get picked up in parse
     class_def = get_class_def(parsed, name_filter="CustomQueryCamelCaseQuery")
     field_names = get_assignment_target_names(class_def)
-    assert field_names == {"in_"}
+    assert field_names == {"in_", "field4", "field5"}
diff --git a/tests/client_generators/result_types_generator/test_unions.py b/tests/client_generators/result_types_generator/test_unions.py
index 0e163ec..a84d248 100644
--- a/tests/client_generators/result_types_generator/test_unions.py
+++ b/tests/client_generators/result_types_generator/test_unions.py
@@ -29,7 +29,7 @@ def test_generate_returns_module_with_handled_typename_field():
     )
     expected_fields_implementations = [
         ast.AnnAssign(
-            target=ast.Name(id="__typename__"),
+            target=ast.Name(id="typename__"),
             annotation=ast.Name(id="str"),
             value=ast.Call(
                 func=ast.Name(id="Field"),
@@ -99,7 +99,7 @@ def test_generate_returns_module_with_classes_for_union_fields():
             keywords=[],
             body=[
                 ast.AnnAssign(
-                    target=ast.Name(id="__typename__"),
+                    target=ast.Name(id="typename__"),
                     annotation=ast.Name(id="str"),
                     value=ast.Call(
                         func=ast.Name(id="Field"),
@@ -126,7 +126,7 @@ def test_generate_returns_module_with_classes_for_union_fields():
             keywords=[],
             body=[
                 ast.AnnAssign(
-                    target=ast.Name(id="__typename__"),
+                    target=ast.Name(id="typename__"),
                     annotation=ast.Name(id="str"),
                     value=ast.Call(
                         func=ast.Name(id="Field"),
diff --git a/tests/client_generators/test_arguments_generator.py b/tests/client_generators/test_arguments_generator.py
index e3f4521..47a77cd 100644
--- a/tests/client_generators/test_arguments_generator.py
+++ b/tests/client_generators/test_arguments_generator.py
@@ -217,7 +217,10 @@ def test_generate_returns_arguments_and_dictionary_with_snake_case_names():
 
 def test_generate_returns_arguments_and_dictionary_with_valid_names():
     generator = ArgumentsGenerator(schema=GraphQLSchema(), convert_to_snake_case=True)
-    query = "query q($from: String!, $and: String!, $in: String!) {r}"
+    query = (
+        "query q($from: String!, $and: String!, $in: String!, "
+        "$_fieldA: String!, $_FieldB: String!) {r}"
+    )
     variable_definitions = _get_variable_definitions_from_query_str(query)
 
     arguments, arguments_dict = generator.generate(variable_definitions)
@@ -229,6 +232,8 @@ def test_generate_returns_arguments_and_dictionary_with_valid_names():
             ast.arg(arg="from_", annotation=ast.Name(id="str")),
             ast.arg(arg="and_", annotation=ast.Name(id="str")),
             ast.arg(arg="in_", annotation=ast.Name(id="str")),
+            ast.arg(arg="field_a", annotation=ast.Name(id="str")),
+            ast.arg(arg="_field_b", annotation=ast.Name(id="str")),
         ],
         kwonlyargs=[],
         kw_defaults=[],
@@ -239,8 +244,16 @@ def test_generate_returns_arguments_and_dictionary_with_valid_names():
             ast.Constant(value="from"),
             ast.Constant(value="and"),
             ast.Constant(value="in"),
+            ast.Constant(value="_fieldA"),
+            ast.Constant(value="_FieldB"),
+        ],
+        values=[
+            ast.Name(id="from_"),
+            ast.Name(id="and_"),
+            ast.Name(id="in_"),
+            ast.Name(id="field_a"),
+            ast.Name(id="_field_b"),
         ],
-        values=[ast.Name(id="from_"), ast.Name(id="and_"), ast.Name(id="in_")],
     )
 
     assert compare_ast(arguments, expected_arguments)
diff --git a/tests/main/clients/inline_fragments/expected_client/interface_a.py b/tests/main/clients/inline_fragments/expected_client/interface_a.py
index 16d4cb2..b630b73 100644
--- a/tests/main/clients/inline_fragments/expected_client/interface_a.py
+++ b/tests/main/clients/inline_fragments/expected_client/interface_a.py
@@ -12,18 +12,18 @@ class InterfaceA(BaseModel):
 
 
 class InterfaceAQueryIInterface(BaseModel):
-    __typename__: str = Field(alias="__typename")
+    typename__: str = Field(alias="__typename")
     id: str
 
 
 class InterfaceAQueryITypeA(BaseModel):
-    __typename__: str = Field(alias="__typename")
+    typename__: str = Field(alias="__typename")
     id: str
     field_a: str = Field(alias="fieldA")
 
 
 class InterfaceAQueryITypeB(BaseModel):
-    __typename__: str = Field(alias="__typename")
+    typename__: str = Field(alias="__typename")
     id: str
     field_b: str = Field(alias="fieldB")
 
diff --git a/tests/main/clients/inline_fragments/expected_client/interface_b.py b/tests/main/clients/inline_fragments/expected_client/interface_b.py
index 4d3b194..311e28b 100644
--- a/tests/main/clients/inline_fragments/expected_client/interface_b.py
+++ b/tests/main/clients/inline_fragments/expected_client/interface_b.py
@@ -12,12 +12,12 @@ class InterfaceB(BaseModel):
 
 
 class InterfaceBQueryIInterface(BaseModel):
-    __typename__: str = Field(alias="__typename")
+    typename__: str = Field(alias="__typename")
     id: str
 
 
 class InterfaceBQueryITypeA(BaseModel):
-    __typename__: str = Field(alias="__typename")
+    typename__: str = Field(alias="__typename")
     id: str
     field_a: str = Field(alias="fieldA")
 
diff --git a/tests/main/clients/inline_fragments/expected_client/union_a.py b/tests/main/clients/inline_fragments/expected_client/union_a.py
index 2b0c1e1..cfc1532 100644
--- a/tests/main/clients/inline_fragments/expected_client/union_a.py
+++ b/tests/main/clients/inline_fragments/expected_client/union_a.py
@@ -10,13 +10,13 @@ class UnionA(BaseModel):
 
 
 class UnionAQueryUTypeA(BaseModel):
-    __typename__: str = Field(alias="__typename")
+    typename__: str = Field(alias="__typename")
     id: str
     field_a: str = Field(alias="fieldA")
 
 
 class UnionAQueryUTypeB(BaseModel):
-    __typename__: str = Field(alias="__typename")
+    typename__: str = Field(alias="__typename")
     id: str
     field_b: str = Field(alias="fieldB")
 
diff --git a/tests/main/clients/inline_fragments/expected_client/union_b.py b/tests/main/clients/inline_fragments/expected_client/union_b.py
index 0791cfa..6f8e8f6 100644
--- a/tests/main/clients/inline_fragments/expected_client/union_b.py
+++ b/tests/main/clients/inline_fragments/expected_client/union_b.py
@@ -10,13 +10,13 @@ class UnionB(BaseModel):
 
 
 class UnionBQueryUTypeA(BaseModel):
-    __typename__: str = Field(alias="__typename")
+    typename__: str = Field(alias="__typename")
     id: str
     field_a: str = Field(alias="fieldA")
 
 
 class UnionBQueryUTypeB(BaseModel):
-    __typename__: str = Field(alias="__typename")
+    typename__: str = Field(alias="__typename")
     id: str
 
 
diff --git a/tests/main/clients/inline_fragments/expected_client/union_c.py b/tests/main/clients/inline_fragments/expected_client/union_c.py
index 1e855c2..0496f9c 100644
--- a/tests/main/clients/inline_fragments/expected_client/union_c.py
+++ b/tests/main/clients/inline_fragments/expected_client/union_c.py
@@ -10,12 +10,12 @@ class UnionC(BaseModel):
 
 
 class UnionCQueryUTypeA(BaseModel):
-    __typename__: str = Field(alias="__typename")
+    typename__: str = Field(alias="__typename")
     id: str
 
 
 class UnionCQueryUTypeB(BaseModel):
-    __typename__: str = Field(alias="__typename")
+    typename__: str = Field(alias="__typename")
     id: str