Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -520,6 +520,7 @@ public static enum ENUM_PROPERTY_NAMING_TYPE {camelCase, PascalCase, snake_case,
public static final String X_PY_EXAMPLE_IMPORT = "x-py-example-import";
public static final String X_PY_FASTAPI_EXAMPLE = "x-py-fastapi-example";
public static final String X_PY_NAME = "x-py-name";
public static final String X_PY_WIRE_NAME_LITERAL = "x-py-wire-name-literal";
public static final String X_PY_ENUM_TYPE = "x-py-enum-type";
public static final String X_PY_READONLY = "x-py-readonly";
public static final String X_PY_MODEL_IMPORTS = "x-py-model-imports";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
package org.openapitools.codegen.languages;

import com.github.curiousoddman.rgxgen.RgxGen;
import com.google.common.collect.ImmutableMap;
import com.samskivert.mustache.Mustache;
import io.swagger.v3.core.util.Json;
import io.swagger.v3.oas.models.examples.Example;
import io.swagger.v3.oas.models.media.Schema;
Expand Down Expand Up @@ -731,11 +733,7 @@ protected String toPythonLiteral(Object value) {
}

protected String toPythonStringLiteral(String value) {
try {
return Json.mapper().writeValueAsString(value);
} catch (Exception e) {
return "\"" + escapeUnsafeCharacters(value) + "\"";
}
return PythonStringUtils.toPythonStringLiteral(value);
}

@Override
Expand Down Expand Up @@ -920,6 +918,11 @@ public GeneratorLanguage generatorLanguage() {
return GeneratorLanguage.PYTHON;
}

@Override
protected ImmutableMap.Builder<String, Mustache.Lambda> addMustacheLambdas() {
return PythonStringUtils.addMustacheLambdas(super.addMustacheLambdas());
}

@Override
public Map<String, ModelsMap> postProcessAllModels(Map<String, ModelsMap> objs) {
final Map<String, ModelsMap> processed = super.postProcessAllModels(objs);
Expand Down Expand Up @@ -1422,6 +1425,9 @@ public void postProcessParameter(CodegenParameter parameter) {

@Override
public void postProcessModelProperty(CodegenModel model, CodegenProperty property) {
property.vendorExtensions.put(
X_PY_WIRE_NAME_LITERAL,
toPythonStringLiteral(property.baseName));
postProcessPattern(property.pattern, property.vendorExtensions);

if (property.isArray) {
Expand Down Expand Up @@ -2238,7 +2244,7 @@ private String finalizeType(CodegenProperty cp, PythonType pt) {

// field
if (cp.baseName != null && !cp.baseName.equals(cp.name)) { // base name not the same as name
pt.annotate("alias", cp.baseName);
pt.annotate("alias", toPythonStringLiteral(cp.baseName), false);
}

String example = toPythonExample(cp);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
package org.openapitools.codegen.languages;

import com.github.curiousoddman.rgxgen.RgxGen;
import com.google.common.collect.ImmutableMap;
import com.samskivert.mustache.Mustache;
import io.swagger.v3.oas.models.examples.Example;
import io.swagger.v3.oas.models.media.Schema;
import io.swagger.v3.oas.models.parameters.Parameter;
Expand Down Expand Up @@ -775,6 +777,11 @@ protected static String dropDots(String str) {
return str.replaceAll("\\.", "_");
}

@Override
protected ImmutableMap.Builder<String, Mustache.Lambda> addMustacheLambdas() {
return PythonStringUtils.addMustacheLambdas(super.addMustacheLambdas());
}

@Override
public GeneratorLanguage generatorLanguage() {
return GeneratorLanguage.PYTHON;
Expand Down Expand Up @@ -907,7 +914,7 @@ private ModelsMap postProcessModelsMap(ModelsMap objs) {

// field
if (cp.baseName != null && !cp.baseName.equals(cp.name)) { // base name not the same as name
fields.add(String.format(Locale.ROOT, "alias=\"%s\"", cp.baseName));
fields.add("alias=" + PythonStringUtils.toPythonStringLiteral(cp.baseName));
}

if (!StringUtils.isEmpty(cp.description)) { // has description
Expand Down Expand Up @@ -1880,6 +1887,9 @@ public void postProcessParameter(CodegenParameter parameter) {

@Override
public void postProcessModelProperty(CodegenModel model, CodegenProperty property) {
property.vendorExtensions.put(
X_PY_WIRE_NAME_LITERAL,
PythonStringUtils.toPythonStringLiteral(property.baseName));
postProcessPattern(property.pattern, property.vendorExtensions);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* Copyright 2026 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.fasterxml.jackson.core.JsonProcessingException;
import com.google.common.collect.ImmutableMap;
import com.samskivert.mustache.Mustache;
import io.swagger.v3.core.util.Json;

public final class PythonStringUtils {
private PythonStringUtils() {}

public static String toPythonStringLiteral(String value) {
try {
return Json.mapper().writeValueAsString(value);
} catch (JsonProcessingException e) {
throw new IllegalStateException("Failed to encode Python string literal", e);
}
}

public static String toPythonSingleQuotedStringLiteral(String value) {
String doubleQuoted = toPythonStringLiteral(value);
String contents = doubleQuoted.substring(1, doubleQuoted.length() - 1)
.replace("\\\"", "\"")
.replace("'", "\\'");
return "'" + contents + "'";
}

public static ImmutableMap.Builder<String, Mustache.Lambda> addMustacheLambdas(
ImmutableMap.Builder<String, Mustache.Lambda> lambdas) {
return lambdas
.put("pythonStringLiteral", (fragment, writer) ->
writer.write(toPythonStringLiteral(fragment.execute())))
.put("pythonSingleQuotedStringLiteral", (fragment, writer) ->
writer.write(toPythonSingleQuotedStringLiteral(fragment.execute())));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ class {{classname}}({{#parent}}{{{.}}}{{/parent}}{{^parent}}BaseModel{{/parent}}
{{#isAdditionalPropertiesTrue}}
additional_properties: Dict[str, Any] = {}
{{/isAdditionalPropertiesTrue}}
__properties = [{{#allVars}}"{{baseName}}"{{^-last}}, {{/-last}}{{/allVars}}]
__properties = [{{#allVars}}{{{vendorExtensions.x-py-wire-name-literal}}}{{^-last}}, {{/-last}}{{/allVars}}]
{{#vars}}
{{#vendorExtensions.x-regex}}

Expand Down Expand Up @@ -93,14 +93,14 @@ class {{classname}}({{#parent}}{{{.}}}{{/parent}}{{^parent}}BaseModel{{/parent}}
{{#hasChildren}}
{{#discriminator}}
# JSON field name that stores the object type
__discriminator_property_name = '{{discriminator.propertyBaseName}}'
__discriminator_property_name = {{#lambda.pythonSingleQuotedStringLiteral}}{{{discriminator.propertyBaseName}}}{{/lambda.pythonSingleQuotedStringLiteral}}

{{#mappedModels}}
{{#-first}}
# discriminator mappings
__discriminator_value_class_map = {
{{/-first}}
'{{{mappingName}}}': '{{{modelName}}}'{{^-last}},{{/-last}}
{{#lambda.pythonSingleQuotedStringLiteral}}{{{mappingName}}}{{/lambda.pythonSingleQuotedStringLiteral}}: '{{{modelName}}}'{{^-last}},{{/-last}}
{{#-last}}
}

Expand Down Expand Up @@ -156,7 +156,7 @@ class {{classname}}({{#parent}}{{{.}}}{{/parent}}{{^parent}}BaseModel{{/parent}}
_items.append(
[_inner_item.to_dict() for _inner_item in _item if _inner_item is not None]
)
_dict['{{{baseName}}}'] = _items
_dict[{{#lambda.pythonSingleQuotedStringLiteral}}{{{baseName}}}{{/lambda.pythonSingleQuotedStringLiteral}}] = _items
{{/items.isArray}}
{{#items.isMap}}
# override the default output from pydantic by calling `to_dict()` of each item in {{{name}}} (list of map)
Expand All @@ -167,7 +167,7 @@ class {{classname}}({{#parent}}{{{.}}}{{/parent}}{{^parent}}BaseModel{{/parent}}
_items.append(
{_inner_key: _inner_value.to_dict() for _inner_key, _inner_value in _item.items() if _inner_value is not None}
)
_dict['{{{baseName}}}'] = _items
_dict[{{#lambda.pythonSingleQuotedStringLiteral}}{{{baseName}}}{{/lambda.pythonSingleQuotedStringLiteral}}] = _items
{{/items.isMap}}
{{/items.items.isPrimitiveType}}
{{/items.isContainer}}
Expand All @@ -180,7 +180,7 @@ class {{classname}}({{#parent}}{{{.}}}{{/parent}}{{^parent}}BaseModel{{/parent}}
for _item in self.{{{name}}}:
if _item:
_items.append(_item.to_dict())
_dict['{{{baseName}}}'] = _items
_dict[{{#lambda.pythonSingleQuotedStringLiteral}}{{{baseName}}}{{/lambda.pythonSingleQuotedStringLiteral}}] = _items
{{/items.isEnumOrRef}}
{{/items.isPrimitiveType}}
{{/items.isContainer}}
Expand All @@ -197,7 +197,7 @@ class {{classname}}({{#parent}}{{{.}}}{{/parent}}{{^parent}}BaseModel{{/parent}}
_field_dict_of_array[_key] = [
_item.to_dict() for _item in self.{{{name}}}[_key]
]
_dict['{{{baseName}}}'] = _field_dict_of_array
_dict[{{#lambda.pythonSingleQuotedStringLiteral}}{{{baseName}}}{{/lambda.pythonSingleQuotedStringLiteral}}] = _field_dict_of_array
{{/items.isArray}}
{{#items.isMap}}
# override the default output from pydantic by calling `to_dict()` of each value in {{{name}}} (dict of dict)
Expand All @@ -208,7 +208,7 @@ class {{classname}}({{#parent}}{{{.}}}{{/parent}}{{^parent}}BaseModel{{/parent}}
_field_dict_of_dict[_key] = {
_inner_key: _inner_value.to_dict() for _inner_key, _inner_value in _value.items() if _inner_value is not None
}
_dict['{{{baseName}}}'] = _field_dict_of_dict
_dict[{{#lambda.pythonSingleQuotedStringLiteral}}{{{baseName}}}{{/lambda.pythonSingleQuotedStringLiteral}}] = _field_dict_of_dict
{{/items.isMap}}
{{/items.items.isPrimitiveType}}
{{/items.isContainer}}
Expand All @@ -221,7 +221,7 @@ class {{classname}}({{#parent}}{{{.}}}{{/parent}}{{^parent}}BaseModel{{/parent}}
for _key in self.{{{name}}}:
if self.{{{name}}}[_key]:
_field_dict[_key] = self.{{{name}}}[_key].to_dict()
_dict['{{{baseName}}}'] = _field_dict
_dict[{{#lambda.pythonSingleQuotedStringLiteral}}{{{baseName}}}{{/lambda.pythonSingleQuotedStringLiteral}}] = _field_dict
{{/items.isEnumOrRef}}
{{/items.isPrimitiveType}}
{{/items.isContainer}}
Expand All @@ -232,7 +232,7 @@ class {{classname}}({{#parent}}{{{.}}}{{/parent}}{{^parent}}BaseModel{{/parent}}
{{^isEnumOrRef}}
# override the default output from pydantic by calling `to_dict()` of {{{name}}}
if self.{{{name}}}:
_dict['{{{baseName}}}'] = self.{{{name}}}.to_dict()
_dict[{{#lambda.pythonSingleQuotedStringLiteral}}{{{baseName}}}{{/lambda.pythonSingleQuotedStringLiteral}}] = self.{{{name}}}.to_dict()
{{/isEnumOrRef}}
{{/isPrimitiveType}}
{{/isContainer}}
Expand All @@ -249,7 +249,7 @@ class {{classname}}({{#parent}}{{{.}}}{{/parent}}{{^parent}}BaseModel{{/parent}}
# set to None if {{{name}}} (nullable) is None
# and __fields_set__ contains the field
if self.{{name}} is None and "{{{name}}}" in self.__fields_set__:
_dict['{{{baseName}}}'] = None
_dict[{{#lambda.pythonSingleQuotedStringLiteral}}{{{baseName}}}{{/lambda.pythonSingleQuotedStringLiteral}}] = None

{{/isNullable}}
{{/allVars}}
Expand Down Expand Up @@ -293,34 +293,34 @@ class {{classname}}({{#parent}}{{{.}}}{{/parent}}{{^parent}}BaseModel{{/parent}}
{{#isArray}}
{{#items.isContainer}}
{{#items.items.isPrimitiveType}}
"{{{name}}}": obj.get("{{{baseName}}}"){{^-last}},{{/-last}}
"{{{name}}}": obj.get({{{vendorExtensions.x-py-wire-name-literal}}}){{^-last}},{{/-last}}
{{/items.items.isPrimitiveType}}
{{^items.items.isPrimitiveType}}
{{#items.isArray}}
"{{{name}}}": [
[{{{items.items.dataType}}}.from_dict(_inner_item) for _inner_item in _item]
for _item in obj.get("{{{baseName}}}")
] if obj.get("{{{baseName}}}") is not None else None{{^-last}},{{/-last}}
for _item in obj.get({{{vendorExtensions.x-py-wire-name-literal}}})
] if obj.get({{{vendorExtensions.x-py-wire-name-literal}}}) is not None else None{{^-last}},{{/-last}}
{{/items.isArray}}
{{#items.isMap}}
"{{{name}}}": [
{_inner_key: {{{items.items.dataType}}}.from_dict(_inner_value) for _inner_key, _inner_value in _item.items()}
for _item in obj.get("{{{baseName}}}")
] if obj.get("{{{baseName}}}") is not None else None{{^-last}},{{/-last}}
for _item in obj.get({{{vendorExtensions.x-py-wire-name-literal}}})
] if obj.get({{{vendorExtensions.x-py-wire-name-literal}}}) is not None else None{{^-last}},{{/-last}}
{{/items.isMap}}
{{/items.items.isPrimitiveType}}
{{/items.isContainer}}
{{^items.isContainer}}
{{^items.isPrimitiveType}}
{{#items.isEnumOrRef}}
"{{{name}}}": obj.get("{{{baseName}}}"){{^-last}},{{/-last}}
"{{{name}}}": obj.get({{{vendorExtensions.x-py-wire-name-literal}}}){{^-last}},{{/-last}}
{{/items.isEnumOrRef}}
{{^items.isEnumOrRef}}
"{{{name}}}": [{{{items.dataType}}}.from_dict(_item) for _item in obj.get("{{{baseName}}}")] if obj.get("{{{baseName}}}") is not None else None{{^-last}},{{/-last}}
"{{{name}}}": [{{{items.dataType}}}.from_dict(_item) for _item in obj.get({{{vendorExtensions.x-py-wire-name-literal}}})] if obj.get({{{vendorExtensions.x-py-wire-name-literal}}}) is not None else None{{^-last}},{{/-last}}
{{/items.isEnumOrRef}}
{{/items.isPrimitiveType}}
{{#items.isPrimitiveType}}
"{{{name}}}": obj.get("{{{baseName}}}"){{^-last}},{{/-last}}
"{{{name}}}": obj.get({{{vendorExtensions.x-py-wire-name-literal}}}){{^-last}},{{/-last}}
{{/items.isPrimitiveType}}
{{/items.isContainer}}
{{/isArray}}
Expand All @@ -331,52 +331,52 @@ class {{classname}}({{#parent}}{{{.}}}{{/parent}}{{^parent}}BaseModel{{/parent}}
{{#items.isMap}}
"{{{name}}}": {
_k: {_ik: {{{items.items.dataType}}}.from_dict(_iv) for _ik, _iv in _v.items()} if _v is not None else None
for _k, _v in obj["{{{baseName}}}"].items()
for _k, _v in obj[{{{vendorExtensions.x-py-wire-name-literal}}}].items()
}
if obj.get("{{{baseName}}}") is not None
if obj.get({{{vendorExtensions.x-py-wire-name-literal}}}) is not None
else None{{^-last}},{{/-last}}
{{/items.isMap}}
{{#items.isArray}}
"{{{name}}}": {
_k: [{{{items.items.dataType}}}.from_dict(_item) for _item in _v] if _v is not None else None
for _k, _v in obj["{{{baseName}}}"].items()
for _k, _v in obj[{{{vendorExtensions.x-py-wire-name-literal}}}].items()
}
if obj.get("{{{baseName}}}") is not None
if obj.get({{{vendorExtensions.x-py-wire-name-literal}}}) is not None
else None{{^-last}},{{/-last}}
{{/items.isArray}}
{{/items.isContainer}}
{{^items.isContainer}}
"{{{name}}}": {
_k: {{{items.dataType}}}.from_dict(_v) for _k, _v in obj["{{{baseName}}}"].items()
_k: {{{items.dataType}}}.from_dict(_v) for _k, _v in obj[{{{vendorExtensions.x-py-wire-name-literal}}}].items()
}
if obj.get("{{{baseName}}}") is not None
if obj.get({{{vendorExtensions.x-py-wire-name-literal}}}) is not None
else None{{^-last}},{{/-last}}
{{/items.isContainer}}
{{/items.isEnumOrRef}}
{{#items.isEnumOrRef}}
"{{{name}}}": obj.get("{{{baseName}}}"){{^-last}},{{/-last}}
"{{{name}}}": obj.get({{{vendorExtensions.x-py-wire-name-literal}}}){{^-last}},{{/-last}}
{{/items.isEnumOrRef}}
{{/items.isPrimitiveType}}
{{#items.isPrimitiveType}}
"{{{name}}}": obj.get("{{{baseName}}}"){{^-last}},{{/-last}}
"{{{name}}}": obj.get({{{vendorExtensions.x-py-wire-name-literal}}}){{^-last}},{{/-last}}
{{/items.isPrimitiveType}}
{{/isMap}}
{{/isContainer}}
{{^isContainer}}
{{^isPrimitiveType}}
{{^isEnumOrRef}}
"{{{name}}}": {{{dataType}}}.from_dict(obj.get("{{{baseName}}}")) if obj.get("{{{baseName}}}") is not None else None{{^-last}},{{/-last}}
"{{{name}}}": {{{dataType}}}.from_dict(obj.get({{{vendorExtensions.x-py-wire-name-literal}}})) if obj.get({{{vendorExtensions.x-py-wire-name-literal}}}) is not None else None{{^-last}},{{/-last}}
{{/isEnumOrRef}}
{{#isEnumOrRef}}
"{{{name}}}": obj.get("{{{baseName}}}"){{#defaultValue}} if obj.get("{{baseName}}") is not None else {{{defaultValue}}}{{/defaultValue}}{{^-last}},{{/-last}}
"{{{name}}}": obj.get({{{vendorExtensions.x-py-wire-name-literal}}}){{#defaultValue}} if obj.get({{{vendorExtensions.x-py-wire-name-literal}}}) is not None else {{{defaultValue}}}{{/defaultValue}}{{^-last}},{{/-last}}
{{/isEnumOrRef}}
{{/isPrimitiveType}}
{{#isPrimitiveType}}
{{#defaultValue}}
"{{{name}}}": obj.get("{{{baseName}}}") if obj.get("{{{baseName}}}") is not None else {{{defaultValue}}}{{^-last}},{{/-last}}
"{{{name}}}": obj.get({{{vendorExtensions.x-py-wire-name-literal}}}) if obj.get({{{vendorExtensions.x-py-wire-name-literal}}}) is not None else {{{defaultValue}}}{{^-last}},{{/-last}}
{{/defaultValue}}
{{^defaultValue}}
"{{{name}}}": obj.get("{{{baseName}}}"){{^-last}},{{/-last}}
"{{{name}}}": obj.get({{{vendorExtensions.x-py-wire-name-literal}}}){{^-last}},{{/-last}}
{{/defaultValue}}
{{/isPrimitiveType}}
{{/isContainer}}
Expand Down
Loading
Loading