Skip to content
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

pylint and mypy support for version tolerant with msrest models #1202

Merged
merged 12 commits into from
Mar 22, 2022
1 change: 1 addition & 0 deletions ChangeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
- Make `--version-tolerant` generated code mypy compatible in the `azure-sdk-for-python` repo. Tested only with the `--black` flag #1185
- Remove unnecessary vendored code in the `_vendor` file if the SDK has no operations #1196
- Fix the generation of the root `__init__` files for packages with only models #1195
- Add pylint and mypy support for `--version-tolerant` generations with `--models-mode=msrest` #1202

### 2022-03-08 - 5.14.0

Expand Down
4 changes: 2 additions & 2 deletions autorest/codegen/models/base_schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,8 @@ def docstring_type(self) -> str:
"""
...

@property
def type_annotation(self) -> str:
@abstractmethod
def type_annotation(self, *, is_operation_file: bool = False) -> str:
"""The python type used for type annotation

Special case for enum, for instance: Union[str, "EnumName"]
Expand Down
5 changes: 2 additions & 3 deletions autorest/codegen/models/constant_schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,8 @@ def docstring_type(self) -> str:
"""
return self.schema.docstring_type

@property
def type_annotation(self) -> str:
return self.schema.type_annotation
def type_annotation(self, *, is_operation_file: bool = False) -> str:
return self.schema.type_annotation(is_operation_file=is_operation_file)

@classmethod
def from_yaml(cls, namespace: str, yaml_data: Dict[str, Any], **kwargs) -> "ConstantSchema":
Expand Down
9 changes: 5 additions & 4 deletions autorest/codegen/models/credential_schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ class CredentialSchema(BaseSchema):
def __init__(self) -> None: # pylint: disable=super-init-not-called
self.default_value = None

def type_annotation(self, *, is_operation_file: bool = False) -> str:
raise ValueError("Children classes should set their own type annotation")

@property
def docstring_type(self) -> str:
return self.serialization_type
Expand Down Expand Up @@ -38,8 +41,7 @@ class AzureKeyCredentialSchema(CredentialSchema):
def serialization_type(self) -> str:
return "~azure.core.credentials.AzureKeyCredential"

@property
def type_annotation(self) -> str:
def type_annotation(self, *, is_operation_file: bool = False) -> str: # pylint: disable=unused-argument
return "AzureKeyCredential"

def imports(self) -> FileImport:
Expand All @@ -66,8 +68,7 @@ def serialization_type(self) -> str:
return self.async_type
return self.sync_type

@property
def type_annotation(self) -> str:
def type_annotation(self, *, is_operation_file: bool = False) -> str: # pylint: disable=unused-argument
if self.async_mode:
return '"AsyncTokenCredential"'
return '"TokenCredential"'
Expand Down
5 changes: 2 additions & 3 deletions autorest/codegen/models/dictionary_schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,13 @@ def serialization_type(self) -> str:
"""
return f"{{{self.element_type.serialization_type}}}"

@property
def type_annotation(self) -> str:
def type_annotation(self, *, is_operation_file: bool = False) -> str:
"""The python type used for type annotation

:return: The type annotation for this schema
:rtype: str
"""
return f"Dict[str, {self.element_type.type_annotation}]"
return f"Dict[str, {self.element_type.type_annotation(is_operation_file=is_operation_file)}]"

@property
def docstring_text(self) -> str:
Expand Down
22 changes: 7 additions & 15 deletions autorest/codegen/models/enum_schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,14 +80,13 @@ def serialization_type(self) -> str:
"""
return self.enum_type.serialization_type

@property
def type_annotation(self) -> str:
def type_annotation(self, *, is_operation_file: bool = False) -> str:
"""The python type used for type annotation

