change python client generator for hydra
This commit is contained in:
parent
c15be406ea
commit
4e9fd55093
128 changed files with 16275 additions and 11 deletions
23
specs/api_template/.gitignore.jinja
Normal file
23
specs/api_template/.gitignore.jinja
Normal file
|
@ -0,0 +1,23 @@
|
|||
__pycache__/
|
||||
build/
|
||||
dist/
|
||||
*.egg-info/
|
||||
.pytest_cache/
|
||||
|
||||
# pyenv
|
||||
.python-version
|
||||
|
||||
# Environments
|
||||
.env
|
||||
.venv
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
.dmypy.json
|
||||
dmypy.json
|
||||
|
||||
# JetBrains
|
||||
.idea/
|
||||
|
||||
/coverage.xml
|
||||
/.coverage
|
87
specs/api_template/README.md.jinja
Normal file
87
specs/api_template/README.md.jinja
Normal file
|
@ -0,0 +1,87 @@
|
|||
# {{ project_name }}
|
||||
{{ package_description }}
|
||||
|
||||
## Usage
|
||||
First, create a client:
|
||||
|
||||
```python
|
||||
from {{ package_name }} import Client
|
||||
|
||||
client = Client(base_url="https://api.example.com")
|
||||
```
|
||||
|
||||
If the endpoints you're going to hit require authentication, use `AuthenticatedClient` instead:
|
||||
|
||||
```python
|
||||
from {{ package_name }} import AuthenticatedClient
|
||||
|
||||
client = AuthenticatedClient(base_url="https://api.example.com", token="SuperSecretToken")
|
||||
```
|
||||
|
||||
Now call your endpoint and use your models:
|
||||
|
||||
```python
|
||||
from {{ package_name }}.models import MyDataModel
|
||||
from {{ package_name }}.api.my_tag import get_my_data_model
|
||||
from {{ package_name }}.types import Response
|
||||
|
||||
my_data: MyDataModel = get_my_data_model.sync(client=client)
|
||||
# or if you need more info (e.g. status_code)
|
||||
response: Response[MyDataModel] = get_my_data_model.sync_detailed(client=client)
|
||||
```
|
||||
|
||||
Or do the same thing with an async version:
|
||||
|
||||
```python
|
||||
from {{ package_name }}.models import MyDataModel
|
||||
from {{ package_name }}.api.my_tag import get_my_data_model
|
||||
from {{ package_name }}.types import Response
|
||||
|
||||
my_data: MyDataModel = await get_my_data_model.asyncio(client=client)
|
||||
response: Response[MyDataModel] = await get_my_data_model.asyncio_detailed(client=client)
|
||||
```
|
||||
|
||||
By default, when you're calling an HTTPS API it will attempt to verify that SSL is working correctly. Using certificate verification is highly recommended most of the time, but sometimes you may need to authenticate to a server (especially an internal server) using a custom certificate bundle.
|
||||
|
||||
```python
|
||||
client = AuthenticatedClient(
|
||||
base_url="https://internal_api.example.com",
|
||||
token="SuperSecretToken",
|
||||
verify_ssl="/path/to/certificate_bundle.pem",
|
||||
)
|
||||
```
|
||||
|
||||
You can also disable certificate validation altogether, but beware that **this is a security risk**.
|
||||
|
||||
```python
|
||||
client = AuthenticatedClient(
|
||||
base_url="https://internal_api.example.com",
|
||||
token="SuperSecretToken",
|
||||
verify_ssl=False
|
||||
)
|
||||
```
|
||||
|
||||
Things to know:
|
||||
1. Every path/method combo becomes a Python module with four functions:
|
||||
1. `sync`: Blocking request that returns parsed data (if successful) or `None`
|
||||
1. `sync_detailed`: Blocking request that always returns a `Request`, optionally with `parsed` set if the request was successful.
|
||||
1. `asyncio`: Like `sync` but the async instead of blocking
|
||||
1. `asyncio_detailed`: Like `sync_detailed` by async instead of blocking
|
||||
|
||||
1. All path/query params, and bodies become method arguments.
|
||||
1. If your endpoint had any tags on it, the first tag will be used as a module name for the function (my_tag above)
|
||||
1. Any endpoint which did not have a tag will be in `{{ package_name }}.api.default`
|
||||
|
||||
## Building / publishing this Client
|
||||
This project uses [Poetry](https://python-poetry.org/) to manage dependencies and packaging. Here are the basics:
|
||||
1. Update the metadata in pyproject.toml (e.g. authors, version)
|
||||
1. If you're using a private repository, configure it with Poetry
|
||||
1. `poetry config repositories.<your-repository-name> <url-to-your-repository>`
|
||||
1. `poetry config http-basic.<your-repository-name> <username> <password>`
|
||||
1. Publish the client with `poetry publish --build -r <your-repository-name>` or, if for public PyPI, just `poetry publish --build`
|
||||
|
||||
If you want to install this client into another project without publishing it (e.g. for development) then:
|
||||
1. If that project **is using Poetry**, you can simply do `poetry add <path-to-this-client>` from that project
|
||||
1. If that project is not using Poetry:
|
||||
1. Build a wheel with `poetry build -f wheel`
|
||||
1. Install that wheel from the other project `pip install <path-to-wheel>`
|
1
specs/api_template/api_init.py.jinja
Normal file
1
specs/api_template/api_init.py.jinja
Normal file
|
@ -0,0 +1 @@
|
|||
""" Contains methods for accessing the API """
|
45
specs/api_template/client.py.jinja
Normal file
45
specs/api_template/client.py.jinja
Normal file
|
@ -0,0 +1,45 @@
|
|||
import ssl
|
||||
from typing import Dict, Union
|
||||
import attr
|
||||
|
||||
@attr.s(auto_attribs=True)
|
||||
class Client:
|
||||
""" A class for keeping track of data related to the API """
|
||||
|
||||
base_url: str
|
||||
cookies: Dict[str, str] = attr.ib(factory=dict, kw_only=True)
|
||||
headers: Dict[str, str] = attr.ib(factory=dict, kw_only=True)
|
||||
timeout: float = attr.ib(5.0, kw_only=True)
|
||||
verify_ssl: Union[str, bool, ssl.SSLContext] = attr.ib(True, kw_only=True)
|
||||
|
||||
def get_headers(self) -> Dict[str, str]:
|
||||
""" Get headers to be used in all endpoints """
|
||||
return {**self.headers}
|
||||
|
||||
def with_headers(self, headers: Dict[str, str]) -> "Client":
|
||||
""" Get a new client matching this one with additional headers """
|
||||
return attr.evolve(self, headers={**self.headers, **headers})
|
||||
|
||||
def get_cookies(self) -> Dict[str, str]:
|
||||
return {**self.cookies}
|
||||
|
||||
def with_cookies(self, cookies: Dict[str, str]) -> "Client":
|
||||
""" Get a new client matching this one with additional cookies """
|
||||
return attr.evolve(self, cookies={**self.cookies, **cookies})
|
||||
|
||||
def get_timeout(self) -> float:
|
||||
return self.timeout
|
||||
|
||||
def with_timeout(self, timeout: float) -> "Client":
|
||||
""" Get a new client matching this one with a new timeout (in seconds) """
|
||||
return attr.evolve(self, timeout=timeout)
|
||||
|
||||
@attr.s(auto_attribs=True)
|
||||
class AuthenticatedClient(Client):
|
||||
""" A Client which has been authenticated for use on secured endpoints """
|
||||
|
||||
token: str
|
||||
|
||||
def get_headers(self) -> Dict[str, str]:
|
||||
""" Get headers to be used in authenticated endpoints """
|
||||
return {"Authorization": f"Bearer {self.token}", **self.headers}
|
0
specs/api_template/endpoint_init.py.jinja
Normal file
0
specs/api_template/endpoint_init.py.jinja
Normal file
165
specs/api_template/endpoint_macros.py.jinja
Normal file
165
specs/api_template/endpoint_macros.py.jinja
Normal file
|
@ -0,0 +1,165 @@
|
|||
{% from "property_templates/helpers.jinja" import guarded_statement %}
|
||||
|
||||
{% macro header_params(endpoint) %}
|
||||
{% if endpoint.header_parameters %}
|
||||
{% for parameter in endpoint.header_parameters.values() %}
|
||||
{% set destination = 'headers["' + parameter.name + '"]' %}
|
||||
{% import "property_templates/" + parameter.template as param_template %}
|
||||
{% if param_template.transform_header %}
|
||||
{% set statement = param_template.transform_header(parameter, parameter.python_name, destination) %}
|
||||
{% else %}
|
||||
{% set statement = destination + " = " + parameter.python_name %}
|
||||
{% endif %}
|
||||
{{ guarded_statement(parameter, parameter.python_name, statement) }}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endmacro %}
|
||||
|
||||
{% macro cookie_params(endpoint) %}
|
||||
{% if endpoint.cookie_parameters %}
|
||||
{% for parameter in endpoint.cookie_parameters.values() %}
|
||||
{% if parameter.required %}
|
||||
cookies["{{ parameter.name}}"] = {{ parameter.python_name }}
|
||||
{% else %}
|
||||
if {{ parameter.python_name }} is not UNSET:
|
||||
cookies["{{ parameter.name}}"] = {{ parameter.python_name }}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endmacro %}
|
||||
|
||||
|
||||
{% macro query_params(endpoint) %}
|
||||
{% if endpoint.query_parameters %}
|
||||
params: Dict[str, Any] = {}
|
||||
{% for property in endpoint.query_parameters.values() %}
|
||||
{% set destination = property.python_name %}
|
||||
{% import "property_templates/" + property.template as prop_template %}
|
||||
{% if prop_template.transform %}
|
||||
{% set destination = "json_" + property.python_name %}
|
||||
{{ prop_template.transform(property, property.python_name, destination) }}
|
||||
{% endif %}
|
||||
{%- if not property.json_is_dict %}
|
||||
params["{{ property.name }}"] = {{ destination }}
|
||||
{% else %}
|
||||
{{ guarded_statement(property, destination, "params.update(" + destination + ")") }}
|
||||
{% endif %}
|
||||
|
||||
|
||||
{% endfor %}
|
||||
|
||||
params = {k: v for k, v in params.items() if v is not UNSET and v is not None}
|
||||
{% endif %}
|
||||
{% endmacro %}
|
||||
|
||||
{% macro json_body(endpoint) %}
|
||||
{% if endpoint.json_body %}
|
||||
{% set property = endpoint.json_body %}
|
||||
{% set destination = "json_" + property.python_name %}
|
||||
{% import "property_templates/" + property.template as prop_template %}
|
||||
{% if prop_template.transform %}
|
||||
{{ prop_template.transform(property, property.python_name, destination) }}
|
||||
{% else %}
|
||||
{{ destination }} = {{ property.python_name }}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endmacro %}
|
||||
|
||||
{% macro multipart_body(endpoint) %}
|
||||
{% if endpoint.multipart_body %}
|
||||
{% set property = endpoint.multipart_body %}
|
||||
{% set destination = "multipart_" + property.python_name %}
|
||||
{% import "property_templates/" + property.template as prop_template %}
|
||||
{% if prop_template.transform_multipart %}
|
||||
{{ prop_template.transform_multipart(property, property.python_name, destination) }}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endmacro %}
|
||||
|
||||
{# The all the kwargs passed into an endpoint (and variants thereof)) #}
|
||||
{% macro arguments(endpoint) %}
|
||||
{# path parameters #}
|
||||
{% for parameter in endpoint.path_parameters.values() %}
|
||||
{{ parameter.to_string() }},
|
||||
{% endfor %}
|
||||
*,
|
||||
{# Proper _client based on whether or not the endpoint requires authentication #}
|
||||
{% if endpoint.requires_security %}
|
||||
_client: AuthenticatedClient,
|
||||
{% else %}
|
||||
_client: Client,
|
||||
{% endif %}
|
||||
{# Form data if any #}
|
||||
{% if endpoint.form_body_class %}
|
||||
form_data: {{ endpoint.form_body_class.name }},
|
||||
{% endif %}
|
||||
{# Multipart data if any #}
|
||||
{% if endpoint.multipart_body %}
|
||||
multipart_data: {{ endpoint.multipart_body.get_type_string() }},
|
||||
{% endif %}
|
||||
{# JSON body if any #}
|
||||
{% if endpoint.json_body %}
|
||||
json_body: {{ endpoint.json_body.get_type_string() }},
|
||||
{% endif %}
|
||||
{# query parameters #}
|
||||
{% for parameter in endpoint.query_parameters.values() %}
|
||||
{{ parameter.to_string() }},
|
||||
{% endfor %}
|
||||
{% for parameter in endpoint.header_parameters.values() %}
|
||||
{{ parameter.to_string() }},
|
||||
{% endfor %}
|
||||
{# cookie parameters #}
|
||||
{% for parameter in endpoint.cookie_parameters.values() %}
|
||||
{{ parameter.to_string() }},
|
||||
{% endfor %}
|
||||
{% endmacro %}
|
||||
|
||||
{# Just lists all kwargs to endpoints as name=name for passing to other functions #}
|
||||
{% macro kwargs(endpoint) %}
|
||||
{% for parameter in endpoint.path_parameters.values() %}
|
||||
{{ parameter.python_name }}={{ parameter.python_name }},
|
||||
{% endfor %}
|
||||
_client=_client,
|
||||
{% if endpoint.form_body_class %}
|
||||
form_data=form_data,
|
||||
{% endif %}
|
||||
{% if endpoint.multipart_body %}
|
||||
multipart_data=multipart_data,
|
||||
{% endif %}
|
||||
{% if endpoint.json_body %}
|
||||
json_body=json_body,
|
||||
{% endif %}
|
||||
{% for parameter in endpoint.query_parameters.values() %}
|
||||
{{ parameter.python_name }}={{ parameter.python_name }},
|
||||
{% endfor %}
|
||||
{% for parameter in endpoint.header_parameters.values() %}
|
||||
{{ parameter.python_name }}={{ parameter.python_name }},
|
||||
{% endfor %}
|
||||
{% for parameter in endpoint.cookie_parameters.values() %}
|
||||
{{ parameter.python_name }}={{ parameter.python_name }},
|
||||
{% endfor %}
|
||||
{% endmacro %}
|
||||
|
||||
{% macro docstring(endpoint, return_string) %}
|
||||
"""{% if endpoint.summary %}{{ endpoint.summary | wordwrap(100)}}
|
||||
|
||||
{% endif -%}
|
||||
{%- if endpoint.description %} {{ endpoint.description | wordwrap(100) }}
|
||||
|
||||
{% endif %}
|
||||
{% if not endpoint.summary and not endpoint.description %}
|
||||
{# Leave extra space so that Args or Returns isn't at the top #}
|
||||
|
||||
{% endif %}
|
||||
{% set all_parameters = endpoint.list_all_parameters() %}
|
||||
{% if all_parameters %}
|
||||
Args:
|
||||
{% for parameter in all_parameters %}
|
||||
{{ parameter.to_docstring() | wordwrap(90) | indent(8) }}
|
||||
{% endfor %}
|
||||
|
||||
{% endif %}
|
||||
Returns:
|
||||
Response[{{ return_string }}]
|
||||
"""
|
||||
{% endmacro %}
|
142
specs/api_template/endpoint_module.py.jinja
Normal file
142
specs/api_template/endpoint_module.py.jinja
Normal file
|
@ -0,0 +1,142 @@
|
|||
from typing import Any, Dict, List, Optional, Union, cast
|
||||
|
||||
import httpx
|
||||
|
||||
from ...client import AuthenticatedClient, Client
|
||||
from ...types import Response, UNSET
|
||||
|
||||
{% for relative in endpoint.relative_imports %}
|
||||
{{ relative }}
|
||||
{% endfor %}
|
||||
|
||||
{% from "endpoint_macros.py.jinja" import header_params, cookie_params, query_params, json_body, multipart_body,
|
||||
arguments, client, kwargs, parse_response, docstring %}
|
||||
|
||||
{% set return_string = endpoint.response_type() %}
|
||||
{% set parsed_responses = (endpoint.responses | length > 0) and return_string != "Any" %}
|
||||
|
||||
def _get_kwargs(
|
||||
{{ arguments(endpoint) | indent(4) }}
|
||||
) -> Dict[str, Any]:
|
||||
url = "{}{{ endpoint.path }}".format(
|
||||
_client.base_url
|
||||
{%- for parameter in endpoint.path_parameters.values() -%}
|
||||
,{{parameter.name}}={{parameter.python_name}}
|
||||
{%- endfor -%}
|
||||
)
|
||||
|
||||
headers: Dict[str, str] = _client.get_headers()
|
||||
cookies: Dict[str, Any] = _client.get_cookies()
|
||||
|
||||
{{ header_params(endpoint) | indent(4) }}
|
||||
|
||||
{{ cookie_params(endpoint) | indent(4) }}
|
||||
|
||||
{{ query_params(endpoint) | indent(4) }}
|
||||
|
||||
{{ json_body(endpoint) | indent(4) }}
|
||||
|
||||
{{ multipart_body(endpoint) | indent(4) }}
|
||||
|
||||
return {
|
||||
"method": "{{ endpoint.method }}",
|
||||
"url": url,
|
||||
"headers": headers,
|
||||
"cookies": cookies,
|
||||
"timeout": _client.get_timeout(),
|
||||
{% if endpoint.form_body_class %}
|
||||
"data": form_data.to_dict(),
|
||||
{% elif endpoint.multipart_body %}
|
||||
"files": {{ "multipart_" + endpoint.multipart_body.python_name }},
|
||||
{% elif endpoint.json_body %}
|
||||
"json": {{ "json_" + endpoint.json_body.python_name }},
|
||||
{% endif %}
|
||||
{% if endpoint.query_parameters %}
|
||||
"params": params,
|
||||
{% endif %}
|
||||
}
|
||||
|
||||
|
||||
{% if parsed_responses %}
|
||||
def _parse_response(*, response: httpx.Response) -> Optional[{{ return_string }}]:
|
||||
{% for response in endpoint.responses %}
|
||||
if response.status_code == {{ response.status_code }}:
|
||||
{% import "property_templates/" + response.prop.template as prop_template %}
|
||||
{% if prop_template.construct %}
|
||||
{{ prop_template.construct(response.prop, response.source) | indent(8) }}
|
||||
{% else %}
|
||||
{{ response.prop.python_name }} = cast({{ response.prop.get_type_string() }}, {{ response.source }})
|
||||
{% endif %}
|
||||
return {{ response.prop.python_name }}
|
||||
{% endfor %}
|
||||
return None
|
||||
{% endif %}
|
||||
|
||||
|
||||
def _build_response(*, response: httpx.Response) -> Response[{{ return_string }}]:
|
||||
return Response(
|
||||
status_code=response.status_code,
|
||||
content=response.content,
|
||||
headers=response.headers,
|
||||
{% if parsed_responses %}
|
||||
parsed=_parse_response(response=response),
|
||||
{% else %}
|
||||
parsed=None,
|
||||
{% endif %}
|
||||
)
|
||||
|
||||
|
||||
def sync_detailed(
|
||||
{{ arguments(endpoint) | indent(4) }}
|
||||
) -> Response[{{ return_string }}]:
|
||||
{{ docstring(endpoint, return_string) | indent(4) }}
|
||||
|
||||
kwargs = _get_kwargs(
|
||||
{{ kwargs(endpoint) }}
|
||||
)
|
||||
|
||||
response = httpx.request(
|
||||
verify=_client.verify_ssl,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
return _build_response(response=response)
|
||||
|
||||
{% if parsed_responses %}
|
||||
def sync(
|
||||
{{ arguments(endpoint) | indent(4) }}
|
||||
) -> Optional[{{ return_string }}]:
|
||||
{{ docstring(endpoint, return_string) | indent(4) }}
|
||||
|
||||
return sync_detailed(
|
||||
{{ kwargs(endpoint) }}
|
||||
).parsed
|
||||
{% endif %}
|
||||
|
||||
async def asyncio_detailed(
|
||||
{{ arguments(endpoint) | indent(4) }}
|
||||
) -> Response[{{ return_string }}]:
|
||||
{{ docstring(endpoint, return_string) | indent(4) }}
|
||||
|
||||
kwargs = _get_kwargs(
|
||||
{{ kwargs(endpoint) }}
|
||||
)
|
||||
|
||||
async with httpx.AsyncClient(verify=_client.verify_ssl) as __client:
|
||||
response = await __client.request(
|
||||
**kwargs
|
||||
)
|
||||
|
||||
return _build_response(response=response)
|
||||
|
||||
{% if parsed_responses %}
|
||||
async def asyncio(
|
||||
{{ arguments(endpoint) | indent(4) }}
|
||||
) -> Optional[{{ return_string }}]:
|
||||
{{ docstring(endpoint, return_string) | indent(4) }}
|
||||
|
||||
return (await asyncio_detailed(
|
||||
{{ kwargs(endpoint) }}
|
||||
)).parsed
|
||||
{% endif %}
|
||||
|
9
specs/api_template/int_enum.py.jinja
Normal file
9
specs/api_template/int_enum.py.jinja
Normal file
|
@ -0,0 +1,9 @@
|
|||
from enum import IntEnum
|
||||
|
||||
class {{ enum.class_info.name }}(IntEnum):
|
||||
{% for key, value in enum.values.items() %}
|
||||
{{ key }} = {{ value }}
|
||||
{% endfor %}
|
||||
|
||||
def __str__(self) -> str:
|
||||
return str(self.value)
|
181
specs/api_template/model.py.jinja
Normal file
181
specs/api_template/model.py.jinja
Normal file
|
@ -0,0 +1,181 @@
|
|||
from typing import Any, Dict, Type, TypeVar, Tuple, Optional, BinaryIO, TextIO
|
||||
|
||||
{% if model.additional_properties %}
|
||||
from typing import List
|
||||
|
||||
{% endif %}
|
||||
|
||||
import attr
|
||||
{% if model.is_multipart_body %}
|
||||
import json
|
||||
{% endif %}
|
||||
|
||||
from ..types import UNSET, Unset
|
||||
|
||||
{% for relative in model.relative_imports %}
|
||||
{{ relative }}
|
||||
{% endfor %}
|
||||
|
||||
|
||||
{% if model.additional_properties %}
|
||||
{% set additional_property_type = 'Any' if model.additional_properties == True else model.additional_properties.get_type_string() %}
|
||||
{% endif %}
|
||||
|
||||
{% set class_name = model.class_info.name %}
|
||||
{% set module_name = model.class_info.module_name %}
|
||||
|
||||
T = TypeVar("T", bound="{{ class_name }}")
|
||||
|
||||
@attr.s(auto_attribs=True)
|
||||
class {{ class_name }}:
|
||||
"""{% if model.title %}{{ model.title | wordwrap(116) }}
|
||||
|
||||
{% endif -%}
|
||||
{%- if model.description %}{{ model.description | wordwrap(116) }}
|
||||
|
||||
{% endif %}
|
||||
{% if not model.title and not model.description %}
|
||||
{# Leave extra space so that a section doesn't start on the first line #}
|
||||
|
||||
{% endif %}
|
||||
{% if model.example %}
|
||||
Example:
|
||||
{{ model.example | string | wordwrap(112) | indent(12) }}
|
||||
|
||||
{% endif %}
|
||||
{% if model.required_properties or model.optional_properties %}
|
||||
Attributes:
|
||||
{% for property in model.required_properties + model.optional_properties %}
|
||||
{{ property.to_docstring() | wordwrap(112) | indent(12) }}
|
||||
{% endfor %}{% endif %}
|
||||
"""
|
||||
|
||||
{% for property in model.required_properties + model.optional_properties %}
|
||||
{% if property.default is none and property.required %}
|
||||
{{ property.to_string() }}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% for property in model.required_properties + model.optional_properties %}
|
||||
{% if property.default is not none or not property.required %}
|
||||
{{ property.to_string() }}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% if model.additional_properties %}
|
||||
additional_properties: Dict[str, {{ additional_property_type }}] = attr.ib(init=False, factory=dict)
|
||||
{% endif %}
|
||||
|
||||
{% macro _to_dict(multipart=False) %}
|
||||
{% for property in model.required_properties + model.optional_properties %}
|
||||
{% import "property_templates/" + property.template as prop_template %}
|
||||
{% if prop_template.transform %}
|
||||
{{ prop_template.transform(property, "self." + property.python_name, property.python_name, multipart=multipart) }}
|
||||
{% elif multipart %}
|
||||
{{ property.python_name }} = self.{{ property.python_name }} if isinstance(self.{{ property.python_name }}, Unset) else (None, str(self.{{ property.python_name }}).encode(), "text/plain")
|
||||
{% else %}
|
||||
{{ property.python_name }} = self.{{ property.python_name }}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
field_dict: Dict[str, Any] = {}
|
||||
{% if model.additional_properties %}
|
||||
{% if model.additional_properties.template %}{# Can be a bool instead of an object #}
|
||||
{% import "property_templates/" + model.additional_properties.template as prop_template %}
|
||||
{% else %}
|
||||
{% set prop_template = None %}
|
||||
{% endif %}
|
||||
{% if prop_template and prop_template.transform %}
|
||||
for prop_name, prop in self.additional_properties.items():
|
||||
{{ prop_template.transform(model.additional_properties, "prop", "field_dict[prop_name]", multipart=multipart) | indent(4) }}
|
||||
{% elif multipart %}
|
||||
field_dict.update({
|
||||
key: (None, str(value).encode(), "text/plain")
|
||||
for key, value in self.additional_properties.items()
|
||||
})
|
||||
{% else %}
|
||||
field_dict.update(self.additional_properties)
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
field_dict.update({
|
||||
{% for property in model.required_properties + model.optional_properties %}
|
||||
{% if property.required %}
|
||||
"{{ property.name }}": {{ property.python_name }},
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
})
|
||||
{% for property in model.optional_properties %}
|
||||
{% if not property.required %}
|
||||
if {{ property.python_name }} is not UNSET:
|
||||
field_dict["{{ property.name }}"] = {{ property.python_name }}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
return field_dict
|
||||
{% endmacro %}
|
||||
|
||||
def to_dict(self) -> Dict[str, Any]:
|
||||
{{ _to_dict() | indent(8) }}
|
||||
|
||||
{% if model.is_multipart_body %}
|
||||
def to_multipart(self) -> Dict[str, Any]:
|
||||
{{ _to_dict(multipart=True) | indent(8) }}
|
||||
{% endif %}
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
|
||||
_d = src_dict.copy()
|
||||
{% for property in model.required_properties + model.optional_properties %}
|
||||
{% if property.required %}
|
||||
{% set property_source = '_d.pop("' + property.name + '")' %}
|
||||
{% else %}
|
||||
{% set property_source = '_d.pop("' + property.name + '", UNSET)' %}
|
||||
{% endif %}
|
||||
{% import "property_templates/" + property.template as prop_template %}
|
||||
{% if prop_template.construct %}
|
||||
{{ prop_template.construct(property, property_source) | indent(8) }}
|
||||
{% else %}
|
||||
{{ property.python_name }} = {{ property_source }}
|
||||
{% endif %}
|
||||
|
||||
{% endfor %}
|
||||
{{ module_name }} = cls(
|
||||
{% for property in model.required_properties + model.optional_properties %}
|
||||
{{ property.python_name }}={{ property.python_name }},
|
||||
{% endfor %}
|
||||
)
|
||||
|
||||
{% if model.additional_properties %}
|
||||
{% if model.additional_properties.template %}{# Can be a bool instead of an object #}
|
||||
{% import "property_templates/" + model.additional_properties.template as prop_template %}
|
||||
{% else %}
|
||||
{% set prop_template = None %}
|
||||
{% endif %}
|
||||
{% if prop_template and prop_template.construct %}
|
||||
additional_properties = {}
|
||||
for prop_name, prop_dict in _d.items():
|
||||
{{ prop_template.construct(model.additional_properties, "prop_dict") | indent(12) }}
|
||||
additional_properties[prop_name] = {{ model.additional_properties.python_name }}
|
||||
|
||||
{{ module_name }}.additional_properties = additional_properties
|
||||
{% else %}
|
||||
{{ module_name }}.additional_properties = _d
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
return {{ module_name }}
|
||||
|
||||
{% if model.additional_properties %}
|
||||
@property
|
||||
def additional_keys(self) -> List[str]:
|
||||
return list(self.additional_properties.keys())
|
||||
|
||||
def __getitem__(self, key: str) -> {{ additional_property_type }}:
|
||||
return self.additional_properties[key]
|
||||
|
||||
def __setitem__(self, key: str, value: {{ additional_property_type }}) -> None:
|
||||
self.additional_properties[key] = value
|
||||
|
||||
def __delitem__(self, key: str) -> None:
|
||||
del self.additional_properties[key]
|
||||
|
||||
def __contains__(self, key: str) -> bool:
|
||||
return key in self.additional_properties
|
||||
{% endif %}
|
5
specs/api_template/models_init.py.jinja
Normal file
5
specs/api_template/models_init.py.jinja
Normal file
|
@ -0,0 +1,5 @@
|
|||
""" Contains all the data models used in inputs/outputs """
|
||||
|
||||
{% for import in imports | sort %}
|
||||
{{ import }}
|
||||
{% endfor %}
|
2
specs/api_template/package_init.py.jinja
Normal file
2
specs/api_template/package_init.py.jinja
Normal file
|
@ -0,0 +1,2 @@
|
|||
""" {{ package_description }} """
|
||||
from .client import AuthenticatedClient, Client
|
|
@ -0,0 +1,3 @@
|
|||
{% macro transform_header(property, source, destination) %}
|
||||
{{ destination }} = "true" if {{ source }} else "false"
|
||||
{% endmacro %}
|
35
specs/api_template/property_templates/date_property.py.jinja
Normal file
35
specs/api_template/property_templates/date_property.py.jinja
Normal file
|
@ -0,0 +1,35 @@
|
|||
{% macro construct_function(property, source) %}
|
||||
isoparse({{ source }}).date()
|
||||
{% endmacro %}
|
||||
|
||||
{% from "property_templates/property_macros.py.jinja" import construct_template %}
|
||||
|
||||
{% macro construct(property, source, initial_value=None) %}
|
||||
{{ construct_template(construct_function, property, source, initial_value=initial_value) }}
|
||||
{% endmacro %}
|
||||
|
||||
{% macro check_type_for_construct(property, source) %}isinstance({{ source }}, str){% endmacro %}
|
||||
|
||||
{% macro transform(property, source, destination, declare_type=True, multipart=False) %}
|
||||
{% set transformed = source + ".isoformat()" %}
|
||||
{% if multipart %}{# Multipart data must be bytes, not str #}
|
||||
{% set transformed = transformed + ".encode()" %}
|
||||
{% endif %}
|
||||
{% if property.required %}
|
||||
{{ destination }} = {{ transformed }} {% if property.nullable %}if {{ source }} else None {%endif%}
|
||||
{% else %}
|
||||
{% if declare_type %}
|
||||
{% set type_annotation = property.get_type_string(json=True) %}
|
||||
{% if multipart %}{% set type_annotation = type_annotation | replace("str", "bytes") %}{% endif %}
|
||||
{{ destination }}: {{ type_annotation }} = UNSET
|
||||
{% else %}
|
||||
{{ destination }} = UNSET
|
||||
{% endif %}
|
||||
if not isinstance({{ source }}, Unset):
|
||||
{% if property.nullable %}
|
||||
{{ destination }} = {{ transformed }} if {{ source }} else None
|
||||
{% else %}
|
||||
{{ destination }} = {{ transformed }}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endmacro %}
|
|
@ -0,0 +1,39 @@
|
|||
{% macro construct_function(property, source) %}
|
||||
isoparse({{ source }})
|
||||
{% endmacro %}
|
||||
|
||||
{% from "property_templates/property_macros.py.jinja" import construct_template %}
|
||||
|
||||
{% macro construct(property, source, initial_value=None) %}
|
||||
{{ construct_template(construct_function, property, source, initial_value=initial_value) }}
|
||||
{% endmacro %}
|
||||
|
||||
{% macro check_type_for_construct(property, source) %}isinstance({{ source }}, str){% endmacro %}
|
||||
|
||||
{% macro transform(property, source, destination, declare_type=True, multipart=False) %}
|
||||
{% set transformed = source + ".isoformat()" %}
|
||||
{% if multipart %}{# Multipart data must be bytes, not str #}
|
||||
{% set transformed = transformed + ".encode()" %}
|
||||
{% endif %}
|
||||
{% if property.required %}
|
||||
{% if property.nullable %}
|
||||
{{ destination }} = {{ transformed }} if {{ source }} else None
|
||||
{% else %}
|
||||
{{ destination }} = {{ transformed }}
|
||||
{% endif %}
|
||||
{% else %}
|
||||
{% if declare_type %}
|
||||
{% set type_annotation = property.get_type_string(json=True) %}
|
||||
{% if multipart %}{% set type_annotation = type_annotation | replace("str", "bytes") %}{% endif %}
|
||||
{{ destination }}: {{ type_annotation }} = UNSET
|
||||
{% else %}
|
||||
{{ destination }} = UNSET
|
||||
{% endif %}
|
||||
if not isinstance({{ source }}, Unset):
|
||||
{% if property.nullable %}
|
||||
{{ destination }} = {{ transformed }} if {{ source }} else None
|
||||
{% else %}
|
||||
{{ destination }} = {{ transformed }}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endmacro %}
|
35
specs/api_template/property_templates/enum_property.py.jinja
Normal file
35
specs/api_template/property_templates/enum_property.py.jinja
Normal file
|
@ -0,0 +1,35 @@
|
|||
{% macro construct_function(property, source) %}
|
||||
{{ property.class_info.name }}({{ source }})
|
||||
{% endmacro %}
|
||||
|
||||
{% from "property_templates/property_macros.py.jinja" import construct_template %}
|
||||
|
||||
{% macro construct(property, source, initial_value=None) %}
|
||||
{{ construct_template(construct_function, property, source, initial_value=initial_value) }}
|
||||
{% endmacro %}
|
||||
|
||||
{% macro check_type_for_construct(property, source) %}isinstance({{ source }}, {{ property.value_type.__name__ }}){% endmacro %}
|
||||
|
||||
{% macro transform(property, source, destination, declare_type=True, multipart=False) %}
|
||||
{% set transformed = source + ".value" %}
|
||||
{% set type_string = property.get_type_string(json=True) %}
|
||||
{% if multipart %}
|
||||
{% set transformed = "(None, str(" + transformed + ").encode(), \"text/plain\")" %}
|
||||
{% set type_string = "Union[Unset, Tuple[None, bytes, str]]" %}
|
||||
{% endif %}
|
||||
{% if property.required %}
|
||||
{% if property.nullable %}
|
||||
{{ destination }} = {{ transformed }} if {{ source }} else None
|
||||
{% else %}
|
||||
{{ destination }} = {{ transformed }}
|
||||
{% endif %}
|
||||
{% else %}
|
||||
{{ destination }}{% if declare_type %}: {{ type_string }}{% endif %} = UNSET
|
||||
if not isinstance({{ source }}, Unset):
|
||||
{% if property.nullable %}
|
||||
{{ destination }} = {{ transformed }} if {{ source }} else None
|
||||
{% else %}
|
||||
{{ destination }} = {{ transformed }}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endmacro %}
|
31
specs/api_template/property_templates/file_property.py.jinja
Normal file
31
specs/api_template/property_templates/file_property.py.jinja
Normal file
|
@ -0,0 +1,31 @@
|
|||
{% macro construct_function(property, source) %}
|
||||
File(
|
||||
payload = BytesIO({{ source }})
|
||||
)
|
||||
{% endmacro %}
|
||||
|
||||
{% from "property_templates/property_macros.py.jinja" import construct_template %}
|
||||
|
||||
{% macro construct(property, source, initial_value=None) %}
|
||||
{{ construct_template(construct_function, property, source, initial_value=initial_value) }}
|
||||
{% endmacro %}
|
||||
|
||||
{% macro check_type_for_construct(property, source) %}isinstance({{ source }}, bytes){% endmacro %}
|
||||
|
||||
{% macro transform(property, source, destination, declare_type=True, multipart=False) %}
|
||||
{% if property.required %}
|
||||
{% if property.nullable %}
|
||||
{{ destination }} = {{ source }}.to_tuple() if {{ source }} else None
|
||||
{% else %}
|
||||
{{ destination }} = {{ source }}.to_tuple()
|
||||
{% endif %}
|
||||
{% else %}
|
||||
{{ destination }}{% if declare_type %}: {{ property.get_type_string(json=True) }}{% endif %} = UNSET
|
||||
if not isinstance({{ source }}, Unset):
|
||||
{% if property.nullable %}
|
||||
{{ destination }} = {{ source }}.to_tuple() if {{ source }} else None
|
||||
{% else %}
|
||||
{{ destination }} = {{ source }}.to_tuple()
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endmacro %}
|
|
@ -0,0 +1,3 @@
|
|||
{% macro transform_header(property, source, destination) %}
|
||||
{{ destination }} = str({{ source }})
|
||||
{% endmacro %}
|
18
specs/api_template/property_templates/helpers.jinja
Normal file
18
specs/api_template/property_templates/helpers.jinja
Normal file
|
@ -0,0 +1,18 @@
|
|||
{% macro guarded_statement(property, source, statement) %}
|
||||
{# If the property can be UNSET or None, this macro returns the provided statement guarded by an if which will check
|
||||
for those invalid values. Otherwise, it returns the statement unmodified. #}
|
||||
{% if property.required and not property.nullable %}
|
||||
{{ statement }}
|
||||
{% else %}
|
||||
{% if property.nullable and not property.required %}
|
||||
if not isinstance({{ source }}, Unset) and {{ source }} is not None:
|
||||
{{ statement }}
|
||||
{% elif property.nullable %}
|
||||
if {{ source }} is not None:
|
||||
{{ statement }}
|
||||
{% else %}
|
||||
if not isinstance({{ source }}, Unset):
|
||||
{{ statement }}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endmacro %}
|
|
@ -0,0 +1,3 @@
|
|||
{% macro transform_header(property, source, destination) %}
|
||||
{{ destination }} = str({{ source }})
|
||||
{% endmacro %}
|
77
specs/api_template/property_templates/list_property.py.jinja
Normal file
77
specs/api_template/property_templates/list_property.py.jinja
Normal file
|
@ -0,0 +1,77 @@
|
|||
{% macro construct(property, source, initial_value="[]") %}
|
||||
{% set inner_property = property.inner_property %}
|
||||
{% import "property_templates/" + inner_property.template as inner_template %}
|
||||
{% if inner_template.construct %}
|
||||
{% set inner_source = inner_property.python_name + "_data" %}
|
||||
{{ property.python_name }} = {{ initial_value }}
|
||||
_{{ property.python_name }} = {{ source }}
|
||||
{% if property.required and not property.nullable %}
|
||||
for {{ inner_source }} in (_{{ property.python_name }}):
|
||||
{% else %}
|
||||
for {{ inner_source }} in (_{{ property.python_name }} or []):
|
||||
{% endif %}
|
||||
{{ inner_template.construct(inner_property, inner_source) | indent(4) }}
|
||||
{{ property.python_name }}.append({{ inner_property.python_name }})
|
||||
{% else %}
|
||||
{{ property.python_name }} = cast({{ property.get_type_string(no_optional=True) }}, {{ source }})
|
||||
{% endif %}
|
||||
{% endmacro %}
|
||||
|
||||
{% macro _transform(property, source, destination, multipart, transform_method) %}
|
||||
{% set inner_property = property.inner_property %}
|
||||
{% if multipart %}
|
||||
{% set multipart_destination = destination %}
|
||||
{% set destination = "_temp_" + destination %}
|
||||
{% endif %}
|
||||
{% import "property_templates/" + inner_property.template as inner_template %}
|
||||
{% if inner_template.transform %}
|
||||
{% set inner_source = inner_property.python_name + "_data" %}
|
||||
{{ destination }} = []
|
||||
for {{ inner_source }} in {{ source }}:
|
||||
{{ inner_template.transform(inner_property, inner_source, inner_property.python_name, transform_method) | indent(4) }}
|
||||
{{ destination }}.append({{ inner_property.python_name }})
|
||||
{% else %}
|
||||
{{ destination }} = {{ source }}
|
||||
{% endif %}
|
||||
{% if multipart %}
|
||||
{{ multipart_destination }} = (None, json.dumps({{ destination }}).encode(), 'application/json')
|
||||
{% endif %}
|
||||
{% endmacro %}
|
||||
|
||||
{% macro check_type_for_construct(property, source) %}isinstance({{ source }}, list){% endmacro %}
|
||||
|
||||
{% macro transform(property, source, destination, declare_type=True, multipart=False, transform_method="to_dict") %}
|
||||
{% set inner_property = property.inner_property %}
|
||||
{% if multipart %}
|
||||
{% set type_string = "Union[Unset, Tuple[None, bytes, str]]" %}
|
||||
{% else %}
|
||||
{% set type_string = property.get_type_string(json=True) %}
|
||||
{% endif %}
|
||||
{% if property.required %}
|
||||
{% if property.nullable %}
|
||||
if {{ source }} is None:
|
||||
{{ destination }} = None
|
||||
else:
|
||||
{{ _transform(property, source, destination, multipart, transform_method) | indent(4) }}
|
||||
{% else %}
|
||||
{{ _transform(property, source, destination, multipart, transform_method) }}
|
||||
{% endif %}
|
||||
{% else %}
|
||||
{{ destination }}{% if declare_type %}: {{ type_string }}{% endif %} = UNSET
|
||||
if not isinstance({{ source }}, Unset):
|
||||
{% if property.nullable %}
|
||||
if {{ source }} is None:
|
||||
{{ destination }} = None
|
||||
else:
|
||||
{{ _transform(property, source, destination, multipart, transform_method) | indent(8)}}
|
||||
{% else %}
|
||||
{{ _transform(property, source, destination, multipart, transform_method) | indent(4)}}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
|
||||
{% endmacro %}
|
||||
|
||||
{% macro transform_multipart(property, source, destination) %}
|
||||
{{ transform(property, source, destination, transform_method="to_multipart") }}
|
||||
{% endmacro %}
|
|
@ -0,0 +1,40 @@
|
|||
{% macro construct_function(property, source) %}
|
||||
{{ property.class_info.name }}.from_dict({{ source }})
|
||||
{% endmacro %}
|
||||
|
||||
{% from "property_templates/property_macros.py.jinja" import construct_template %}
|
||||
|
||||
{% macro construct(property, source, initial_value=None) %}
|
||||
{{ construct_template(construct_function, property, source, initial_value=initial_value) }}
|
||||
{% endmacro %}
|
||||
|
||||
{% macro check_type_for_construct(property, source) %}isinstance({{ source }}, dict){% endmacro %}
|
||||
|
||||
{% macro transform(property, source, destination, declare_type=True, multipart=False, transform_method="to_dict") %}
|
||||
{% set transformed = source + "." + transform_method + "()" %}
|
||||
{% if multipart %}
|
||||
{% set transformed = "(None, json.dumps(" + transformed + ").encode(), 'application/json')" %}
|
||||
{% set type_string = "Union[Unset, Tuple[None, bytes, str]]" %}
|
||||
{% else %}
|
||||
{% set type_string = property.get_type_string(json=True) %}
|
||||
{% endif %}
|
||||
{% if property.required %}
|
||||
{% if property.nullable %}
|
||||
{{ destination }} = {{ transformed }} if {{ source }} else None
|
||||
{% else %}
|
||||
{{ destination }} = {{ transformed }}
|
||||
{% endif %}
|
||||
{% else %}
|
||||
{{ destination }}{% if declare_type %}: {{ type_string }}{% endif %} = UNSET
|
||||
if not isinstance({{ source }}, Unset):
|
||||
{% if property.nullable %}
|
||||
{{ destination }} = {{ transformed }} if {{ source }} else None
|
||||
{% else %}
|
||||
{{ destination }} = {{ transformed }}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endmacro %}
|
||||
|
||||
{% macro transform_multipart(property, source, destination) %}
|
||||
{{ transform(property, source, destination, transform_method="to_multipart") }}
|
||||
{% endmacro %}
|
|
@ -0,0 +1,20 @@
|
|||
{% macro construct_template(construct_function, property, source, initial_value=None) %}
|
||||
{% if property.required and not property.nullable %}
|
||||
{{ property.python_name }} = {{ construct_function(property, source) }}
|
||||
{% else %}{# Must be nullable OR non-required #}
|
||||
_{{ property.python_name }} = {{ source }}
|
||||
{{ property.python_name }}: {{ property.get_type_string() }}
|
||||
{% if property.nullable %}
|
||||
if _{{ property.python_name }} is None:
|
||||
{{ property.python_name }} = {% if initial_value != None %}{{ initial_value }}{% else %}None{% endif %}
|
||||
|
||||
{% endif %}
|
||||
{% if not property.required %}
|
||||
{% if property.nullable %}elif{% else %}if{% endif %} isinstance(_{{ property.python_name }}, Unset):
|
||||
{{ property.python_name }} = {% if initial_value != None %}{{ initial_value }}{% else %}UNSET{% endif %}
|
||||
|
||||
{% endif %}
|
||||
else:
|
||||
{{ property.python_name }} = {{ construct_function(property, "_" + property.python_name) }}
|
||||
{% endif %}
|
||||
{% endmacro %}
|
|
@ -0,0 +1,85 @@
|
|||
{% macro construct(property, source, initial_value=None) %}
|
||||
def _parse_{{ property.python_name }}(data: object) -> {{ property.get_type_string() }}:
|
||||
{% if "None" in property.get_type_strings_in_union(json=True) %}
|
||||
if data is None:
|
||||
return data
|
||||
{% endif %}
|
||||
{% if "Unset" in property.get_type_strings_in_union(json=True) %}
|
||||
if isinstance(data, Unset):
|
||||
return data
|
||||
{% endif %}
|
||||
{% set ns = namespace(contains_unmodified_properties = false) %}
|
||||
{% for inner_property in property.inner_properties %}
|
||||
{% import "property_templates/" + inner_property.template as inner_template %}
|
||||
{% if not inner_template.construct %}
|
||||
{% set ns.contains_unmodified_properties = true %}
|
||||
{% continue %}
|
||||
{% endif %}
|
||||
{% if inner_template.check_type_for_construct and (not loop.last or ns.contains_unmodified_properties) %}
|
||||
try:
|
||||
if not {{ inner_template.check_type_for_construct(inner_property, "data") }}:
|
||||
raise TypeError()
|
||||
{{ inner_template.construct(inner_property, "data", initial_value="UNSET") | indent(8) }}
|
||||
return {{ inner_property.python_name }}
|
||||
except: # noqa: E722
|
||||
pass
|
||||
{% else %}{# Don't do try/except for the last one nor any properties with no type checking #}
|
||||
{% if inner_template.check_type_for_construct %}
|
||||
if not {{ inner_template.check_type_for_construct(inner_property, "data") }}:
|
||||
raise TypeError()
|
||||
{% endif %}
|
||||
{{ inner_template.construct(inner_property, "data", initial_value="UNSET") | indent(4) }}
|
||||
return {{ inner_property.python_name }}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% if ns.contains_unmodified_properties %}
|
||||
return cast({{ property.get_type_string() }}, data)
|
||||
{% endif %}
|
||||
|
||||
{{ property.python_name }} = _parse_{{ property.python_name }}({{ source }})
|
||||
{% endmacro %}
|
||||
|
||||
{% macro transform(property, source, destination, declare_type=True, multipart=False) %}
|
||||
{% if not property.required or property.nullable %}
|
||||
{{ destination }}{% if declare_type %}: {{ property.get_type_string(json=True) }}{% endif %}
|
||||
|
||||
{% if not property.required %}
|
||||
if isinstance({{ source }}, Unset):
|
||||
{{ destination }} = UNSET
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% if property.nullable %}
|
||||
{% if property.required %}
|
||||
if {{ source }} is None:
|
||||
{% else %}{# There's an if UNSET statement before this #}
|
||||
elif {{ source }} is None:
|
||||
{% endif %}
|
||||
{{ destination }} = None
|
||||
{% endif %}
|
||||
|
||||
{% set ns = namespace(contains_properties_without_transform = false, contains_modified_properties = not property.required) %}
|
||||
{% for inner_property in property.inner_properties %}
|
||||
{% import "property_templates/" + inner_property.template as inner_template %}
|
||||
{% if not inner_template.transform %}
|
||||
{% set ns.contains_properties_without_transform = true %}
|
||||
{% continue %}
|
||||
{% else %}
|
||||
{% set ns.contains_modified_properties = true %}
|
||||
{% endif %}
|
||||
{% if loop.first and property.required and not property.nullable %}{# No if UNSET or if None statement before this #}
|
||||
if isinstance({{ source }}, {{ inner_property.get_instance_type_string() }}):
|
||||
{% elif not loop.last or ns.contains_properties_without_transform %}
|
||||
elif isinstance({{ source }}, {{ inner_property.get_instance_type_string() }}):
|
||||
{% else %}
|
||||
else:
|
||||
{% endif %}
|
||||
{{ inner_template.transform(inner_property, source, destination, declare_type=False, multipart=multipart) | indent(4) }}
|
||||
{% endfor %}
|
||||
{% if ns.contains_properties_without_transform and ns.contains_modified_properties %}
|
||||
else:
|
||||
{{ destination }} = {{ source }}
|
||||
{% elif ns.contains_properties_without_transform %}
|
||||
{{ destination }} = {{ source }}
|
||||
{% endif %}
|
||||
|
||||
{% endmacro %}
|
41
specs/api_template/pyproject.toml.jinja
Normal file
41
specs/api_template/pyproject.toml.jinja
Normal file
|
@ -0,0 +1,41 @@
|
|||
{% if use_poetry %}
|
||||
[tool.poetry]
|
||||
name = "{{ project_name }}"
|
||||
version = "{{ package_version }}"
|
||||
description = "{{ package_description }}"
|
||||
|
||||
authors = []
|
||||
|
||||
readme = "README.md"
|
||||
packages = [
|
||||
{include = "{{ package_name }}"},
|
||||
]
|
||||
include = ["CHANGELOG.md", "{{ package_name }}/py.typed"]
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = "^3.7"
|
||||
httpx = ">=0.15.4,<0.23.0"
|
||||
attrs = ">=21.3.0"
|
||||
python-dateutil = "^2.8.0"
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry-core>=1.0.0"]
|
||||
build-backend = "poetry.core.masonry.api"
|
||||
|
||||
{% endif %}
|
||||
[tool.black]
|
||||
line-length = 120
|
||||
target_version = ['py37', 'py38', 'py39']
|
||||
exclude = '''
|
||||
(
|
||||
/(
|
||||
| \.git
|
||||
| \.venv
|
||||
| \.mypy_cache
|
||||
)/
|
||||
)
|
||||
'''
|
||||
|
||||
[tool.isort]
|
||||
line_length = 120
|
||||
profile = "black"
|
16
specs/api_template/pyproject_no_poetry.toml.jinja
Normal file
16
specs/api_template/pyproject_no_poetry.toml.jinja
Normal file
|
@ -0,0 +1,16 @@
|
|||
[tool.black]
|
||||
line-length = 120
|
||||
target_version = ['py36', 'py37', 'py38']
|
||||
exclude = '''
|
||||
(
|
||||
/(
|
||||
| \.git
|
||||
| \.venv
|
||||
| \.mypy_cache
|
||||
)/
|
||||
)
|
||||
'''
|
||||
|
||||
[tool.isort]
|
||||
line_length = 120
|
||||
profile = "black"
|
18
specs/api_template/setup.py.jinja
Normal file
18
specs/api_template/setup.py.jinja
Normal file
|
@ -0,0 +1,18 @@
|
|||
import pathlib
|
||||
|
||||
from setuptools import find_packages, setup
|
||||
|
||||
here = pathlib.Path(__file__).parent.resolve()
|
||||
long_description = (here / "README.md").read_text(encoding="utf-8")
|
||||
|
||||
setup(
|
||||
name="{{ project_name }}",
|
||||
version="{{ package_version }}",
|
||||
description="{{ package_description }}",
|
||||
long_description=long_description,
|
||||
long_description_content_type="text/markdown",
|
||||
packages=find_packages(),
|
||||
python_requires=">=3.7, <4",
|
||||
install_requires=["httpx >= 0.15.0, < 0.23.0", "attrs >= 21.3.0", "python-dateutil >= 2.8.0, < 3"],
|
||||
package_data={"{{ package_name }}": ["py.typed"]},
|
||||
)
|
9
specs/api_template/str_enum.py.jinja
Normal file
9
specs/api_template/str_enum.py.jinja
Normal file
|
@ -0,0 +1,9 @@
|
|||
from enum import Enum
|
||||
|
||||
class {{ enum.class_info.name }}(str, Enum):
|
||||
{% for key, value in enum.values.items() %}
|
||||
{{ key }} = "{{ value }}"
|
||||
{% endfor %}
|
||||
|
||||
def __str__(self) -> str:
|
||||
return str(self.value)
|
44
specs/api_template/types.py.jinja
Normal file
44
specs/api_template/types.py.jinja
Normal file
|
@ -0,0 +1,44 @@
|
|||
""" Contains some shared types for properties """
|
||||
from typing import Any, BinaryIO, Generic, MutableMapping, Optional, Tuple, TypeVar
|
||||
|
||||
import attr
|
||||
|
||||
|
||||
class Unset:
|
||||
def __bool__(self) -> bool:
|
||||
return False
|
||||
|
||||
|
||||
UNSET: Unset = Unset()
|
||||
|
||||
{# Used as `FileProperty._json_type_string` #}
|
||||
FileJsonType = Tuple[Optional[str], BinaryIO, Optional[str]]
|
||||
|
||||
|
||||
@attr.s(auto_attribs=True)
|
||||
class File:
|
||||
""" Contains information for file uploads """
|
||||
|
||||
payload: BinaryIO
|
||||
file_name: Optional[str] = None
|
||||
mime_type: Optional[str] = None
|
||||
|
||||
def to_tuple(self) -> FileJsonType:
|
||||
""" Return a tuple representation that httpx will accept for multipart/form-data """
|
||||
return self.file_name, self.payload, self.mime_type
|
||||
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
|
||||
@attr.s(auto_attribs=True)
|
||||
class Response(Generic[T]):
|
||||
""" A response from an endpoint """
|
||||
|
||||
status_code: int
|
||||
content: bytes
|
||||
headers: MutableMapping[str, str]
|
||||
parsed: Optional[T]
|
||||
|
||||
|
||||
__all__ = ["File", "Response", "FileJsonType"]
|
2960
specs/hydra.yaml
Normal file
2960
specs/hydra.yaml
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue