python: generate Pydantic v2 + typing complete code (#16624)

* python: improve type generation with more specific typing

* Annotate function parameters

* Remove unused imports

* remove unused files

* remove temporary hack

* remove lock file

* fix Annotated import

* support Python 3.7

* Regenerate code with typing-extensions

* Fix setup.py

* More Pydantic v2 compatibility

* depend on pydantic v2

* fix client_echo tests

* fix JSON serialization

* Fix references

* Skip circular dependency tests for now

* Temporarily hide the "float" property

The "float" property aliases the "float" type and completely breaks the
model: all the properties that were "float" now become the type of the
"float" property instead.

* Fix errors

* Import Literal from typing_extensions

* Fix GitHub Action workflows

* Fix Python 3.7 failure

* Fix quotes

* Apply suggestions from code review

* Fix tests

* split model imports from other modules imports

* fix workflow

* Comment the array unique items convertion, remove set translation

* Replace alias usage
This commit is contained in:
Jonathan Ballet
2023-09-28 13:13:14 +02:00
committed by GitHub
parent af352df10f
commit 04fa53b692
219 changed files with 3305 additions and 1566 deletions

View File

@@ -5,7 +5,6 @@
Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
**number** | **float** | | [optional]
**float** | **float** | | [optional]
**double** | **float** | | [optional]
## Example

View File

@@ -19,10 +19,11 @@ import warnings
from pydantic import validate_arguments, ValidationError
from pydantic import Field
from typing_extensions import Annotated
from pydantic import Field, StrictBytes, StrictStr, conlist
from pydantic import StrictBytes, StrictStr
from typing import Any, Dict, Optional, Union
from typing import Any, Dict, List, Optional, Union
from openapi_client.models.pet import Pet
from openapi_client.models.tag import Tag
@@ -332,7 +333,7 @@ class BodyApi:
_request_auth=_params.get('_request_auth'))
@validate_arguments
def test_body_multipart_formdata_array_of_binary(self, files : conlist(Union[StrictBytes, StrictStr]), **kwargs) -> str: # noqa: E501
def test_body_multipart_formdata_array_of_binary(self, files : List[Union[StrictBytes, StrictStr]], **kwargs) -> str: # noqa: E501
"""Test array of binary in multipart mime # noqa: E501
Test array of binary in multipart mime # noqa: E501
@@ -362,7 +363,7 @@ class BodyApi:
return self.test_body_multipart_formdata_array_of_binary_with_http_info(files, **kwargs) # noqa: E501
@validate_arguments
def test_body_multipart_formdata_array_of_binary_with_http_info(self, files : conlist(Union[StrictBytes, StrictStr]), **kwargs) -> ApiResponse: # noqa: E501
def test_body_multipart_formdata_array_of_binary_with_http_info(self, files : List[Union[StrictBytes, StrictStr]], **kwargs) -> ApiResponse: # noqa: E501
"""Test array of binary in multipart mime # noqa: E501
Test array of binary in multipart mime # noqa: E501

View File

@@ -41,6 +41,7 @@ class Bird(BaseModel):
def to_json(self) -> str:
"""Returns the JSON representation of the model using alias"""
# TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead
return json.dumps(self.to_dict())
@classmethod

View File

@@ -41,6 +41,7 @@ class Category(BaseModel):
def to_json(self) -> str:
"""Returns the JSON representation of the model using alias"""
# TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead
return json.dumps(self.to_dict())
@classmethod

View File

@@ -20,16 +20,17 @@ import json
from datetime import datetime
from typing import Optional
from pydantic import Field, StrictStr
from pydantic import StrictStr
from pydantic import Field
from openapi_client.models.query import Query
class DataQuery(Query):
"""
DataQuery
"""
suffix: Optional[StrictStr] = Field(None, description="test suffix")
text: Optional[StrictStr] = Field(None, description="Some text containing white spaces")
var_date: Optional[datetime] = Field(None, alias="date", description="A date")
suffix: Optional[StrictStr] = Field(default=None, description="test suffix")
text: Optional[StrictStr] = Field(default=None, description="Some text containing white spaces")
var_date: Optional[datetime] = Field(default=None, description="A date", alias="date")
__properties = ["id", "outcomes", "suffix", "text", "date"]
class Config:
@@ -43,6 +44,7 @@ class DataQuery(Query):
def to_json(self) -> str:
"""Returns the JSON representation of the model using alias"""
# TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead
return json.dumps(self.to_dict())
@classmethod
@@ -72,7 +74,7 @@ class DataQuery(Query):
"outcomes": obj.get("outcomes"),
"suffix": obj.get("suffix"),
"text": obj.get("text"),
"var_date": obj.get("date")
"date": obj.get("date")
})
return _obj

