OpenAPITools / OpenAPITools/openapi-generator

[BUG] [python-fastapi] doesn't support additionalProperties properly

Open
#19,311 0 comments 4 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Issue: Bug
Dominant language
Java
Stars
26.8k
Forks
7.7k
PR merge metrics
PR metrics pending

Description

Bug Report Checklist
  • Have you provided a full/minimal spec to reproduce the issue?
  • Have you validated the input using an OpenAPI validator (example)?
  • Have you tested with the latest master to confirm the issue still exists?
  • Have you searched for related issues/PRs?
  • What's the actual output vs expected output?
  • [Optional] Sponsorship to speed up the bug fix or feature request (example)
Description

The python-fastapi generator generates invalid model code when the model's OpenAPI schema specifies additionalProperties:
It lets the generated model class extend the type of the value of additionalProperties.
Also, it generates an additional_properties dict field to hold the additional properties but the generated to_dict method that would map it to the correct transfer representation is not honoured by pydantic v2 (which supports storing additional properties within the model object's __dict__ when model_config.extra=allow is specified).

For instance currently the generator generates the following model code when given a type ExampleObject that specifies a static name property as well as additionalProperties: {"type": "string"}:

example_object.py
from __future__ import annotations
import pprint
import re  # noqa: F401
import json

from pydantic import ConfigDict, StrictStr
from typing import Any, ClassVar, Dict, List
from example.gen.models.str import str
try:
    from typing import Self
except ImportError:
    from typing_extensions import Self

class ExampleObject(str):
    name: StrictStr
    additional_properties: Dict[str, Any] = {}
    __properties: ClassVar[List[str]] = ["name"]

    model_config = {
        "populate_by_name": True,
        "validate_assignment": True,
        "protected_namespaces": (),
    }


    def to_str(self) -> str:
        ...
    def to_json(self) -> str:
        ...
    @classmethod
    def from_json(cls, json_str: str) -> Self:
        ...
    def to_dict(self) -> Dict[str, Any]:
        ...
    @classmethod
    def from_dict(cls, obj: Dict) -> Self:
        ...

Please note:

  • It lets the model class inherit from str (specified by additionalProperties schema) and import the non-existing package example.gen.models.str.
  • It generated an additional_properties dict in addition to the __dict__ that already holds all model fields dynamically under the hood - the latter should be leveraged and is sufficient.
Expectation

Instead, I'd expect the following:

  • It should not inherit the value type specified within the additionalProperties schema.
  • It should not generate an additional additional_properties dict but leverage the built-in __dict__ which is anyway how pydantic v2 supports additional properties.
openapi-generator version

131fd518fbfe894cfa23619ede96adab707630d9 # v7.7.0+patch

OpenAPI declaration file content or url
openapi.yaml with additionalProperties schema
openapi: "3.0.1"

info:
  title: Example API
  description: Example API to test additionalProperties support
  version: 1.0.0

servers:
  - url: /

tags:
  - name: example

paths:
  /example:
    get:
      operationId: example
      summary: Example operation
      description: An operation that returns an object with dynamic properties
      tags:
        - example
      responses:
        '200':
          description: Example object
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ExampleObject'

components:
  schemas:
    ExampleObject:
      type: object
      properties:
        name:
          type: string
      additionalProperties:
        type: string
      required:
        - name
Generation Details

Generate a python server from the previously listed openapi.yaml file using the python-fastapi generator:

openapi-generator generate -i ./openapi.yaml -g python-fastapi -o ./generated --package-name=example.generated -p sourceFolder= -p fastapiImplementationPackage=example.controller
Steps to reproduce
java -jar ./modules/openapi-generator-cli/target/openapi-generator-cli.jar generate -i ./openapi.yaml -g python-fastapi -o ./generated --package-name=example.generated -p sourceFolder= -p fastapiImplementationPackage=example.controller
Related issues/PRs
  • #17703
Suggest a fix
  • #19312

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Reproduce the issue with the provided openapi.yaml and the python-fastapi generation command, then inspect the generated example_object.py model. Compare its inheritance and additional-properties handling with the expected Pydantic v2 behavior; done means the generated model no longer inherits the additionalProperties value type or creates a redundant additional_properties field.

Written by the indexing model from the issue text.

Assessment

Tech stack
fastapi, python
Domain
api, backend, devtools
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.