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 612e7dc8f86d..5c0df0ab8178 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 @@ -683,12 +683,8 @@ public String toDefaultValue(Schema p) { if (p.getDefault() != null) { if (Pattern.compile("\r\n|\r|\n").matcher((String) p.getDefault()).find()) return "'''" + p.getDefault() + "'''"; - else if (p.getEnum() == null) - // wrap using double quotes to avoid the need to escape any embedded single quotes - return "\"" + p.getDefault() + "\""; else - // convert to enum var name later in postProcessModels - return (String) p.getDefault(); + return "'" + ((String) p.getDefault()).replaceAll("'", "\'") + "'"; } } else if (ModelUtils.isArraySchema(p)) { if (p.getDefault() != null) { @@ -711,7 +707,7 @@ public String toExampleValue(Schema schema) { private String toExampleValueRecursive(Schema schema, List included_schemas, int indentation) { String indentation_string = ""; - for (int i = 0; i < indentation; i++) indentation_string += " "; + for (int i=0 ; i< indentation ; i++) indentation_string += " "; String example = super.toExampleValue(schema); if (ModelUtils.isNullType(schema) && null != example) { @@ -720,11 +716,9 @@ private String toExampleValueRecursive(Schema schema, List included_sche return "None"; } // correct "true"s into "True"s, since super.toExampleValue uses "toString()" on Java booleans - if (ModelUtils.isBooleanSchema(schema) && null != example) { - if ("false".equalsIgnoreCase(example)) - example = "False"; - else - example = "True"; + if (ModelUtils.isBooleanSchema(schema) && null!=example) { + if ("false".equalsIgnoreCase(example)) example = "False"; + else example = "True"; } // correct "'"s into "'"s after toString() @@ -734,14 +728,13 @@ private String toExampleValueRecursive(Schema schema, List included_sche if (StringUtils.isNotBlank(example) && !"null".equals(example)) { if (ModelUtils.isStringSchema(schema)) { - // wrap using double quotes to avoid the need to escape any embedded single quotes - example = "\"" + example + "\""; + example = "'" + example + "'"; } return example; } if (schema.getEnum() != null && !schema.getEnum().isEmpty()) { - // Enum case: + // Enum case: example = schema.getEnum().get(0).toString(); if (ModelUtils.isStringSchema(schema)) { example = "'" + escapeText(example) + "'"; @@ -751,7 +744,7 @@ private String toExampleValueRecursive(Schema schema, List included_sche return example; } else if (null != schema.get$ref()) { - // $ref case: + // $ref case: Map allDefinitions = ModelUtils.getSchemas(this.openAPI); String ref = ModelUtils.getSimpleRef(schema.get$ref()); if (allDefinitions != null) { @@ -785,22 +778,13 @@ private String toExampleValueRecursive(Schema schema, List included_sche example = "YQ=="; } else if (ModelUtils.isStringSchema(schema)) { // a BigDecimal: - if ("Number".equalsIgnoreCase(schema.getFormat())) { - return "1"; - } - if (StringUtils.isNotBlank(schema.getPattern())) - return "'a'"; // I cheat here, since it would be too complicated to generate a string from a regexp - + if ("Number".equalsIgnoreCase(schema.getFormat())) {return "1";} + if (StringUtils.isNotBlank(schema.getPattern())) return "'a'"; // I cheat here, since it would be too complicated to generate a string from a regexp int len = 0; - - if (null != schema.getMinLength()) - len = schema.getMinLength().intValue(); - - if (len < 1) - len = 1; - + if (null != schema.getMinLength()) len = schema.getMinLength().intValue(); + if (len < 1) len = 1; example = ""; - for (int i = 0; i < len; i++) example += i; + for (int i=0;i included_sche included_schemas.add(schema.getTitle()); } ArraySchema arrayschema = (ArraySchema) schema; - example = "[\n" + indentation_string + toExampleValueRecursive(arrayschema.getItems(), included_schemas, indentation + 1) + "\n" + indentation_string + "]"; + example = "[\n" + indentation_string + toExampleValueRecursive(arrayschema.getItems(), included_schemas, indentation+1) + "\n" + indentation_string + "]"; } else if (ModelUtils.isMapSchema(schema)) { if (StringUtils.isNotBlank(schema.getTitle()) && !"null".equals(schema.getTitle())) { included_schemas.add(schema.getTitle()); @@ -833,7 +817,7 @@ private String toExampleValueRecursive(Schema schema, List included_sche the_key = "'" + escapeText(the_key) + "'"; } } - example = "{\n" + indentation_string + the_key + " : " + toExampleValueRecursive(additional, included_schemas, indentation + 1) + "\n" + indentation_string + "}"; + example = "{\n" + indentation_string + the_key + " : " + toExampleValueRecursive(additional, included_schemas, indentation+1) + "\n" + indentation_string + "}"; } else { example = "{ }"; } @@ -845,11 +829,11 @@ private String toExampleValueRecursive(Schema schema, List included_sche // I remove any property that is a discriminator, since it is not well supported by the python generator String toExclude = null; - if (schema.getDiscriminator() != null) { + if (schema.getDiscriminator()!=null) { toExclude = schema.getDiscriminator().getPropertyName(); } - example = packageName + ".models." + underscore(schema.getTitle()) + "." + schema.getTitle() + "("; + example = packageName + ".models." + underscore(schema.getTitle())+"."+schema.getTitle()+"("; // if required only: // List reqs = schema.getRequired(); @@ -863,8 +847,7 @@ private String toExampleValueRecursive(Schema schema, List included_sche Map properties = schema.getProperties(); Set propkeys = null; - if (properties != null) - propkeys = properties.keySet(); + if (properties != null) propkeys = properties.keySet(); if (toExclude != null && reqs.contains(toExclude)) { reqs.remove(toExclude); } @@ -891,7 +874,7 @@ private String toExampleValueRecursive(Schema schema, List included_sche } } } - example += ")"; + example +=")"; } else { LOGGER.warn("Type " + schema.getType() + " not handled properly in toExampleValue"); } @@ -919,45 +902,43 @@ public void setParameterExampleValue(CodegenParameter p) { type = p.dataType; } - if (type != null) { - if ("String".equalsIgnoreCase(type) || "str".equalsIgnoreCase(type)) { - if (example == null) { - example = p.paramName + "_example"; - } - example = "'" + escapeText(example) + "'"; - } else if ("Integer".equals(type) || "int".equals(type)) { - if (example == null) { - example = "56"; - } - } else if ("Float".equalsIgnoreCase(type) || "Double".equalsIgnoreCase(type)) { - if (example == null) { - example = "3.4"; - } - } else if ("BOOLEAN".equalsIgnoreCase(type) || "bool".equalsIgnoreCase(type)) { - if (example == null) { - example = "True"; - } - } else if ("file".equalsIgnoreCase(type)) { - if (example == null) { - example = "/path/to/file"; - } - example = "'" + escapeText(example) + "'"; - } else if ("Date".equalsIgnoreCase(type)) { - if (example == null) { - example = "2013-10-20"; - } - example = "'" + escapeText(example) + "'"; - } else if ("DateTime".equalsIgnoreCase(type)) { - if (example == null) { - example = "2013-10-20T19:20:30+01:00"; - } - example = "'" + escapeText(example) + "'"; - } else if (!languageSpecificPrimitives.contains(type)) { - // type is a model class, e.g. User - example = this.packageName + "." + type + "()"; - } else { - LOGGER.warn("Type " + type + " not handled properly in setParameterExampleValue"); + if ("String".equalsIgnoreCase(type) || "str".equalsIgnoreCase(type)) { + if (example == null) { + example = p.paramName + "_example"; + } + example = "'" + escapeText(example) + "'"; + } else if ("Integer".equals(type) || "int".equals(type)) { + if (example == null) { + example = "56"; + } + } else if ("Float".equalsIgnoreCase(type) || "Double".equalsIgnoreCase(type)) { + if (example == null) { + example = "3.4"; + } + } else if ("BOOLEAN".equalsIgnoreCase(type) || "bool".equalsIgnoreCase(type)) { + if (example == null) { + example = "True"; + } + } else if ("file".equalsIgnoreCase(type)) { + if (example == null) { + example = "/path/to/file"; + } + example = "'" + escapeText(example) + "'"; + } else if ("Date".equalsIgnoreCase(type)) { + if (example == null) { + example = "2013-10-20"; } + example = "'" + escapeText(example) + "'"; + } else if ("DateTime".equalsIgnoreCase(type)) { + if (example == null) { + example = "2013-10-20T19:20:30+01:00"; + } + example = "'" + escapeText(example) + "'"; + } else if (!languageSpecificPrimitives.contains(type)) { + // type is a model class, e.g. User + example = this.packageName + "." + type + "()"; + } else { + LOGGER.warn("Type " + type + " not handled properly in setParameterExampleValue"); } if (example == null) { diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PythonClientExperimentalCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PythonClientExperimentalCodegen.java index 8fff23a7af31..384960012385 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PythonClientExperimentalCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PythonClientExperimentalCodegen.java @@ -17,24 +17,30 @@ package org.openapitools.codegen.languages; import io.swagger.v3.oas.models.Operation; +import io.swagger.v3.oas.models.examples.Example; import io.swagger.v3.oas.models.media.*; +import io.swagger.v3.oas.models.media.ArraySchema; +import io.swagger.v3.oas.models.media.MediaType; +import io.swagger.v3.oas.models.media.Schema; +import io.swagger.v3.oas.models.parameters.Parameter; import io.swagger.v3.oas.models.parameters.RequestBody; import io.swagger.v3.oas.models.responses.ApiResponse; import io.swagger.v3.oas.models.security.SecurityScheme; +import org.apache.commons.io.FilenameUtils; import org.apache.commons.lang3.StringUtils; import org.openapitools.codegen.*; import org.openapitools.codegen.examples.ExampleGenerator; -import org.openapitools.codegen.meta.GeneratorMetadata; -import org.openapitools.codegen.meta.Stability; import org.openapitools.codegen.meta.features.*; import org.openapitools.codegen.utils.ModelUtils; import org.openapitools.codegen.utils.ProcessUtils; +import org.openapitools.codegen.meta.GeneratorMetadata; +import org.openapitools.codegen.meta.Stability; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.File; import java.text.DateFormat; import java.text.SimpleDateFormat; +import java.io.File; import java.util.*; import java.util.regex.Pattern; @@ -77,7 +83,7 @@ public PythonClientExperimentalCodegen() { .excludeParameterFeatures( ParameterFeature.Cookie ) - ); + ); // this may set datatype right for additional properties instantiationTypes.put("map", "dict"); @@ -139,7 +145,7 @@ public void processOpts() { // Generate the 'signing.py' module, but only if the 'HTTP signature' security scheme is specified in the OAS. Map securitySchemeMap = openAPI != null ? - (openAPI.getComponents() != null ? openAPI.getComponents().getSecuritySchemes() : null) : null; + (openAPI.getComponents() != null ? openAPI.getComponents().getSecuritySchemes() : null) : null; List authMethods = fromSecurity(securitySchemeMap); if (ProcessUtils.hasHttpSignatureMethods(authMethods)) { supportingFiles.add(new SupportingFile("python-experimental/signing.mustache", packagePath(), "signing.py")); @@ -166,12 +172,12 @@ public void processOpts() { supportingFiles.add(new SupportingFile(readmeTemplate, "", readmePath)); if (!generateSourceCodeOnly) { - supportingFiles.remove(new SupportingFile("setup.mustache", "", "setup.py")); - supportingFiles.add(new SupportingFile("python-experimental/setup.mustache", "", "setup.py")); - supportingFiles.remove(new SupportingFile("requirements.mustache", "", "requirements.txt")); - supportingFiles.add(new SupportingFile("python-experimental/requirements.mustache", "", "requirements.txt")); - supportingFiles.remove(new SupportingFile("test-requirements.mustache", "", "test-requirements.txt")); - supportingFiles.add(new SupportingFile("python-experimental/test-requirements.mustache", "", "test-requirements.txt")); + supportingFiles.remove(new SupportingFile("setup.mustache", "", "setup.py")); + supportingFiles.add(new SupportingFile("python-experimental/setup.mustache", "", "setup.py")); + supportingFiles.remove(new SupportingFile("requirements.mustache", "", "requirements.txt")); + supportingFiles.add(new SupportingFile("python-experimental/requirements.mustache", "", "requirements.txt")); + supportingFiles.remove(new SupportingFile("test-requirements.mustache", "", "test-requirements.txt")); + supportingFiles.add(new SupportingFile("python-experimental/test-requirements.mustache", "", "test-requirements.txt")); } // default this to true so the python ModelSimple models will be generated @@ -299,7 +305,7 @@ public String toModelImport(String name) { // name looks like cat.Cat String moduleName = name.split("\\.")[0]; // https://exceptionshub.com/circular-or-cyclic-imports-in-python.html - return "from " + modelPackage() + " import " + moduleName; + return "from " + modelPackage() + " import "+ moduleName; } private String robustImport(String name) { @@ -307,8 +313,8 @@ private String robustImport(String name) { String moduleName = name.split("\\.")[0]; // https://exceptionshub.com/circular-or-cyclic-imports-in-python.html String modelImport = "try:\n from " + modelPackage() + - " import " + moduleName + "\nexcept ImportError:\n " + - moduleName + " = sys.modules[\n '" + modelPackage() + "." + moduleName + "']"; + " import " + moduleName+ "\nexcept ImportError:\n " + + moduleName + " = sys.modules[\n '" + modelPackage() + "." + moduleName + "']"; return modelImport; } @@ -340,9 +346,9 @@ private void fixOperationImports(Set imports) { @Override @SuppressWarnings("static-method") public Map postProcessOperationsWithModels(Map objs, List allModels) { - HashMap val = (HashMap) objs.get("operations"); + HashMap val = (HashMap)objs.get("operations"); ArrayList operations = (ArrayList) val.get("operation"); - ArrayList> imports = (ArrayList>) objs.get("imports"); + ArrayList> imports = (ArrayList>)objs.get("imports"); imports.clear(); for (CodegenOperation operation : operations) { fixOperationImports(operation.imports); @@ -371,7 +377,7 @@ private void fixModelImports(Set imports) { /** * Override with special post-processing for all models. - */ + */ @SuppressWarnings({"static-method", "unchecked"}) public Map postProcessAllModels(Map objs) { // loop through all models and delete ones where type!=object and the model has no validations and enums @@ -714,7 +720,7 @@ public void postProcessModelProperty(CodegenModel model, CodegenProperty p) { public void postProcessParameter(CodegenParameter p) { postProcessPattern(p.pattern, p.vendorExtensions); // set baseType to null so the api docs will not point to a model for languageSpecificPrimitives - if (p.baseType != null && languageSpecificPrimitives.contains(p.baseType)) { + if (p.baseType != null && languageSpecificPrimitives.contains(p.baseType)){ p.baseType = null; } else if (p.isListContainer && p.mostInnerItems.complexType != null && !languageSpecificPrimitives.contains(p.mostInnerItems.complexType)) { // fix ListContainers @@ -722,7 +728,7 @@ public void postProcessParameter(CodegenParameter p) { } } - private void addNullDefaultToOneOfAnyOfReqProps(Schema schema, CodegenModel result) { + private void addNullDefaultToOneOfAnyOfReqProps(Schema schema, CodegenModel result){ // for composed schema models, if the required properties are only from oneOf or anyOf models // give them a nulltype.Null so the user can omit including them in python ComposedSchema cs = (ComposedSchema) schema; @@ -744,7 +750,7 @@ private void addNullDefaultToOneOfAnyOfReqProps(Schema schema, CodegenModel resu if (anyOf != null) { oneOfanyOfSchemas.addAll(anyOf); } - for (Schema sc : oneOfanyOfSchemas) { + for (Schema sc: oneOfanyOfSchemas) { Schema refSchema = ModelUtils.getReferencedSchema(this.openAPI, sc); addProperties(otherProperties, otherRequired, refSchema); } @@ -762,7 +768,7 @@ private void addNullDefaultToOneOfAnyOfReqProps(Schema schema, CodegenModel resu List reqVars = result.getRequiredVars(); if (reqVars != null) { - for (CodegenProperty cp : reqVars) { + for (CodegenProperty cp: reqVars) { String propName = cp.baseName; if (otherRequiredSet.contains(propName) && !selfRequiredSet.contains(propName)) { // if var is in otherRequiredSet and is not in selfRequiredSet and is in result.requiredVars @@ -899,20 +905,20 @@ public String getSimpleTypeDeclaration(Schema schema) { * Primitive types in the OAS specification are implemented in Python using the corresponding * Python primitive types. * Composed types (e.g. allAll, oneOf, anyOf) are represented in Python using list of types. - *

+ * * The caller should set the prefix and suffix arguments to empty string, except when * getTypeString invokes itself recursively. A non-empty prefix/suffix may be specified * to wrap the return value in a python dict, list or tuple. - *

+ * * Examples: * - "bool, date, float" The data must be a bool, date or float. * - "[bool, date]" The data must be an array, and the array items must be a bool or date. - * - * @param p The OAS schema. - * @param prefix prepended to the returned value. - * @param suffix appended to the returned value. + * + * @param p The OAS schema. + * @param prefix prepended to the returned value. + * @param suffix appended to the returned value. * @param referencedModelNames a list of models that are being referenced while generating the types, - * may be used to generate imports. + * may be used to generate imports. * @return a comma-separated string representation of the Python types */ private String getTypeString(Schema p, String prefix, String suffix, List referencedModelNames) { @@ -962,7 +968,7 @@ private String getTypeString(Schema p, String prefix, String suffix, List()); - - Assert.assertEquals(codegenParameter.defaultValue, "-efg"); - } - - @Test - public void testExample1() { - final OpenAPI openAPI = TestUtils.parseFlattenSpec("src/test/resources/3_0/examples.yaml"); - final PythonClientCodegen codegen = new PythonClientCodegen(); - - Operation operation = openAPI.getPaths().get("/example1/singular").getGet(); - CodegenParameter codegenParameter = CodegenModelFactory.newInstance(CodegenModelType.PARAMETER); - codegen.setParameterExampleValue(codegenParameter, operation.getParameters().get(0)); - - Assert.assertEquals(codegenParameter.example, "example1 value"); - - Operation operation2 = openAPI.getPaths().get("/example1/plural").getGet(); - CodegenParameter codegenParameter2 = CodegenModelFactory.newInstance(CodegenModelType.PARAMETER); - codegen.setParameterExampleValue(codegenParameter2, operation2.getParameters().get(0)); - - Assert.assertEquals(codegenParameter2.example, "An example1 value"); - } - - @Test - public void testExample2() { - final OpenAPI openAPI = TestUtils.parseFlattenSpec("src/test/resources/3_0/examples.yaml"); - final PythonClientCodegen codegen = new PythonClientCodegen(); - - Operation operation = openAPI.getPaths().get("/example2/singular").getGet(); - CodegenParameter codegenParameter = CodegenModelFactory.newInstance(CodegenModelType.PARAMETER); - codegen.setParameterExampleValue(codegenParameter, operation.getParameters().get(0)); - - Assert.assertEquals(codegenParameter.example, "example2 value"); - } - - @Test - public void testExample3() { - final OpenAPI openAPI = TestUtils.parseFlattenSpec("src/test/resources/3_0/examples.yaml"); - final PythonClientCodegen codegen = new PythonClientCodegen(); - - Operation operation = openAPI.getPaths().get("/example3/singular").getGet(); - CodegenParameter codegenParameter = CodegenModelFactory.newInstance(CodegenModelType.PARAMETER); - codegen.setParameterExampleValue(codegenParameter, operation.getParameters().get(0)); - - Assert.assertEquals(codegenParameter.example, "example3: parameter value"); - - Operation operation2 = openAPI.getPaths().get("/example3/plural").getGet(); - CodegenParameter codegenParameter2 = CodegenModelFactory.newInstance(CodegenModelType.PARAMETER); - codegen.setParameterExampleValue(codegenParameter2, operation2.getParameters().get(0)); - - Assert.assertEquals(codegenParameter2.example, "example3: parameter value"); - } - - @Test - public void testExample4() { - final OpenAPI openAPI = TestUtils.parseFlattenSpec("src/test/resources/3_0/examples.yaml"); - final PythonClientCodegen codegen = new PythonClientCodegen(); - - Operation operation = openAPI.getPaths().get("/example4/singular").getPost(); - CodegenParameter codegenParameter = CodegenModelFactory.newInstance(CodegenModelType.PARAMETER); - codegen.setParameterExampleValue(codegenParameter, operation.getRequestBody()); - - Assert.assertEquals(codegenParameter.example, "example4 value"); - - Operation operation2 = openAPI.getPaths().get("/example4/plural").getPost(); - CodegenParameter codegenParameter2 = CodegenModelFactory.newInstance(CodegenModelType.PARAMETER); - codegen.setParameterExampleValue(codegenParameter2, operation2.getRequestBody()); - - Assert.assertEquals(codegenParameter2.example, "An example4 value"); + Assert.assertEquals("'Text containing \'single\' quote'", defaultValue); } } diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/python/PythonClientExperimentalTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/python/PythonClientExperimentalTest.java index 04424369ea1d..c7988952b7f5 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/python/PythonClientExperimentalTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/python/PythonClientExperimentalTest.java @@ -31,7 +31,7 @@ public class PythonClientExperimentalTest { @Test(description = "convert a python model with dots") public void modelTest() { - final OpenAPI openAPI = TestUtils.parseFlattenSpec("src/test/resources/2_0/v1beta3.json"); + final OpenAPI openAPI= TestUtils.parseFlattenSpec("src/test/resources/2_0/v1beta3.json"); final DefaultCodegen codegen = new PythonClientExperimentalCodegen(); codegen.setOpenAPI(openAPI);