From 6ad5864d28519207c86ea21eb88766cb0c5c78ca Mon Sep 17 00:00:00 2001 From: m-standfuss <52842887+m-standfuss@users.noreply.github.com> Date: Tue, 23 Jul 2024 02:16:23 -0600 Subject: [PATCH] Adding decimal support for python client generation (#19203) * Adding decimal to template for Python generator * Rerunning the build steps * Add tests for decimal serialization and deserialization. * Move test to python not legacy pydantic sample * readd old imports --------- Co-authored-by: Adam --- .../src/main/resources/python/api_client.mustache | 7 +++++++ .../openapi_client/api_client.py | 7 +++++++ .../echo_api/python/openapi_client/api_client.py | 7 +++++++ .../petstore/python-aiohttp/petstore_api/api_client.py | 7 +++++++ .../client/petstore/python/petstore_api/api_client.py | 7 +++++++ .../client/petstore/python/tests/test_api_client.py | 6 ++++++ .../petstore/python/tests/test_deserialization.py | 10 ++++++++++ 7 files changed, 51 insertions(+) diff --git a/modules/openapi-generator/src/main/resources/python/api_client.mustache b/modules/openapi-generator/src/main/resources/python/api_client.mustache index c7d4a6d700f..aad33bcc06c 100644 --- a/modules/openapi-generator/src/main/resources/python/api_client.mustache +++ b/modules/openapi-generator/src/main/resources/python/api_client.mustache @@ -5,6 +5,7 @@ import datetime from dateutil.parser import parse from enum import Enum +import decimal import json import mimetypes import os @@ -59,6 +60,7 @@ class ApiClient: 'bool': bool, 'date': datetime.date, 'datetime': datetime.datetime, + 'decimal': decimal.Decimal, 'object': object, } _pool = None @@ -346,6 +348,7 @@ class ApiClient: If obj is str, int, long, float, bool, return directly. If obj is datetime.datetime, datetime.date convert to string in iso8601 format. + If obj is decimal.Decimal return string representation. If obj is list, sanitize each element in the list. If obj is dict, return the dict. If obj is OpenAPI model, return the properties dict. @@ -371,6 +374,8 @@ class ApiClient: ) elif isinstance(obj, (datetime.datetime, datetime.date)): return obj.isoformat() + elif isinstance(obj, decimal.Decimal): + return str(obj) elif isinstance(obj, dict): obj_dict = obj @@ -462,6 +467,8 @@ class ApiClient: return self.__deserialize_date(data) elif klass == datetime.datetime: return self.__deserialize_datetime(data) + elif klass == decimal.Decimal: + return decimal.Decimal(data) elif issubclass(klass, Enum): return self.__deserialize_enum(data, klass) else: diff --git a/samples/client/echo_api/python-disallowAdditionalPropertiesIfNotPresent/openapi_client/api_client.py b/samples/client/echo_api/python-disallowAdditionalPropertiesIfNotPresent/openapi_client/api_client.py index feef534a2f2..c6fa28de319 100644 --- a/samples/client/echo_api/python-disallowAdditionalPropertiesIfNotPresent/openapi_client/api_client.py +++ b/samples/client/echo_api/python-disallowAdditionalPropertiesIfNotPresent/openapi_client/api_client.py @@ -16,6 +16,7 @@ import datetime from dateutil.parser import parse from enum import Enum +import decimal import json import mimetypes import os @@ -67,6 +68,7 @@ class ApiClient: 'bool': bool, 'date': datetime.date, 'datetime': datetime.datetime, + 'decimal': decimal.Decimal, 'object': object, } _pool = None @@ -339,6 +341,7 @@ class ApiClient: If obj is str, int, long, float, bool, return directly. If obj is datetime.datetime, datetime.date convert to string in iso8601 format. + If obj is decimal.Decimal return string representation. If obj is list, sanitize each element in the list. If obj is dict, return the dict. If obj is OpenAPI model, return the properties dict. @@ -364,6 +367,8 @@ class ApiClient: ) elif isinstance(obj, (datetime.datetime, datetime.date)): return obj.isoformat() + elif isinstance(obj, decimal.Decimal): + return str(obj) elif isinstance(obj, dict): obj_dict = obj @@ -455,6 +460,8 @@ class ApiClient: return self.__deserialize_date(data) elif klass == datetime.datetime: return self.__deserialize_datetime(data) + elif klass == decimal.Decimal: + return decimal.Decimal(data) elif issubclass(klass, Enum): return self.__deserialize_enum(data, klass) else: diff --git a/samples/client/echo_api/python/openapi_client/api_client.py b/samples/client/echo_api/python/openapi_client/api_client.py index feef534a2f2..c6fa28de319 100644 --- a/samples/client/echo_api/python/openapi_client/api_client.py +++ b/samples/client/echo_api/python/openapi_client/api_client.py @@ -16,6 +16,7 @@ import datetime from dateutil.parser import parse from enum import Enum +import decimal import json import mimetypes import os @@ -67,6 +68,7 @@ class ApiClient: 'bool': bool, 'date': datetime.date, 'datetime': datetime.datetime, + 'decimal': decimal.Decimal, 'object': object, } _pool = None @@ -339,6 +341,7 @@ class ApiClient: If obj is str, int, long, float, bool, return directly. If obj is datetime.datetime, datetime.date convert to string in iso8601 format. + If obj is decimal.Decimal return string representation. If obj is list, sanitize each element in the list. If obj is dict, return the dict. If obj is OpenAPI model, return the properties dict. @@ -364,6 +367,8 @@ class ApiClient: ) elif isinstance(obj, (datetime.datetime, datetime.date)): return obj.isoformat() + elif isinstance(obj, decimal.Decimal): + return str(obj) elif isinstance(obj, dict): obj_dict = obj @@ -455,6 +460,8 @@ class ApiClient: return self.__deserialize_date(data) elif klass == datetime.datetime: return self.__deserialize_datetime(data) + elif klass == decimal.Decimal: + return decimal.Decimal(data) elif issubclass(klass, Enum): return self.__deserialize_enum(data, klass) else: diff --git a/samples/openapi3/client/petstore/python-aiohttp/petstore_api/api_client.py b/samples/openapi3/client/petstore/python-aiohttp/petstore_api/api_client.py index 302e5dac53c..db57dd1827b 100644 --- a/samples/openapi3/client/petstore/python-aiohttp/petstore_api/api_client.py +++ b/samples/openapi3/client/petstore/python-aiohttp/petstore_api/api_client.py @@ -15,6 +15,7 @@ import datetime from dateutil.parser import parse from enum import Enum +import decimal import json import mimetypes import os @@ -66,6 +67,7 @@ class ApiClient: 'bool': bool, 'date': datetime.date, 'datetime': datetime.datetime, + 'decimal': decimal.Decimal, 'object': object, } _pool = None @@ -341,6 +343,7 @@ class ApiClient: If obj is str, int, long, float, bool, return directly. If obj is datetime.datetime, datetime.date convert to string in iso8601 format. + If obj is decimal.Decimal return string representation. If obj is list, sanitize each element in the list. If obj is dict, return the dict. If obj is OpenAPI model, return the properties dict. @@ -366,6 +369,8 @@ class ApiClient: ) elif isinstance(obj, (datetime.datetime, datetime.date)): return obj.isoformat() + elif isinstance(obj, decimal.Decimal): + return str(obj) elif isinstance(obj, dict): obj_dict = obj @@ -457,6 +462,8 @@ class ApiClient: return self.__deserialize_date(data) elif klass == datetime.datetime: return self.__deserialize_datetime(data) + elif klass == decimal.Decimal: + return decimal.Decimal(data) elif issubclass(klass, Enum): return self.__deserialize_enum(data, klass) else: diff --git a/samples/openapi3/client/petstore/python/petstore_api/api_client.py b/samples/openapi3/client/petstore/python/petstore_api/api_client.py index 9ab0756d53b..cb831d0de81 100755 --- a/samples/openapi3/client/petstore/python/petstore_api/api_client.py +++ b/samples/openapi3/client/petstore/python/petstore_api/api_client.py @@ -15,6 +15,7 @@ import datetime from dateutil.parser import parse from enum import Enum +import decimal import json import mimetypes import os @@ -66,6 +67,7 @@ class ApiClient: 'bool': bool, 'date': datetime.date, 'datetime': datetime.datetime, + 'decimal': decimal.Decimal, 'object': object, } _pool = None @@ -338,6 +340,7 @@ class ApiClient: If obj is str, int, long, float, bool, return directly. If obj is datetime.datetime, datetime.date convert to string in iso8601 format. + If obj is decimal.Decimal return string representation. If obj is list, sanitize each element in the list. If obj is dict, return the dict. If obj is OpenAPI model, return the properties dict. @@ -363,6 +366,8 @@ class ApiClient: ) elif isinstance(obj, (datetime.datetime, datetime.date)): return obj.isoformat() + elif isinstance(obj, decimal.Decimal): + return str(obj) elif isinstance(obj, dict): obj_dict = obj @@ -454,6 +459,8 @@ class ApiClient: return self.__deserialize_date(data) elif klass == datetime.datetime: return self.__deserialize_datetime(data) + elif klass == decimal.Decimal: + return decimal.Decimal(data) elif issubclass(klass, Enum): return self.__deserialize_enum(data, klass) else: diff --git a/samples/openapi3/client/petstore/python/tests/test_api_client.py b/samples/openapi3/client/petstore/python/tests/test_api_client.py index e80bc135296..b444f326c67 100644 --- a/samples/openapi3/client/petstore/python/tests/test_api_client.py +++ b/samples/openapi3/client/petstore/python/tests/test_api_client.py @@ -10,6 +10,7 @@ $ pytest """ import unittest +from decimal import Decimal from enum import Enum from dateutil.parser import parse @@ -180,6 +181,11 @@ class ApiClientTests(unittest.TestCase): result = self.api_client.sanitize_for_serialization(data) self.assertEqual(result, "1997-07-16T19:20:30.450000+01:00") + def test_sanitize_for_serialization_decimal(self): + data = Decimal("1.0") + result = self.api_client.sanitize_for_serialization(data) + self.assertEquals(result, "1.0") + def test_sanitize_for_serialization_list_enum(self): class EnumSerialization(int, Enum): NUMBER_0 = 0 diff --git a/samples/openapi3/client/petstore/python/tests/test_deserialization.py b/samples/openapi3/client/petstore/python/tests/test_deserialization.py index 8fd654e4bb7..11b2955015a 100644 --- a/samples/openapi3/client/petstore/python/tests/test_deserialization.py +++ b/samples/openapi3/client/petstore/python/tests/test_deserialization.py @@ -14,6 +14,7 @@ import os import time import unittest import datetime +from decimal import Decimal import pytest as pytest @@ -130,6 +131,15 @@ class DeserializationTests(unittest.TestCase): deserialized = self.deserialize(response, "datetime", 'application/json') self.assertTrue(isinstance(deserialized, datetime.datetime)) + def test_deserialize_decimal(self): + """ deserialize decimal """ + data = 1.1 + response = json.dumps(data) + + deserialized = self.deserialize(response, "decimal", 'application/json') + self.assertTrue(isinstance(deserialized, Decimal)) + self.assertEqual(deserialized, Decimal(1.1)) + def test_deserialize_pet(self): """ deserialize pet """ data = {