:return: The type annotation for this schema
:rtype: str
"""
return f'Union[{self.enum_type.type_annotation}, "_models.{self.name}"]'
return f'Union[{self.enum_type.type_annotation(is_operation_file=is_operation_file)}, "_models.{self.name}"]'

def get_declaration(self, value: Any) -> str:
return self.enum_type.get_declaration(value)
Expand All @@ -100,7 +99,7 @@ def docstring_text(self) -> str:
def docstring_type(self) -> str:
"""The python type used for RST syntax input and type annotation.
"""
return f"{self.enum_type.type_annotation} or ~{self.namespace}.models.{self.name}"
return f"{self.enum_type.type_annotation()} or ~{self.namespace}.models.{self.name}"

@staticmethod
def _get_enum_values(yaml_data: List[Dict[str, Any]]) -> List["EnumValue"]:
Expand Down Expand Up @@ -173,12 +172,6 @@ def imports(self) -> FileImport:
file_import.merge(self.enum_type.imports())
return file_import

def model_file_imports(self) -> FileImport:
imports = self.imports()
# we import every enum since we can get extremely long imports
# if we import my name
imports.add_submodule_import("." + self.enum_file_name, "*", ImportType.LOCAL)
return imports

class HiddenModelEnumSchema(EnumSchema):

Expand All @@ -187,18 +180,17 @@ def imports(self) -> FileImport:
file_import.merge(self.enum_type.imports())
return file_import

@property
def type_annotation(self) -> str:
def type_annotation(self, *, is_operation_file: bool = False) -> str:
"""The python type used for type annotation

