Skip to content
Open
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 @@ -340,6 +340,7 @@ public static enum ENUM_PROPERTY_NAMING_TYPE {camelCase, PascalCase, snake_case,
// Not user-configurable. System provided for use in templates.
public static final String GENERATE_MODELS = "generateModels";
public static final String GENERATE_MODEL_DOCS = "generateModelDocs";
public static final String GENERATE_SUPPORTING_FILES = "generateSupportingFiles";

public static final String VIRTUAL_SERVICE = "virtualService";
public static final String VIRTUAL_SERVICE_DESC = "Generate Spring boot rest service as virtual service with Virtualan";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,7 @@ void configureGeneratorProperties() {

config.additionalProperties().put(CodegenConstants.GENERATE_APIS, generateApis);
config.additionalProperties().put(CodegenConstants.GENERATE_MODELS, generateModels);
config.additionalProperties().put(CodegenConstants.GENERATE_SUPPORTING_FILES, generateSupportingFiles);
config.additionalProperties().put(CodegenConstants.GENERATE_WEBHOOKS, generateWebhooks);
config.additionalProperties().put(CodegenConstants.GENERATE_RECURSIVE_DEPENDENT_MODELS, generateRecursiveDependentModels);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import lombok.Setter;
import org.apache.commons.lang3.Strings;
import org.openapitools.codegen.*;
import org.openapitools.codegen.config.GlobalSettings;
import org.openapitools.codegen.meta.GeneratorMetadata;
import org.openapitools.codegen.meta.Stability;
import org.openapitools.codegen.meta.features.*;
Expand Down Expand Up @@ -65,6 +66,8 @@ public class PythonClientCodegen extends AbstractPythonCodegen implements Codege
public static final String BUILD_SYSTEM = "buildSystem";
public static final String SUPPORT_HTTPX_SYNC = "supportHttpxSync";
public static final String USE_INDEPENDENT_IMPLICIT_CLIENTS = "useIndependentImplicitClients";
private static final String USE_LEGACY_MODEL_HELPERS_MODULE = "useLegacyModelHelpersModule";
private static final String LEGACY_MODEL_HELPERS_FILE = "_legacy_model_helpers.py";
private static final Set<String> SYNC_API_LIFECYCLE_METHODS =
Set.of("close", "__enter__", "__exit__");
private static final Set<String> ASYNC_API_LIFECYCLE_METHODS =
Expand Down Expand Up @@ -391,6 +394,14 @@ public void processOpts() {
String modelPath = packagePath() + File.separatorChar + modelPackage.replace('.', File.separatorChar);
String apiPath = packagePath() + File.separatorChar + apiPackage.replace('.', File.separatorChar);

if (compatibleWithPythonLegacy && generatesLegacyModelHelpersModule()) {
additionalProperties.put(USE_LEGACY_MODEL_HELPERS_MODULE, true);
supportingFiles.add(new SupportingFile(
"_legacy_model_helpers.mustache", packagePath(), LEGACY_MODEL_HELPERS_FILE));
} else {
additionalProperties.remove(USE_LEGACY_MODEL_HELPERS_MODULE);
}

String readmePath = "README.md";
String readmeTemplate = "README.mustache";
if (generateSourceCodeOnly) {
Expand Down Expand Up @@ -659,6 +670,24 @@ private boolean usesLegacyApiCompatibility() {
return compatibleWithPythonLegacy && DEFAULT_LIBRARY.equals(getLibrary());
}

private boolean generatesLegacyModelHelpersModule() {
if (!Boolean.TRUE.equals(additionalProperties.get(CodegenConstants.GENERATE_SUPPORTING_FILES))) {
return false;
}

String requestedSupportingFiles = GlobalSettings.getProperty(CodegenConstants.SUPPORTING_FILES);
if (requestedSupportingFiles == null || requestedSupportingFiles.isBlank()) {
return true;
}

for (String requestedFile : requestedSupportingFiles.split(",")) {
if (LEGACY_MODEL_HELPERS_FILE.equals(requestedFile.trim())) {
return true;
}
}
return false;
}

private boolean supportsHttpxSync() {
return "httpx".equals(getLibrary())
&& Boolean.parseBoolean(String.valueOf(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{{>partial_header}}

from typing import Any

{{>_legacy_model_dict_helpers}}
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,12 @@ from {{packageName}}.exceptions import (

{{#compatibleWithPythonLegacy}}

{{#useLegacyModelHelpersModule}}
from {{packageName}}._legacy_model_helpers import _get_openapi_to_dict
{{/useLegacyModelHelpersModule}}
{{^useLegacyModelHelpersModule}}
{{>_legacy_model_identity}}
{{/useLegacyModelHelpersModule}}


{{/compatibleWithPythonLegacy}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,17 @@ from pydantic import Field
from pydantic_core import to_jsonable_python


{{#useLegacyModelHelpersModule}}
from {{packageName}}._legacy_model_helpers import (
_OPENAPI_GENERATOR_TO_DICT,
_get_openapi_to_dict,
_to_legacy_value,
_to_openapi_value,
)
{{/useLegacyModelHelpersModule}}
{{^useLegacyModelHelpersModule}}
{{>_legacy_model_dict_helpers}}
{{/useLegacyModelHelpersModule}}

{{/compatibleWithPythonLegacy}}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,17 @@ if TYPE_CHECKING:
{{/hasChildren}}
{{#compatibleWithPythonLegacy}}

{{#useLegacyModelHelpersModule}}
from {{packageName}}._legacy_model_helpers import (
_OPENAPI_GENERATOR_TO_DICT,
_get_openapi_to_dict,
_to_legacy_value,
_to_openapi_value,
)
{{/useLegacyModelHelpersModule}}
{{^useLegacyModelHelpersModule}}
{{>_legacy_model_dict_helpers}}
{{/useLegacyModelHelpersModule}}


{{/compatibleWithPythonLegacy}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,17 @@ from typing_extensions import Literal, Self
from pydantic_core import to_jsonable_python


{{#useLegacyModelHelpersModule}}
from {{packageName}}._legacy_model_helpers import (
_OPENAPI_GENERATOR_TO_DICT,
_get_openapi_to_dict,
_to_legacy_value,
_to_openapi_value,
)
{{/useLegacyModelHelpersModule}}
{{^useLegacyModelHelpersModule}}
{{>_legacy_model_dict_helpers}}
{{/useLegacyModelHelpersModule}}

{{/compatibleWithPythonLegacy}}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -974,6 +974,8 @@ public void testLegacyModelToDictRendering() throws IOException {
defaultOutputPath + "openapi_client/api/default_api.py");
final Path defaultApiClient = Paths.get(
defaultOutputPath + "openapi_client/api_client.py");
final Path defaultLegacyHelpers = Paths.get(
defaultOutputPath + "openapi_client/_legacy_model_helpers.py");

assertFileContains(defaultModel,
"return json.dumps(to_jsonable_python(self.to_dict()))",
Expand All @@ -997,6 +999,7 @@ public void testLegacyModelToDictRendering() throws IOException {
"async_req", " _preload_content: bool = True",
"return self.api_client.pool.apply_async(");
TestUtils.assertFileNotContains(defaultApiClient, "_OPENAPI_GENERATOR_TO_DICT");
Assert.assertFalse(Files.exists(defaultLegacyHelpers));

final PythonClientCodegen codegen = new PythonClientCodegen();
codegen.additionalProperties().put(
Expand All @@ -1010,12 +1013,12 @@ public void testLegacyModelToDictRendering() throws IOException {
final Path api = Paths.get(
outputPath + "openapi_client/api/default_api.py");
final Path apiClient = Paths.get(outputPath + "openapi_client/api_client.py");
final Path legacyHelpers = Paths.get(
outputPath + "openapi_client/_legacy_model_helpers.py");

assertFileContains(model,
"def _get_openapi_to_dict(value: Any) -> Any:",
"def _to_legacy_item(value: Any, serialize: bool) -> Any:",
"def _to_legacy_value(value: Any, serialize: bool) -> Any:",
"def _to_openapi_value(value: Any) -> Any:",
"from openapi_client._legacy_model_helpers import (",
" _OPENAPI_GENERATOR_TO_DICT,",
"def to_dict(self, serialize: bool = False) -> Dict[str, Any]:",
"_to_legacy_value(getattr(self, \"renamed\", None), serialize)",
"def __openapi_generator_modern_projection(self) -> Dict[str, Any]:",
Expand All @@ -1033,6 +1036,10 @@ public void testLegacyModelToDictRendering() throws IOException {
"def __eq__(self, other: object) -> bool:",
"def __ne__(self, other: object) -> bool:");
TestUtils.assertFileNotContains(model,
"def _get_openapi_to_dict(value: Any) -> Any:",
"def _to_legacy_item(value: Any, serialize: bool) -> Any:",
"def _to_legacy_value(value: Any, serialize: bool) -> Any:",
"def _to_openapi_value(value: Any) -> Any:",
"_legacy_model_to_dict_impl: ClassVar", "def _to_openapi_dict(");
assertFileContains(nestedModel,
"camel_case: Optional[StrictStr]",
Expand All @@ -1045,16 +1052,21 @@ public void testLegacyModelToDictRendering() throws IOException {
for (String wrapper : Arrays.asList("one_of_model.py", "any_of_model.py")) {
final Path wrapperModel = Paths.get(outputPath + "openapi_client/models/" + wrapper);
assertFileContains(wrapperModel,
"from openapi_client._legacy_model_helpers import (",
"def to_dict(self, serialize: bool = False) -> Any:",
"def __openapi_generator_modern_projection(self) -> Any:",
"del __openapi_generator_modern_projection");
TestUtils.assertFileNotContains(wrapperModel,
"def _get_openapi_to_dict(value: Any) -> Any:",
"def _to_legacy_item(value: Any, serialize: bool) -> Any:",
"def _to_legacy_value(value: Any, serialize: bool) -> Any:",
"def _to_openapi_value(value: Any) -> Any:",
"_legacy_model_to_dict_impl: ClassVar", "def _to_openapi_dict(",
"openapi_types", "attribute_map", "extra=\"forbid\"",
"def __repr__", "def __eq__");
}
assertFileContains(apiClient,
"def _get_openapi_to_dict(value: Any) -> Any:",
"from openapi_client._legacy_model_helpers import _get_openapi_to_dict",
"to_dict = getattr(obj, 'to_dict', None)",
"to_openapi_dict = _get_openapi_to_dict(obj)",
"if to_openapi_dict is not None:",
Expand All @@ -1071,6 +1083,13 @@ public void testLegacyModelToDictRendering() throws IOException {
"response_types_map: Dict[str, Optional[str]]",
"return self._get_pool().apply_async(call)",
"response_data.getheaders()");
TestUtils.assertFileNotContains(apiClient,
"def _get_openapi_to_dict(value: Any) -> Any:");
assertFileContains(legacyHelpers,
"def _get_openapi_to_dict(value: Any) -> Any:",
"def _to_legacy_item(value: Any, serialize: bool) -> Any:",
"def _to_legacy_value(value: Any, serialize: bool) -> Any:",
"def _to_openapi_value(value: Any) -> Any:");
assertFileContains(api,
"async_req: Optional[bool] = None",
"_preload_content: bool = True",
Expand Down Expand Up @@ -1169,9 +1188,9 @@ public void testLegacyModelToDictSupportsModelOnlyGeneration() throws IOExceptio
"def _to_legacy_item(value: Any, serialize: bool) -> Any:",
"def _to_legacy_value(value: Any, serialize: bool) -> Any:",
"def _to_openapi_value(value: Any) -> Any:");
TestUtils.assertFileNotContains(model, "_legacy_model_dict import");
TestUtils.assertFileNotContains(model, "_legacy_model_helpers import");
Assert.assertFalse(Files.exists(Paths.get(
outputPath + "openapi_client/_legacy_model_dict.py")));
outputPath + "openapi_client/_legacy_model_helpers.py")));
} finally {
if (oldModels == null) {
GlobalSettings.clearProperty(CodegenConstants.MODELS);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ docs/NestedModel.md
docs/OneOfModel.md
git_push.sh
legacy_model_dict_client/__init__.py
legacy_model_dict_client/_legacy_model_helpers.py
legacy_model_dict_client/api/__init__.py
legacy_model_dict_client/api/default_api.py
legacy_model_dict_client/api_client.py
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
"""
Legacy model dictionaries

No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)

The version of the OpenAPI document: 1.0.0
Generated by OpenAPI Generator (https://openapi-generator.tech)

Do not edit the class manually.
""" # noqa: E501


from typing import Any

_OPENAPI_GENERATOR_TO_DICT = "_openapi_generator_to_dict"


def _get_openapi_to_dict(value: Any) -> Any:
# Reciprocal function references preserve inherited generated methods
# without reserving model member names. Checking both directions also
# rejects overrides whose decorators copy function attributes.
to_dict = getattr(value, "to_dict", None)
type_to_dict = getattr(type(value), "to_dict", None)
if (
not callable(to_dict)
or getattr(to_dict, "__func__", None) is not type_to_dict
):
return None

openapi_to_dict = getattr(
type_to_dict, _OPENAPI_GENERATOR_TO_DICT, None
)
if (
not callable(openapi_to_dict)
or getattr(
openapi_to_dict,
_OPENAPI_GENERATOR_TO_DICT,
None,
) is not type_to_dict
):
return None
return openapi_to_dict


def _to_legacy_item(value: Any, serialize: bool) -> Any:
to_dict = getattr(value, "to_dict", None)
if _get_openapi_to_dict(value) is not None and callable(to_dict):
return to_dict(serialize=serialize)
if callable(to_dict):
return to_dict()
return value


def _to_legacy_value(value: Any, serialize: bool) -> Any:
# python-legacy converted only immediate list elements or dictionary values:
# https://github.com/OpenAPITools/openapi-generator/blob/c84b949df1a9ec04ba75989cb49b45c940c2f694/modules/openapi-generator/src/main/resources/python-legacy/model.mustache#L203-L231
if isinstance(value, list):
return [_to_legacy_item(item, serialize) for item in value]
if isinstance(value, dict):
return {
key: _to_legacy_item(item, serialize)
for key, item in value.items()
}
return _to_legacy_item(value, serialize)


def _to_openapi_value(value: Any) -> Any:
if isinstance(value, list):
return [_to_openapi_value(item) for item in value]
if isinstance(value, dict):
return {key: _to_openapi_value(item) for key, item in value.items()}

to_openapi_dict = _get_openapi_to_dict(value)
if to_openapi_dict is not None:
return to_openapi_dict(value)

to_dict = getattr(value, "to_dict", None)
if callable(to_dict):
return to_dict()
return value
Original file line number Diff line number Diff line change
Expand Up @@ -44,34 +44,7 @@
)


_OPENAPI_GENERATOR_TO_DICT = "_openapi_generator_to_dict"


def _get_openapi_to_dict(value: Any) -> Any:
# Reciprocal function references preserve inherited generated methods
# without reserving model member names. Checking both directions also
# rejects overrides whose decorators copy function attributes.
to_dict = getattr(value, "to_dict", None)
type_to_dict = getattr(type(value), "to_dict", None)
if (
not callable(to_dict)
or getattr(to_dict, "__func__", None) is not type_to_dict
):
return None

openapi_to_dict = getattr(
type_to_dict, _OPENAPI_GENERATOR_TO_DICT, None
)
if (
not callable(openapi_to_dict)
or getattr(
openapi_to_dict,
_OPENAPI_GENERATOR_TO_DICT,
None,
) is not type_to_dict
):
return None
return openapi_to_dict
from legacy_model_dict_client._legacy_model_helpers import _get_openapi_to_dict


RequestSerialized = Tuple[str, str, Dict[str, str], Optional[str], List[str]]
Expand Down
Loading
Loading