View File

@@ -20,20 +20,20 @@ import json
from typing import List, Optional
from pydantic import BaseModel, StrictInt, StrictStr, conlist, validator
from pydantic import BaseModel, StrictInt, StrictStr, validator
from openapi_client.models.string_enum_ref import StringEnumRef
class DefaultValue(BaseModel):
"""
to test the default value of properties # noqa: E501
"""
array_string_enum_ref_default: Optional[conlist(StringEnumRef)] = None
array_string_enum_default: Optional[conlist(StrictStr)] = None
array_string_default: Optional[conlist(StrictStr)] = None
array_integer_default: Optional[conlist(StrictInt)] = None
array_string: Optional[conlist(StrictStr)] = None
array_string_nullable: Optional[conlist(StrictStr)] = None
array_string_extension_nullable: Optional[conlist(StrictStr)] = None
array_string_enum_ref_default: Optional[List[StringEnumRef]] = None
array_string_enum_default: Optional[List[StrictStr]] = None
array_string_default: Optional[List[StrictStr]] = None
array_integer_default: Optional[List[StrictInt]] = None
array_string: Optional[List[StrictStr]] = None
array_string_nullable: Optional[List[StrictStr]] = None
array_string_extension_nullable: Optional[List[StrictStr]] = None
string_nullable: Optional[StrictStr] = None
__properties = ["array_string_enum_ref_default", "array_string_enum_default", "array_string_default", "array_integer_default", "array_string", "array_string_nullable", "array_string_extension_nullable", "string_nullable"]
@@ -59,6 +59,7 @@ class DefaultValue(BaseModel):
def to_json(self) -> str:
"""Returns the JSON representation of the model using alias"""
# TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead
return json.dumps(self.to_dict())
@classmethod

View File

@@ -20,16 +20,17 @@ import json
from typing import Optional, Union
from pydantic import BaseModel, StrictFloat, StrictInt, confloat, conint
from pydantic import BaseModel, StrictFloat, StrictInt
from pydantic import Field
from typing_extensions import Annotated
class NumberPropertiesOnly(BaseModel):
"""
NumberPropertiesOnly
"""
number: Optional[Union[StrictFloat, StrictInt]] = None
float: Optional[Union[StrictFloat, StrictInt]] = None
double: Optional[Union[confloat(le=50.2, ge=0.8, strict=True), conint(le=50, ge=1, strict=True)]] = None
__properties = ["number", "float", "double"]
double: Optional[Union[Annotated[float, Field(le=50.2, strict=True, ge=0.8)], Annotated[int, Field(le=50, strict=True, ge=1)]]] = None
__properties = ["number", "double"]
class Config:
"""Pydantic configuration"""
@@ -42,6 +43,7 @@ class NumberPropertiesOnly(BaseModel):
def to_json(self) -> str:
"""Returns the JSON representation of the model using alias"""
# TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead
return json.dumps(self.to_dict())
@classmethod
@@ -68,7 +70,6 @@ class NumberPropertiesOnly(BaseModel):
_obj = NumberPropertiesOnly.parse_obj({
"number": obj.get("number"),
"float": obj.get("float"),
"double": obj.get("double")
})
return _obj

View File