:return: The type annotation for this schema
:rtype: str
"""
return self.enum_type.type_annotation
return self.enum_type.type_annotation(is_operation_file=is_operation_file)

@property
def docstring_text(self) -> str:
return f"{self.enum_type.type_annotation}. {self.extra_description_information}"
return f"{self.enum_type.type_annotation()}. {self.extra_description_information}"

@property
def extra_description_information(self):
Expand All @@ -220,7 +212,7 @@ def extra_description_information(self):
def docstring_type(self) -> str:
"""The python type used for RST syntax input and type annotation.
"""
return self.enum_type.type_annotation
return self.enum_type.type_annotation()

def get_enum_schema(code_model) -> Type[EnumSchema]:
if code_model.options["models_mode"]:
Expand Down
11 changes: 5 additions & 6 deletions autorest/codegen/models/list_schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,11 @@ def __init__(
def serialization_type(self) -> str:
return f"[{self.element_type.serialization_type}]"

@property
def type_annotation(self) -> str:
if self.element_type.type_annotation == "ET.Element":
def type_annotation(self, *, is_operation_file: bool = False) -> str:
if self.element_type.type_annotation() == "ET.Element":
# this means we're version tolerant XML, we just return the XML element
return self.element_type.type_annotation
return f"List[{self.element_type.type_annotation}]"
return self.element_type.type_annotation(is_operation_file=is_operation_file)
return f"List[{self.element_type.type_annotation(is_operation_file=is_operation_file)}]"

@property
def docstring_type(self) -> str:
Expand Down Expand Up @@ -113,7 +112,7 @@ def from_yaml(cls, namespace: str, yaml_data: Dict[str, Any], **kwargs) -> "List

def imports(self) -> FileImport:
file_import = FileImport()
if not self.element_type.type_annotation == "ET.Element":
if not self.element_type.type_annotation(is_operation_file=True) == "ET.Element":
file_import.add_submodule_import("typing", "List", ImportType.STDLIB, TypingSection.CONDITIONAL)
file_import.merge(self.element_type.imports())
return file_import
Expand Down
9 changes: 4 additions & 5 deletions autorest/codegen/models/object_schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,9 @@ def __init__(
def serialization_type(self) -> str:
return self.name

@property
def type_annotation(self) -> str:
return f'"_models.{self.name}"'
def type_annotation(self, *, is_operation_file: bool = False) -> str:
retval = f"_models.{self.name}"
return retval if is_operation_file else f'"{retval}"'

@property
def docstring_type(self) -> str:
Expand Down Expand Up @@ -223,8 +223,7 @@ class HiddenModelObjectSchema(ObjectSchema):
def serialization_type(self) -> str:
return "object"

@property
def type_annotation(self) -> str:
def type_annotation(self, *, is_operation_file: bool = False) -> str: # pylint: disable=unused-argument
if self.xml_metadata:
return "ET.Element"
return "JSONType"
Expand Down
4 changes: 2 additions & 2 deletions autorest/codegen/models/operation.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ def _imports_shared(self, async_mode: bool) -> FileImport: # pylint: disable=unu
if response.has_body:
file_import.merge(cast(BaseSchema, response.schema).imports())

response_types = [r.type_annotation for r in self.responses if r.has_body]
response_types = [r.type_annotation(is_operation_file=True) for r in self.responses if r.has_body]
if len(set(response_types)) > 1:
file_import.add_submodule_import("typing", "Union", ImportType.STDLIB, TypingSection.CONDITIONAL)

Expand Down Expand Up @@ -273,7 +273,7 @@ def link_body_kwargs_to_body_params(self) -> None:

def convert_multiple_content_type_parameters(self) -> None:
type_annot = ", ".join([
param.schema.type_annotation
param.schema.type_annotation(is_operation_file=True)
for param in self.multiple_content_type_parameters
])
docstring_type = " or ".join([
Expand Down
1 change: 1 addition & 0 deletions autorest/codegen/models/operation_group.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ def imports_for_multiapi(self, async_mode: bool) -> FileImport:
file_import = FileImport()
for operation in self.operations:
file_import.merge(operation.imports_for_multiapi(async_mode))
file_import.add_submodule_import(".." if async_mode else ".", "models", ImportType.LOCAL, alias="_models")
return file_import

def imports(self, async_mode: bool) -> FileImport:
Expand Down
14 changes: 7 additions & 7 deletions autorest/codegen/models/parameter.py
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ def _is_io_json(self):
) and isinstance(self.schema, IOSchema)

def _default_value(self) -> Tuple[Optional[Any], str, str]:
type_annot = self.multiple_content_types_type_annot or self.schema.type_annotation
type_annot = self.multiple_content_types_type_annot or self.schema.type_annotation(is_operation_file=True)
if self._is_io_json:
type_annot = f"Union[{type_annot}, JSONType]"
any_types = ["Any", "JSONType"]
Expand Down Expand Up @@ -277,8 +277,7 @@ def default_value(self) -> Optional[Any]:
def default_value_declaration(self) -> Optional[Any]:
return self._default_value()[1]

@property
def type_annotation(self) -> str:
def type_annotation(self, *, is_operation_file: bool = False) -> str: # pylint: disable=unused-argument
return self._default_value()[2]

@property
Expand All @@ -297,13 +296,14 @@ def has_default_value(self):
return self.default_value is not None or not self.required

def method_signature(self, is_python3_file: bool) -> str:
type_annot = self.type_annotation(is_operation_file=True)
if is_python3_file:
if self.has_default_value:
return f"{self.serialized_name}: {self.type_annotation} = {self.default_value_declaration},"
return f"{self.serialized_name}: {self.type_annotation},"
return f"{self.serialized_name}: {type_annot} = {self.default_value_declaration},"
return f"{self.serialized_name}: {type_annot},"
if self.has_default_value:
return f"{self.serialized_name}={self.default_value_declaration}, # type: {self.type_annotation}"
return f"{self.serialized_name}, # type: {self.type_annotation}"
return f"{self.serialized_name}={self.default_value_declaration}, # type: {type_annot}"
return f"{self.serialized_name}, # type: {type_annot}"

@property
def full_serialized_name(self) -> str:
Expand Down
40 changes: 15 additions & 25 deletions autorest/codegen/models/primitive_schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,7 @@ def serialization_type(self) -> str:
def docstring_type(self) -> str:
return self._to_python_type()

@property
def type_annotation(self) -> str:
def type_annotation(self, *, is_operation_file: bool = False) -> str: # pylint: disable=unused-argument
return self.docstring_type

@property
Expand Down Expand Up @@ -102,8 +101,7 @@ def serialization_type(self) -> str:
def docstring_type(self) -> str:
return self.type

@property
def type_annotation(self) -> str:
def type_annotation(self, *, is_operation_file: bool = False) -> str: # pylint: disable=unused-argument
return self.docstring_type

@property
Expand All @@ -128,8 +126,7 @@ def serialization_type(self) -> str:
def docstring_type(self) -> str:
return "any"

@property
def type_annotation(self) -> str:
def type_annotation(self, *, is_operation_file: bool = False) -> str: # pylint: disable=unused-argument
return "Any"

@property
Expand All @@ -147,8 +144,7 @@ class JSONSchema(AnySchema):
def docstring_type(self) -> str:
return "JSONType"

@property
def type_annotation(self) -> str:
def type_annotation(self, *, is_operation_file: bool = False) -> str: # pylint: disable=unused-argument
return "JSONType"


Expand Down Expand Up @@ -206,8 +202,7 @@ def docstring_type(self) -> str:
return "int"
return "float"

@property
def type_annotation(self) -> str:
def type_annotation(self, *, is_operation_file: bool = False) -> str: # pylint: disable=unused-argument
python_type = self.docstring_type
if python_type == "long":
return "int"
Expand Down Expand Up @@ -269,10 +264,9 @@ def serialization_type(self) -> str:

@property
def docstring_type(self) -> str:
return "~" + self.type_annotation
return "~" + self.type_annotation()

@property
def type_annotation(self) -> str:
def type_annotation(self, *, is_operation_file: bool = False) -> str: # pylint: disable=unused-argument
return "datetime.datetime"

@property
Expand Down Expand Up @@ -301,10 +295,9 @@ def serialization_type(self) -> str:

@property
def docstring_type(self) -> str:
return "~" + self.type_annotation
return "~" + self.type_annotation()

@property
def type_annotation(self) -> str:
def type_annotation(self, *, is_operation_file: bool = False) -> str: # pylint: disable=unused-argument
return "datetime.time"

@property
Expand Down Expand Up @@ -334,10 +327,9 @@ def serialization_type(self) -> str:

@property
def docstring_type(self) -> str:
return "~" + self.type_annotation
return "~" + self.type_annotation()

@property
def type_annotation(self) -> str:
def type_annotation(self, *, is_operation_file: bool = False) -> str: # pylint: disable=unused-argument
return "datetime.datetime"

@property
Expand Down Expand Up @@ -367,10 +359,9 @@ def serialization_type(self) -> str:

@property
def docstring_type(self) -> str:
return "~" + self.type_annotation
return "~" + self.type_annotation()

@property
def type_annotation(self) -> str:
def type_annotation(self, *, is_operation_file: bool = False) -> str: # pylint: disable=unused-argument
return "datetime.date"

@property
Expand Down Expand Up @@ -400,10 +391,9 @@ def serialization_type(self) -> str:

@property
def docstring_type(self) -> str:
return "~" + self.type_annotation
return "~" + self.type_annotation()

@property
def type_annotation(self) -> str:
def type_annotation(self, *, is_operation_file: bool = False) -> str: # pylint: disable=unused-argument
return "datetime.timedelta"

@property
Expand Down
7 changes: 3 additions & 4 deletions autorest/codegen/models/property.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,11 +149,10 @@ def default_value_declaration(self) -> Any:
return self.schema.get_declaration(self.client_default_value)
return self.schema.default_value_declaration

@property
def type_annotation(self) -> str:
def type_annotation(self, *, is_operation_file: bool = False) -> str:
if self.required:
return self.schema.type_annotation
return f"Optional[{self.schema.type_annotation}]"
return self.schema.type_annotation(is_operation_file=is_operation_file)
return f"Optional[{self.schema.type_annotation(is_operation_file=is_operation_file)}]"

def get_json_template_representation(self, **kwargs: Any) -> Any:
kwargs["optional"] = not self.required
Expand Down
Loading