openapi-python-client 0.29.0__py3-none-any.whl → 0.29.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- openapi_python_client/__init__.py +26 -17
- openapi_python_client/cli.py +1 -2
- openapi_python_client/config.py +3 -1
- openapi_python_client/parser/bodies.py +8 -7
- openapi_python_client/parser/openapi.py +58 -48
- openapi_python_client/parser/properties/__init__.py +34 -33
- openapi_python_client/parser/properties/any.py +11 -10
- openapi_python_client/parser/properties/boolean.py +13 -13
- openapi_python_client/parser/properties/const.py +11 -10
- openapi_python_client/parser/properties/date.py +12 -11
- openapi_python_client/parser/properties/datetime.py +12 -11
- openapi_python_client/parser/properties/enum_property.py +23 -20
- openapi_python_client/parser/properties/file.py +11 -10
- openapi_python_client/parser/properties/float.py +13 -13
- openapi_python_client/parser/properties/int.py +11 -11
- openapi_python_client/parser/properties/list_property.py +22 -22
- openapi_python_client/parser/properties/literal_enum_property.py +20 -20
- openapi_python_client/parser/properties/merge_properties.py +9 -6
- openapi_python_client/parser/properties/model_property.py +34 -33
- openapi_python_client/parser/properties/none.py +11 -11
- openapi_python_client/parser/properties/protocol.py +42 -32
- openapi_python_client/parser/properties/schemas.py +30 -23
- openapi_python_client/parser/properties/string.py +10 -11
- openapi_python_client/parser/properties/union.py +24 -22
- openapi_python_client/parser/properties/uuid.py +11 -11
- openapi_python_client/parser/responses.py +30 -23
- openapi_python_client/schema/__init__.py +2 -1
- openapi_python_client/schema/openapi_schema_pydantic/callback.py +3 -1
- openapi_python_client/schema/openapi_schema_pydantic/components.py +10 -9
- openapi_python_client/schema/openapi_schema_pydantic/contact.py +5 -3
- openapi_python_client/schema/openapi_schema_pydantic/discriminator.py +4 -2
- openapi_python_client/schema/openapi_schema_pydantic/encoding.py +4 -3
- openapi_python_client/schema/openapi_schema_pydantic/example.py +5 -3
- openapi_python_client/schema/openapi_schema_pydantic/external_documentation.py +4 -2
- openapi_python_client/schema/openapi_schema_pydantic/header.py +20 -6
- openapi_python_client/schema/openapi_schema_pydantic/info.py +5 -4
- openapi_python_client/schema/openapi_schema_pydantic/license.py +4 -2
- openapi_python_client/schema/openapi_schema_pydantic/link.py +5 -4
- openapi_python_client/schema/openapi_schema_pydantic/media_type.py +3 -2
- openapi_python_client/schema/openapi_schema_pydantic/oauth_flow.py +6 -4
- openapi_python_client/schema/openapi_schema_pydantic/open_api.py +5 -3
- openapi_python_client/schema/openapi_schema_pydantic/operation.py +6 -5
- openapi_python_client/schema/openapi_schema_pydantic/parameter.py +6 -5
- openapi_python_client/schema/openapi_schema_pydantic/path_item.py +5 -3
- openapi_python_client/schema/openapi_schema_pydantic/paths.py +2 -1
- openapi_python_client/schema/openapi_schema_pydantic/reference.py +3 -1
- openapi_python_client/schema/openapi_schema_pydantic/request_body.py +3 -2
- openapi_python_client/schema/openapi_schema_pydantic/response.py +5 -4
- openapi_python_client/schema/openapi_schema_pydantic/responses.py +2 -1
- openapi_python_client/schema/openapi_schema_pydantic/schema.py +7 -6
- openapi_python_client/schema/openapi_schema_pydantic/security_requirement.py +3 -1
- openapi_python_client/schema/openapi_schema_pydantic/security_scheme.py +8 -7
- openapi_python_client/schema/openapi_schema_pydantic/server.py +4 -3
- openapi_python_client/schema/openapi_schema_pydantic/server_variable.py +5 -3
- openapi_python_client/schema/openapi_schema_pydantic/tag.py +3 -2
- openapi_python_client/schema/openapi_schema_pydantic/xml.py +5 -3
- openapi_python_client/schema/ref.py +52 -0
- openapi_python_client/schema/untrusted_string.py +56 -0
- openapi_python_client/strings.py +245 -0
- openapi_python_client/templates/README.md.jinja +3 -3
- openapi_python_client/templates/client.py.jinja +5 -5
- openapi_python_client/templates/endpoint_macros.py.jinja +18 -18
- openapi_python_client/templates/endpoint_module.py.jinja +13 -13
- openapi_python_client/templates/helpers.jinja +3 -7
- openapi_python_client/templates/literal_enum.py.jinja +1 -1
- openapi_python_client/templates/model.py.jinja +18 -18
- openapi_python_client/templates/package_init.py.jinja +2 -2
- openapi_python_client/templates/property_templates/const_property.py.jinja +3 -3
- openapi_python_client/templates/property_templates/date_property.py.jinja +1 -1
- openapi_python_client/templates/property_templates/datetime_property.py.jinja +1 -1
- openapi_python_client/templates/property_templates/enum_property.py.jinja +1 -1
- openapi_python_client/templates/property_templates/file_property.py.jinja +1 -1
- openapi_python_client/templates/property_templates/list_property.py.jinja +3 -3
- openapi_python_client/templates/property_templates/literal_enum_property.py.jinja +2 -2
- openapi_python_client/templates/property_templates/model_property.py.jinja +1 -1
- openapi_python_client/templates/property_templates/property_macros.py.jinja +1 -1
- openapi_python_client/templates/property_templates/union_property.py.jinja +9 -9
- openapi_python_client/templates/property_templates/uuid_property.py.jinja +1 -1
- openapi_python_client/templates/pyproject_pdm.toml.jinja +2 -2
- openapi_python_client/templates/pyproject_poetry.toml.jinja +2 -2
- openapi_python_client/templates/pyproject_uv.toml.jinja +3 -3
- openapi_python_client/templates/setup.py.jinja +2 -2
- openapi_python_client/templates/str_enum.py.jinja +2 -2
- {openapi_python_client-0.29.0.dist-info → openapi_python_client-0.29.1.dist-info}/METADATA +4 -5
- openapi_python_client-0.29.1.dist-info/RECORD +115 -0
- {openapi_python_client-0.29.0.dist-info → openapi_python_client-0.29.1.dist-info}/WHEEL +1 -1
- openapi_python_client/utils.py +0 -122
- openapi_python_client-0.29.0.dist-info/RECORD +0 -113
- {openapi_python_client-0.29.0.dist-info → openapi_python_client-0.29.1.dist-info}/entry_points.txt +0 -0
- {openapi_python_client-0.29.0.dist-info → openapi_python_client-0.29.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{% from "property_templates/helpers.jinja" import guarded_statement %}
|
|
2
|
-
{% from "helpers.jinja" import
|
|
2
|
+
{% from "helpers.jinja" import docstring as _docstring %}
|
|
3
3
|
|
|
4
4
|
{% macro header_params(endpoint) %}
|
|
5
5
|
{% if endpoint.header_parameters or endpoint.bodies | length > 0 %}
|
|
@@ -12,7 +12,7 @@ headers: dict[str, Any] = {}
|
|
|
12
12
|
{% else %}
|
|
13
13
|
{% set expression = parameter.python_name %}
|
|
14
14
|
{% endif %}
|
|
15
|
-
{% set statement = 'headers["' +
|
|
15
|
+
{% set statement = 'headers["' + (parameter.name | in_double_quote_literal) + '"]' + " = " + expression %}
|
|
16
16
|
{{ guarded_statement(parameter, parameter.python_name, statement) }}
|
|
17
17
|
{% endfor %}
|
|
18
18
|
{% endif %}
|
|
@@ -24,10 +24,10 @@ headers: dict[str, Any] = {}
|
|
|
24
24
|
cookies = {}
|
|
25
25
|
{% for parameter in endpoint.cookie_parameters %}
|
|
26
26
|
{% if parameter.required %}
|
|
27
|
-
cookies["{{ parameter.name}}"] = {{ parameter.python_name }}
|
|
27
|
+
cookies["{{ parameter.name | in_double_quote_literal }}"] = {{ parameter.python_name }}
|
|
28
28
|
{% else %}
|
|
29
29
|
if {{ parameter.python_name }} is not UNSET:
|
|
30
|
-
cookies["{{ parameter.name}}"] = {{ parameter.python_name }}
|
|
30
|
+
cookies["{{ parameter.name | in_double_quote_literal }}"] = {{ parameter.python_name }}
|
|
31
31
|
{% endif %}
|
|
32
32
|
|
|
33
33
|
{% endfor %}
|
|
@@ -47,7 +47,7 @@ params: dict[str, Any] = {}
|
|
|
47
47
|
{{ prop_template.transform(property, property.python_name, destination) }}
|
|
48
48
|
{% endif %}
|
|
49
49
|
{%- if not property.json_is_dict %}
|
|
50
|
-
params["{{ property.name }}"] = {{ destination }}
|
|
50
|
+
params["{{ property.name | in_double_quote_literal }}"] = {{ destination }}
|
|
51
51
|
{% else %}
|
|
52
52
|
{{ guarded_statement(property, destination, "params.update(" + destination + ")") }}
|
|
53
53
|
{% endif %}
|
|
@@ -105,7 +105,7 @@ if not isinstance({{property.python_name}}, Unset):
|
|
|
105
105
|
{% macro arguments(endpoint, include_client=True) %}
|
|
106
106
|
{# path parameters #}
|
|
107
107
|
{% for parameter in endpoint.path_parameters %}
|
|
108
|
-
{{ parameter.to_string() }},
|
|
108
|
+
{{ parameter.to_string() | as_unembedded_code }},
|
|
109
109
|
{% endfor %}
|
|
110
110
|
{% if include_client or ((endpoint.list_all_parameters() | length) > (endpoint.path_parameters | length)) %}
|
|
111
111
|
*,
|
|
@@ -120,24 +120,24 @@ client: AuthenticatedClient | Client,
|
|
|
120
120
|
{% endif %}
|
|
121
121
|
{# Any allowed bodies #}
|
|
122
122
|
{% if endpoint.bodies | length == 1 %}
|
|
123
|
-
body: {{ endpoint.bodies[0].prop.get_type_string() }}{% if not endpoint.bodies[0].prop.required %} = UNSET{% endif %},
|
|
123
|
+
body: {{ endpoint.bodies[0].prop.get_type_string() | as_unembedded_code }}{% if not endpoint.bodies[0].prop.required %} = UNSET{% endif %},
|
|
124
124
|
{% elif endpoint.bodies | length > 1 %}
|
|
125
125
|
body:
|
|
126
126
|
{%- for body in endpoint.bodies -%}{% set body_required = body_required and body.prop.required %}
|
|
127
|
-
{{ body.prop.get_type_string(no_optional=True) }} {% if not loop.last %} | {% endif %}
|
|
127
|
+
{{ body.prop.get_type_string(no_optional=True) | as_unembedded_code }} {% if not loop.last %} | {% endif %}
|
|
128
128
|
{%- endfor -%}{% if not body_required %} | Unset = UNSET{% endif %}
|
|
129
129
|
,
|
|
130
130
|
{% endif %}
|
|
131
131
|
{# query parameters #}
|
|
132
132
|
{% for parameter in endpoint.query_parameters %}
|
|
133
|
-
{{ parameter.to_string() }},
|
|
133
|
+
{{ parameter.to_string() | as_unembedded_code }},
|
|
134
134
|
{% endfor %}
|
|
135
135
|
{% for parameter in endpoint.header_parameters %}
|
|
136
|
-
{{ parameter.to_string() }},
|
|
136
|
+
{{ parameter.to_string() | as_unembedded_code }},
|
|
137
137
|
{% endfor %}
|
|
138
138
|
{# cookie parameters #}
|
|
139
139
|
{% for parameter in endpoint.cookie_parameters %}
|
|
140
|
-
{{ parameter.to_string() }},
|
|
140
|
+
{{ parameter.to_string() | as_unembedded_code }},
|
|
141
141
|
{% endfor %}
|
|
142
142
|
{% endmacro %}
|
|
143
143
|
|
|
@@ -164,10 +164,10 @@ body=body,
|
|
|
164
164
|
{% endmacro %}
|
|
165
165
|
|
|
166
166
|
{% macro docstring_content(endpoint, return_string, is_detailed) %}
|
|
167
|
-
{% if endpoint.summary %}{{ endpoint.summary | wordwrap(100)}}
|
|
167
|
+
{% if endpoint.summary %}{{ endpoint.summary | safe_for_docstring | wordwrap(100)}}
|
|
168
168
|
|
|
169
169
|
{% endif -%}
|
|
170
|
-
{%- if endpoint.description %} {{ endpoint.description | wordwrap(100) }}
|
|
170
|
+
{%- if endpoint.description %} {{ endpoint.description | safe_for_docstring | wordwrap(100) }}
|
|
171
171
|
|
|
172
172
|
{% endif %}
|
|
173
173
|
{% if not endpoint.summary and not endpoint.description %}
|
|
@@ -188,24 +188,24 @@ Raises:
|
|
|
188
188
|
|
|
189
189
|
Returns:
|
|
190
190
|
{% if is_detailed %}
|
|
191
|
-
Response[{{ return_string }}]
|
|
191
|
+
Response[{{ return_string | safe_for_docstring }}]
|
|
192
192
|
{% else %}
|
|
193
|
-
{{ return_string }}
|
|
193
|
+
{{ return_string | safe_for_docstring }}
|
|
194
194
|
{% endif %}
|
|
195
195
|
{% endmacro %}
|
|
196
196
|
|
|
197
197
|
{% macro docstring(endpoint, return_string, is_detailed) %}
|
|
198
|
-
{{
|
|
198
|
+
{{ _docstring(docstring_content(endpoint, return_string, is_detailed)) }}
|
|
199
199
|
{% endmacro %}
|
|
200
200
|
|
|
201
201
|
{% macro parse_response(parsed_responses, response) %}
|
|
202
202
|
{% if parsed_responses %}{% import "property_templates/" + response.prop.template as prop_template %}
|
|
203
203
|
{% if prop_template.construct %}
|
|
204
204
|
{{ prop_template.construct(response.prop, response.source.attribute) }}
|
|
205
|
-
{% elif response.source.return_type == response.prop.get_type_string() %}
|
|
205
|
+
{% elif response.source.return_type == response.prop.get_type_string() | as_unembedded_code %}
|
|
206
206
|
{{ response.prop.python_name }} = {{ response.source.attribute }}
|
|
207
207
|
{% else %}
|
|
208
|
-
{{ response.prop.python_name }} = cast({{ response.prop.get_type_string() }}, {{ response.source.attribute }})
|
|
208
|
+
{{ response.prop.python_name }} = cast({{ response.prop.get_type_string() | as_unembedded_code }}, {{ response.source.attribute }})
|
|
209
209
|
{% endif %}
|
|
210
210
|
return {{ response.prop.python_name }}
|
|
211
211
|
{% else %}
|
|
@@ -16,7 +16,7 @@ from ... import errors
|
|
|
16
16
|
arguments, client, kwargs, parse_response, docstring, body_to_kwarg %}
|
|
17
17
|
|
|
18
18
|
{% set return_string = endpoint.response_type() %}
|
|
19
|
-
{% set parsed_responses = (endpoint.responses | length > 0) and return_string != "Any" %}
|
|
19
|
+
{% set parsed_responses = (endpoint.responses | length > 0) and return_string != PythonCode("Any") %}
|
|
20
20
|
|
|
21
21
|
def _get_kwargs(
|
|
22
22
|
{{ arguments(endpoint, include_client=False) | indent(4) }}
|
|
@@ -30,13 +30,13 @@ def _get_kwargs(
|
|
|
30
30
|
_kwargs: dict[str, Any] = {
|
|
31
31
|
"method": "{{ endpoint.method }}",
|
|
32
32
|
{% if endpoint.path_parameters %}
|
|
33
|
-
"url": "{{ endpoint.path }}".format(
|
|
33
|
+
"url": "{{ endpoint.path | in_double_quote_literal }}".format(
|
|
34
34
|
{%- for parameter in endpoint.path_parameters -%}
|
|
35
35
|
{{parameter.python_name}}=quote(str({{parameter.python_name}}), safe=""),
|
|
36
36
|
{%- endfor -%}
|
|
37
37
|
),
|
|
38
38
|
{% else %}
|
|
39
|
-
"url": "{{ endpoint.path }}",
|
|
39
|
+
"url": "{{ endpoint.path | in_double_quote_literal }}",
|
|
40
40
|
{% endif %}
|
|
41
41
|
{% if endpoint.query_parameters %}
|
|
42
42
|
"params": params,
|
|
@@ -48,12 +48,12 @@ def _get_kwargs(
|
|
|
48
48
|
|
|
49
49
|
{% if endpoint.bodies | length > 1 %}
|
|
50
50
|
{% for body in endpoint.bodies %}
|
|
51
|
-
if isinstance(body, {{body.prop.get_type_string(no_optional=True) }}):
|
|
51
|
+
if isinstance(body, {{body.prop.get_type_string(no_optional=True) | as_unembedded_code }}):
|
|
52
52
|
{{ body_to_kwarg(body) | indent(8) }}
|
|
53
53
|
{%- if body.content_type == "multipart/form-data" %}
|
|
54
54
|
headers["Content-Type"] = "multipart/form-data; boundary=+++"
|
|
55
55
|
{% else %}
|
|
56
|
-
headers["Content-Type"] = "{{ body.content_type }}"
|
|
56
|
+
headers["Content-Type"] = "{{ body.content_type | in_double_quote_literal }}"
|
|
57
57
|
{% endif %}
|
|
58
58
|
{% endfor %}
|
|
59
59
|
{% elif endpoint.bodies | length == 1 %}
|
|
@@ -62,7 +62,7 @@ def _get_kwargs(
|
|
|
62
62
|
{%- if body.content_type == "multipart/form-data" %}
|
|
63
63
|
headers["Content-Type"] = "multipart/form-data; boundary=+++"
|
|
64
64
|
{% else %}
|
|
65
|
-
headers["Content-Type"] = "{{ body.content_type }}"
|
|
65
|
+
headers["Content-Type"] = "{{ body.content_type | in_double_quote_literal }}"
|
|
66
66
|
{% endif %}
|
|
67
67
|
{% endif %}
|
|
68
68
|
|
|
@@ -72,9 +72,9 @@ def _get_kwargs(
|
|
|
72
72
|
return _kwargs
|
|
73
73
|
|
|
74
74
|
{% if endpoint.responses.default %}
|
|
75
|
-
{% set return_type = return_string %}
|
|
75
|
+
{% set return_type = return_string | as_unembedded_code %}
|
|
76
76
|
{% else %}
|
|
77
|
-
{% set return_type = return_string + " | None" %}
|
|
77
|
+
{% set return_type = (return_string | as_unembedded_code) + " | None" %}
|
|
78
78
|
{% endif %}
|
|
79
79
|
|
|
80
80
|
|
|
@@ -98,7 +98,7 @@ def _parse_response(*, client: AuthenticatedClient | Client, response: httpx.Res
|
|
|
98
98
|
{% endif %}
|
|
99
99
|
|
|
100
100
|
|
|
101
|
-
def _build_response(*, client: AuthenticatedClient | Client, response: httpx.Response) -> Response[{{ return_string }}]:
|
|
101
|
+
def _build_response(*, client: AuthenticatedClient | Client, response: httpx.Response) -> Response[{{ return_string | as_unembedded_code }}]:
|
|
102
102
|
return Response(
|
|
103
103
|
status_code=HTTPStatus(response.status_code),
|
|
104
104
|
content=response.content,
|
|
@@ -109,7 +109,7 @@ def _build_response(*, client: AuthenticatedClient | Client, response: httpx.Res
|
|
|
109
109
|
|
|
110
110
|
def sync_detailed(
|
|
111
111
|
{{ arguments(endpoint) | indent(4) }}
|
|
112
|
-
) -> Response[{{ return_string }}]:
|
|
112
|
+
) -> Response[{{ return_string | as_unembedded_code }}]:
|
|
113
113
|
{{ docstring(endpoint, return_string, is_detailed=true) | indent(4) }}
|
|
114
114
|
|
|
115
115
|
kwargs = _get_kwargs(
|
|
@@ -125,7 +125,7 @@ def sync_detailed(
|
|
|
125
125
|
{% if parsed_responses %}
|
|
126
126
|
def sync(
|
|
127
127
|
{{ arguments(endpoint) | indent(4) }}
|
|
128
|
-
) -> {{ return_string }} | None:
|
|
128
|
+
) -> {{ return_string | as_unembedded_code }} | None:
|
|
129
129
|
{{ docstring(endpoint, return_string, is_detailed=false) | indent(4) }}
|
|
130
130
|
|
|
131
131
|
return sync_detailed(
|
|
@@ -135,7 +135,7 @@ def sync(
|
|
|
135
135
|
|
|
136
136
|
async def asyncio_detailed(
|
|
137
137
|
{{ arguments(endpoint) | indent(4) }}
|
|
138
|
-
) -> Response[{{ return_string }}]:
|
|
138
|
+
) -> Response[{{ return_string | as_unembedded_code }}]:
|
|
139
139
|
{{ docstring(endpoint, return_string, is_detailed=true) | indent(4) }}
|
|
140
140
|
|
|
141
141
|
kwargs = _get_kwargs(
|
|
@@ -151,7 +151,7 @@ async def asyncio_detailed(
|
|
|
151
151
|
{% if parsed_responses %}
|
|
152
152
|
async def asyncio(
|
|
153
153
|
{{ arguments(endpoint) | indent(4) }}
|
|
154
|
-
) -> {{ return_string }} | None:
|
|
154
|
+
) -> {{ return_string | as_unembedded_code }} | None:
|
|
155
155
|
{{ docstring(endpoint, return_string, is_detailed=false) | indent(4) }}
|
|
156
156
|
|
|
157
157
|
return (await asyncio_detailed(
|
|
@@ -1,10 +1,6 @@
|
|
|
1
|
-
{% macro
|
|
2
|
-
{# This macro returns the provided content as a docstring
|
|
3
|
-
{% if (
|
|
4
|
-
{% if '\\' in content -%}
|
|
5
|
-
r""" {{ content }} """
|
|
6
|
-
{%- else -%}
|
|
1
|
+
{% macro docstring(content) %}
|
|
2
|
+
{# This macro returns the provided content as a docstring unless it's empty, then it emits nothing. #}
|
|
3
|
+
{% if (content | trim) %}
|
|
7
4
|
""" {{ content }} """
|
|
8
5
|
{%- endif -%}
|
|
9
|
-
{% endif %}
|
|
10
6
|
{% endmacro %}
|
|
@@ -4,7 +4,7 @@ from typing import Literal
|
|
|
4
4
|
|
|
5
5
|
{{ enum.get_class_name_snake_case() | upper }}_VALUES: set[{{ enum.class_info.name }}] = { {% for v in enum.values|list|sort %}{{"%r"|format(v)}}, {% endfor %} }
|
|
6
6
|
|
|
7
|
-
def check_{{ enum.get_class_name_snake_case() }}(value: {{ enum.get_instance_type_string() }}) -> {{ enum.class_info.name}}:
|
|
7
|
+
def check_{{ enum.get_class_name_snake_case() }}(value: {{ enum.get_instance_type_string() | as_unembedded_code }}) -> {{ enum.class_info.name}}:
|
|
8
8
|
if value in {{ enum.get_class_name_snake_case() | upper }}_VALUES:
|
|
9
9
|
return value
|
|
10
10
|
raise TypeError(f"Unexpected value {value!r}. Expected one of {{"{"}}{{ enum.get_class_name_snake_case() | upper }}_VALUES!r}")
|
|
@@ -25,21 +25,21 @@ if TYPE_CHECKING:
|
|
|
25
25
|
|
|
26
26
|
|
|
27
27
|
{% if model.additional_properties %}
|
|
28
|
-
{% set additional_property_type = 'Any' if model.additional_properties == True else model.additional_properties.get_type_string() %}
|
|
28
|
+
{% set additional_property_type = 'Any' if model.additional_properties == True else model.additional_properties.get_type_string() | as_unembedded_code %}
|
|
29
29
|
{% endif %}
|
|
30
30
|
|
|
31
31
|
{% set class_name = model.class_info.name %}
|
|
32
32
|
{% set module_name = model.class_info.module_name %}
|
|
33
33
|
|
|
34
|
-
{% from "helpers.jinja" import
|
|
34
|
+
{% from "helpers.jinja" import docstring %}
|
|
35
35
|
|
|
36
36
|
T = TypeVar("T", bound="{{ class_name }}")
|
|
37
37
|
|
|
38
38
|
{% macro class_docstring_content(model) %}
|
|
39
|
-
{% if model.title %}{{ model.title | wordwrap(116) }}
|
|
39
|
+
{% if model.title %}{{ model.title | safe_for_docstring | wordwrap(116) }}
|
|
40
40
|
|
|
41
41
|
{% endif -%}
|
|
42
|
-
{%- if model.description %}{{ model.description | wordwrap(116) }}
|
|
42
|
+
{%- if model.description %}{{ model.description | safe_for_docstring | wordwrap(116) }}
|
|
43
43
|
|
|
44
44
|
{% endif %}
|
|
45
45
|
{% if not model.title and not model.description %}
|
|
@@ -48,7 +48,7 @@ T = TypeVar("T", bound="{{ class_name }}")
|
|
|
48
48
|
{% endif %}
|
|
49
49
|
{% if model.example %}
|
|
50
50
|
Example:
|
|
51
|
-
{{ model.example |
|
|
51
|
+
{{ model.example | safe_for_docstring | wordwrap(112) | indent(12) }}
|
|
52
52
|
|
|
53
53
|
{% endif %}
|
|
54
54
|
{% if (not config.docstrings_on_attributes) and (model.required_properties or model.optional_properties) %}
|
|
@@ -60,16 +60,16 @@ T = TypeVar("T", bound="{{ class_name }}")
|
|
|
60
60
|
|
|
61
61
|
{% macro declare_property(property) %}
|
|
62
62
|
{%- if config.docstrings_on_attributes and property.description -%}
|
|
63
|
-
{{ property.to_string() }}
|
|
64
|
-
{{
|
|
63
|
+
{{ property.to_string() | as_unembedded_code }}
|
|
64
|
+
{{ docstring(property.description | safe_for_docstring) | wordwrap(112) }}
|
|
65
65
|
{%- else -%}
|
|
66
|
-
{{ property.to_string() }}
|
|
66
|
+
{{ property.to_string() | as_unembedded_code }}
|
|
67
67
|
{%- endif -%}
|
|
68
68
|
{% endmacro %}
|
|
69
69
|
|
|
70
70
|
@_attrs_define
|
|
71
71
|
class {{ class_name }}:
|
|
72
|
-
{{
|
|
72
|
+
{{ docstring(class_docstring_content(model)) | indent(4) }}
|
|
73
73
|
|
|
74
74
|
{% for property in model.required_properties + model.optional_properties %}
|
|
75
75
|
{% if property.default is none and property.required %}
|
|
@@ -128,7 +128,7 @@ field_dict.update(self.additional_properties)
|
|
|
128
128
|
field_dict.update({
|
|
129
129
|
{% for property in model.required_properties + model.optional_properties %}
|
|
130
130
|
{% if property.required %}
|
|
131
|
-
"{{ property.name }}": {{ property.python_name }},
|
|
131
|
+
"{{ property.name | in_double_quote_literal }}": {{ property.python_name }},
|
|
132
132
|
{% endif %}
|
|
133
133
|
{% endfor %}
|
|
134
134
|
})
|
|
@@ -136,7 +136,7 @@ field_dict.update({
|
|
|
136
136
|
{% for property in model.optional_properties %}
|
|
137
137
|
{% if not property.required %}
|
|
138
138
|
if {{ property.python_name }} is not UNSET:
|
|
139
|
-
field_dict["{{ property.name }}"] = {{ property.python_name }}
|
|
139
|
+
field_dict["{{ property.name | in_double_quote_literal }}"] = {{ property.python_name }}
|
|
140
140
|
{% endif %}
|
|
141
141
|
{% endfor %}
|
|
142
142
|
|
|
@@ -145,19 +145,19 @@ return field_dict
|
|
|
145
145
|
|
|
146
146
|
def to_dict(self) -> dict[str, Any]:
|
|
147
147
|
{% for lazy_import in model.lazy_imports | sort %}
|
|
148
|
-
{{ lazy_import }}
|
|
148
|
+
{{ lazy_import }} # noqa: PLC0415
|
|
149
149
|
{% endfor %}
|
|
150
150
|
{{ _to_dict() | indent(8) }}
|
|
151
151
|
|
|
152
152
|
{% if model.is_multipart_body %}
|
|
153
153
|
def to_multipart(self) -> types.RequestFiles:
|
|
154
154
|
{% for lazy_import in model.lazy_imports | sort %}
|
|
155
|
-
{{ lazy_import }}
|
|
155
|
+
{{ lazy_import }} # noqa: PLC0415
|
|
156
156
|
{% endfor %}
|
|
157
157
|
files: types.RequestFiles = []
|
|
158
158
|
|
|
159
159
|
{% for property in model.required_properties + model.optional_properties %}
|
|
160
|
-
{% set destination = "\"" + property.name + "\"" %}
|
|
160
|
+
{% set destination = "\"" + (property.name | in_double_quote_literal) + "\"" %}
|
|
161
161
|
{{ multipart(property, "self." + property.python_name, destination) | indent(8) }}
|
|
162
162
|
|
|
163
163
|
{% endfor %}
|
|
@@ -174,15 +174,15 @@ return field_dict
|
|
|
174
174
|
@classmethod
|
|
175
175
|
def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
|
|
176
176
|
{% for lazy_import in model.lazy_imports | sort %}
|
|
177
|
-
{{ lazy_import }}
|
|
177
|
+
{{ lazy_import }} # noqa: PLC0415
|
|
178
178
|
{% endfor %}
|
|
179
179
|
{% if (model.required_properties or model.optional_properties or model.additional_properties) %}
|
|
180
180
|
d = dict(src_dict)
|
|
181
181
|
{% for property in model.required_properties + model.optional_properties %}
|
|
182
182
|
{% if property.required %}
|
|
183
|
-
{% set property_source = 'd.pop("' + property.name + '")' %}
|
|
183
|
+
{% set property_source = 'd.pop("' + (property.name | in_double_quote_literal) + '")' %}
|
|
184
184
|
{% else %}
|
|
185
|
-
{% set property_source = 'd.pop("' + property.name + '", UNSET)' %}
|
|
185
|
+
{% set property_source = 'd.pop("' + (property.name | in_double_quote_literal) + '", UNSET)' %}
|
|
186
186
|
{% endif %}
|
|
187
187
|
{% import "property_templates/" + property.template as prop_template %}
|
|
188
188
|
{% if prop_template.construct %}
|
|
@@ -205,7 +205,7 @@ return field_dict
|
|
|
205
205
|
|
|
206
206
|
{% if model.additional_properties.lazy_imports %}
|
|
207
207
|
{% for lazy_import in model.additional_properties.lazy_imports | sort %}
|
|
208
|
-
{{ lazy_import }}
|
|
208
|
+
{{ lazy_import }} # noqa: PLC0415
|
|
209
209
|
{% endfor %}
|
|
210
210
|
{% endif %}
|
|
211
211
|
{% else %}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
{% from "helpers.jinja" import
|
|
1
|
+
{% from "helpers.jinja" import docstring %}
|
|
2
2
|
|
|
3
|
-
{{
|
|
3
|
+
{{ docstring(package_description | safe_for_docstring) }}
|
|
4
4
|
from .client import AuthenticatedClient, Client
|
|
5
5
|
|
|
6
6
|
__all__ = (
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{% macro construct(property, source) %}
|
|
2
|
-
{{ property.python_name }} = cast({{ property.get_type_string() }} , {{ source }})
|
|
3
|
-
if {{ property.python_name }} != {{ property.value.python_code }}{% if not property.required %}and not isinstance({{ property.python_name }}, Unset){% endif %}:
|
|
4
|
-
raise ValueError(f"{{ property.name }} must match const {{ property.value.python_code }}, got '{{'{' + property.python_name + '}' }}'")
|
|
2
|
+
{{ property.python_name }} = cast({{ property.get_type_string() | as_unembedded_code }} , {{ source }})
|
|
3
|
+
if {{ property.python_name }} != {{ property.value.python_code | as_unembedded_code }}{% if not property.required %}and not isinstance({{ property.python_name }}, Unset){% endif %}:
|
|
4
|
+
raise ValueError(f"{{ property.name | in_f_string_literal }} must match const {{ property.value.python_code | in_f_string_literal }}, got '{{'{' + property.python_name + '}' }}'")
|
|
5
5
|
{%- endmacro %}
|
|
6
6
|
|
|
7
7
|
{% macro multipart(property, source, name) %}
|
|
@@ -17,7 +17,7 @@ datetime.date.fromisoformat({{ source }})
|
|
|
17
17
|
{%- else %}
|
|
18
18
|
{% if not skip_unset %}
|
|
19
19
|
{% if declare_type %}
|
|
20
|
-
{% set type_annotation = property.get_type_string(json=True) %}
|
|
20
|
+
{% set type_annotation = property.get_type_string(json=True) | as_unembedded_code %}
|
|
21
21
|
{{ destination }}: {{ type_annotation }} = UNSET
|
|
22
22
|
{% else %}
|
|
23
23
|
{{ destination }} = UNSET
|
|
@@ -17,7 +17,7 @@ datetime.datetime.fromisoformat({{ source }})
|
|
|
17
17
|
{%- else %}
|
|
18
18
|
{% if not skip_unset %}
|
|
19
19
|
{% if declare_type %}
|
|
20
|
-
{% set type_annotation = property.get_type_string(json=True) %}
|
|
20
|
+
{% set type_annotation = property.get_type_string(json=True) | as_unembedded_code %}
|
|
21
21
|
{{ destination }}: {{ type_annotation }} = UNSET
|
|
22
22
|
{% else %}
|
|
23
23
|
{{ destination }} = UNSET
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
|
|
13
13
|
{% macro transform(property, source, destination, declare_type=True, skip_unset=False) %}
|
|
14
14
|
{% set transformed = source + ".value" %}
|
|
15
|
-
{% set type_string = property.get_type_string(json=True) %}
|
|
15
|
+
{% set type_string = property.get_type_string(json=True) | as_unembedded_code %}
|
|
16
16
|
{% if property.required %}
|
|
17
17
|
{{ destination }} = {{ transformed }}
|
|
18
18
|
{%- else %}
|
|
@@ -16,7 +16,7 @@ File(
|
|
|
16
16
|
{% if property.required %}
|
|
17
17
|
{{ destination }} = {{ source }}.to_tuple()
|
|
18
18
|
{% else %}
|
|
19
|
-
{% if not skip_unset %}{{ destination }}{% if declare_type %}: {{ property.get_type_string(json=True) }}{% endif %} = UNSET{% endif +%}
|
|
19
|
+
{% if not skip_unset %}{{ destination }}{% if declare_type %}: {{ property.get_type_string(json=True) | as_unembedded_code }}{% endif %} = UNSET{% endif +%}
|
|
20
20
|
if not isinstance({{ source }}, Unset):
|
|
21
21
|
{{ destination }} = {{ source }}.to_tuple()
|
|
22
22
|
{% endif %}
|
|
@@ -11,7 +11,7 @@ for {{ inner_source }} in (_{{ property.python_name }}):
|
|
|
11
11
|
{{ property.python_name }}.append({{ inner_property.python_name }})
|
|
12
12
|
{% else %}
|
|
13
13
|
_{{ property.python_name }} = {{ source }}
|
|
14
|
-
{{ property.python_name }}: {{ property.get_type_string() }} = UNSET
|
|
14
|
+
{{ property.python_name }}: {{ property.get_type_string() | as_unembedded_code }} = UNSET
|
|
15
15
|
if _{{ property.python_name }} is not UNSET:
|
|
16
16
|
{{ property.python_name }} = []
|
|
17
17
|
for {{ inner_source }} in _{{ property.python_name }}:
|
|
@@ -19,7 +19,7 @@ if _{{ property.python_name }} is not UNSET:
|
|
|
19
19
|
{{ property.python_name }}.append({{ inner_property.python_name }})
|
|
20
20
|
{% endif %}
|
|
21
21
|
{% else %}
|
|
22
|
-
{{ property.python_name }} = cast({{ property.get_type_string(no_optional=True) }}, {{ source }})
|
|
22
|
+
{{ property.python_name }} = cast({{ property.get_type_string(no_optional=True) | as_unembedded_code }}, {{ source }})
|
|
23
23
|
{% endif %}
|
|
24
24
|
{% endmacro %}
|
|
25
25
|
|
|
@@ -41,7 +41,7 @@ for {{ inner_source }} in {{ source }}:
|
|
|
41
41
|
|
|
42
42
|
{% macro transform(property, source, destination, declare_type=True, skip_unset=False) %}
|
|
43
43
|
{% set inner_property = property.inner_property %}
|
|
44
|
-
{% set type_string = property.get_type_string(json=True) %}
|
|
44
|
+
{% set type_string = property.get_type_string(json=True) | as_unembedded_code %}
|
|
45
45
|
{% if property.required %}
|
|
46
46
|
{{ _transform(property, source, destination, "to_dict") }}
|
|
47
47
|
{% else %}
|
|
@@ -8,10 +8,10 @@ check_{{ property.get_class_name_snake_case() }}({{ source }})
|
|
|
8
8
|
{{ construct_template(construct_function, property, source) }}
|
|
9
9
|
{% endmacro %}
|
|
10
10
|
|
|
11
|
-
{% macro check_type_for_construct(property, source) %}isinstance({{ source }}, {{ property.get_instance_type_string() }}){% endmacro %}
|
|
11
|
+
{% macro check_type_for_construct(property, source) %}isinstance({{ source }}, {{ property.get_instance_type_string() | as_unembedded_code }}){% endmacro %}
|
|
12
12
|
|
|
13
13
|
{% macro transform(property, source, destination, declare_type=True, skip_unset=False) %}
|
|
14
|
-
{% set type_string = property.get_type_string(json=True) %}
|
|
14
|
+
{% set type_string = property.get_type_string(json=True) | as_unembedded_code %}
|
|
15
15
|
{% if property.required %}
|
|
16
16
|
{{ destination }}{% if declare_type %}: {{ type_string }}{% endif %} = {{ source }}
|
|
17
17
|
{%- else %}
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
|
|
13
13
|
{% macro transform(property, source, destination, declare_type=True, skip_unset=False) %}
|
|
14
14
|
{% set transformed = source + ".to_dict()" %}
|
|
15
|
-
{% set type_string = property.get_type_string(json=True) %}
|
|
15
|
+
{% set type_string = property.get_type_string(json=True) | as_unembedded_code %}
|
|
16
16
|
{% if property.required %}
|
|
17
17
|
{{ destination }} = {{ transformed }}
|
|
18
18
|
{%- else %}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
{{ property.python_name }} = {{ construct_function(property, source) }}
|
|
4
4
|
{% else %}{# Must be non-required #}
|
|
5
5
|
_{{ property.python_name }} = {{ source }}
|
|
6
|
-
{{ property.python_name }}: {{ property.get_type_string() }}
|
|
6
|
+
{{ property.python_name }}: {{ property.get_type_string() | as_unembedded_code }}
|
|
7
7
|
{% if not property.required %}
|
|
8
8
|
if isinstance(_{{ property.python_name }}, Unset):
|
|
9
9
|
{{ property.python_name }} = UNSET
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{% macro construct(property, source) %}
|
|
2
|
-
def _parse_{{ property.python_name }}(data: object) -> {{ property.get_type_string() }}:
|
|
3
|
-
{% if "None" in property.get_type_strings_in_union(json=True) %}
|
|
2
|
+
def _parse_{{ property.python_name }}(data: object) -> {{ property.get_type_string() | as_unembedded_code }}:
|
|
3
|
+
{% if PythonCode("None") in property.get_type_strings_in_union(json=True) %}
|
|
4
4
|
if data is None:
|
|
5
5
|
return data
|
|
6
6
|
{% endif %}
|
|
7
|
-
{% if "Unset" in property.get_type_strings_in_union(json=True) %}
|
|
7
|
+
{% if PythonCode("Unset") in property.get_type_strings_in_union(json=True) %}
|
|
8
8
|
if isinstance(data, Unset):
|
|
9
9
|
return data
|
|
10
10
|
{% endif %}
|
|
@@ -33,7 +33,7 @@ def _parse_{{ property.python_name }}(data: object) -> {{ property.get_type_stri
|
|
|
33
33
|
{% endif %}
|
|
34
34
|
{% endfor %}
|
|
35
35
|
{% if ns.contains_unmodified_properties %}
|
|
36
|
-
return cast({{ property.get_type_string() }}, data)
|
|
36
|
+
return cast({{ property.get_type_string() | as_unembedded_code }}, data)
|
|
37
37
|
{% endif %}
|
|
38
38
|
|
|
39
39
|
{{ property.python_name }} = _parse_{{ property.python_name }}({{ source }})
|
|
@@ -41,7 +41,7 @@ def _parse_{{ property.python_name }}(data: object) -> {{ property.get_type_stri
|
|
|
41
41
|
|
|
42
42
|
{% macro transform(property, source, destination, declare_type=True, skip_unset=False) %}
|
|
43
43
|
{% set ns = namespace(contains_properties_without_transform = false, contains_modified_properties = not property.required, has_if = false) %}
|
|
44
|
-
{% if declare_type %}{{ destination }}: {{ property.get_type_string(json=True) }}{% endif %}
|
|
44
|
+
{% if declare_type %}{{ destination }}: {{ property.get_type_string(json=True) | as_unembedded_code }}{% endif %}
|
|
45
45
|
|
|
46
46
|
{% if not property.required and not skip_unset %}
|
|
47
47
|
if isinstance({{ source }}, Unset):
|
|
@@ -57,10 +57,10 @@ if isinstance({{ source }}, Unset):
|
|
|
57
57
|
{% set ns.contains_modified_properties = true %}
|
|
58
58
|
{% endif %}
|
|
59
59
|
{% if not ns.has_if %}
|
|
60
|
-
if isinstance({{ source }}, {{ inner_property.get_instance_type_string() }}):
|
|
60
|
+
if isinstance({{ source }}, {{ inner_property.get_instance_type_string() | as_unembedded_code }}):
|
|
61
61
|
{% set ns.has_if = true %}
|
|
62
62
|
{% elif not loop.last or ns.contains_properties_without_transform %}
|
|
63
|
-
elif isinstance({{ source }}, {{ inner_property.get_instance_type_string() }}):
|
|
63
|
+
elif isinstance({{ source }}, {{ inner_property.get_instance_type_string() | as_unembedded_code }}):
|
|
64
64
|
{% else %}
|
|
65
65
|
else:
|
|
66
66
|
{% endif %}
|
|
@@ -76,10 +76,10 @@ else:
|
|
|
76
76
|
|
|
77
77
|
|
|
78
78
|
{% macro instance_check(inner_property, source) %}
|
|
79
|
-
{% if inner_property.get_instance_type_string() == "None" %}
|
|
79
|
+
{% if inner_property.get_instance_type_string() == PythonCode("None") %}
|
|
80
80
|
if {{ source }} is None:
|
|
81
81
|
{% else %}
|
|
82
|
-
if isinstance({{ source }}, {{ inner_property.get_instance_type_string() }}):
|
|
82
|
+
if isinstance({{ source }}, {{ inner_property.get_instance_type_string() | as_unembedded_code }}):
|
|
83
83
|
{% endif %}
|
|
84
84
|
{% endmacro %}
|
|
85
85
|
|
|
@@ -17,7 +17,7 @@ UUID({{ source }})
|
|
|
17
17
|
{%- else %}
|
|
18
18
|
{% if not skip_unset %}
|
|
19
19
|
{% if declare_type %}
|
|
20
|
-
{% set type_annotation = property.get_type_string(json=True) %}
|
|
20
|
+
{% set type_annotation = property.get_type_string(json=True) | as_unembedded_code %}
|
|
21
21
|
{{ destination }}: {{ type_annotation }} = UNSET
|
|
22
22
|
{% else %}
|
|
23
23
|
{{ destination }} = UNSET
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "{{ project_name }}"
|
|
3
|
-
version = "{{ package_version }}"
|
|
4
|
-
description = "{{ package_description }}"
|
|
3
|
+
version = "{{ package_version | in_double_quote_literal }}"
|
|
4
|
+
description = "{{ package_description | in_double_quote_literal }}"
|
|
5
5
|
authors = []
|
|
6
6
|
readme = "README.md"
|
|
7
7
|
requires-python = ">=3.11"
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "{{ project_name }}"
|
|
3
|
-
version = "{{ package_version }}"
|
|
4
|
-
description = "{{ package_description }}"
|
|
3
|
+
version = "{{ package_version | in_double_quote_literal }}"
|
|
4
|
+
description = "{{ package_description | in_double_quote_literal }}"
|
|
5
5
|
authors = []
|
|
6
6
|
readme = "README.md"
|
|
7
7
|
packages = [
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "{{ project_name }}"
|
|
3
|
-
version = "{{ package_version }}"
|
|
4
|
-
description = "{{ package_description }}"
|
|
3
|
+
version = "{{ package_version | in_double_quote_literal }}"
|
|
4
|
+
description = "{{ package_description | in_double_quote_literal }}"
|
|
5
5
|
authors = []
|
|
6
6
|
requires-python = ">=3.11"
|
|
7
7
|
readme = "README.md"
|
|
@@ -15,5 +15,5 @@ module-name = "{{ package_name }}"
|
|
|
15
15
|
module-root = ""
|
|
16
16
|
|
|
17
17
|
[build-system]
|
|
18
|
-
requires = ["uv_build>=0.
|
|
18
|
+
requires = ["uv_build>=0.12.0,<0.13.0"]
|
|
19
19
|
build-backend = "uv_build"
|
|
@@ -7,8 +7,8 @@ long_description = (here / "README.md").read_text(encoding="utf-8")
|
|
|
7
7
|
|
|
8
8
|
setup(
|
|
9
9
|
name="{{ project_name }}",
|
|
10
|
-
version="{{ package_version }}",
|
|
11
|
-
description="{{ package_description }}",
|
|
10
|
+
version="{{ package_version | in_double_quote_literal }}",
|
|
11
|
+
description="{{ package_description | in_double_quote_literal }}",
|
|
12
12
|
long_description=long_description,
|
|
13
13
|
long_description_content_type="text/markdown",
|
|
14
14
|
packages=find_packages(),
|
|
@@ -1,15 +1,14 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
2
|
Name: openapi-python-client
|
|
3
|
-
Version: 0.29.
|
|
3
|
+
Version: 0.29.1
|
|
4
4
|
Summary: Generate modern Python clients from OpenAPI
|
|
5
5
|
Project-URL: repository, https://github.com/openapi-generators/openapi-python-client
|
|
6
6
|
Author-email: Dylan Anthony <contact@dylananthony.com>
|
|
7
|
-
License: MIT
|
|
7
|
+
License-Expression: MIT
|
|
8
8
|
License-File: LICENSE
|
|
9
9
|
Keywords: Client,Generator,OpenAPI
|
|
10
10
|
Classifier: Development Status :: 4 - Beta
|
|
11
11
|
Classifier: Intended Audience :: Developers
|
|
12
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
13
12
|
Classifier: Programming Language :: Python :: 3
|
|
14
13
|
Classifier: Programming Language :: Python :: 3.11
|
|
15
14
|
Classifier: Programming Language :: Python :: 3.12
|
|
@@ -26,7 +25,7 @@ Requires-Dist: pydantic<3.0.0,>=2.10
|
|
|
26
25
|
Requires-Dist: ruamel-yaml<0.20.0,>=0.18.6
|
|
27
26
|
Requires-Dist: ruff>=0.2
|
|
28
27
|
Requires-Dist: shellingham<2.0.0,>=1.3.2
|
|
29
|
-
Requires-Dist: typer<0.
|
|
28
|
+
Requires-Dist: typer<0.28,>0.16
|
|
30
29
|
Description-Content-Type: text/markdown
|
|
31
30
|
|
|
32
31
|

|