Skip to content

Commit

Permalink
Merge branch 'autorestv3' of https://github.com/Azure/autorest.python
Browse files Browse the repository at this point in the history
…into autorestv3

* 'autorestv3' of https://github.com/Azure/autorest.python:
  Fix config async token credential (#536)
  Allows enums to have non-string values (#534)
  • Loading branch information
iscai-msft committed Apr 1, 2020
2 parents c02508d + 740571f commit f6e7289
Show file tree
Hide file tree
Showing 86 changed files with 1,394 additions and 318 deletions.
16 changes: 11 additions & 5 deletions autorest/codegen/models/code_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,20 +26,26 @@


class CredentialSchema(BaseSchema):
def __init__(self) -> None: # pylint: disable=super-init-not-called
self.type = "azure.core.credentials.TokenCredential"
def __init__(self, async_mode) -> None: # pylint: disable=super-init-not-called
self.async_mode = async_mode
self.async_type = "azure.core.credentials.AsyncTokenCredential"
self.sync_type = "azure.core.credentials.TokenCredential"
self.default_value = None

@property
def serialization_type(self) -> str:
return self.type
if self.async_mode:
return self.async_type
return self.sync_type

@property
def docstring_type(self) -> str:
return self.type
return self.serialization_type

@property
def type_annotation(self) -> str:
if self.async_mode:
return '"AsyncTokenCredential"'
return '"TokenCredential"'

@property
Expand Down Expand Up @@ -168,7 +174,7 @@ def add_credential_global_parameter(self) -> None:
:return: None
:rtype: None
"""
credential_schema = CredentialSchema()
credential_schema = CredentialSchema(async_mode=False)
credential_parameter = Parameter(
yaml_data={},
schema=credential_schema,
Expand Down
36 changes: 26 additions & 10 deletions autorest/codegen/models/enum_schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
# --------------------------------------------------------------------------
from typing import Any, Dict, List, Optional, Set
from .base_schema import BaseSchema
from .primitive_schemas import PrimitiveSchema, get_primitive_schema, StringSchema
from .imports import FileImport, ImportType


Expand Down Expand Up @@ -44,19 +45,26 @@ class EnumSchema(BaseSchema):
:param yaml_data: the yaml data for this schema
:type yaml_data: dict[str, Any]
:param str description: The description of this enum
:param enum_type: The type of the enum. Currently we're only allowing strings.
:param str name: The name of the enum.
:type element_type: ~autorest.models.PrimitiveSchema
:param values: List of the values for this enum
:type values: list[~autorest.models.EnumValue]
"""

def __init__(
self, namespace: str, yaml_data: Dict[str, Any], description: str, enum_type: str, values: List["EnumValue"]
self,
namespace: str,
yaml_data: Dict[str, Any],
description: str,
name: str,
values: List["EnumValue"],
enum_type: PrimitiveSchema
) -> None:
super(EnumSchema, self).__init__(namespace=namespace, yaml_data=yaml_data)
self.description = description
self.enum_type = enum_type
self.name = name
self.values = values
self.enum_type = enum_type

@property
def serialization_type(self) -> str:
Expand All @@ -65,7 +73,7 @@ def serialization_type(self) -> str:
:return: The serialization value for msrest
:rtype: str
"""
return "str"
return self.enum_type.serialization_type

@property
def type_annotation(self) -> str:
Expand All @@ -74,7 +82,7 @@ def type_annotation(self) -> str:
:return: The type annotation for this schema
:rtype: str
"""
return f'Union[str, "{self.enum_type}"]'
return f'Union[{self.enum_type.type_annotation}, "{self.name}"]'

@property
def operation_type_annotation(self) -> str:
Expand All @@ -83,20 +91,20 @@ def operation_type_annotation(self) -> str:
:return: The type annotation for this schema
:rtype: str
"""
return f'Union[str, "models.{self.enum_type}"]'
return f'Union[{self.enum_type.type_annotation}, "models.{self.name}"]'

def get_declaration(self, value: Any) -> str:
return f'"{value}"'

@property
def docstring_text(self) -> str:
return self.enum_type
return self.name

@property
def docstring_type(self) -> str:
"""The python type used for RST syntax input and type annotation.
"""
return f"str or ~{self.namespace}.models.{self.enum_type}"
return f"str or ~{self.namespace}.models.{self.name}"

@staticmethod
def _get_enum_values(yaml_data: List[Dict[str, Any]]) -> List["EnumValue"]:
Expand Down Expand Up @@ -128,18 +136,26 @@ def from_yaml(cls, namespace: str, yaml_data: Dict[str, Any], **kwargs) -> "Enum
:return: A created EnumSchema
:rtype: ~autorest.models.EnumSchema
"""
enum_type = yaml_data["language"]["python"]["name"]
name = yaml_data["language"]["python"]["name"]

# choice type doesn't always exist. if there is no choiceType, we default to string
if yaml_data.get("choiceType"):
enum_type = get_primitive_schema(namespace, yaml_data["choiceType"])
else:
enum_type = StringSchema(namespace, {"type": "str"})
values = EnumSchema._get_enum_values(yaml_data["choices"])

return cls(
namespace=namespace,
yaml_data=yaml_data,
description=yaml_data["language"]["python"]["description"],
enum_type=enum_type,
name=name,
values=values,
enum_type=enum_type
)

def imports(self) -> FileImport:
file_import = FileImport()
file_import.add_from_import("typing", "Union", ImportType.STDLIB)
file_import.merge(self.enum_type.imports())
return file_import
15 changes: 8 additions & 7 deletions autorest/codegen/serializers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ def serialize(self, code_model: CodeModel) -> None:
Path(".") if code_model.options["no_namespace_folders"] else Path(*(code_model.namespace.split(".")))
)

if code_model.schemas:
if code_model.schemas or code_model.enums:
self._serialize_and_write_models_folder(code_model=code_model, env=env, namespace_path=namespace_path)

self._serialize_and_write_operations_folder(code_model=code_model, env=env, namespace_path=namespace_path)
Expand All @@ -62,12 +62,13 @@ def serialize(self, code_model: CodeModel) -> None:
def _serialize_and_write_models_folder(self, code_model: CodeModel, env: Environment, namespace_path: Path) -> None:
# Write the models folder
models_path = namespace_path / Path("models")
self._autorestapi.write_file(
models_path / Path("_models.py"), ModelGenericSerializer(code_model=code_model, env=env).serialize()
)
self._autorestapi.write_file(
models_path / Path("_models_py3.py"), ModelPython3Serializer(code_model=code_model, env=env).serialize()
)
if code_model.schemas:
self._autorestapi.write_file(
models_path / Path("_models.py"), ModelGenericSerializer(code_model=code_model, env=env).serialize()
)
self._autorestapi.write_file(
models_path / Path("_models_py3.py"), ModelPython3Serializer(code_model=code_model, env=env).serialize()
)
if code_model.enums:
self._autorestapi.write_file(
models_path / Path(f"_{code_model.module_name}_enums.py"),
Expand Down
23 changes: 17 additions & 6 deletions autorest/codegen/serializers/general_serializer.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
# --------------------------------------------------------------------------
from jinja2 import Environment
from .import_serializer import FileImportSerializer
from ..models import FileImport, ImportType, CodeModel
from ..models import FileImport, ImportType, CodeModel, CredentialSchema


class GeneralSerializer:
Expand All @@ -20,10 +20,20 @@ def serialize_pkgutil_init_file(self) -> str:

def serialize_init_file(self) -> str:
template = self.env.get_template("init.py.jinja2")
return template.render(code_model=self.code_model, async_mode=self.async_mode,)
return template.render(code_model=self.code_model, async_mode=self.async_mode)

def _correct_credential_parameter(self):
credential_param = [
gp for gp in self.code_model.global_parameters.parameters if isinstance(gp.schema, CredentialSchema)
][0]
credential_param.schema = CredentialSchema(async_mode=self.async_mode)

def serialize_service_client_file(self) -> str:
template = self.env.get_template("service_client.py.jinja2")

if self.code_model.options['credential']:
self._correct_credential_parameter()

return template.render(
code_model=self.code_model,
async_mode=self.async_mode,
Expand All @@ -38,17 +48,18 @@ def _config_imports(async_mode: bool) -> FileImport:
file_import.add_from_import("typing", "Any", ImportType.STDLIB)
if self.code_model.options["package_version"]:
file_import.add_from_import(".._version" if async_mode else "._version", "VERSION", ImportType.LOCAL)
if any(not gp.required for gp in self.code_model.global_parameters):
file_import.add_from_import("typing", "Optional", ImportType.STDLIB)
# if self.code_model.options['credential']:
# file_import.add_from_import("azure.core.credentials", "TokenCredential", ImportType.AZURECORE)
for gp in self.code_model.global_parameters:
file_import.merge(gp.imports())
return file_import

package_name = self.code_model.options['package_name']
if package_name and package_name.startswith("azure-"):
package_name = package_name[len("azure-"):]
sdk_moniker = package_name if package_name else self.code_model.class_name.lower()

if self.code_model.options['credential']:
self._correct_credential_parameter()

template = self.env.get_template("config.py.jinja2")
return template.render(
code_model=self.code_model,
Expand Down
14 changes: 12 additions & 2 deletions autorest/codegen/serializers/metadata_serializer.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,10 @@
# --------------------------------------------------------------------------
from typing import List, Optional, Set, Tuple
from jinja2 import Environment
from ..models import CodeModel, Operation, OperationGroup, LROOperation, PagingOperation
from ..models import CodeModel, Operation, OperationGroup, LROOperation, PagingOperation, CredentialSchema
from ..models.imports import FileImport



class MetadataSerializer:
def __init__(self, code_model: CodeModel, env: Environment) -> None:
self.code_model = code_model
Expand All @@ -36,6 +35,14 @@ def _choose_api_version(self) -> Tuple[str, List[str]]:

return chosen_version, total_api_version_list

def _correct_credential_parameter(self):
# currently this is just copied over from general_serializer.
# this code will be changed when i merge my async multiapi client
# pr, since I need two copies of global parameters (one sync and one async).
credential_param = [
gp for gp in self.code_model.global_parameters.parameters if isinstance(gp.schema, CredentialSchema)
][0]
credential_param.schema = CredentialSchema(async_mode=False)

def serialize(self) -> str:
def _is_lro(operation):
Expand All @@ -60,6 +67,9 @@ def _is_paging(operation):
for parameter in operation.parameters:
parameter_imports.merge(parameter.imports())

if self.code_model.options['credential']:
self._correct_credential_parameter()

template = self.env.get_template("metadata.json.jinja2")
return template.render(
chosen_version=chosen_version,
Expand Down
2 changes: 1 addition & 1 deletion autorest/codegen/serializers/model_base_serializer.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ def prop_documentation_string(prop: Property) -> str:
if prop.is_discriminator:
description += "Constant filled by server. "
if isinstance(prop.schema, EnumSchema):
values = ["'" + v.value + "'" for v in prop.schema.values]
values = [prop.schema.enum_type.get_declaration(v.value) for v in prop.schema.values]
description += " Possible values include: {}.".format(", ".join(values))
if prop.schema.default_value:
description += f' Default value: "{prop.schema.default_value}".'
Expand Down
2 changes: 1 addition & 1 deletion autorest/codegen/serializers/model_init_serializer.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ def __init__(self, code_model: CodeModel, env: Environment) -> None:
def serialize(self) -> str:
schemas = [s.name for s in self.code_model.sorted_schemas]
schemas.sort()
enums = [e.enum_type for e in self.code_model.enums.values()] if self.code_model.enums else None
enums = [e.name for e in self.code_model.enums.values()] if self.code_model.enums else None

if enums:
enums.sort()
Expand Down
8 changes: 2 additions & 6 deletions autorest/codegen/templates/enum.py.jinja2
Original file line number Diff line number Diff line change
@@ -1,14 +1,10 @@

class {{ enum.enum_type }}(str, Enum):
class {{ enum.name }}({{ enum.enum_type.type_annotation }}, Enum):
{% if enum.description %}
"""{{ enum.description | wordwrap(width=95, break_long_words=False, wrapstring='\n ') }}
"""
{% endif %}

{% for value in enum.values %}
{% if value.description %}
{{ value.name }} = "{{ value.value }}" {{ "#: " + value.description }}
{% else %}
{{ value.name }} = "{{ value.value }}"
{% endif %}
{{ value.name }} = {{ enum.enum_type.get_declaration(value.value) }}{{ " #: " + value.description if value.description else "" }}
{% endfor %}
43 changes: 22 additions & 21 deletions autorest/codegen/templates/metadata.json.jinja2
Original file line number Diff line number Diff line change
Expand Up @@ -8,29 +8,30 @@
"description": {{ code_model.description | tojson }},
"has_subscription_id": {{ "true" if "subscription_id" in code_model.global_parameters | map(attribute="serialized_name") | list else "false" }}
},
"global_parameters": {
"method": {
{% for gp in code_model.global_parameters.method %}
{{ gp.serialized_name | tojson }}: {
"method_signature": {{ gp.sync_method_signature | tojson }},
"description": {{ gp.description | tojson }},
"docstring_type": {{ gp.schema.docstring_type | tojson }},
"required": {{ gp.required | tojson }}
}{{ "," if not loop.last else "" }}
{% endfor %}
},
"constant": {
{% for gp in code_model.global_parameters.constant %}
{% if gp.serialized_name != "api_version" %}
{{ gp.serialized_name | tojson }}: {{ gp.schema.constant_value | tojson }}{{ "," if not loop.last else "" }}
{% endif %}
{% endfor %}
},
"call": {{ code_model.global_parameters.method | map(attribute="serialized_name") | join(', ') | tojson }}
},
"config": {
"credential": {{ code_model.options['credential'] | tojson }},
"credential_scopes": {{ code_model.options['credential_scopes'] | tojson }},
"package_version": {{ "true" if code_model.options['package_version'] else "false"}},
"global_parameters": {
"method": {
{% for gp in code_model.global_parameters.method %}
{{ gp.serialized_name | tojson }}: {
"method_signature": {{ gp.sync_method_signature | tojson }},
"description": {{ gp.description | tojson }},
"docstring_type": {{ gp.schema.docstring_type | tojson }},
"required": {{ gp.required | tojson }}
}{{ "," if not loop.last else "" }}
{% endfor %}
},
"constant": {
{% for gp in code_model.global_parameters.constant %}
{% if gp.serialized_name != "api_version" %}
{{ gp.serialized_name | tojson }}: {{ gp.schema.constant_value | tojson }}{{ "," if not loop.last else "" }}
{% endif %}
{% endfor %}
}
}
"credential_scopes": {{ code_model.options['credential_scopes'] | tojson }}

},
"operation_groups": {
{% for operation_group in code_model.operation_groups %}
Expand Down
2 changes: 2 additions & 0 deletions autorest/codegen/templates/model_init.py.jinja2
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# coding=utf-8
{{ code_model.options['license_header'] }}

{% if schemas %}
try:
{% for schema in schemas %}
from ._models_py3 import {{ schema }}
Expand All @@ -9,6 +10,7 @@ except (SyntaxError, ImportError):
{% for schema in schemas %}
from ._models import {{ schema }} # type: ignore
{% endfor %}
{% endif %}
{% if enums %}

from ._{{ code_model.module_name }}_enums import (
Expand Down
3 changes: 2 additions & 1 deletion autorest/multiapi/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,8 @@ def process(self) -> bool:
"default_models": sorted(
{last_api_version} | {versions for _, versions in last_rt_list.items()}
),
"config": metadata_json["config"]
"config": metadata_json["config"],
"global_parameters": metadata_json["global_parameters"]
}
multiapi_serializer = MultiAPISerializer(conf=conf)

Expand Down
6 changes: 1 addition & 5 deletions autorest/multiapi/multiapi_serializer.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,7 @@ def serialize_multiapi_client(self) -> str:

def serialize_multiapi_config(self) -> str:
template = self.env.get_template("multiapi_config.py.jinja2")
return template.render(
client_name=self.conf["client_name"],
package_name=self.conf["package_name"],
**self.conf["config"]
)
return template.render(**self.conf)

def serialize_multiapi_models(self) -> str:
template = self.env.get_template("multiapi_models.py.jinja2")
Expand Down
Loading

0 comments on commit f6e7289

Please sign in to comment.