Repository: tartiflette/tartiflette
Pull request #522 (2021-09-03 15:25:08)

Allow for specifing SDL file encoding (or rely on locale.getprefferedencoding(False))
On future version of python, open will use UTF-8 as a default and not the system prefered one. So I'd like to offer the capability to user to specify at Init/cook time the encoding of their SDL file (we'll use default system one per default if value is None).


Patch:
diff --git a/setup.py b/setup.py
index 23d2aa5..1510e03 100644
--- a/setup.py
+++ b/setup.py
@@ -56,7 +56,7 @@ _TEST_REQUIRE = [
     "pytest-cov==2.12.1",
     "pytest-asyncio==0.15.1",
     "pytest-xdist==2.3.0",
-    "pylint==2.9.5",
+    "pylint==2.10.2",
     "black==21.8b0",
     "isort==5.9.3",
 ]
diff --git a/tartiflette/engine.py b/tartiflette/engine.py
index 1110a06..ca8cfaa 100644
--- a/tartiflette/engine.py
+++ b/tartiflette/engine.py
@@ -1,4 +1,5 @@
 import json as default_json_module
+import locale
 import logging
 
 from functools import lru_cache, partial
@@ -155,6 +156,7 @@ class Engine:
         custom_default_arguments_coercer=None,
         coerce_list_concurrently=None,
         coerce_parent_concurrently=None,
+        sdl_file_encoding=None,
     ) -> None:
         """
         Creates an uncooked Engine instance.
@@ -183,6 +185,7 @@ class Engine:
         self._subscription_executor = None
         self._cached_parse_and_validate_query = None
         self._json_loader = json_loader or default_json_module.loads
+        self._sdl_file_encoding = sdl_file_encoding
 
     async def cook(
         self,
@@ -199,6 +202,7 @@ class Engine:
         coerce_list_concurrently: Optional[bool] = None,
         coerce_parent_concurrently: Optional[bool] = None,
         schema_name: Optional[str] = None,
+        sdl_file_encoding: str = None,
     ) -> None:
         """
         Cook the tartiflette, basically prepare the engine by binding it to
@@ -227,6 +231,8 @@ class Engine:
         :param coerce_parent_concurrently: whether or not field will be coerced
         concurrently
         :param schema_name: name of the SDL
+        :param sdl_file_encoding: allow for specify the file encoding of the SDL if
+        != from local.getpreferedencoding(false)
         :type sdl: Union[str, List[str]]
         :type error_coercer: Callable[[Exception, Dict[str, Any]], Dict[str, Any]]
         :type custom_default_resolver: Optional[Callable]
@@ -238,6 +244,7 @@ class Engine:
         :type coerce_list_concurrently: Optional[bool]
         :type coerce_parent_concurrently: Optional[bool]
         :type schema_name: Optional[str]
+        :type sdl_file_encodign: Optional[str]
         """
         # pylint: disable=too-many-arguments,too-many-locals
         if self._cooked:
@@ -303,7 +310,12 @@ class Engine:
             modules, schema_name
         )
 
-        SchemaRegistry.register_sdl(schema_name, sdl, modules_sdl)
+        sfe = (
+            sdl_file_encoding
+            or self._sdl_file_encoding
+            or locale.getpreferredencoding(False)
+        )
+        SchemaRegistry.register_sdl(schema_name, sdl, sfe, modules_sdl)
         self._schema = await SchemaBakery.bake(
             schema_name,
             custom_default_resolver,
diff --git a/tartiflette/schema/builtins/introspection.py b/tartiflette/schema/builtins/introspection.py
index 76b2434..857b701 100644
--- a/tartiflette/schema/builtins/introspection.py
+++ b/tartiflette/schema/builtins/introspection.py
@@ -86,6 +86,7 @@ def bake(schema_name: str, config: Optional[Dict[str, Any]] = None) -> str:
     )
     Resolver("__Type.fields", schema_name=schema_name)(resolve_type_fields)
     with open(
-        os.path.join(os.path.dirname(__file__), "introspection.sdl")
+        os.path.join(os.path.dirname(__file__), "introspection.sdl"),
+        encoding="UTF-8",
     ) as file:
         return file.read()
diff --git a/tartiflette/schema/registry.py b/tartiflette/schema/registry.py
index 2e1f615..f5be3e9 100644
--- a/tartiflette/schema/registry.py
+++ b/tartiflette/schema/registry.py
@@ -161,6 +161,7 @@ class SchemaRegistry:
     def register_sdl(
         schema_name: str,
         sdl: Union[str, List[str], "GraphQLSchema"],
+        sdl_file_encoding: str,
         modules_sdl: Optional[str] = None,
     ) -> None:
         """
@@ -168,9 +169,11 @@ class SchemaRegistry:
         :param schema_name: name of the schema
         :param sdl: path(s) to the SDL or raw string representing the SDL
         :param modules_sdl: extra SDL from the imported modules
