From d8a0355ae2845ac05ccf8d0415b4108e8dd9b2e0 Mon Sep 17 00:00:00 2001 From: Tanbir Hossain Ramim <96797470+TanbirRamim@users.noreply.github.com> Date: Tue, 6 Oct 2026 10:44:50 +0200 Subject: [PATCH 1/2] [python] Use generated model names in examples and plain dicts for free-form objects --- .../languages/AbstractPythonCodegen.java | 6 ++++- .../python/PythonClientCodegenTest.java | 25 +++++++++++++++++++ .../python-aiohttp/test/test_parent.py | 2 +- .../test/test_parent_with_optional_dict.py | 2 +- .../python-httpx-sync/test/test_parent.py | 2 +- .../test/test_parent_with_optional_dict.py | 2 +- .../petstore/python-httpx/test/test_parent.py | 2 +- .../test/test_parent_with_optional_dict.py | 2 +- .../python-httpx2-sync/test/test_parent.py | 2 +- .../test/test_parent_with_optional_dict.py | 2 +- .../python-httpx2/test/test_parent.py | 2 +- .../test/test_parent_with_optional_dict.py | 2 +- .../python-lazyImports/test/test_parent.py | 2 +- .../test/test_parent_with_optional_dict.py | 2 +- .../petstore/python/test/test_parent.py | 2 +- .../test/test_parent_with_optional_dict.py | 2 +- 16 files changed, 44 insertions(+), 15 deletions(-) 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 14e5468e7e84..432a3f84e248 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 @@ -567,6 +567,10 @@ private String toExampleValueRecursive(Schema schema, List includedSchem example = "None"; return example; } + if (ModelUtils.isFreeFormObject(schema, this.openAPI)) { + // free-form objects have no generated model, so the example is a plain dict + return "{ }"; + } // I remove any property that is a discriminator, since it is not well supported by the python generator String toExclude = null; @@ -574,7 +578,7 @@ private String toExampleValueRecursive(Schema schema, List includedSchem toExclude = schema.getDiscriminator().getPropertyName(); } - example = packageName + ".models." + underscore(schema.getTitle()) + "." + schema.getTitle() + "("; + example = packageName + ".models." + toModelFilename(schema.getTitle()) + "." + toModelName(schema.getTitle()) + "("; // if required only: // List reqs = schema.getRequired(); 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 4c90e38d4ab7..0510211616c1 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 @@ -329,6 +329,31 @@ public void testExampleValueForSiblingsSharingAModel() { } } + @Test(description = "inline object examples use the generated module and class names") + public void testExampleValueForInlineObjects() { + // The inline model resolver names this schema _holder_child; the generated file is + // holder_child.py with class HolderChild, so the example must use those names. + final Schema child = new ObjectSchema().addProperty("prop", new StringSchema()); + child.setTitle("_holder_child"); + final Schema holder = new ObjectSchema() + .addProperty("child", child) + // a free-form object has no model at all, it is a plain dict + .addProperty("aProperty", new ObjectSchema()); + holder.setTitle("Holder"); + final OpenAPI openAPI = new OpenAPI().components(new io.swagger.v3.oas.models.Components() + .addSchemas("Holder", holder)); + final PythonClientCodegen codegen = new PythonClientCodegen(); + codegen.setOpenAPI(openAPI); + + final String example = codegen.toExampleValue(holder); + + Assert.assertTrue(example.contains("child = openapi_client.models.holder_child.HolderChild("), + "inline model path wrong in:\n" + example); + Assert.assertTrue(example.contains("a_property = { }"), + "free-form object should be a dict in:\n" + example); + Assert.assertFalse(example.contains("models.a_property."), "free-form object got a model path in:\n" + example); + } + @Test(description = "test single quotes escape") public void testSingleQuotes() { final PythonClientCodegen codegen = new PythonClientCodegen(); diff --git a/samples/openapi3/client/petstore/python-aiohttp/test/test_parent.py b/samples/openapi3/client/petstore/python-aiohttp/test/test_parent.py index ea1242c24f4e..d1b3d02bc1ff 100644 --- a/samples/openapi3/client/petstore/python-aiohttp/test/test_parent.py +++ b/samples/openapi3/client/petstore/python-aiohttp/test/test_parent.py @@ -37,7 +37,7 @@ def make_instance(self, include_optional) -> Parent: return Parent( optional_dict = { 'key' : petstore_api.models.inner_dict_with_property.InnerDictWithProperty( - a_property = petstore_api.models.a_property.aProperty(), ) + a_property = { }, ) } ) else: diff --git a/samples/openapi3/client/petstore/python-aiohttp/test/test_parent_with_optional_dict.py b/samples/openapi3/client/petstore/python-aiohttp/test/test_parent_with_optional_dict.py index 7cc74f9e98ec..9d5092145c2f 100644 --- a/samples/openapi3/client/petstore/python-aiohttp/test/test_parent_with_optional_dict.py +++ b/samples/openapi3/client/petstore/python-aiohttp/test/test_parent_with_optional_dict.py @@ -37,7 +37,7 @@ def make_instance(self, include_optional) -> ParentWithOptionalDict: return ParentWithOptionalDict( optional_dict = { 'key' : petstore_api.models.inner_dict_with_property.InnerDictWithProperty( - a_property = petstore_api.models.a_property.aProperty(), ) + a_property = { }, ) } ) else: diff --git a/samples/openapi3/client/petstore/python-httpx-sync/test/test_parent.py b/samples/openapi3/client/petstore/python-httpx-sync/test/test_parent.py index ea1242c24f4e..d1b3d02bc1ff 100644 --- a/samples/openapi3/client/petstore/python-httpx-sync/test/test_parent.py +++ b/samples/openapi3/client/petstore/python-httpx-sync/test/test_parent.py @@ -37,7 +37,7 @@ def make_instance(self, include_optional) -> Parent: return Parent( optional_dict = { 'key' : petstore_api.models.inner_dict_with_property.InnerDictWithProperty( - a_property = petstore_api.models.a_property.aProperty(), ) + a_property = { }, ) } ) else: diff --git a/samples/openapi3/client/petstore/python-httpx-sync/test/test_parent_with_optional_dict.py b/samples/openapi3/client/petstore/python-httpx-sync/test/test_parent_with_optional_dict.py index 7cc74f9e98ec..9d5092145c2f 100644 --- a/samples/openapi3/client/petstore/python-httpx-sync/test/test_parent_with_optional_dict.py +++ b/samples/openapi3/client/petstore/python-httpx-sync/test/test_parent_with_optional_dict.py @@ -37,7 +37,7 @@ def make_instance(self, include_optional) -> ParentWithOptionalDict: return ParentWithOptionalDict( optional_dict = { 'key' : petstore_api.models.inner_dict_with_property.InnerDictWithProperty( - a_property = petstore_api.models.a_property.aProperty(), ) + a_property = { }, ) } ) else: diff --git a/samples/openapi3/client/petstore/python-httpx/test/test_parent.py b/samples/openapi3/client/petstore/python-httpx/test/test_parent.py index ea1242c24f4e..d1b3d02bc1ff 100644 --- a/samples/openapi3/client/petstore/python-httpx/test/test_parent.py +++ b/samples/openapi3/client/petstore/python-httpx/test/test_parent.py @@ -37,7 +37,7 @@ def make_instance(self, include_optional) -> Parent: return Parent( optional_dict = { 'key' : petstore_api.models.inner_dict_with_property.InnerDictWithProperty( - a_property = petstore_api.models.a_property.aProperty(), ) + a_property = { }, ) } ) else: diff --git a/samples/openapi3/client/petstore/python-httpx/test/test_parent_with_optional_dict.py b/samples/openapi3/client/petstore/python-httpx/test/test_parent_with_optional_dict.py index 7cc74f9e98ec..9d5092145c2f 100644 --- a/samples/openapi3/client/petstore/python-httpx/test/test_parent_with_optional_dict.py +++ b/samples/openapi3/client/petstore/python-httpx/test/test_parent_with_optional_dict.py @@ -37,7 +37,7 @@ def make_instance(self, include_optional) -> ParentWithOptionalDict: return ParentWithOptionalDict( optional_dict = { 'key' : petstore_api.models.inner_dict_with_property.InnerDictWithProperty( - a_property = petstore_api.models.a_property.aProperty(), ) + a_property = { }, ) } ) else: diff --git a/samples/openapi3/client/petstore/python-httpx2-sync/test/test_parent.py b/samples/openapi3/client/petstore/python-httpx2-sync/test/test_parent.py index ea1242c24f4e..d1b3d02bc1ff 100644 --- a/samples/openapi3/client/petstore/python-httpx2-sync/test/test_parent.py +++ b/samples/openapi3/client/petstore/python-httpx2-sync/test/test_parent.py @@ -37,7 +37,7 @@ def make_instance(self, include_optional) -> Parent: return Parent( optional_dict = { 'key' : petstore_api.models.inner_dict_with_property.InnerDictWithProperty( - a_property = petstore_api.models.a_property.aProperty(), ) + a_property = { }, ) } ) else: diff --git a/samples/openapi3/client/petstore/python-httpx2-sync/test/test_parent_with_optional_dict.py b/samples/openapi3/client/petstore/python-httpx2-sync/test/test_parent_with_optional_dict.py index 7cc74f9e98ec..9d5092145c2f 100644 --- a/samples/openapi3/client/petstore/python-httpx2-sync/test/test_parent_with_optional_dict.py +++ b/samples/openapi3/client/petstore/python-httpx2-sync/test/test_parent_with_optional_dict.py @@ -37,7 +37,7 @@ def make_instance(self, include_optional) -> ParentWithOptionalDict: return ParentWithOptionalDict( optional_dict = { 'key' : petstore_api.models.inner_dict_with_property.InnerDictWithProperty( - a_property = petstore_api.models.a_property.aProperty(), ) + a_property = { }, ) } ) else: diff --git a/samples/openapi3/client/petstore/python-httpx2/test/test_parent.py b/samples/openapi3/client/petstore/python-httpx2/test/test_parent.py index ea1242c24f4e..d1b3d02bc1ff 100644 --- a/samples/openapi3/client/petstore/python-httpx2/test/test_parent.py +++ b/samples/openapi3/client/petstore/python-httpx2/test/test_parent.py @@ -37,7 +37,7 @@ def make_instance(self, include_optional) -> Parent: return Parent( optional_dict = { 'key' : petstore_api.models.inner_dict_with_property.InnerDictWithProperty( - a_property = petstore_api.models.a_property.aProperty(), ) + a_property = { }, ) } ) else: diff --git a/samples/openapi3/client/petstore/python-httpx2/test/test_parent_with_optional_dict.py b/samples/openapi3/client/petstore/python-httpx2/test/test_parent_with_optional_dict.py index 7cc74f9e98ec..9d5092145c2f 100644 --- a/samples/openapi3/client/petstore/python-httpx2/test/test_parent_with_optional_dict.py +++ b/samples/openapi3/client/petstore/python-httpx2/test/test_parent_with_optional_dict.py @@ -37,7 +37,7 @@ def make_instance(self, include_optional) -> ParentWithOptionalDict: return ParentWithOptionalDict( optional_dict = { 'key' : petstore_api.models.inner_dict_with_property.InnerDictWithProperty( - a_property = petstore_api.models.a_property.aProperty(), ) + a_property = { }, ) } ) else: diff --git a/samples/openapi3/client/petstore/python-lazyImports/test/test_parent.py b/samples/openapi3/client/petstore/python-lazyImports/test/test_parent.py index ea1242c24f4e..d1b3d02bc1ff 100644 --- a/samples/openapi3/client/petstore/python-lazyImports/test/test_parent.py +++ b/samples/openapi3/client/petstore/python-lazyImports/test/test_parent.py @@ -37,7 +37,7 @@ def make_instance(self, include_optional) -> Parent: return Parent( optional_dict = { 'key' : petstore_api.models.inner_dict_with_property.InnerDictWithProperty( - a_property = petstore_api.models.a_property.aProperty(), ) + a_property = { }, ) } ) else: diff --git a/samples/openapi3/client/petstore/python-lazyImports/test/test_parent_with_optional_dict.py b/samples/openapi3/client/petstore/python-lazyImports/test/test_parent_with_optional_dict.py index 7cc74f9e98ec..9d5092145c2f 100644 --- a/samples/openapi3/client/petstore/python-lazyImports/test/test_parent_with_optional_dict.py +++ b/samples/openapi3/client/petstore/python-lazyImports/test/test_parent_with_optional_dict.py @@ -37,7 +37,7 @@ def make_instance(self, include_optional) -> ParentWithOptionalDict: return ParentWithOptionalDict( optional_dict = { 'key' : petstore_api.models.inner_dict_with_property.InnerDictWithProperty( - a_property = petstore_api.models.a_property.aProperty(), ) + a_property = { }, ) } ) else: diff --git a/samples/openapi3/client/petstore/python/test/test_parent.py b/samples/openapi3/client/petstore/python/test/test_parent.py index ea1242c24f4e..d1b3d02bc1ff 100644 --- a/samples/openapi3/client/petstore/python/test/test_parent.py +++ b/samples/openapi3/client/petstore/python/test/test_parent.py @@ -37,7 +37,7 @@ def make_instance(self, include_optional) -> Parent: return Parent( optional_dict = { 'key' : petstore_api.models.inner_dict_with_property.InnerDictWithProperty( - a_property = petstore_api.models.a_property.aProperty(), ) + a_property = { }, ) } ) else: diff --git a/samples/openapi3/client/petstore/python/test/test_parent_with_optional_dict.py b/samples/openapi3/client/petstore/python/test/test_parent_with_optional_dict.py index 7cc74f9e98ec..9d5092145c2f 100644 --- a/samples/openapi3/client/petstore/python/test/test_parent_with_optional_dict.py +++ b/samples/openapi3/client/petstore/python/test/test_parent_with_optional_dict.py @@ -37,7 +37,7 @@ def make_instance(self, include_optional) -> ParentWithOptionalDict: return ParentWithOptionalDict( optional_dict = { 'key' : petstore_api.models.inner_dict_with_property.InnerDictWithProperty( - a_property = petstore_api.models.a_property.aProperty(), ) + a_property = { }, ) } ) else: From fe2483487643e99b701d59be68981c2caf0ca88e Mon Sep 17 00:00:00 2001 From: Tanbir Hossain Ramim <96797470+TanbirRamim@users.noreply.github.com> Date: Tue, 6 Oct 2026 10:58:22 +0200 Subject: [PATCH 2/2] [python] Check for free-form objects before the title, so untitled ones become dicts too --- .../codegen/languages/AbstractPythonCodegen.java | 8 ++++---- .../codegen/python/PythonClientCodegenTest.java | 3 +++ 2 files changed, 7 insertions(+), 4 deletions(-) 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 432a3f84e248..8f15852af247 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 @@ -563,14 +563,14 @@ private String toExampleValueRecursive(Schema schema, List includedSchem example = "{ }"; } } else if (ModelUtils.isObjectSchema(schema)) { - if (StringUtils.isBlank(schema.getTitle())) { - example = "None"; - return example; - } if (ModelUtils.isFreeFormObject(schema, this.openAPI)) { // free-form objects have no generated model, so the example is a plain dict return "{ }"; } + if (StringUtils.isBlank(schema.getTitle())) { + example = "None"; + return example; + } // I remove any property that is a discriminator, since it is not well supported by the python generator String toExclude = null; 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 0510211616c1..4a36de6e7a77 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 @@ -352,6 +352,9 @@ public void testExampleValueForInlineObjects() { Assert.assertTrue(example.contains("a_property = { }"), "free-form object should be a dict in:\n" + example); Assert.assertFalse(example.contains("models.a_property."), "free-form object got a model path in:\n" + example); + + // the same holds for a free-form object that never got a title, e.g. bare array items + Assert.assertEquals(codegen.toExampleValue(new ObjectSchema()), "{ }"); } @Test(description = "test single quotes escape")