diff --git a/docs/generators/python.md b/docs/generators/python.md index f62f396f2786..d82d0e630ff2 100644 --- a/docs/generators/python.md +++ b/docs/generators/python.md @@ -27,7 +27,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl |hideGenerationTimestamp|Hides the generation timestamp when files are generated.| |true| |lazyImports|Enable lazy imports.| |false| |library|library template (sub-template) to use: asyncio, tornado (deprecated), urllib3, httpx| |urllib3| -|mapNumberTo|Map number to Union[StrictFloat, StrictInt], StrictStr or float.| |Union[StrictFloat, StrictInt]| +|mapNumberTo|Map number to Union[StrictFloat, StrictInt], StrictFloat, float or Decimal.| |Union[StrictFloat, StrictInt]| |packageName|python package name (convention: snake_case).| |openapi_client| |packageUrl|python package URL.| |null| |packageVersion|python package version.| |1.0.0| diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractPythonCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractPythonCodegen.java index d741ba71d1e2..3e9ab4d0ff8b 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractPythonCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractPythonCodegen.java @@ -54,7 +54,7 @@ public abstract class AbstractPythonCodegen extends DefaultCodegen implements Co public static final String MAP_NUMBER_TO = "mapNumberTo"; public static final String PYDANTIC = "pydantic"; public static final Set SUPPORTED_NUMBER_MAPPINGS = - Set.of("Union[StrictFloat, StrictInt]", "StrictFloat", "float"); + Set.of("Union[StrictFloat, StrictInt]", "StrictFloat", "float", "Decimal"); protected String packageName = "openapi_client"; @Setter protected String packageVersion = "1.0.0"; @@ -1953,6 +1953,8 @@ private PythonType numberType(IJsonSchemaValidationProperties cp) { } else if ("StrictFloat".equals(mapNumberTo)) { floatt.constrain("strict", true); return floatt; + } else if (DECIMAL.equals(mapNumberTo)) { + return decimalType(cp); } else { // float return floatt; } @@ -1968,6 +1970,9 @@ private PythonType numberType(IJsonSchemaValidationProperties cp) { } else if ("StrictFloat".equals(mapNumberTo)) { moduleImports.add(PYDANTIC, "StrictFloat"); return new PythonType("StrictFloat"); + } else if (DECIMAL.equals(mapNumberTo)) { + moduleImports.add("decimal", DECIMAL); + return new PythonType(DECIMAL); } else { return new PythonType("float"); } @@ -2277,7 +2282,7 @@ private PythonType getType(CodegenParameter cp) { private String finalizeType(CodegenParameter cp, PythonType pt) { if (!cp.required || cp.isNullable) { - moduleImports.add("typing", "Optional"); + moduleImports.add(TYPING, "Optional"); PythonType opt = new PythonType("Optional"); opt.addTypeParam(pt); pt = opt; diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PythonClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PythonClientCodegen.java index c12cab69b103..16c0f3ce8e0d 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PythonClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PythonClientCodegen.java @@ -149,7 +149,7 @@ public PythonClientCodegen() { cliOptions.add(new CliOption(SET_ENSURE_ASCII_TO_FALSE, "When set to true, add `ensure_ascii=False` in json.dumps when creating the HTTP request body.") .defaultValue(Boolean.FALSE.toString())); cliOptions.add(new CliOption(RECURSION_LIMIT, "Set the recursion limit. If not set, use the system default value.")); - cliOptions.add(new CliOption(MAP_NUMBER_TO, "Map number to Union[StrictFloat, StrictInt], StrictStr or float.") + cliOptions.add(new CliOption(MAP_NUMBER_TO, "Map number to Union[StrictFloat, StrictInt], StrictFloat, float or Decimal.") .defaultValue("Union[StrictFloat, StrictInt]")); cliOptions.add(new CliOption(DATETIME_FORMAT, "datetime format for query parameters") .defaultValue("%Y-%m-%dT%H:%M:%S%z")); diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/python/PythonClientCodegenTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/python/PythonClientCodegenTest.java index 1ac8d68d4303..6f9a00780e6d 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/python/PythonClientCodegenTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/python/PythonClientCodegenTest.java @@ -29,6 +29,7 @@ import org.openapitools.codegen.languages.PythonClientCodegen; import org.openapitools.codegen.languages.features.CXFServerFeatures; import org.testng.Assert; +import org.testng.annotations.DataProvider; import org.testng.annotations.Test; import java.io.File; @@ -36,10 +37,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.function.Function; import java.util.stream.Collectors; @@ -771,4 +769,36 @@ public void testModelNameMappingDoesNotRenameParameters() { codegen.parameterNameMapping().put("some-value", "parameter_value"); Assert.assertEquals(codegen.toParamName("some-value"), "parameter_value"); } + + @Test(dataProvider = "numberMappings") + public void testMapNumberTo(String mapToNumber, String expectedType) throws IOException { + File output = Files.createTempDirectory("test").toFile().getCanonicalFile(); + output.deleteOnExit(); + + final CodegenConfigurator configurator = new CodegenConfigurator() + .setGeneratorName("python") + .setInputSpec("src/test/resources/3_0/echo_api.yaml") + .setOutputDir(output.getAbsolutePath()) + .addAdditionalProperty("mapNumberTo", mapToNumber); + + DefaultGenerator generator = new DefaultGenerator(); + List files = generator.opts(configurator.toClientOptInput()).generate(); + files.forEach(File::deleteOnExit); + + Path numberPropertiesOnlyPath = Paths.get(output.getAbsolutePath(), "openapi_client/models/number_properties_only.py"); + TestUtils.assertFileExists(numberPropertiesOnlyPath); + + TestUtils.assertFileContains(numberPropertiesOnlyPath, String.format(Locale.ROOT, "number: Optional[%s]", expectedType)); + TestUtils.assertFileContains(numberPropertiesOnlyPath, String.format(Locale.ROOT, "var_float: Optional[%s]", expectedType)); + } + + @DataProvider(name = "numberMappings") + public Object[][] numberMappings() { + return new Object[][] { + { "float", "float" }, + { "Decimal", "Decimal" }, + { "StrictFloat", "StrictFloat" }, + { "Union[StrictFloat, StrictInt]", "Union[StrictFloat, StrictInt]" } + }; + } }