+        :param sdl_file_encoding: sdl file encoding
         :type schema_name: str
         :type sdl: Union[str, List[str], GraphQLSchema]
         :type modules_sdl: Optional[str]
+        :type sdl_file_encoding: str
         """
         SchemaRegistry._schemas.setdefault(schema_name, {})
 
@@ -190,7 +193,9 @@ class SchemaRegistry:
 
         # Convert SDL files into big schema and parse it
         for filepath in sdl_files_list:
-            with open(filepath, mode="r") as sdl_file:
+            with open(
+                filepath, mode="r", encoding=sdl_file_encoding
+            ) as sdl_file:
                 full_sdl += "\n" + sdl_file.read()
 
         if modules_sdl:


Test patch:
diff --git a/tests/unit/test_resolver_decorator.py b/tests/unit/test_resolver_decorator.py
index e4b906c..1145e8c 100644
--- a/tests/unit/test_resolver_decorator.py
+++ b/tests/unit/test_resolver_decorator.py
@@ -69,7 +69,7 @@ async def test_resolver_decorator(clean_registry):
 
     _, schema_sdl = await _import_builtins([], schema_sdl, "default")
 
-    clean_registry.register_sdl("default", schema_sdl)
+    clean_registry.register_sdl("default", schema_sdl, "UTF-8")
 
     mock_one = Mock()
     mock_two = Mock()
diff --git a/tests/unit/test_schema_unit.py b/tests/unit/test_schema_unit.py
index a131ab8..558eead 100644
--- a/tests/unit/test_schema_unit.py
+++ b/tests/unit/test_schema_unit.py
@@ -58,7 +58,7 @@ async def test_schema_object_get_field_name(clean_registry):
     """
 
     _, schema_sdl = await _import_builtins([], schema_sdl, "default")
-    clean_registry.register_sdl("A", schema_sdl)
+    clean_registry.register_sdl("A", schema_sdl, "UTF-8")
     generated_schema = SchemaBakery._preheat("A")
 
     with pytest.raises(ImproperlyConfigured):
@@ -162,7 +162,7 @@ async def test_schema_validate_named_types(
 ):
 
     _, full_sdl = await _import_builtins([], full_sdl, "A")
-    clean_registry.register_sdl("A", full_sdl)
+    clean_registry.register_sdl("A", full_sdl, "UTF-8")
     generated_schema = SchemaBakery._preheat("A")
 
     if expected_error:
@@ -324,7 +324,7 @@ async def test_schema_validate_object_follow_interfaces(
     full_sdl, expected_error, clean_registry
 ):
     _, full_sdl = await _import_builtins([], full_sdl, "A")
-    clean_registry.register_sdl("A", full_sdl)
+    clean_registry.register_sdl("A", full_sdl, "UTF-8")
     generated_schema = SchemaBakery._preheat("A")
 
     try:
@@ -447,7 +447,7 @@ async def test_schema_validate_root_types_exist(
     full_sdl, expected_error, clean_registry
 ):
     _, full_sdl = await _import_builtins([], full_sdl, "a")
-    clean_registry.register_sdl("a", full_sdl)
+    clean_registry.register_sdl("a", full_sdl, "UTF-8")
     generated_schema = SchemaBakery._preheat("a")
 
     if expected_error:
@@ -484,7 +484,7 @@ async def test_schema_validate_non_empty_object(
     full_sdl, expected_error, clean_registry
 ):
     _, full_sdl = await _import_builtins([], full_sdl, "a")
-    clean_registry.register_sdl("a", full_sdl)
+    clean_registry.register_sdl("a", full_sdl, "UTF-8")
     generated_schema = SchemaBakery._preheat("a")
 
     if expected_error:
@@ -534,7 +534,7 @@ async def test_schema_validate_union_is_acceptable(
     full_sdl, expected_error, clean_registry
 ):
     _, full_sdl = await _import_builtins([], full_sdl, "a")
-    clean_registry.register_sdl("a", full_sdl)
+    clean_registry.register_sdl("a", full_sdl, "UTF-8")
     generated_schema = SchemaBakery._preheat("a")
 
     if expected_error:
@@ -554,7 +554,7 @@ async def test_schema_bake_schema(clean_registry):
         }""",
         "a",
     )
-    clean_registry.register_sdl("a", full_sdl)
+    clean_registry.register_sdl("a", full_sdl, "UTF-8")
     assert await SchemaBakery.bake("a") is not None
 
 
@@ -576,7 +576,7 @@ async def test_schema_has_type(clean_registry, type_name, expected):
         """,
         "a",
     )
-    clean_registry.register_sdl("a", full_sdl)
+    clean_registry.register_sdl("a", full_sdl, "UTF-8")
     schema = await SchemaBakery.bake("a")
     assert schema.has_type(type_name) is expected