From 4ce412d2c204f728a06ef18c30ae9c8e7b045516 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=B5=E3=81=81?= Date: Mon, 20 Jul 2026 06:57:51 +0000 Subject: [PATCH 1/2] [python] reserve generated helper and pydantic model_* names A property named from_dict (or to_dict, to_json, ...) shadows the helper classmethods generated on every model, so Model.from_dict() raises AttributeError. A property named model_config is even worse: the class level 'model_config = ConfigDict(...)' assignment clobbers the field and the value is silently dropped from serialization. model_dump / model_fields etc. shadow the pydantic methods the generated code itself calls. Treat those names as reserved words so they get the usual var_ prefix and keep their wire name through the alias, the same way schema/json/field are handled today. Names like model_configuration that merely share the prefix are not affected. testUnmappedMemberCollisionsRemainOutsideNameMappingValidation pinned the old broken output for unmapped collisions; it now expects the mangled fields (still no hard failure, which is what the test is about). --- .../languages/PythonClientCodegen.java | 11 ++++++++++ .../python/PythonClientCodegenTest.java | 22 +++++++++++++++++-- 2 files changed, 31 insertions(+), 2 deletions(-) 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 b246a2a09724..8a4643196c45 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 @@ -151,6 +151,17 @@ public PythonClientCodegen() { .stability(Stability.STABLE) .build(); + // fields with these names would clash with the helper methods generated on every + // model (a field named from_dict shadows the classmethod) or with pydantic's + // model_* namespace (a field named model_config is clobbered by the ConfigDict + // assignment and silently dropped), so mangle them like any other reserved word + reservedWords.addAll(GENERATED_MODEL_MEMBER_NAMES); + for (String memberName : PYDANTIC_BASE_MODEL_MEMBER_NAMES) { + if (memberName.startsWith("model_")) { + reservedWords.add(memberName); + } + } + // clear import mapping (from default generator) as python does not use it // at the moment importMapping.clear(); 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 702d8d23f2af..200ed0c5c241 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 @@ -1267,9 +1267,11 @@ public void testUnmappedMemberCollisionsRemainOutsideNameMappingValidation() "src/test/resources/3_0/python/model-attribute-alias.yaml"); final Path model = Paths.get(outputPath + "openapi_client/models/alias_model.py"); + // unmapped collisions are not rejected; they fall back to reserved-word + // mangling and keep their wire name through the alias assertFileContains(model, - " model_dump: Optional[StrictStr]", - " to_dict: Optional[StrictStr]"); + " var_model_dump: Optional[StrictStr] = Field(default=None, alias=\"model_dump\")", + " var_to_dict: Optional[StrictStr] = Field(default=None, alias=\"to_dict\")"); } @Test @@ -1358,6 +1360,22 @@ public void testModelNameMappingDoesNotRenameParameters() { Assert.assertEquals(codegen.toParamName("some-value"), "parameter_value"); } + @Test + public void testGeneratedHelperAndPydanticNamesAreReserved() { + final PythonClientCodegen codegen = new PythonClientCodegen(); + + // names of the helper methods generated on every model + Assert.assertEquals(codegen.toVarName("to_dict"), "var_to_dict"); + Assert.assertEquals(codegen.toVarName("from_dict"), "var_from_dict"); + Assert.assertEquals(codegen.toVarName("to_json"), "var_to_json"); + // pydantic BaseModel namespace + Assert.assertEquals(codegen.toVarName("model_config"), "var_model_config"); + Assert.assertEquals(codegen.toVarName("model_fields"), "var_model_fields"); + Assert.assertEquals(codegen.toVarName("model_dump"), "var_model_dump"); + // similar-looking names must not be mangled + Assert.assertEquals(codegen.toVarName("model_configuration"), "model_configuration"); + } + @Test(dataProvider = "numberMappings") public void testMapNumberTo(String mapToNumber, String expectedType) throws IOException { File output = Files.createTempDirectory("test").toFile().getCanonicalFile(); From c6f1d00973f5d7357fd497829b820d962962ccab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=B5=E3=81=81?= Date: Mon, 20 Jul 2026 07:30:39 +0000 Subject: [PATCH 2/2] update docs --- docs/generators/python.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/docs/generators/python.md b/docs/generators/python.md index d48066a8d0a0..416c17f0714d 100644 --- a/docs/generators/python.md +++ b/docs/generators/python.md @@ -101,6 +101,8 @@ These options may be applied as additional-properties (cli) or configOptions (pl
  • for
  • form_params
  • from
  • +
  • from_dict
  • +
  • from_json
  • global
  • header_params
  • if
  • @@ -110,6 +112,22 @@ These options may be applied as additional-properties (cli) or configOptions (pl
  • json
  • lambda
  • local_var_files
  • +
  • model_computed_fields
  • +
  • model_config
  • +
  • model_construct
  • +
  • model_copy
  • +
  • model_dump
  • +
  • model_dump_json
  • +
  • model_extra
  • +
  • model_fields
  • +
  • model_fields_set
  • +
  • model_json_schema
  • +
  • model_parametrized_name
  • +
  • model_post_init
  • +
  • model_rebuild
  • +
  • model_validate
  • +
  • model_validate_json
  • +
  • model_validate_strings
  • none
  • nonlocal
  • not
  • @@ -124,6 +142,9 @@ These options may be applied as additional-properties (cli) or configOptions (pl
  • return
  • schema
  • self
  • +
  • to_dict
  • +
  • to_json
  • +
  • to_str
  • true
  • try
  • while