Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
13 changes: 13 additions & 0 deletions bin/configs/python-pydantic-v2.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
generatorName: python
outputDir: samples/openapi3/client/petstore/python-pydantic-v2
inputSpec: modules/openapi-generator/src/test/resources/3_0/python/petstore-with-fake-endpoints-models-for-testing-pydantic-v2.yaml
templateDir: modules/openapi-generator/src/main/resources/python
additionalProperties:
packageName: petstore_api
useOneOfDiscriminatorLookup: "true"
disallowAdditionalPropertiesIfNotPresent: false
mapNumberTo: StrictFloat
usePydanticV2: true
nameMappings:
_type: underscore_type
type_: type_with_underscore
1 change: 1 addition & 0 deletions docs/generators/python.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
|projectName|python project name in setup.py (e.g. petstore-api).| |null|
|recursionLimit|Set the recursion limit. If not set, use the system default value.| |null|
|useOneOfDiscriminatorLookup|Use the discriminator's mapping in oneOf to speed up the model lookup. IMPORTANT: Validation (e.g. one and only one match in oneOf's schemas) will be skipped.| |false|
|usePydanticV2|Use Pydantic v2 (beta support)| |false|

## IMPORT MAPPING

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ public abstract class AbstractPythonCodegen extends DefaultCodegen implements Co
protected boolean hasModelsToImport = Boolean.FALSE;
protected String mapNumberTo = "Union[StrictFloat, StrictInt]";
protected Map<Character, String> regexModifiers;
protected boolean usePydanticV2;

private Map<String, String> schemaKeyToModelNameCache = new HashMap<>();
// map of set (model imports)
Expand Down Expand Up @@ -638,6 +639,10 @@ public String patternCorrection(String pattern) {
return pattern;
}

public void setUsePydanticV2(boolean usePydanticV2) {
this.usePydanticV2 = usePydanticV2;
}

