Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* Copyright 2018 OpenAPI-Generator Contributors (https://openapi-generator.tech)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.openapitools.codegen.languages;
import com.google.common.collect.Sets;
import io.swagger.v3.core.util.Json;
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.security.SecurityScheme;
import org.apache.commons.lang3.StringUtils;
import org.openapitools.codegen.*;
import org.openapitools.codegen.CodegenDiscriminator.MappedModel;
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 com.github.curiousoddman.rgxgen.RgxGen;
import com.github.curiousoddman.rgxgen.config.RgxGenOption;
import com.github.curiousoddman.rgxgen.config.RgxGenProperties;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.io.File;
import java.util.*;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
import static org.openapitools.codegen.utils.OnceLogger.once;
public class PythonClientCodegen extends PythonLegacyClientCodegen {
private final Logger LOGGER = LoggerFactory.getLogger(PythonClientCodegen.class);
// A cache to efficiently lookup a Schema instance based on the return value of `toModelName()`.
private Map modelNameToSchemaCache;
private DateTimeFormatter iso8601Date = DateTimeFormatter.ISO_DATE;
private DateTimeFormatter iso8601DateTime = DateTimeFormatter.ISO_DATE_TIME;
public PythonClientCodegen() {
super();
embeddedTemplateDir = templateDir = "python";
// Composed schemas can have the 'additionalProperties' keyword, as specified in JSON schema.
// In principle, this should be enabled by default for all code generators. However due to limitations
// in other code generators, support needs to be enabled on a case-by-case basis.
supportsAdditionalPropertiesWithComposedSchema = true;
modifyFeatureSet(features -> features
.includeDocumentationFeatures(DocumentationFeature.Readme)
.wireFormatFeatures(EnumSet.of(WireFormatFeature.JSON, WireFormatFeature.XML, WireFormatFeature.Custom))
.securityFeatures(EnumSet.of(
SecurityFeature.BasicAuth,
SecurityFeature.BearerToken,
SecurityFeature.ApiKey,
SecurityFeature.OAuth2_Implicit
))
.includeGlobalFeatures(
GlobalFeature.ParameterizedServer
)
.excludeGlobalFeatures(
GlobalFeature.XMLStructureDefinitions,
GlobalFeature.Callbacks,
GlobalFeature.LinkObjects,
GlobalFeature.ParameterStyling
)
.excludeSchemaSupportFeatures(
SchemaSupportFeature.Polymorphism
)
.excludeParameterFeatures(
ParameterFeature.Cookie
)
);
// needed for type object with additionalProperties: false
typeMapping.put("object", "dict");
languageSpecificPrimitives.add("file_type");
languageSpecificPrimitives.add("none_type");
// this generator does not use SORT_PARAMS_BY_REQUIRED_FLAG
// this generator uses the following order for endpoint parameters and model properties
// required params/props with no enum of length one
// required params/props with enum of length one (which is used to set a default value as a python named arg value)
// optional params/props with **kwargs in python
cliOptions.remove(4);
cliOptions.add(new CliOption(CodegenConstants.PYTHON_ATTR_NONE_IF_UNSET, CodegenConstants.PYTHON_ATTR_NONE_IF_UNSET_DESC)
.defaultValue(Boolean.FALSE.toString()));
// option to change how we process + set the data in the 'additionalProperties' keyword.
CliOption disallowAdditionalPropertiesIfNotPresentOpt = CliOption.newBoolean(
CodegenConstants.DISALLOW_ADDITIONAL_PROPERTIES_IF_NOT_PRESENT,
CodegenConstants.DISALLOW_ADDITIONAL_PROPERTIES_IF_NOT_PRESENT_DESC).defaultValue(Boolean.FALSE.toString());
Map disallowAdditionalPropertiesIfNotPresentOpts = new HashMap<>();
disallowAdditionalPropertiesIfNotPresentOpts.put("false",
"The 'additionalProperties' implementation is compliant with the OAS and JSON schema specifications.");
disallowAdditionalPropertiesIfNotPresentOpts.put("true",
"Keep the old (incorrect) behaviour that 'additionalProperties' is set to false by default. NOTE: "+
"this option breaks composition and will be removed in 6.0.0"
);
disallowAdditionalPropertiesIfNotPresentOpt.setEnum(disallowAdditionalPropertiesIfNotPresentOpts);
cliOptions.add(disallowAdditionalPropertiesIfNotPresentOpt);
generatorMetadata = GeneratorMetadata.newBuilder(generatorMetadata)
.stability(Stability.EXPERIMENTAL)
.build();
}
@Override
public void processOpts() {
this.setLegacyDiscriminatorBehavior(false);
super.processOpts();
modelPackage = packageName + "." + "model";
supportingFiles.add(new SupportingFile("model_utils.mustache", packagePath(), "model_utils.py"));
// add the models and apis folders
supportingFiles.add(new SupportingFile("__init__models.mustache", packagePath() + File.separatorChar + "models", "__init__.py"));
SupportingFile originalInitModel = supportingFiles.stream()
.filter(sf -> sf.getTemplateFile().equals("__init__model.mustache"))
.reduce((a, b) -> {
throw new IllegalStateException("Multiple elements: " + a + ", " + b);
})
.get();
supportingFiles.remove(originalInitModel);
supportingFiles.add(new SupportingFile("__init__model.mustache", packagePath() + File.separatorChar + "model", "__init__.py"));
supportingFiles.add(new SupportingFile("__init__apis.mustache", packagePath() + File.separatorChar + "apis", "__init__.py"));
// 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;
List authMethods = fromSecurity(securitySchemeMap);
if (ProcessUtils.hasHttpSignatureMethods(authMethods)) {
supportingFiles.add(new SupportingFile("signing.mustache", packagePath(), "signing.py"));
}
Boolean generateSourceCodeOnly = false;
if (additionalProperties.containsKey(CodegenConstants.SOURCECODEONLY_GENERATION)) {
generateSourceCodeOnly = Boolean.valueOf(additionalProperties.get(CodegenConstants.SOURCECODEONLY_GENERATION).toString());
}
// default this to true so the python ModelSimple models will be generated
ModelUtils.setGenerateAliasAsModel(true);
LOGGER.info(
"{} is hard coded to true in this generator. Alias models will only be generated if they contain validations or enums",
CodegenConstants.GENERATE_ALIAS_AS_MODEL);
Boolean attrNoneIfUnset = false;
if (additionalProperties.containsKey(CodegenConstants.PYTHON_ATTR_NONE_IF_UNSET)) {
attrNoneIfUnset = Boolean.valueOf(additionalProperties.get(CodegenConstants.PYTHON_ATTR_NONE_IF_UNSET).toString());
}
additionalProperties.put("attrNoneIfUnset", attrNoneIfUnset);
// When the 'additionalProperties' keyword is not present in a OAS schema, allow
// undeclared properties. This is compliant with the JSON schema specification.
// setting this to false is required to have composed schemas work because:
// anyOf SchemaA + SchemaB, requires that props present only in A are accepted in B because in B
// they are additional properties
Boolean disallowAddProps = false;
if (additionalProperties.containsKey(CodegenConstants.DISALLOW_ADDITIONAL_PROPERTIES_IF_NOT_PRESENT)) {
disallowAddProps = Boolean.valueOf(additionalProperties.get(CodegenConstants.DISALLOW_ADDITIONAL_PROPERTIES_IF_NOT_PRESENT).toString());
}
this.setDisallowAdditionalPropertiesIfNotPresent(disallowAddProps);
// check library option to ensure only urllib3 is supported
if (!DEFAULT_LIBRARY.equals(getLibrary())) {
throw new RuntimeException("Only the `urllib3` library is supported in the refactored `python` client generator at the moment. Please fall back to `python-legacy` client generator for the time being. We welcome contributions to add back `asyncio`, `tornado` support to the `python` client generator.");
}
}
/**
* Configures a friendly name for the generator. This will be used by the
* generator to select the library with the -g flag.
*
* @return the friendly name for the generator
*/
@Override
public String getName() {
return "python";
}
@Override
public Schema unaliasSchema(Schema schema, Map usedImportMappings) {
Map allSchemas = ModelUtils.getSchemas(openAPI);
if (allSchemas == null || allSchemas.isEmpty()) {
// skip the warning as the spec can have no model defined
//LOGGER.warn("allSchemas cannot be null/empty in unaliasSchema. Returned 'schema'");
return schema;
}
if (schema != null && StringUtils.isNotEmpty(schema.get$ref())) {
String simpleRef = ModelUtils.getSimpleRef(schema.get$ref());
if (usedImportMappings.containsKey(simpleRef)) {
LOGGER.debug("Schema unaliasing of {} omitted because aliased class is to be mapped to {}", simpleRef, usedImportMappings.get(simpleRef));
return schema;
}
Schema ref = allSchemas.get(simpleRef);
if (ref == null) {
once(LOGGER).warn("{} is not defined", schema.get$ref());
return schema;
} else if (ref.getEnum() != null && !ref.getEnum().isEmpty()) {
// top-level enum class
return schema;
} else if (ModelUtils.isArraySchema(ref)) {
if (ModelUtils.isGenerateAliasAsModel(ref)) {
return schema; // generate a model extending array
} else {
return unaliasSchema(allSchemas.get(ModelUtils.getSimpleRef(schema.get$ref())),
usedImportMappings);
}
} else if (ModelUtils.isComposedSchema(ref)) {
return schema;
} else if (ModelUtils.isMapSchema(ref)) {
if (ref.getProperties() != null && !ref.getProperties().isEmpty()) // has at least one property
return schema; // treat it as model
else {
if (ModelUtils.isGenerateAliasAsModel(ref)) {
return schema; // generate a model extending map
} else {
// treat it as a typical map
return unaliasSchema(allSchemas.get(ModelUtils.getSimpleRef(schema.get$ref())),
usedImportMappings);
}
}
} else if (ModelUtils.isObjectSchema(ref)) { // model
if (ref.getProperties() != null && !ref.getProperties().isEmpty()) { // has at least one property
return schema;
} else {
// free form object (type: object)
if (ModelUtils.hasValidation(ref)) {
return schema;
} else if (!getAllOfDescendants(simpleRef, openAPI).isEmpty()) {
return schema;
} else {
return unaliasSchema(allSchemas.get(ModelUtils.getSimpleRef(schema.get$ref())),
usedImportMappings);
}
}
} else if (ModelUtils.hasValidation(ref)) {
// non object non array non map schemas that have validations
// are returned so we can generate those schemas as models
// we do this to:
// - preserve the validations in that model class in python
// - use those validations when we use this schema in composed oneOf schemas
return schema;
} else {
return unaliasSchema(allSchemas.get(ModelUtils.getSimpleRef(schema.get$ref())), usedImportMappings);
}
}
return schema;
}
public String pythonDate(Object dateValue) {
String strValue = null;
if (dateValue instanceof OffsetDateTime) {
OffsetDateTime date = null;
try {
date = (OffsetDateTime) dateValue;
} catch (ClassCastException e) {
LOGGER.warn("Invalid `date` format for value {}", dateValue);
date = ((Date) dateValue).toInstant().atOffset(ZoneOffset.UTC);
}
strValue = date.format(iso8601Date);
} else {
strValue = dateValue.toString();
}
return "dateutil_parser('" + strValue + "').date()";
}
public String pythonDateTime(Object dateTimeValue) {
String strValue = null;
if (dateTimeValue instanceof OffsetDateTime) {
OffsetDateTime dateTime = null;
try {
dateTime = (OffsetDateTime) dateTimeValue;
} catch (ClassCastException e) {
LOGGER.warn("Invalid `date-time` format for value {}", dateTimeValue);
dateTime = ((Date) dateTimeValue).toInstant().atOffset(ZoneOffset.UTC);
}
strValue = dateTime.format(iso8601DateTime);
} else {
strValue = dateTimeValue.toString();
}
return "dateutil_parser('" + strValue + "')";
}
/**
* Return the default value of the property
*
* @param p OpenAPI property object
* @return string presentation of the default value of the property
*/
@Override
public String toDefaultValue(Schema p) {
// if a variable has no default set and only has one allowed value
// using enum of length == 1 we use that value. Server/client usage:
// python servers: should only use default values for optional params
// python clients: should only use default values for required params
Object defaultObject = null;
if (p.getDefault() != null) {
defaultObject = p.getDefault();
} else if (p.getEnum() != null && p.getEnum().size() == 1) {
defaultObject = p.getEnum().get(0);
}
if (defaultObject == null) {
return null;
}
String defaultValue = defaultObject.toString();
if (ModelUtils.isDateSchema(p)) {
defaultValue = pythonDate(defaultObject);
} else if (ModelUtils.isDateTimeSchema(p)) {
defaultValue = pythonDateTime(defaultObject);
} else if (ModelUtils.isStringSchema(p) && !ModelUtils.isByteArraySchema(p) && !ModelUtils.isBinarySchema(p) && !ModelUtils.isFileSchema(p) && !ModelUtils.isUUIDSchema(p) && !ModelUtils.isEmailSchema(p)) {
defaultValue = ensureQuotes(defaultValue);
} else if (ModelUtils.isBooleanSchema(p)) {
if (!Boolean.valueOf(defaultValue)) {
defaultValue = "False";
} else {
defaultValue = "True";
}
}
return defaultValue;
}
@Override
public String toModelImport(String name) {
// name looks like Cat
return "from " + modelPackage() + "." + toModelFilename(name) + " import " + toModelName(name);
}
@Override
@SuppressWarnings("static-method")
public Map postProcessOperationsWithModels(Map objs, List