Skip to content

Commit 72c02e4

Browse files
authored
optionally support float strict type (#14618)
1 parent d7a2e4a commit 72c02e4

File tree

18 files changed

+76
-40
lines changed

18 files changed

+76
-40
lines changed

bin/configs/python-nextgen-aiohttp.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@ templateDir: modules/openapi-generator/src/main/resources/python-nextgen
55
library: asyncio
66
additionalProperties:
77
packageName: petstore_api
8+
floatStrictType: false

docs/generators/python-nextgen.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
2020
| Option | Description | Values | Default |
2121
| ------ | ----------- | ------ | ------- |
2222
|disallowAdditionalPropertiesIfNotPresent|If false, the 'additionalProperties' implementation (set to true by default) is compliant with the OAS and JSON schema specifications. If true (default), keep the old (incorrect) behaviour that 'additionalProperties' is set to false by default.|<dl><dt>**false**</dt><dd>The 'additionalProperties' implementation is compliant with the OAS and JSON schema specifications.</dd><dt>**true**</dt><dd>Keep the old (incorrect) behaviour that 'additionalProperties' is set to false by default.</dd></dl>|true|
23+
|floatStrictType|Use strict type for float, i.e. StrictFloat or confloat(strict=true, ...)| |true|
2324
|generateSourceCodeOnly|Specifies that only a library source code is to be generated.| |false|
2425
|hideGenerationTimestamp|Hides the generation timestamp when files are generated.| |true|
2526
|library|library template (sub-template) to use: asyncio, tornado (deprecated), urllib3| |urllib3|

modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PythonNextgenClientCodegen.java

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -46,13 +46,14 @@ public class PythonNextgenClientCodegen extends AbstractPythonCodegen implements
4646
public static final String PACKAGE_URL = "packageUrl";
4747
public static final String DEFAULT_LIBRARY = "urllib3";
4848
public static final String RECURSION_LIMIT = "recursionLimit";
49-
public static final String PYTHON_ATTR_NONE_IF_UNSET = "pythonAttrNoneIfUnset";
49+
public static final String FLOAT_STRICT_TYPE = "floatStrictType";
5050

5151
protected String packageUrl;
5252
protected String apiDocPath = "docs" + File.separator;
5353
protected String modelDocPath = "docs" + File.separator;
5454
protected boolean hasModelsToImport = Boolean.FALSE;
5555
protected boolean useOneOfDiscriminatorLookup = false; // use oneOf discriminator's mapping for model lookup
56+
protected boolean floatStrictType = true;
5657

5758
protected Map<Character, String> regexModifiers;
5859

@@ -164,6 +165,8 @@ public PythonNextgenClientCodegen() {
164165
cliOptions.add(new CliOption(CodegenConstants.SOURCECODEONLY_GENERATION, CodegenConstants.SOURCECODEONLY_GENERATION_DESC)
165166
.defaultValue(Boolean.FALSE.toString()));
166167
cliOptions.add(new CliOption(RECURSION_LIMIT, "Set the recursion limit. If not set, use the system default value."));
168+
cliOptions.add(new CliOption(FLOAT_STRICT_TYPE, "Use strict type for float, i.e. StrictFloat or confloat(strict=true, ...)")
169+
.defaultValue(Boolean.TRUE.toString()));
167170

168171
supportedLibraries.put("urllib3", "urllib3-based client");
169172
supportedLibraries.put("asyncio", "asyncio-based client");
@@ -259,6 +262,10 @@ public void processOpts() {
259262
additionalProperties.put(CodegenConstants.USE_ONEOF_DISCRIMINATOR_LOOKUP, useOneOfDiscriminatorLookup);
260263
}
261264

265+
if (additionalProperties.containsKey(FLOAT_STRICT_TYPE)) {
266+
setFloatStrictType(convertPropertyToBooleanAndWriteBack(FLOAT_STRICT_TYPE));
267+
}
268+
262269
String modelPath = packagePath() + File.separatorChar + modelPackage.replace('.', File.separatorChar);
263270
String apiPath = packagePath() + File.separatorChar + apiPackage.replace('.', File.separatorChar);
264271

@@ -424,7 +431,6 @@ private String getPydanticType(CodegenParameter cp,
424431
if (cp.hasValidation) {
425432
List<String> fieldCustomization = new ArrayList<>();
426433
// e.g. confloat(ge=10, le=100, strict=True)
427-
fieldCustomization.add("strict=True");
428434
if (cp.getMaximum() != null) {
429435
if (cp.getExclusiveMaximum()) {
430436
fieldCustomization.add("gt=" + cp.getMaximum());
@@ -443,12 +449,20 @@ private String getPydanticType(CodegenParameter cp,
443449
fieldCustomization.add("multiple_of=" + cp.getMultipleOf());
444450
}
445451

452+
if (floatStrictType) {
453+
fieldCustomization.add("strict=True");
454+
}
455+
446456
pydanticImports.add("confloat");
447457
return String.format(Locale.ROOT, "%s(%s)", "confloat",
448458
StringUtils.join(fieldCustomization, ", "));
449459
} else {
450-
pydanticImports.add("StrictFloat");
451-
return "StrictFloat";
460+
if (floatStrictType) {
461+
pydanticImports.add("StrictFloat");
462+
return "StrictFloat";
463+
} else {
464+
return "float";
465+
}
452466
}
453467
} else if (cp.isInteger || cp.isLong || cp.isShort || cp.isUnboundedInteger) {
454468
if (cp.hasValidation) {
@@ -645,7 +659,6 @@ private String getPydanticType(CodegenProperty cp,
645659
if (cp.hasValidation) {
646660
List<String> fieldCustomization = new ArrayList<>();
647661
// e.g. confloat(ge=10, le=100, strict=True)
648-
fieldCustomization.add("strict=True");
649662
if (cp.getMaximum() != null) {
650663
if (cp.getExclusiveMaximum()) {
651664
fieldCustomization.add("lt=" + cp.getMaximum());
@@ -664,12 +677,20 @@ private String getPydanticType(CodegenProperty cp,
664677
fieldCustomization.add("multiple_of=" + cp.getMultipleOf());
665678
}
666679

680+
if (floatStrictType) {
681+
fieldCustomization.add("strict=True");
682+
}
683+
667684
pydanticImports.add("confloat");
668685
return String.format(Locale.ROOT, "%s(%s)", "confloat",
669686
StringUtils.join(fieldCustomization, ", "));
670687
} else {
671-
pydanticImports.add("StrictFloat");
672-
return "StrictFloat";
688+
if (floatStrictType) {
689+
pydanticImports.add("StrictFloat");
690+
return "StrictFloat";
691+
} else {
692+
return "float";
693+
}
673694
}
674695
} else if (cp.isInteger || cp.isLong || cp.isShort || cp.isUnboundedInteger) {
675696
if (cp.hasValidation) {
@@ -1316,4 +1337,8 @@ public String escapeReservedWord(String name) {
13161337
}
13171338
return "var_" + name;
13181339
}
1340+
1341+
public void setFloatStrictType(boolean floatStrictType) {
1342+
this.floatStrictType = floatStrictType;
1343+
}
13191344
}

modules/openapi-generator/src/test/resources/3_0/python/petstore-with-fake-endpoints-models-for-testing.yaml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1521,7 +1521,6 @@ components:
15211521
type: object
15221522
required:
15231523
- number
1524-
- byte
15251524
- date
15261525
- password
15271526
properties:

samples/openapi3/client/petstore/python-nextgen-aiohttp/docs/FormatTest.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ Name | Type | Description | Notes
1212
**double** | **float** | | [optional]
1313
**decimal** | **decimal.Decimal** | | [optional]
1414
**string** | **str** | | [optional]
15-
**byte** | **str** | |
15+
**byte** | **str** | | [optional]
1616
**binary** | **str** | | [optional]
1717
**var_date** | **date** | |
1818
**date_time** | **datetime** | | [optional]

samples/openapi3/client/petstore/python-nextgen-aiohttp/petstore_api/api/fake_api.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919

2020
from datetime import date, datetime
2121

22-
from pydantic import Field, StrictBool, StrictFloat, StrictInt, StrictStr, confloat, conint, constr, validator
22+
from pydantic import Field, StrictBool, StrictInt, StrictStr, confloat, conint, constr, validator
2323

2424
from typing import Dict, List, Optional
2525

@@ -641,7 +641,7 @@ def fake_outer_composite_serialize_with_http_info(self, outer_composite : Annota
641641
_request_auth=_params.get('_request_auth'))
642642

643643
@validate_arguments
644-
def fake_outer_number_serialize(self, body : Annotated[Optional[StrictFloat], Field(description="Input number as post body")] = None, **kwargs) -> float: # noqa: E501
644+
def fake_outer_number_serialize(self, body : Annotated[Optional[float], Field(description="Input number as post body")] = None, **kwargs) -> float: # noqa: E501
645645
"""fake_outer_number_serialize # noqa: E501
646646
647647
Test serialization of outer number types # noqa: E501
@@ -672,7 +672,7 @@ def fake_outer_number_serialize(self, body : Annotated[Optional[StrictFloat], Fi
672672
return self.fake_outer_number_serialize_with_http_info(body, **kwargs) # noqa: E501
673673

674674
@validate_arguments
675-
def fake_outer_number_serialize_with_http_info(self, body : Annotated[Optional[StrictFloat], Field(description="Input number as post body")] = None, **kwargs): # noqa: E501
675+
def fake_outer_number_serialize_with_http_info(self, body : Annotated[Optional[float], Field(description="Input number as post body")] = None, **kwargs): # noqa: E501
676676
"""fake_outer_number_serialize # noqa: E501
677677
678678
Test serialization of outer number types # noqa: E501
@@ -1678,7 +1678,7 @@ def test_client_model_with_http_info(self, client : Annotated[Client, Field(...,
16781678
_request_auth=_params.get('_request_auth'))
16791679

16801680
@validate_arguments
1681-
def test_endpoint_parameters(self, number : Annotated[confloat(strict=True, ge=543.2, le=32.1), Field(..., description="None")], double : Annotated[confloat(strict=True, ge=123.4, le=67.8), Field(..., description="None")], pattern_without_delimiter : Annotated[constr(strict=True), Field(..., description="None")], byte : Annotated[StrictStr, Field(..., description="None")], integer : Annotated[Optional[conint(strict=True, le=100, ge=10)], Field(description="None")] = None, int32 : Annotated[Optional[conint(strict=True, le=200, ge=20)], Field(description="None")] = None, int64 : Annotated[Optional[StrictInt], Field(description="None")] = None, float : Annotated[Optional[confloat(strict=True, ge=987.6)], Field(description="None")] = None, string : Annotated[Optional[constr(strict=True)], Field(description="None")] = None, binary : Annotated[Optional[StrictStr], Field(description="None")] = None, var_date : Annotated[Optional[date], Field(description="None")] = None, date_time : Annotated[Optional[datetime], Field(description="None")] = None, password : Annotated[Optional[constr(strict=True, max_length=64, min_length=10)], Field(description="None")] = None, param_callback : Annotated[Optional[StrictStr], Field(description="None")] = None, **kwargs) -> None: # noqa: E501
1681+
def test_endpoint_parameters(self, number : Annotated[confloat(ge=543.2, le=32.1), Field(..., description="None")], double : Annotated[confloat(ge=123.4, le=67.8), Field(..., description="None")], pattern_without_delimiter : Annotated[constr(strict=True), Field(..., description="None")], byte : Annotated[StrictStr, Field(..., description="None")], integer : Annotated[Optional[conint(strict=True, le=100, ge=10)], Field(description="None")] = None, int32 : Annotated[Optional[conint(strict=True, le=200, ge=20)], Field(description="None")] = None, int64 : Annotated[Optional[StrictInt], Field(description="None")] = None, float : Annotated[Optional[confloat(ge=987.6)], Field(description="None")] = None, string : Annotated[Optional[constr(strict=True)], Field(description="None")] = None, binary : Annotated[Optional[StrictStr], Field(description="None")] = None, var_date : Annotated[Optional[date], Field(description="None")] = None, date_time : Annotated[Optional[datetime], Field(description="None")] = None, password : Annotated[Optional[constr(strict=True, max_length=64, min_length=10)], Field(description="None")] = None, param_callback : Annotated[Optional[StrictStr], Field(description="None")] = None, **kwargs) -> None: # noqa: E501
16821682
"""Fake endpoint for testing various parameters 假端點 偽のエンドポイント 가짜 엔드 포인트 # noqa: E501
16831683
16841684
Fake endpoint for testing various parameters 假端點 偽のエンドポイント 가짜 엔드 포인트 # noqa: E501
@@ -1735,7 +1735,7 @@ def test_endpoint_parameters(self, number : Annotated[confloat(strict=True, ge=5
17351735
return self.test_endpoint_parameters_with_http_info(number, double, pattern_without_delimiter, byte, integer, int32, int64, float, string, binary, var_date, date_time, password, param_callback, **kwargs) # noqa: E501
17361736

17371737
@validate_arguments
1738-
def test_endpoint_parameters_with_http_info(self, number : Annotated[confloat(strict=True, ge=543.2, le=32.1), Field(..., description="None")], double : Annotated[confloat(strict=True, ge=123.4, le=67.8), Field(..., description="None")], pattern_without_delimiter : Annotated[constr(strict=True), Field(..., description="None")], byte : Annotated[StrictStr, Field(..., description="None")], integer : Annotated[Optional[conint(strict=True, le=100, ge=10)], Field(description="None")] = None, int32 : Annotated[Optional[conint(strict=True, le=200, ge=20)], Field(description="None")] = None, int64 : Annotated[Optional[StrictInt], Field(description="None")] = None, float : Annotated[Optional[confloat(strict=True, ge=987.6)], Field(description="None")] = None, string : Annotated[Optional[constr(strict=True)], Field(description="None")] = None, binary : Annotated[Optional[StrictStr], Field(description="None")] = None, var_date : Annotated[Optional[date], Field(description="None")] = None, date_time : Annotated[Optional[datetime], Field(description="None")] = None, password : Annotated[Optional[constr(strict=True, max_length=64, min_length=10)], Field(description="None")] = None, param_callback : Annotated[Optional[StrictStr], Field(description="None")] = None, **kwargs): # noqa: E501
1738+
def test_endpoint_parameters_with_http_info(self, number : Annotated[confloat(ge=543.2, le=32.1), Field(..., description="None")], double : Annotated[confloat(ge=123.4, le=67.8), Field(..., description="None")], pattern_without_delimiter : Annotated[constr(strict=True), Field(..., description="None")], byte : Annotated[StrictStr, Field(..., description="None")], integer : Annotated[Optional[conint(strict=True, le=100, ge=10)], Field(description="None")] = None, int32 : Annotated[Optional[conint(strict=True, le=200, ge=20)], Field(description="None")] = None, int64 : Annotated[Optional[StrictInt], Field(description="None")] = None, float : Annotated[Optional[confloat(ge=987.6)], Field(description="None")] = None, string : Annotated[Optional[constr(strict=True)], Field(description="None")] = None, binary : Annotated[Optional[StrictStr], Field(description="None")] = None, var_date : Annotated[Optional[date], Field(description="None")] = None, date_time : Annotated[Optional[datetime], Field(description="None")] = None, password : Annotated[Optional[constr(strict=True, max_length=64, min_length=10)], Field(description="None")] = None, param_callback : Annotated[Optional[StrictStr], Field(description="None")] = None, **kwargs): # noqa: E501
17391739
"""Fake endpoint for testing various parameters 假端點 偽のエンドポイント 가짜 엔드 포인트 # noqa: E501
17401740
17411741
Fake endpoint for testing various parameters 假端點 偽のエンドポイント 가짜 엔드 포인트 # noqa: E501

samples/openapi3/client/petstore/python-nextgen-aiohttp/petstore_api/models/array_of_array_of_number_only.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,15 @@
1818

1919

2020
from typing import List, Optional
21-
from pydantic import BaseModel, Field, StrictFloat
21+
from pydantic import BaseModel, Field
2222

2323
class ArrayOfArrayOfNumberOnly(BaseModel):
2424
"""NOTE: This class is auto generated by OpenAPI Generator.
2525
Ref: https://openapi-generator.tech
2626
2727
Do not edit the class manually.
2828
"""
29-
array_array_number: Optional[List[List[StrictFloat]]] = Field(None, alias="ArrayArrayNumber")
29+
array_array_number: Optional[List[List[float]]] = Field(None, alias="ArrayArrayNumber")
3030
__properties = ["ArrayArrayNumber"]
3131

3232
class Config:

samples/openapi3/client/petstore/python-nextgen-aiohttp/petstore_api/models/array_of_number_only.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,15 @@
1818

1919

2020
from typing import List, Optional
21-
from pydantic import BaseModel, Field, StrictFloat
21+
from pydantic import BaseModel, Field
2222

2323
class ArrayOfNumberOnly(BaseModel):
2424
"""NOTE: This class is auto generated by OpenAPI Generator.
2525
Ref: https://openapi-generator.tech
2626
2727
Do not edit the class manually.
2828
"""
29-
array_number: Optional[List[StrictFloat]] = Field(None, alias="ArrayNumber")
29+
array_number: Optional[List[float]] = Field(None, alias="ArrayNumber")
3030
__properties = ["ArrayNumber"]
3131

3232
class Config:

samples/openapi3/client/petstore/python-nextgen-aiohttp/petstore_api/models/enum_test.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818

1919

2020
from typing import Optional
21-
from pydantic import BaseModel, Field, StrictFloat, StrictInt, StrictStr, validator
21+
from pydantic import BaseModel, Field, StrictInt, StrictStr, validator
2222
from petstore_api.models.outer_enum import OuterEnum
2323
from petstore_api.models.outer_enum_default_value import OuterEnumDefaultValue
2424
from petstore_api.models.outer_enum_integer import OuterEnumInteger
@@ -33,7 +33,7 @@ class EnumTest(BaseModel):
3333
enum_string: Optional[StrictStr] = None
3434
enum_string_required: StrictStr = ...
3535
enum_integer: Optional[StrictInt] = None
36-
enum_number: Optional[StrictFloat] = None
36+
enum_number: Optional[float] = None
3737
outer_enum: Optional[OuterEnum] = Field(None, alias="outerEnum")
3838
outer_enum_integer: Optional[OuterEnumInteger] = Field(None, alias="outerEnumInteger")
3939
outer_enum_default_value: Optional[OuterEnumDefaultValue] = Field(None, alias="outerEnumDefaultValue")

samples/openapi3/client/petstore/python-nextgen-aiohttp/petstore_api/models/format_test.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,12 @@ class FormatTest(BaseModel):
2929
integer: Optional[conint(strict=True, le=100, ge=10)] = None
3030
int32: Optional[conint(strict=True, le=200, ge=20)] = None
3131
int64: Optional[StrictInt] = None
32-
number: confloat(strict=True, le=543.2, ge=32.1) = ...
33-
float: Optional[confloat(strict=True, le=987.6, ge=54.3)] = None
34-
double: Optional[confloat(strict=True, le=123.4, ge=67.8)] = None
32+
number: confloat(le=543.2, ge=32.1) = ...
33+
float: Optional[confloat(le=987.6, ge=54.3)] = None
34+
double: Optional[confloat(le=123.4, ge=67.8)] = None
3535
decimal: Optional[condecimal()] = None
3636
string: Optional[constr(strict=True)] = None
37-
byte: StrictBytes = ...
37+
byte: Optional[StrictBytes] = None
3838
binary: Optional[StrictBytes] = None
3939
var_date: date = Field(..., alias="date")
4040
date_time: Optional[datetime] = Field(None, alias="dateTime")

samples/openapi3/client/petstore/python-nextgen-aiohttp/petstore_api/models/nullable_class.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818

1919
from datetime import date, datetime
2020
from typing import Any, Dict, List, Optional
21-
from pydantic import BaseModel, StrictBool, StrictFloat, StrictInt, StrictStr
21+
from pydantic import BaseModel, StrictBool, StrictInt, StrictStr
2222

2323
class NullableClass(BaseModel):
2424
"""NOTE: This class is auto generated by OpenAPI Generator.
@@ -28,7 +28,7 @@ class NullableClass(BaseModel):
2828
"""
2929
required_integer_prop: Optional[StrictInt] = ...
3030
integer_prop: Optional[StrictInt] = None
31-
number_prop: Optional[StrictFloat] = None
31+
number_prop: Optional[float] = None
3232
boolean_prop: Optional[StrictBool] = None
3333
string_prop: Optional[StrictStr] = None
3434
date_prop: Optional[date] = None

samples/openapi3/client/petstore/python-nextgen-aiohttp/petstore_api/models/number_only.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,15 @@
1818

1919

2020
from typing import Optional
21-
from pydantic import BaseModel, Field, StrictFloat
21+
from pydantic import BaseModel, Field
2222

2323
class NumberOnly(BaseModel):
2424
"""NOTE: This class is auto generated by OpenAPI Generator.
2525
Ref: https://openapi-generator.tech
2626
2727
Do not edit the class manually.
2828
"""
29-
just_number: Optional[StrictFloat] = Field(None, alias="JustNumber")
29+
just_number: Optional[float] = Field(None, alias="JustNumber")
3030
__properties = ["JustNumber"]
3131

3232
class Config:

samples/openapi3/client/petstore/python-nextgen-aiohttp/petstore_api/models/object_with_deprecated_fields.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818

1919

2020
from typing import List, Optional
21-
from pydantic import BaseModel, Field, StrictFloat, StrictStr
21+
from pydantic import BaseModel, Field, StrictStr
2222
from petstore_api.models.deprecated_object import DeprecatedObject
2323

2424
class ObjectWithDeprecatedFields(BaseModel):
@@ -28,7 +28,7 @@ class ObjectWithDeprecatedFields(BaseModel):
2828
Do not edit the class manually.
2929
"""
3030
uuid: Optional[StrictStr] = None
31-
id: Optional[StrictFloat] = None
31+
id: Optional[float] = None
3232
deprecated_ref: Optional[DeprecatedObject] = Field(None, alias="deprecatedRef")
3333
bars: Optional[List[StrictStr]] = None
3434
__properties = ["uuid", "id", "deprecatedRef", "bars"]

0 commit comments

Comments
 (0)