-
-
Notifications
You must be signed in to change notification settings - Fork 7.3k
enhance python openapi generator #22600
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
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -9,169 +9,31 @@ import re # noqa: F401 | |||||||||||||||||
| {{#vendorExtensions.x-py-model-imports}} | ||||||||||||||||||
| {{{.}}} | ||||||||||||||||||
| {{/vendorExtensions.x-py-model-imports}} | ||||||||||||||||||
| from typing import Union, Any, List, Set, TYPE_CHECKING, Optional, Dict | ||||||||||||||||||
| from typing_extensions import Literal, Self | ||||||||||||||||||
| from pydantic import Field | ||||||||||||||||||
| from typing import Union, Any, List, Set, TYPE_CHECKING, Optional, Dict, Literal, Self | ||||||||||||||||||
| from pydantic import Field, RootModel | ||||||||||||||||||
|
|
||||||||||||||||||
| {{#lambda.uppercase}}{{{classname}}}{{/lambda.uppercase}}_ANY_OF_SCHEMAS = [{{#anyOf}}"{{.}}"{{^-last}}, {{/-last}}{{/anyOf}}] | ||||||||||||||||||
|
|
||||||||||||||||||
| class {{classname}}({{#parent}}{{{.}}}{{/parent}}{{^parent}}BaseModel{{/parent}}): | ||||||||||||||||||
|
|
||||||||||||||||||
| class {{classname}}(RootModel[Union[{{#anyOf}}{{.}}{{^-last}}, {{/-last}}{{/anyOf}}]]): | ||||||||||||||||||
| """ | ||||||||||||||||||
| {{{description}}}{{^description}}{{{classname}}}{{/description}} | ||||||||||||||||||
| """ | ||||||||||||||||||
|
|
||||||||||||||||||
| {{#composedSchemas.anyOf}} | ||||||||||||||||||
| # data type: {{{dataType}}} | ||||||||||||||||||
| {{vendorExtensions.x-py-name}}: {{{vendorExtensions.x-py-typing}}} | ||||||||||||||||||
| {{/composedSchemas.anyOf}} | ||||||||||||||||||
| if TYPE_CHECKING: | ||||||||||||||||||
| actual_instance: Optional[Union[{{#anyOf}}{{{.}}}{{^-last}}, {{/-last}}{{/anyOf}}]] = None | ||||||||||||||||||
| else: | ||||||||||||||||||
| actual_instance: Any = None | ||||||||||||||||||
| any_of_schemas: Set[str] = { {{#anyOf}}"{{.}}"{{^-last}}, {{/-last}}{{/anyOf}} } | ||||||||||||||||||
|
|
||||||||||||||||||
| model_config = { | ||||||||||||||||||
| "validate_assignment": True, | ||||||||||||||||||
| "protected_namespaces": (), | ||||||||||||||||||
| } | ||||||||||||||||||
| {{#discriminator}} | ||||||||||||||||||
|
|
||||||||||||||||||
| discriminator_value_class_map: Dict[str, str] = { | ||||||||||||||||||
| {{#children}} | ||||||||||||||||||
| '{{^vendorExtensions.x-discriminator-value}}{{name}}{{/vendorExtensions.x-discriminator-value}}{{#vendorExtensions.x-discriminator-value}}{{{vendorExtensions.x-discriminator-value}}}{{/vendorExtensions.x-discriminator-value}}': '{{{classname}}}'{{^-last}},{{/-last}} | ||||||||||||||||||
| {{/children}} | ||||||||||||||||||
| } | ||||||||||||||||||
| {{/discriminator}} | ||||||||||||||||||
|
|
||||||||||||||||||
| def __init__(self, *args, **kwargs) -> None: | ||||||||||||||||||
| if args: | ||||||||||||||||||
| if len(args) > 1: | ||||||||||||||||||
| raise ValueError("If a position argument is used, only 1 is allowed to set `actual_instance`") | ||||||||||||||||||
| if kwargs: | ||||||||||||||||||
| raise ValueError("If a position argument is used, keyword arguments cannot be used.") | ||||||||||||||||||
| super().__init__(actual_instance=args[0]) | ||||||||||||||||||
| else: | ||||||||||||||||||
| super().__init__(**kwargs) | ||||||||||||||||||
|
|
||||||||||||||||||
| @field_validator('actual_instance') | ||||||||||||||||||
| def actual_instance_must_validate_anyof(cls, v): | ||||||||||||||||||
| {{#isNullable}} | ||||||||||||||||||
| if v is None: | ||||||||||||||||||
| return v | ||||||||||||||||||
|
|
||||||||||||||||||
| {{/isNullable}} | ||||||||||||||||||
| instance = {{{classname}}}.model_construct() | ||||||||||||||||||
| error_messages = [] | ||||||||||||||||||
| {{#composedSchemas.anyOf}} | ||||||||||||||||||
| # validate data type: {{{dataType}}} | ||||||||||||||||||
| {{#isContainer}} | ||||||||||||||||||
| try: | ||||||||||||||||||
| instance.{{vendorExtensions.x-py-name}} = v | ||||||||||||||||||
| return v | ||||||||||||||||||
| except (ValidationError, ValueError) as e: | ||||||||||||||||||
| error_messages.append(str(e)) | ||||||||||||||||||
| {{/isContainer}} | ||||||||||||||||||
| {{^isContainer}} | ||||||||||||||||||
| {{#isPrimitiveType}} | ||||||||||||||||||
| try: | ||||||||||||||||||
| instance.{{vendorExtensions.x-py-name}} = v | ||||||||||||||||||
| return v | ||||||||||||||||||
| except (ValidationError, ValueError) as e: | ||||||||||||||||||
| error_messages.append(str(e)) | ||||||||||||||||||
| {{/isPrimitiveType}} | ||||||||||||||||||
| {{^isPrimitiveType}} | ||||||||||||||||||
| if not isinstance(v, {{{dataType}}}): | ||||||||||||||||||
| error_messages.append(f"Error! Input type `{type(v)}` is not `{{{dataType}}}`") | ||||||||||||||||||
| else: | ||||||||||||||||||
| return v | ||||||||||||||||||
|
|
||||||||||||||||||
| {{/isPrimitiveType}} | ||||||||||||||||||
| {{/isContainer}} | ||||||||||||||||||
| {{/composedSchemas.anyOf}} | ||||||||||||||||||
| if error_messages: | ||||||||||||||||||
| # no match | ||||||||||||||||||
| raise ValueError("No match found when setting the actual_instance in {{{classname}}} with anyOf schemas: {{#anyOf}}{{{.}}}{{^-last}}, {{/-last}}{{/anyOf}}. Details: " + ", ".join(error_messages)) | ||||||||||||||||||
| else: | ||||||||||||||||||
| return v | ||||||||||||||||||
|
|
||||||||||||||||||
| @classmethod | ||||||||||||||||||
| def from_dict(cls, obj: Dict[str, Any]) -> Self: | ||||||||||||||||||
| return cls.from_json(json.dumps(obj)) | ||||||||||||||||||
|
|
||||||||||||||||||
| @classmethod | ||||||||||||||||||
| def from_json(cls, json_str: str) -> Self: | ||||||||||||||||||
| """Returns the object represented by the json string""" | ||||||||||||||||||
| instance = cls.model_construct() | ||||||||||||||||||
| {{#isNullable}} | ||||||||||||||||||
| if json_str is None: | ||||||||||||||||||
| return instance | ||||||||||||||||||
|
|
||||||||||||||||||
| {{/isNullable}} | ||||||||||||||||||
| error_messages = [] | ||||||||||||||||||
| {{#composedSchemas.anyOf}} | ||||||||||||||||||
| {{#isContainer}} | ||||||||||||||||||
| # deserialize data into {{{dataType}}} | ||||||||||||||||||
| try: | ||||||||||||||||||
| # validation | ||||||||||||||||||
| instance.{{vendorExtensions.x-py-name}} = json.loads(json_str) | ||||||||||||||||||
| # assign value to actual_instance | ||||||||||||||||||
| instance.actual_instance = instance.{{vendorExtensions.x-py-name}} | ||||||||||||||||||
| return instance | ||||||||||||||||||
| except (ValidationError, ValueError) as e: | ||||||||||||||||||
| error_messages.append(str(e)) | ||||||||||||||||||
| {{/isContainer}} | ||||||||||||||||||
| {{^isContainer}} | ||||||||||||||||||
| {{#isPrimitiveType}} | ||||||||||||||||||
| # deserialize data into {{{dataType}}} | ||||||||||||||||||
| try: | ||||||||||||||||||
| # validation | ||||||||||||||||||
| instance.{{vendorExtensions.x-py-name}} = json.loads(json_str) | ||||||||||||||||||
| # assign value to actual_instance | ||||||||||||||||||
| instance.actual_instance = instance.{{vendorExtensions.x-py-name}} | ||||||||||||||||||
| return instance | ||||||||||||||||||
| except (ValidationError, ValueError) as e: | ||||||||||||||||||
| error_messages.append(str(e)) | ||||||||||||||||||
| {{/isPrimitiveType}} | ||||||||||||||||||
| {{^isPrimitiveType}} | ||||||||||||||||||
| # {{vendorExtensions.x-py-name}}: {{{vendorExtensions.x-py-typing}}} | ||||||||||||||||||
| try: | ||||||||||||||||||
| instance.actual_instance = {{{dataType}}}.from_json(json_str) | ||||||||||||||||||
| return instance | ||||||||||||||||||
| except (ValidationError, ValueError) as e: | ||||||||||||||||||
| error_messages.append(str(e)) | ||||||||||||||||||
| {{/isPrimitiveType}} | ||||||||||||||||||
| {{/isContainer}} | ||||||||||||||||||
| {{/composedSchemas.anyOf}} | ||||||||||||||||||
|
|
||||||||||||||||||
| if error_messages: | ||||||||||||||||||
| # no match | ||||||||||||||||||
| raise ValueError("No match found when deserializing the JSON string into {{{classname}}} with anyOf schemas: {{#anyOf}}{{{.}}}{{^-last}}, {{/-last}}{{/anyOf}}. Details: " + ", ".join(error_messages)) | ||||||||||||||||||
| else: | ||||||||||||||||||
| return instance | ||||||||||||||||||
|
|
||||||||||||||||||
| def to_json(self) -> str: | ||||||||||||||||||
| """Returns the JSON representation of the actual instance""" | ||||||||||||||||||
| if self.actual_instance is None: | ||||||||||||||||||
| return "null" | ||||||||||||||||||
|
|
||||||||||||||||||
| if hasattr(self.actual_instance, "to_json") and callable(self.actual_instance.to_json): | ||||||||||||||||||
| return self.actual_instance.to_json() | ||||||||||||||||||
| else: | ||||||||||||||||||
| return json.dumps(self.actual_instance) | ||||||||||||||||||
|
|
||||||||||||||||||
| def to_dict(self) -> Optional[Union[Dict[str, Any], {{#anyOf}}{{.}}{{^-last}}, {{/-last}}{{/anyOf}}]]: | ||||||||||||||||||
| """Returns the dict representation of the actual instance""" | ||||||||||||||||||
| if self.actual_instance is None: | ||||||||||||||||||
| return None | ||||||||||||||||||
|
|
||||||||||||||||||
| if hasattr(self.actual_instance, "to_dict") and callable(self.actual_instance.to_dict): | ||||||||||||||||||
| return self.actual_instance.to_dict() | ||||||||||||||||||
| else: | ||||||||||||||||||
| return self.actual_instance | ||||||||||||||||||
|
|
||||||||||||||||||
| def to_str(self) -> str: | ||||||||||||||||||
| """Returns the string representation of the actual instance""" | ||||||||||||||||||
| return pprint.pformat(self.model_dump()) | ||||||||||||||||||
| root: Union[{{#anyOf}}{{.}}{{^-last}}, {{/-last}}{{/anyOf}}] = Field( | ||||||||||||||||||
| ...{{#discriminator}}, discriminator="{{discriminatorName}}"{{/discriminator}} | ||||||||||||||||||
| ) | ||||||||||||||||||
|
|
||||||||||||||||||
| def __getattr__(self, name): | ||||||||||||||||||
| """ | ||||||||||||||||||
| Delegate attribute access to the root model if the attribute | ||||||||||||||||||
| doesn't exist on the main class. | ||||||||||||||||||
| """ | ||||||||||||||||||
| if name in self.__dict__: | ||||||||||||||||||
| return super().__getattribute__(name) | ||||||||||||||||||
| if hasattr(self, 'root') and self.root is not None: | ||||||||||||||||||
| return getattr(self.root, name) | ||||||||||||||||||
|
Comment on lines
+34
to
+35
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P2: Using Prompt for AI agents
Suggested change
|
||||||||||||||||||
| raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{name}'") | ||||||||||||||||||
|
|
||||||||||||||||||
| {{#vendorExtensions.x-py-postponed-model-imports.size}} | ||||||||||||||||||
| {{#vendorExtensions.x-py-postponed-model-imports}} | ||||||||||||||||||
|
|
||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -19,14 +19,14 @@ from {{modelPackage}}.{{#lambda.snakecase}}{{classname}}{{/lambda.snakecase}} im | |||||||||
| # TODO update the JSON string below | ||||||||||
| json = "{}" | ||||||||||
| # create an instance of {{classname}} from a JSON string | ||||||||||
| {{#lambda.snakecase}}{{classname}}{{/lambda.snakecase}}_instance = {{classname}}.from_json(json) | ||||||||||
| {{#lambda.snakecase}}{{classname}}{{/lambda.snakecase}}_instance = {{classname}}.model_validate_json(json) | ||||||||||
| # print the JSON string representation of the object | ||||||||||
| print({{classname}}.to_json()) | ||||||||||
| print({{classname}}.model_dump_json(by_alias=True, exclude_unset=True)) | ||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P1: Prompt for AI agents
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P1: Prompt for AI agents
Suggested change
|
||||||||||
|
|
||||||||||
| # convert the object into a dict | ||||||||||
| {{#lambda.snakecase}}{{classname}}{{/lambda.snakecase}}_dict = {{#lambda.snakecase}}{{classname}}{{/lambda.snakecase}}_instance.to_dict() | ||||||||||
| {{#lambda.snakecase}}{{classname}}{{/lambda.snakecase}}_dict = {{#lambda.snakecase}}{{classname}}{{/lambda.snakecase}}_instance.model_dump(by_alias=True) | ||||||||||
| # create an instance of {{classname}} from a dict | ||||||||||
| {{#lambda.snakecase}}{{classname}}{{/lambda.snakecase}}_from_dict = {{classname}}.from_dict({{#lambda.snakecase}}{{classname}}{{/lambda.snakecase}}_dict) | ||||||||||
| {{#lambda.snakecase}}{{classname}}{{/lambda.snakecase}}_from_dict = {{classname}}.model_validate({{#lambda.snakecase}}{{classname}}{{/lambda.snakecase}}_dict) | ||||||||||
| ``` | ||||||||||
| {{/isEnum}} | ||||||||||
| {{#isEnum}} | ||||||||||
|
|
||||||||||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -1,36 +1,32 @@ | ||||||
| from __future__ import annotations | ||||||
| import json | ||||||
| from enum import Enum | ||||||
| from enum import Enum, StrEnum, IntEnum | ||||||
| from typing import Self | ||||||
| {{#vendorExtensions.x-py-other-imports}} | ||||||
| {{{.}}} | ||||||
| {{/vendorExtensions.x-py-other-imports}} | ||||||
| from typing_extensions import Self | ||||||
|
|
||||||
|
|
||||||
| class {{classname}}({{vendorExtensions.x-py-enum-type}}, Enum): | ||||||
| {{#isString}} | ||||||
| class {{classname}}(StrEnum): | ||||||
| {{/isString}} | ||||||
| {{#isInteger}} | ||||||
| class {{classname}}(IntEnum): | ||||||
| {{/isInteger}} | ||||||
| {{^isString}}{{^isInteger}} | ||||||
| class {{classname}}(Enum): | ||||||
| {{/isInteger}}{{/isString}} | ||||||
| """ | ||||||
| {{{description}}}{{^description}}{{{classname}}}{{/description}} | ||||||
| """ | ||||||
|
|
||||||
| """ | ||||||
| allowed enum values | ||||||
| """ | ||||||
| {{#allowableValues}} | ||||||
| # Allowed enum values | ||||||
| {{#enumVars}} | ||||||
| {{{name}}} = {{{value}}} | ||||||
| {{name}} = {{{value}}} | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P2: Using Prompt for AI agents
Suggested change
|
||||||
| {{/enumVars}} | ||||||
|
|
||||||
| @classmethod | ||||||
| def from_json(cls, json_str: str) -> Self: | ||||||
| """Create an instance of {{classname}} from a JSON string""" | ||||||
| return cls(json.loads(json_str)) | ||||||
|
|
||||||
| {{#defaultValue}} | ||||||
|
|
||||||
| # | ||||||
| @classmethod | ||||||
| def _missing_value_(cls, value): | ||||||
| if value is no_arg: | ||||||
| return cls.{{{.}}} | ||||||
| {{/defaultValue}} | ||||||
| {{/allowableValues}} | ||||||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
P2: Using
oroperator to select betweendataandbodyfails for falsy but valid data values like empty dicts/lists. Ifself.datais{}or[], it will incorrectly showself.bodyinstead. Consider using explicitNonechecks:Prompt for AI agents