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
  • 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();