update to new version of openapi-python-client
now: 0.13.0
This commit is contained in:
parent
deb73d06e6
commit
4a31250bca
159 changed files with 13668 additions and 11420 deletions
|
@ -61,12 +61,14 @@ client = AuthenticatedClient(
|
|||
)
|
||||
```
|
||||
|
||||
There are more settings on the generated `Client` class which let you control more runtime behavior, check out the docstring on that class for more info.
|
||||
|
||||
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. `asyncio`: Like `sync` but async instead of blocking
|
||||
1. `asyncio_detailed`: Like `sync_detailed` but 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)
|
||||
|
|
|
@ -4,13 +4,26 @@ import attr
|
|||
|
||||
@attr.s(auto_attribs=True)
|
||||
class Client:
|
||||
""" A class for keeping track of data related to the API """
|
||||
""" A class for keeping track of data related to the API
|
||||
|
||||
Attributes:
|
||||
base_url: The base URL for the API, all requests are made to a relative path to this URL
|
||||
cookies: A dictionary of cookies to be sent with every request
|
||||
headers: A dictionary of headers to be sent with every request
|
||||
timeout: The maximum amount of a time in seconds a request can take. API functions will raise
|
||||
httpx.TimeoutException if this is exceeded.
|
||||
verify_ssl: Whether or not to verify the SSL certificate of the API server. This should be True in production,
|
||||
but can be set to False for testing purposes.
|
||||
raise_on_unexpected_status: Whether or not to raise an errors.UnexpectedStatus if the API returns a
|
||||
status code that was not documented in the source OpenAPI document.
|
||||
"""
|
||||
|
||||
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)
|
||||
raise_on_unexpected_status: bool = attr.ib(False, kw_only=True)
|
||||
|
||||
def get_headers(self) -> Dict[str, str]:
|
||||
""" Get headers to be used in all endpoints """
|
||||
|
@ -39,7 +52,10 @@ class AuthenticatedClient(Client):
|
|||
""" A Client which has been authenticated for use on secured endpoints """
|
||||
|
||||
token: str
|
||||
prefix: str = "Bearer"
|
||||
auth_header_name: str = "Authorization"
|
||||
|
||||
def get_headers(self) -> Dict[str, str]:
|
||||
""" Get headers to be used in authenticated endpoints """
|
||||
return {"Authorization": f"Bearer {self.token}", **self.headers}
|
||||
"""Get headers to be used in authenticated endpoints"""
|
||||
auth_header_value = f"{self.prefix} {self.token}" if self.prefix else self.token
|
||||
return {self.auth_header_name: auth_header_value, **self.headers}
|
||||
|
|
|
@ -83,15 +83,15 @@ params = {k: v for k, v in params.items() if v is not UNSET and v is not None}
|
|||
{{ parameter.to_string() }},
|
||||
{% endfor %}
|
||||
*,
|
||||
{# Proper _client based on whether or not the endpoint requires authentication #}
|
||||
{# 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 }},
|
||||
{% if endpoint.form_body %}
|
||||
form_data: {{ endpoint.form_body.get_type_string() }},
|
||||
{% endif %}
|
||||
{# Multipart data if any #}
|
||||
{% if endpoint.multipart_body %}
|
||||
|
@ -120,7 +120,7 @@ json_body: {{ endpoint.json_body.get_type_string() }},
|
|||
{{ parameter.python_name }}={{ parameter.python_name }},
|
||||
{% endfor %}
|
||||
_client=_client,
|
||||
{% if endpoint.form_body_class %}
|
||||
{% if endpoint.form_body %}
|
||||
form_data=form_data,
|
||||
{% endif %}
|
||||
{% if endpoint.multipart_body %}
|
||||
|
@ -159,6 +159,10 @@ Args:
|
|||
{% endfor %}
|
||||
|
||||
{% endif %}
|
||||
Raises:
|
||||
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
|
||||
httpx.TimeoutException: If the request takes longer than Client.timeout.
|
||||
|
||||
Returns:
|
||||
Response[{{ return_string }}]
|
||||
"""
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
from http import HTTPStatus
|
||||
from typing import Any, Dict, List, Optional, Union, cast
|
||||
|
||||
import httpx
|
||||
|
||||
from ...client import AuthenticatedClient, Client
|
||||
from ...types import Response, UNSET
|
||||
from ... import errors
|
||||
|
||||
{% for relative in endpoint.relative_imports %}
|
||||
{{ relative }}
|
||||
|
@ -44,7 +46,7 @@ def _get_kwargs(
|
|||
"headers": headers,
|
||||
"cookies": cookies,
|
||||
"timeout": _client.get_timeout(),
|
||||
{% if endpoint.form_body_class %}
|
||||
{% if endpoint.form_body %}
|
||||
"data": form_data.to_dict(),
|
||||
{% elif endpoint.multipart_body %}
|
||||
"files": {{ "multipart_" + endpoint.multipart_body.python_name }},
|
||||
|
@ -57,32 +59,32 @@ def _get_kwargs(
|
|||
}
|
||||
|
||||
|
||||
{% if parsed_responses %}
|
||||
def _parse_response(*, response: httpx.Response) -> Optional[{{ return_string }}]:
|
||||
def _parse_response(*, client: Client, 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 response.status_code == HTTPStatus.{{ response.status_code.name }}:
|
||||
{% if parsed_responses %}{% 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 }}
|
||||
{% else %}
|
||||
return None
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
return None
|
||||
{% endif %}
|
||||
if client.raise_on_unexpected_status:
|
||||
raise errors.UnexpectedStatus(f"Unexpected status code: {response.status_code}")
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
def _build_response(*, response: httpx.Response) -> Response[{{ return_string }}]:
|
||||
def _build_response(*, client: Client, response: httpx.Response) -> Response[{{ return_string }}]:
|
||||
return Response(
|
||||
status_code=response.status_code,
|
||||
status_code=HTTPStatus(response.status_code),
|
||||
content=response.content,
|
||||
headers=response.headers,
|
||||
{% if parsed_responses %}
|
||||
parsed=_parse_response(response=response),
|
||||
{% else %}
|
||||
parsed=None,
|
||||
{% endif %}
|
||||
parsed=_parse_response(client=client, response=response),
|
||||
)
|
||||
|
||||
|
||||
|
@ -100,7 +102,7 @@ def sync_detailed(
|
|||
**kwargs,
|
||||
)
|
||||
|
||||
return _build_response(response=response)
|
||||
return _build_response(client=_client, response=response)
|
||||
|
||||
{% if parsed_responses %}
|
||||
def sync(
|
||||
|
@ -127,7 +129,7 @@ async def asyncio_detailed(
|
|||
**kwargs
|
||||
)
|
||||
|
||||
return _build_response(response=response)
|
||||
return _build_response(client=_client, response=response)
|
||||
|
||||
{% if parsed_responses %}
|
||||
async def asyncio(
|
||||
|
|
7
specs/api_template/errors.py.jinja
Normal file
7
specs/api_template/errors.py.jinja
Normal file
|
@ -0,0 +1,7 @@
|
|||
""" Contains shared errors types that can be raised from API functions """
|
||||
|
||||
class UnexpectedStatus(Exception):
|
||||
""" Raised by api functions when the response status an undocumented status and Client.raise_on_unexpected_status is True """
|
||||
...
|
||||
|
||||
__all__ = ["UnexpectedStatus"]
|
|
@ -1,4 +1,4 @@
|
|||
from typing import Any, Dict, Type, TypeVar, Tuple, Optional, BinaryIO, TextIO
|
||||
from typing import Any, Dict, Type, TypeVar, Tuple, Optional, BinaryIO, TextIO, TYPE_CHECKING
|
||||
|
||||
{% if model.additional_properties %}
|
||||
from typing import List
|
||||
|
@ -16,9 +16,16 @@ from ..types import UNSET, Unset
|
|||
{{ relative }}
|
||||
{% endfor %}
|
||||
|
||||
{% for lazy_import in model.lazy_imports %}
|
||||
{% if loop.first %}
|
||||
if TYPE_CHECKING:
|
||||
{% endif %}
|
||||
{{ lazy_import }}
|
||||
{% endfor %}
|
||||
|
||||
|
||||
{% if model.additional_properties %}
|
||||
{% set additional_property_type = 'Any' if model.additional_properties == True else model.additional_properties.get_type_string() %}
|
||||
{% set additional_property_type = 'Any' if model.additional_properties == True else model.additional_properties.get_type_string(quoted=not model.additional_properties.is_base_type) %}
|
||||
{% endif %}
|
||||
|
||||
{% set class_name = model.class_info.name %}
|
||||
|
@ -85,7 +92,7 @@ field_dict: Dict[str, Any] = {}
|
|||
{% 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) }}
|
||||
{{ prop_template.transform(model.additional_properties, "prop", "field_dict[prop_name]", multipart=multipart, declare_type=false) | indent(4) }}
|
||||
{% elif multipart %}
|
||||
field_dict.update({
|
||||
key: (None, str(value).encode(), "text/plain")
|
||||
|
@ -113,6 +120,9 @@ return field_dict
|
|||
{% endmacro %}
|
||||
|
||||
def to_dict(self) -> Dict[str, Any]:
|
||||
{% for lazy_import in model.lazy_imports %}
|
||||
{{ lazy_import }}
|
||||
{% endfor %}
|
||||
{{ _to_dict() | indent(8) }}
|
||||
|
||||
{% if model.is_multipart_body %}
|
||||
|
@ -122,6 +132,9 @@ return field_dict
|
|||
|
||||
@classmethod
|
||||
def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
|
||||
{% for lazy_import in model.lazy_imports %}
|
||||
{{ lazy_import }}
|
||||
{% endfor %}
|
||||
_d = src_dict.copy()
|
||||
{% for property in model.required_properties + model.optional_properties %}
|
||||
{% if property.required %}
|
||||
|
@ -146,12 +159,18 @@ return field_dict
|
|||
{% 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 %}
|
||||
|
||||
{% if model.additional_properties.lazy_imports %}
|
||||
{% for lazy_import in model.additional_properties.lazy_imports %}
|
||||
{{ lazy_import }}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% else %}
|
||||
{% set prop_template = None %}
|
||||
{% endif %}
|
||||
{% if prop_template and prop_template.construct %}
|
||||
additional_properties = {}
|
||||
for prop_name, prop_dict in _d.items():
|
||||
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 }}
|
||||
|
||||
|
|
|
@ -3,3 +3,11 @@
|
|||
{% for import in imports | sort %}
|
||||
{{ import }}
|
||||
{% endfor %}
|
||||
|
||||
{% if imports %}
|
||||
__all__ = (
|
||||
{% for all in alls | sort %}
|
||||
"{{ all }}",
|
||||
{% endfor %}
|
||||
)
|
||||
{% endif %}
|
||||
|
|
|
@ -1,2 +1,7 @@
|
|||
""" {{ package_description }} """
|
||||
from .client import AuthenticatedClient, Client
|
||||
|
||||
__all__ = (
|
||||
"AuthenticatedClient",
|
||||
"Client",
|
||||
)
|
||||
|
|
|
@ -33,3 +33,7 @@ if not isinstance({{ source }}, Unset):
|
|||
{% endif %}
|
||||
{% endif %}
|
||||
{% endmacro %}
|
||||
|
||||
{% macro transform_header(property, source, destination) %}
|
||||
{{ destination }} = str({{ source }})
|
||||
{% endmacro %}
|
||||
|
|
|
@ -40,24 +40,24 @@ def _parse_{{ property.python_name }}(data: object) -> {{ property.get_type_stri
|
|||
{% 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 %}
|
||||
{% set ns = namespace(contains_properties_without_transform = false, contains_modified_properties = not property.required, has_if = false) %}
|
||||
{% if declare_type %}{{ destination }}: {{ property.get_type_string(json=True) }}{% endif %}
|
||||
|
||||
{% if not property.required %}
|
||||
if isinstance({{ source }}, Unset):
|
||||
{{ destination }} = UNSET
|
||||
{% endif %}
|
||||
{% set ns.has_if = true %}
|
||||
{% endif %}
|
||||
{% if property.nullable %}
|
||||
{% if property.required %}
|
||||
if {{ source }} is None:
|
||||
{% else %}{# There's an if UNSET statement before this #}
|
||||
{% if ns.has_if %}
|
||||
elif {{ source }} is None:
|
||||
{% else %}
|
||||
if {{ source }} is None:
|
||||
{% set ns.has_if = true %}
|
||||
{% 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 %}
|
||||
|
@ -66,8 +66,9 @@ elif {{ source }} is None:
|
|||
{% 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 not ns.has_if %}
|
||||
if isinstance({{ source }}, {{ inner_property.get_instance_type_string() }}):
|
||||
{% set ns.has_if = true %}
|
||||
{% elif not loop.last or ns.contains_properties_without_transform %}
|
||||
elif isinstance({{ source }}, {{ inner_property.get_instance_type_string() }}):
|
||||
{% else %}
|
||||
|
|
|
@ -14,7 +14,7 @@ include = ["CHANGELOG.md", "{{ package_name }}/py.typed"]
|
|||
|
||||
[tool.poetry.dependencies]
|
||||
python = "^3.7"
|
||||
httpx = ">=0.15.4,<0.23.0"
|
||||
httpx = ">=0.15.4,<0.24.0"
|
||||
attrs = ">=21.3.0"
|
||||
python-dateutil = "^2.8.0"
|
||||
|
||||
|
|
|
@ -13,6 +13,6 @@ setup(
|
|||
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"],
|
||||
install_requires=["httpx >= 0.15.0, < 0.24.0", "attrs >= 21.3.0", "python-dateutil >= 2.8.0, < 3"],
|
||||
package_data={"{{ package_name }}": ["py.typed"]},
|
||||
)
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
""" Contains some shared types for properties """
|
||||
from http import HTTPStatus
|
||||
from typing import Any, BinaryIO, Generic, MutableMapping, Optional, Tuple, TypeVar
|
||||
|
||||
import attr
|
||||
|
@ -35,7 +36,7 @@ T = TypeVar("T")
|
|||
class Response(Generic[T]):
|
||||
""" A response from an endpoint """
|
||||
|
||||
status_code: int
|
||||
status_code: HTTPStatus
|
||||
content: bytes
|
||||
headers: MutableMapping[str, str]
|
||||
parsed: Optional[T]
|
||||
|
|
5630
specs/hydra.yaml
5630
specs/hydra.yaml
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue