From b55ec71d9b2e81df7b09d7440215e796e2cd8129 Mon Sep 17 00:00:00 2001 From: Kevin Squire Date: Thu, 5 Dec 2024 14:02:10 -0800 Subject: [PATCH] Fix render_tuple with coerce We were not properly escaping quotes when rendering tuples, leading to a SyntaxError. --- serde/de.py | 4 +++- serde/se.py | 4 +++- tests/test_de.py | 4 ++-- tests/test_type_check.py | 9 +++++++++ 4 files changed, 17 insertions(+), 4 deletions(-) diff --git a/serde/de.py b/serde/de.py index bd0d78ee..5e3e4789 100644 --- a/serde/de.py +++ b/serde/de.py @@ -956,7 +956,9 @@ def primitive(self, arg: DeField[Any], suppress_coerce: bool = False) -> str: if self.suppress_coerce and suppress_coerce: return dat else: - return f'coerce_object("{self.class_name}", "{arg.name}", {typ}, {dat})' + assert arg.name + escaped_arg_name = arg.name.replace('"', '\\"') + return f'coerce_object("{self.class_name}", "{escaped_arg_name}", {typ}, {dat})' def c_tor(self, arg: DeField[Any]) -> str: return f"{typename(arg.type)}({arg.data})" diff --git a/serde/se.py b/serde/se.py index b74abe4e..e258fb93 100644 --- a/serde/se.py +++ b/serde/se.py @@ -922,7 +922,9 @@ def primitive(self, arg: SeField[Any]) -> str: if self.suppress_coerce: return var else: - return f'coerce_object("{self.class_name}", "{arg.name}", {typ}, {var})' + assert arg.name + escaped_arg_name = arg.name.replace('"', '\\"') + return f'coerce_object("{self.class_name}", "{escaped_arg_name}", {typ}, {var})' def string(self, arg: SeField[Any]) -> str: return f"str({arg.varname})" diff --git a/tests/test_de.py b/tests/test_de.py index 6bb64dde..63486088 100644 --- a/tests/test_de.py +++ b/tests/test_de.py @@ -60,8 +60,8 @@ class Foo: pass rendered = Renderer("foo").render(DeField(tuple[str, int, list[int], Foo], "d", datavar="data")) - rendered_str = 'coerce_object("None", "data["d"][0]", str, data["d"][0])' - rendered_int = 'coerce_object("None", "data["d"][1]", int, data["d"][1])' + rendered_str = 'coerce_object("None", "data[\\"d\\"][0]", str, data["d"][0])' + rendered_int = 'coerce_object("None", "data[\\"d\\"][1]", int, data["d"][1])' rendered_lst = '[coerce_object("None", "v", int, v) for v in data["d"][2]]' rendered_foo = f"Foo.__serde__.funcs['foo'](data=data[\"d\"][3], {kwargs})" assert rendered == f"({rendered_str}, {rendered_int}, {rendered_lst}, {rendered_foo},)" diff --git a/tests/test_type_check.py b/tests/test_type_check.py index e45ef4ba..d3678821 100644 --- a/tests/test_type_check.py +++ b/tests/test_type_check.py @@ -184,3 +184,12 @@ class Nested: assert p3.s.s == "100" assert p3.f.f == 1000.0 assert p3.b.b + + @serde.serde(type_check=serde.coerce) + class InnerTuple: + # Note: `foo` needs to be longer than 1 char, to properly test + # quote escaping + foo: tuple[float, float] + + f = InnerTuple(foo=(1, 2)) + assert f.foo == (1.0, 2.0)