public void setPackageName(String packageName) {
this.packageName = packageName;
additionalProperties.put(CodegenConstants.PACKAGE_NAME, this.packageName);
Expand Down Expand Up @@ -1075,10 +1080,12 @@ private String getPydanticType(CodegenParameter cp,
if (cp.isArray) {
String constraints = "";
if (cp.maxItems != null) {
constraints += String.format(Locale.ROOT, ", max_items=%d", cp.maxItems);
String max = usePydanticV2 ? "max_length" : "max_items";
constraints += String.format(Locale.ROOT, ", %s=%d", max, cp.maxItems);
}
if (cp.minItems != null) {
constraints += String.format(Locale.ROOT, ", min_items=%d", cp.minItems);
String min = usePydanticV2 ? "min_length" : "min_items";
constraints += String.format(Locale.ROOT, ", %s=%d", min, cp.minItems);
}
if (cp.getUniqueItems()) {
constraints += ", unique_items=True";
Expand Down Expand Up @@ -1360,10 +1367,12 @@ private String getPydanticType(CodegenProperty cp,
if (cp.isArray) {
String constraints = "";
if (cp.maxItems != null) {
constraints += String.format(Locale.ROOT, ", max_items=%d", cp.maxItems);
String max = usePydanticV2 ? "max_length" : "max_items";
constraints += String.format(Locale.ROOT, ", %s=%d", max, cp.maxItems);
}
if (cp.minItems != null) {
constraints += String.format(Locale.ROOT, ", min_items=%d", cp.minItems);
String min = usePydanticV2 ? "min_length" : "min_items";
constraints += String.format(Locale.ROOT, ", %s=%d", min, cp.minItems);
}
if (cp.getUniqueItems()) {
constraints += ", unique_items=True";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ public class PythonClientCodegen extends AbstractPythonCodegen implements Codege
public static final String RECURSION_LIMIT = "recursionLimit";
public static final String DATETIME_FORMAT = "datetimeFormat";
public static final String DATE_FORMAT = "dateFormat";
public static final String USE_PYDANTIC_V2 = "usePydanticV2";

protected String packageUrl;
protected String apiDocPath = "docs/";
Expand Down Expand Up @@ -164,6 +165,8 @@ public PythonClientCodegen() {
cliOptions.add(new CliOption(DATE_FORMAT, "date format for query parameters")
.defaultValue("%Y-%m-%d"));
cliOptions.add(new CliOption(CodegenConstants.USE_ONEOF_DISCRIMINATOR_LOOKUP, CodegenConstants.USE_ONEOF_DISCRIMINATOR_LOOKUP_DESC).defaultValue("false"));
cliOptions.add(new CliOption(USE_PYDANTIC_V2, "Use Pydantic v2 (beta support)")
.defaultValue("false"));

supportedLibraries.put("urllib3", "urllib3-based client");
supportedLibraries.put("asyncio", "asyncio-based client");
Expand Down Expand Up @@ -256,6 +259,11 @@ public void processOpts() {
}
}

// to use Pydantic V2?
if (additionalProperties.containsKey(USE_PYDANTIC_V2)) {
setUsePydanticV2(Boolean.valueOf(additionalProperties.get(USE_PYDANTIC_V2).toString()));
}

if (additionalProperties.containsKey(CodegenConstants.USE_ONEOF_DISCRIMINATOR_LOOKUP)) {
setUseOneOfDiscriminatorLookup(convertPropertyToBooleanAndWriteBack(CodegenConstants.USE_ONEOF_DISCRIMINATOR_LOOKUP));
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ chardet==4.0.0
click==7.1.2
dnspython==2.1.0
email-validator==1.1.2
fastapi==0.95.2
fastapi==0.103.0
graphene==2.1.8
graphql-core==2.3.2
graphql-relay==2.0.1
Expand All @@ -20,7 +20,7 @@ Jinja2==2.11.3
MarkupSafe==2.0.1
orjson==3.5.2
promise==2.3
pydantic==1.8.2
pydantic==2.3.0
python-dotenv==0.17.1
python-multipart==0.0.5
PyYAML==5.4.1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ import re # noqa: F401
import io
import warnings

from pydantic import validate_arguments, ValidationError
from pydantic import {{#usePydanticV2}}validate_call{{/usePydanticV2}}{{^usePydanticV2}}validate_arguments{{/usePydanticV2}}, ValidationError
from typing_extensions import Annotated
{{#asyncio}}
from typing import overload, Optional, Union, Awaitable
{{/asyncio}}
Expand Down Expand Up @@ -47,7 +48,7 @@ class {{classname}}:
...

{{/asyncio}}
@validate_arguments
@{{#usePydanticV2}}validate_call{{/usePydanticV2}}{{^usePydanticV2}}validate_arguments{{/usePydanticV2}}
def {{operationId}}(self, {{#allParams}}{{paramName}} : {{{vendorExtensions.x-py-typing}}}{{^required}} = None{{/required}}, {{/allParams}}{{#asyncio}}async_req: Optional[bool]=None, {{/asyncio}}**kwargs) -> {{#asyncio}}Union[{{{returnType}}}{{^returnType}}None{{/returnType}}, Awaitable[{{{returnType}}}{{^returnType}}None{{/returnType}}]]{{/asyncio}}{{^asyncio}}{{{returnType}}}{{^returnType}}None{{/returnType}}{{/asyncio}}: # noqa: E501
"""{{#isDeprecated}}(Deprecated) {{/isDeprecated}}{{{summary}}}{{^summary}}{{operationId}}{{/summary}} # noqa: E501

Expand Down Expand Up @@ -85,7 +86,7 @@ class {{classname}}:
{{/asyncio}}
return self.{{operationId}}_with_http_info({{#allParams}}{{paramName}}, {{/allParams}}**kwargs) # noqa: E501

@validate_arguments
@{{#usePydanticV2}}validate_call{{/usePydanticV2}}{{^usePydanticV2}}validate_arguments{{/usePydanticV2}}
def {{operationId}}_with_http_info(self, {{#allParams}}{{paramName}} : {{{vendorExtensions.x-py-typing}}}{{^required}} = None{{/required}}, {{/allParams}}**kwargs) -> ApiResponse: # noqa: E501
"""{{#isDeprecated}}(Deprecated) {{/isDeprecated}}{{{summary}}}{{^summary}}{{operationId}}{{/summary}} # noqa: E501

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,13 @@ import re # noqa: F401
{{{.}}}
{{/vendorExtensions.x-py-model-imports}}
from typing import Union, Any, List, TYPE_CHECKING
{{#usePydanticV2}}
try:
from typing import Literal
except ImportError:
from typing_extensions import Literal
from pydantic import StrictStr, Field
{{/usePydanticV2}}

{{#lambda.uppercase}}{{{classname}}}{{/lambda.uppercase}}_ANY_OF_SCHEMAS = [{{#anyOf}}"{{.}}"{{^-last}}, {{/-last}}{{/anyOf}}]

Expand All @@ -27,10 +33,23 @@ class {{classname}}({{#parent}}{{{.}}}{{/parent}}{{^parent}}BaseModel{{/parent}}
actual_instance: Union[{{#anyOf}}{{{.}}}{{^-last}}, {{/-last}}{{/anyOf}}]
else:
actual_instance: Any
{{^usePydanticV2}}
any_of_schemas: List[str] = Field({{#lambda.uppercase}}{{{classname}}}{{/lambda.uppercase}}_ANY_OF_SCHEMAS, const=True)

{{/usePydanticV2}}
{{#usePydanticV2}}
any_of_schemas: List[str] = Literal[{{#anyOf}}"{{.}}"{{^-last}}, {{/-last}}{{/anyOf}}]
{{/usePydanticV2}}

"""Pydantic configuration"""
{{#usePydanticV2}}
model_config = {
"validate_assignment": True,
}
{{/usePydanticV2}}
{{^usePydanticV2}}
class Config:
validate_assignment = True
{{/usePydanticV2}}
{{#discriminator}}

discriminator_value_class_map = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,18 @@ class {{classname}}({{#parent}}{{{.}}}{{/parent}}{{^parent}}BaseModel{{/parent}}
{{/isEnum}}
{{/vars}}

"""Pydantic configuration"""
{{#usePydanticV2}}
model_config = {
"populate_by_name": True,
"validate_assignment": True,
}
{{/usePydanticV2}}
{{^usePydanticV2}}
class Config:
"""Pydantic configuration"""
allow_population_by_field_name = True
validate_assignment = True
{{/usePydanticV2}}

{{#hasChildren}}
{{#discriminator}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,13 @@ import re # noqa: F401
{{{.}}}
{{/vendorExtensions.x-py-model-imports}}
from typing import Union, Any, List, TYPE_CHECKING
{{#usePydanticV2}}
try:
from typing import Literal
except ImportError:
from typing_extensions import Literal
from pydantic import StrictStr, Field
{{/usePydanticV2}}

{{#lambda.uppercase}}{{{classname}}}{{/lambda.uppercase}}_ONE_OF_SCHEMAS = [{{#oneOf}}"{{.}}"{{^-last}}, {{/-last}}{{/oneOf}}]

Expand All @@ -26,10 +32,24 @@ class {{classname}}({{#parent}}{{{.}}}{{/parent}}{{^parent}}BaseModel{{/parent}}
actual_instance: Union[{{#oneOf}}{{{.}}}{{^-last}}, {{/-last}}{{/oneOf}}]
else:
actual_instance: Any

{{#usePydanticV2}}
one_of_schemas: List[str] = Literal[{{#oneOf}}"{{.}}"{{^-last}}, {{/-last}}{{/oneOf}}]
{{/usePydanticV2}}
{{^usePydanticV2}}
one_of_schemas: List[str] = Field({{#lambda.uppercase}}{{{classname}}}{{/lambda.uppercase}}_ONE_OF_SCHEMAS, const=True)
{{/usePydanticV2}}

"""Pydantic configuration"""
{{#usePydanticV2}}
model_config = {
"validate_assignment": True,
}
{{/usePydanticV2}}
{{^usePydanticV2}}
class Config:
validate_assignment = True
{{/usePydanticV2}}
{{#discriminator}}

discriminator_value_class_map = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,12 @@ tornado = ">=4.2,<5"
pem = ">= 19.3.0"
pycryptodome = ">= 3.9.0"
{{/hasHttpSignatureMethods}}
{{#usePydanticV2}}
pydantic = ">=2"
{{/usePydanticV2}}
{{^usePydanticV2}}
pydantic = "^1.10.5, <2"
{{/usePydanticV2}}
aenum = ">=3.1.11"

[tool.poetry.dev-dependencies]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
python_dateutil >= 2.5.3
setuptools >= 21.0.0
urllib3 >= 1.25.3, < 2.1.0
{{#usePydanticV2}}
pydantic >= 2
{{/usePydanticV2}}
{{^usePydanticV2}}
pydantic >= 1.10.5, < 2
{{/usePydanticV2}}
aenum >= 3.1.11
{{#asyncio}}
aiohttp >= 3.0.0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,12 @@ REQUIRES = [
"pem>=19.3.0",
"pycryptodome>=3.9.0",
{{/hasHttpSignatureMethods}}
{{#usePydanticV2}}
"pydantic >= 2",
{{/usePydanticV2}}
{{^usePydanticV2}}
"pydantic >= 1.10.5, < 2",
{{/usePydanticV2}}
"aenum"
]

Expand Down
Loading