Skip to content

[BUG][PYTHON] Validation on dict values with enum not working correctly #19274

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
5 of 6 tasks
vcutrona opened this issue Jul 31, 2024 · 0 comments
Open
5 of 6 tasks

Comments

@vcutrona
Copy link
Contributor

vcutrona commented Jul 31, 2024

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 model validation is wrong for dictionaries with values in Enums. The fields_validate_enum method is not expecting a dict as input.

openapi-generator version

Docker image: openapitools/openapi-generator-cli
Versions: v7.7.0

OpenAPI declaration file content or url

YAML code

Generation Details

Generated via Docker, using the following command:

docker run --rm -v $(pwd):/api openapitools/openapi-generator-cli:v7.7.0 \
  generate -g python \
  -c /api/python.yml \
  -i /api/openapi.yaml \
  -o /api/clients/python

Here is the python.yml config file:

packageName: python_client
projectName: python-client
packageVersion: 2.2.0
hideGenerationTimestamp: true
generateSourceCodeOnly: false
mapNumberTo: Union[StrictFloat, StrictInt]
datetimeFormat: "%Y-%m-%dT%H:%M:%S%z"
dateFormat: "%Y-%m-%d"
useOneOfDiscriminatorLookup: false
library: urllib3
disallowAdditionalPropertiesIfNotPresent: true
Steps to reproduce

OS: Ubuntu 22.04
Python: 3.10.12

  1. curl https://gist.githubusercontent.com/vcutrona/adb7571338fce6cb5c495ba4abd725f7/raw/b736a812adcaac6a118f0f7c8d714f81084b63c4/openapi.yaml -o openapi.yaml
  2. Generate the python.yml file with the config specified above
  3. docker run --rm -v $(pwd):/api openapitools/openapi-generator-cli:v7.7.0 generate -g python -c /api/python.yml -i /api/openapi.yaml -o /api/clients/python
  4. Install the new client with pip install -e clients/python
  5. execute the following code snippet:
from python_client.models import MeasurementDescriptorDto
MeasurementDescriptorDto(fields={"aField": "STRING", "anotherField": "BOOLEAN"})

Exception thrown:

    if value not in set(['STRING', 'BOOLEAN', 'NUMBER']):
TypeError: unhashable type: 'dict'
Related issues/PRs
Suggest a fix

In my example (OAS 3.0), fields is a key-value dictionary (defined as documented here).
Also, the additionalProperties keyword is used to specify the type of values in that dictionary.
In my example, fields accepts strings as values, in particular only those matching an enum (i.e., "STRING", "BOOLEAN", "NUMBER").

The python generator can indeed recognize this spec, generating the MeasurementDescriptorDto class as it follows:

class MeasurementDescriptorDto(BaseModel):
    """
    MeasurementDescriptorDto
    """ # noqa: E501
    id: Optional[StrictStr] = None
    name: Optional[StrictStr] = None
    description: Optional[StrictStr] = None
    unit_of_measure_id: Optional[StrictStr] = Field(default=None, alias="unitOfMeasureId")
    taxonomy_item_id: Optional[StrictStr] = Field(default=None, alias="taxonomyItemId")
    scale_id: Optional[StrictStr] = Field(default=None, alias="scaleId")
    factory_entity_model_id: Optional[StrictStr] = Field(default=None, alias="factoryEntityModelId")
    categories_id: Optional[List[StrictStr]] = Field(default=None, alias="categoriesId")
    functional_module_inputs_id: Optional[List[StrictStr]] = Field(default=None, alias="functionalModuleInputsId")
    fields: Optional[Dict[str, StrictStr]] = None
    output_maps_id: Optional[List[StrictStr]] = Field(default=None, alias="outputMapsId")
    __properties: ClassVar[List[str]] = ["id", "name", "description", "unitOfMeasureId", "taxonomyItemId", "scaleId", "factoryEntityModelId", "categoriesId", "functionalModuleInputsId", "fields", "outputMapsId"]

Please note the type of fields declared as Optional[Dict[str, StrictStr]].
Given that fields is a dictionary (optional), the following validator cannot work.
Indeed, the fields_validate_enum is not expecting a dictionary as input:

    @field_validator('fields')
    def fields_validate_enum(cls, value):
        """Validates the enum"""
        if value is None:
            return value

        if value not in set(['STRING', 'BOOLEAN', 'NUMBER']):
            raise ValueError("must be one of enum values ('STRING', 'BOOLEAN', 'NUMBER')")
        return value

The suggested PR #19316 applies the validation function to the values of the dictionary (i.e., value.values()), not to the dictionary itself (i.e., value).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant