From 25e49cf3bdf787bacfa7f00cb634ff409a384706 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Fern=C3=A1ndez?= <7312236+fernandezcuesta@users.noreply.github.com> Date: Fri, 18 Oct 2024 01:37:51 +0200 Subject: [PATCH 1/4] fix: structs containing listvalues are properly converted MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jesús Fernández <7312236+fernandezcuesta@users.noreply.github.com> --- .../proto/v1/run_function_pb2_grpc.py | 2 +- .../proto/v1beta1/run_function_pb2_grpc.py | 2 +- crossplane/function/resource.py | 6 ++--- tests/test_resource.py | 22 +++++++++++++++++++ 4 files changed, 26 insertions(+), 6 deletions(-) diff --git a/crossplane/function/proto/v1/run_function_pb2_grpc.py b/crossplane/function/proto/v1/run_function_pb2_grpc.py index cc1d191..551cf18 100644 --- a/crossplane/function/proto/v1/run_function_pb2_grpc.py +++ b/crossplane/function/proto/v1/run_function_pb2_grpc.py @@ -5,7 +5,7 @@ import warnings from crossplane.function.proto.v1 import run_function_pb2 as crossplane_dot_function_dot_proto_dot_v1_dot_run__function__pb2 -GRPC_GENERATED_VERSION = '1.66.0' +GRPC_GENERATED_VERSION = '1.66.2' GRPC_VERSION = grpc.__version__ _version_not_supported = False diff --git a/crossplane/function/proto/v1beta1/run_function_pb2_grpc.py b/crossplane/function/proto/v1beta1/run_function_pb2_grpc.py index 04e7b83..156d057 100644 --- a/crossplane/function/proto/v1beta1/run_function_pb2_grpc.py +++ b/crossplane/function/proto/v1beta1/run_function_pb2_grpc.py @@ -5,7 +5,7 @@ import warnings from crossplane.function.proto.v1beta1 import run_function_pb2 as crossplane_dot_function_dot_proto_dot_v1beta1_dot_run__function__pb2 -GRPC_GENERATED_VERSION = '1.66.0' +GRPC_GENERATED_VERSION = '1.66.2' GRPC_VERSION = grpc.__version__ _version_not_supported = False diff --git a/crossplane/function/resource.py b/crossplane/function/resource.py index 322a482..0892e7d 100644 --- a/crossplane/function/resource.py +++ b/crossplane/function/resource.py @@ -18,6 +18,7 @@ import dataclasses import datetime import pydantic +from google.protobuf import json_format from google.protobuf import struct_pb2 as structpb import crossplane.function.proto.v1.run_function_pb2 as fnv1 @@ -69,10 +70,7 @@ def struct_to_dict(s: structpb.Struct) -> dict: protobuf struct. This function makes it possible to convert resources to a dictionary. """ - return { - k: (struct_to_dict(v) if isinstance(v, structpb.Struct) else v) - for k, v in s.items() - } + return json_format.MessageToDict(s, preserving_proto_field_name=True) @dataclasses.dataclass diff --git a/tests/test_resource.py b/tests/test_resource.py index e9c818f..c04ca14 100644 --- a/tests/test_resource.py +++ b/tests/test_resource.py @@ -275,6 +275,28 @@ class TestResource(unittest.TestCase): ), want={"foo": {"bar": "baz"}}, ), + TestCase( + reason="Convert a nested struct containing ListValues to a dictionary.", + s=structpb.Struct( + fields={ + "foo": structpb.Value( + struct_value=structpb.Struct( + fields={ + "bar": structpb.Value( + list_value=structpb.ListValue( + values=[ + structpb.Value(string_value="baz"), + structpb.Value(string_value="qux"), + ] + ) + ) + } + ) + ) + } + ), + want={"foo": {"bar": ["baz", "qux"]}}, + ), ] for case in cases: From c632b0c1ad110f1655465416bdf557f78084aa1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Fern=C3=A1ndez?= <7312236+fernandezcuesta@users.noreply.github.com> Date: Mon, 25 Nov 2024 12:07:47 +0100 Subject: [PATCH 2/4] feat: use json_format.ParseDict to convert dicts to structs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jesús Fernández <7312236+fernandezcuesta@users.noreply.github.com> --- crossplane/function/resource.py | 4 +-- tests/test_resource.py | 60 +++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 3 deletions(-) diff --git a/crossplane/function/resource.py b/crossplane/function/resource.py index 0892e7d..c33e7aa 100644 --- a/crossplane/function/resource.py +++ b/crossplane/function/resource.py @@ -58,9 +58,7 @@ def dict_to_struct(d: dict) -> structpb.Struct: function makes it possible to work with a Python dict, then convert it to a struct in a RunFunctionResponse. """ - s = structpb.Struct() - s.update(d) - return s + return json_format.ParseDict(d, structpb.Struct()) def struct_to_dict(s: structpb.Struct) -> dict: diff --git a/tests/test_resource.py b/tests/test_resource.py index c04ca14..52de5f5 100644 --- a/tests/test_resource.py +++ b/tests/test_resource.py @@ -244,6 +244,66 @@ class TestResource(unittest.TestCase): dataclasses.asdict(case.want), dataclasses.asdict(got), "-want, +got" ) + def test_dict_to_struct(self) -> None: + @dataclasses.dataclass + class TestCase: + reason: str + d: dict + want: structpb.Struct + + cases = [ + TestCase( + reason="Convert an empty dictionary to a struct.", + d={}, + want=structpb.Struct(), + ), + TestCase( + reason="Convert a dictionary with a single field to a struct.", + d={"foo": "bar"}, + want=structpb.Struct( + fields={"foo": structpb.Value(string_value="bar")} + ), + ), + TestCase( + reason="Convert a nested dictionary to a struct.", + d={"foo": {"bar": "baz"}}, + want=structpb.Struct( + fields={ + "foo": structpb.Value( + struct_value=structpb.Struct( + fields={"bar": structpb.Value(string_value="baz")} + ) + ) + } + ), + ), + TestCase( + reason="Convert a nested dictionary containing lists to a struct.", + d={"foo": {"bar": ["baz", "qux"]}}, + want=structpb.Struct( + fields={ + "foo": structpb.Value( + struct_value=structpb.Struct( + fields={ + "bar": structpb.Value( + list_value=structpb.ListValue( + values=[ + structpb.Value(string_value="baz"), + structpb.Value(string_value="qux"), + ] + ) + ) + } + ) + ) + } + ), + ), + ] + for case in cases: + got = resource.dict_to_struct(case.d) + self.assertEqual(case.want, got, "-want, +got") + def test_struct_to_dict(self) -> None: @dataclasses.dataclass class TestCase: From 5cf7fffbf18d35bdc517010051c776a28f9f2dac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20Fern=C3=A1ndez?= <7312236+fernandezcuesta@users.noreply.github.com> Date: Tue, 26 Nov 2024 19:53:08 +0100 Subject: [PATCH 3/4] bump protobuf MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jesús Fernández <7312236+fernandezcuesta@users.noreply.github.com> --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 40c2cb9..5f42005 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -19,7 +19,7 @@ classifiers = [ dependencies = [ "grpcio==1.*", "grpcio-reflection==1.*", - "protobuf==5.27.2", + "protobuf==5.28.3", "pydantic==2.*", "structlog==24.*", ] From 36e5600356ad089c27c7289776c53dfd190f9cb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Fern=C3=A1ndez?= <7312236+fernandezcuesta@users.noreply.github.com> Date: Tue, 26 Nov 2024 20:02:53 +0100 Subject: [PATCH 4/4] chore: stick to grpcio 1.67.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jesús Fernández <7312236+fernandezcuesta@users.noreply.github.com> --- crossplane/function/proto/v1/run_function_pb2_grpc.py | 2 +- crossplane/function/proto/v1beta1/run_function_pb2_grpc.py | 2 +- pyproject.toml | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/crossplane/function/proto/v1/run_function_pb2_grpc.py b/crossplane/function/proto/v1/run_function_pb2_grpc.py index 551cf18..87c39cb 100644 --- a/crossplane/function/proto/v1/run_function_pb2_grpc.py +++ b/crossplane/function/proto/v1/run_function_pb2_grpc.py @@ -5,7 +5,7 @@ import warnings from crossplane.function.proto.v1 import run_function_pb2 as crossplane_dot_function_dot_proto_dot_v1_dot_run__function__pb2 -GRPC_GENERATED_VERSION = '1.66.2' +GRPC_GENERATED_VERSION = '1.67.0' GRPC_VERSION = grpc.__version__ _version_not_supported = False diff --git a/crossplane/function/proto/v1beta1/run_function_pb2_grpc.py b/crossplane/function/proto/v1beta1/run_function_pb2_grpc.py index 156d057..d8814f8 100644 --- a/crossplane/function/proto/v1beta1/run_function_pb2_grpc.py +++ b/crossplane/function/proto/v1beta1/run_function_pb2_grpc.py @@ -5,7 +5,7 @@ import warnings from crossplane.function.proto.v1beta1 import run_function_pb2 as crossplane_dot_function_dot_proto_dot_v1beta1_dot_run__function__pb2 -GRPC_GENERATED_VERSION = '1.66.2' +GRPC_GENERATED_VERSION = '1.67.0' GRPC_VERSION = grpc.__version__ _version_not_supported = False diff --git a/pyproject.toml b/pyproject.toml index 5f42005..5cf2e67 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -17,9 +17,9 @@ classifiers = [ ] dependencies = [ - "grpcio==1.*", + "grpcio==1.67.0", "grpcio-reflection==1.*", - "protobuf==5.28.3", + "protobuf==5.27.2", "pydantic==2.*", "structlog==24.*", ] @@ -44,7 +44,7 @@ dependencies = ["ipython==8.28.0"] type = "virtual" detached = true path = ".venv-generate" -dependencies = ["grpcio-tools==1.66.2"] +dependencies = ["grpcio-tools==1.67.0"] [tool.hatch.envs.generate.scripts] protoc = "python -m grpc_tools.protoc --proto_path=. --python_out=. --pyi_out=. --grpc_python_out=. crossplane/function/proto/v1beta1/run_function.proto crossplane/function/proto/v1/run_function.proto"