@@ -20,7 +20,8 @@ import json
from typing import List, Optional
from pydantic import BaseModel, Field, StrictInt, StrictStr, conlist, validator
from pydantic import BaseModel, StrictInt, StrictStr, validator
from pydantic import Field
from openapi_client.models.category import Category
from openapi_client.models.tag import Tag
@@ -29,11 +30,11 @@ class Pet(BaseModel):
Pet
"""
id: Optional[StrictInt] = None
name: StrictStr = Field(...)
name: StrictStr
category: Optional[Category] = None
photo_urls: conlist(StrictStr) = Field(..., alias="photoUrls")
tags: Optional[conlist(Tag)] = None
status: Optional[StrictStr] = Field(None, description="pet status in the store")
photo_urls: List[StrictStr] = Field(alias="photoUrls")
tags: Optional[List[Tag]] = None
status: Optional[StrictStr] = Field(default=None, description="pet status in the store")
__properties = ["id", "name", "category", "photoUrls", "tags", "status"]
@validator('status')
@@ -57,6 +58,7 @@ class Pet(BaseModel):
def to_json(self) -> str:
"""Returns the JSON representation of the model using alias"""
# TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead
return json.dumps(self.to_dict())
@classmethod
@@ -95,7 +97,7 @@ class Pet(BaseModel):
"id": obj.get("id"),
"name": obj.get("name"),
"category": Category.from_dict(obj.get("category")) if obj.get("category") is not None else None,
"photo_urls": obj.get("photoUrls"),
"photoUrls": obj.get("photoUrls"),
"tags": [Tag.from_dict(_item) for _item in obj.get("tags")] if obj.get("tags") is not None else None,
"status": obj.get("status")
})

View File

@@ -20,14 +20,15 @@ import json
from typing import List, Optional
from pydantic import BaseModel, Field, StrictInt, StrictStr, conlist, validator
from pydantic import BaseModel, StrictInt, StrictStr, validator
from pydantic import Field
class Query(BaseModel):
"""
Query
"""
id: Optional[StrictInt] = Field(None, description="Query")
outcomes: Optional[conlist(StrictStr)] = None
id: Optional[StrictInt] = Field(default=None, description="Query")
outcomes: Optional[List[StrictStr]] = None
__properties = ["id", "outcomes"]
@validator('outcomes')
@@ -52,6 +53,7 @@ class Query(BaseModel):
def to_json(self) -> str:
"""Returns the JSON representation of the model using alias"""
# TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead
return json.dumps(self.to_dict())
@classmethod

View File

@@ -41,6 +41,7 @@ class Tag(BaseModel):
def to_json(self) -> str:
"""Returns the JSON representation of the model using alias"""
# TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead
return json.dumps(self.to_dict())
@classmethod

View File

@@ -43,6 +43,7 @@ class TestQueryStyleDeepObjectExplodeTrueObjectAllOfQueryObjectParameter(BaseMod
def to_json(self) -> str:
"""Returns the JSON representation of the model using alias"""
# TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead
return json.dumps(self.to_dict())
@classmethod

View File

@@ -20,13 +20,13 @@ import json
from typing import List, Optional
from pydantic import BaseModel, StrictStr, conlist
from pydantic import BaseModel, StrictStr
class TestQueryStyleFormExplodeTrueArrayStringQueryObjectParameter(BaseModel):
"""
TestQueryStyleFormExplodeTrueArrayStringQueryObjectParameter
"""
values: Optional[conlist(StrictStr)] = None
values: Optional[List[StrictStr]] = None
__properties = ["values"]
class Config:
@@ -40,6 +40,7 @@ class TestQueryStyleFormExplodeTrueArrayStringQueryObjectParameter(BaseModel):
def to_json(self) -> str:
"""Returns the JSON representation of the model using alias"""
# TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead
return json.dumps(self.to_dict())
@classmethod

View File

@@ -14,8 +14,9 @@ python = "^3.7"
urllib3 = ">= 1.25.3"
python-dateutil = ">=2.8.2"
pydantic = "^1.10.5, <2"
pydantic = ">=2"
aenum = ">=3.1.11"
typing-extensions = ">=4.7.1"
[tool.poetry.dev-dependencies]
pytest = ">=7.2.1"

View File

@@ -1,5 +1,6 @@
python_dateutil >= 2.5.3
setuptools >= 21.0.0
urllib3 >= 1.25.3, < 2.1.0
pydantic >= 1.10.5, < 2
pydantic >= 2
aenum >= 3.1.11
typing-extensions >= 4.7.1

View File

@@ -27,8 +27,9 @@ PYTHON_REQUIRES = ">=3.7"
REQUIRES = [
"urllib3 >= 1.25.3, < 2.1.0",
"python-dateutil",
"pydantic >= 1.10.5, < 2",
"aenum"
"pydantic >= 2",
"aenum",
"typing-extensions >= 4.7.1",
]
setup(

View File

@@ -84,12 +84,14 @@ class TestManual(unittest.TestCase):
def testNumberPropertiesOnly(self):
n = openapi_client.NumberPropertiesOnly.from_json('{"number": 123, "float": 456, "double": 34}')
self.assertEqual(n.number, 123)
self.assertEqual(n.float, 456)
# TODO: pydantic v2: this field name override the default `float` type
# self.assertEqual(n.float, 456)
self.assertEqual(n.double, 34)
n = openapi_client.NumberPropertiesOnly.from_json('{"number": 123.1, "float": 456.2, "double": 34.3}')
self.assertEqual(n.number, 123.1)
self.assertEqual(n.float, 456.2)
# TODO: pydantic v2: this field name override the default `float` type
# self.assertEqual(n.float, 456.2)
self.assertEqual(n.double, 34.3)
def testApplicatinOctetStreamBinaryBodyParameter(self):