openapi-python-client 0.29.0__py3-none-any.whl → 0.30.0__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.
Files changed (91) hide show
  1. openapi_python_client/__init__.py +30 -21
  2. openapi_python_client/cli.py +10 -3
  3. openapi_python_client/config.py +4 -1
  4. openapi_python_client/parser/bodies.py +8 -7
  5. openapi_python_client/parser/openapi.py +58 -48
  6. openapi_python_client/parser/properties/__init__.py +36 -33
  7. openapi_python_client/parser/properties/any.py +12 -10
  8. openapi_python_client/parser/properties/boolean.py +14 -13
  9. openapi_python_client/parser/properties/const.py +11 -10
  10. openapi_python_client/parser/properties/date.py +13 -11
  11. openapi_python_client/parser/properties/datetime.py +13 -11
  12. openapi_python_client/parser/properties/enum_property.py +23 -20
  13. openapi_python_client/parser/properties/file.py +12 -10
  14. openapi_python_client/parser/properties/float.py +14 -13
  15. openapi_python_client/parser/properties/int.py +12 -11
  16. openapi_python_client/parser/properties/list_property.py +22 -22
  17. openapi_python_client/parser/properties/literal_enum_property.py +20 -20
  18. openapi_python_client/parser/properties/merge_properties.py +9 -6
  19. openapi_python_client/parser/properties/model_property.py +35 -34
  20. openapi_python_client/parser/properties/none.py +12 -11
  21. openapi_python_client/parser/properties/protocol.py +42 -32
  22. openapi_python_client/parser/properties/schemas.py +30 -23
  23. openapi_python_client/parser/properties/string.py +11 -11
  24. openapi_python_client/parser/properties/union.py +24 -22
  25. openapi_python_client/parser/properties/uuid.py +12 -11
  26. openapi_python_client/parser/responses.py +31 -24
  27. openapi_python_client/schema/__init__.py +2 -1
  28. openapi_python_client/schema/openapi_schema_pydantic/callback.py +3 -1
  29. openapi_python_client/schema/openapi_schema_pydantic/components.py +10 -9
  30. openapi_python_client/schema/openapi_schema_pydantic/contact.py +5 -3
  31. openapi_python_client/schema/openapi_schema_pydantic/discriminator.py +4 -2
  32. openapi_python_client/schema/openapi_schema_pydantic/encoding.py +4 -3
  33. openapi_python_client/schema/openapi_schema_pydantic/example.py +5 -3
  34. openapi_python_client/schema/openapi_schema_pydantic/external_documentation.py +4 -2
  35. openapi_python_client/schema/openapi_schema_pydantic/header.py +20 -6
  36. openapi_python_client/schema/openapi_schema_pydantic/info.py +5 -4
  37. openapi_python_client/schema/openapi_schema_pydantic/license.py +4 -2
  38. openapi_python_client/schema/openapi_schema_pydantic/link.py +5 -4
  39. openapi_python_client/schema/openapi_schema_pydantic/media_type.py +3 -2
  40. openapi_python_client/schema/openapi_schema_pydantic/oauth_flow.py +6 -4
  41. openapi_python_client/schema/openapi_schema_pydantic/open_api.py +5 -3
  42. openapi_python_client/schema/openapi_schema_pydantic/operation.py +6 -5
  43. openapi_python_client/schema/openapi_schema_pydantic/parameter.py +6 -5
  44. openapi_python_client/schema/openapi_schema_pydantic/path_item.py +5 -3
  45. openapi_python_client/schema/openapi_schema_pydantic/paths.py +2 -1
  46. openapi_python_client/schema/openapi_schema_pydantic/reference.py +3 -1
  47. openapi_python_client/schema/openapi_schema_pydantic/request_body.py +3 -2
  48. openapi_python_client/schema/openapi_schema_pydantic/response.py +5 -4
  49. openapi_python_client/schema/openapi_schema_pydantic/responses.py +2 -1
  50. openapi_python_client/schema/openapi_schema_pydantic/schema.py +9 -8
  51. openapi_python_client/schema/openapi_schema_pydantic/security_requirement.py +3 -1
  52. openapi_python_client/schema/openapi_schema_pydantic/security_scheme.py +8 -7
  53. openapi_python_client/schema/openapi_schema_pydantic/server.py +4 -3
  54. openapi_python_client/schema/openapi_schema_pydantic/server_variable.py +5 -3
  55. openapi_python_client/schema/openapi_schema_pydantic/tag.py +3 -2
  56. openapi_python_client/schema/openapi_schema_pydantic/xml.py +5 -3
  57. openapi_python_client/schema/ref.py +52 -0
  58. openapi_python_client/schema/untrusted_string.py +56 -0
  59. openapi_python_client/strings.py +245 -0
  60. openapi_python_client/templates/README.md.jinja +9 -9
  61. openapi_python_client/templates/client.py.jinja +31 -31
  62. openapi_python_client/templates/endpoint_macros.py.jinja +19 -19
  63. openapi_python_client/templates/endpoint_module.py.jinja +18 -21
  64. openapi_python_client/templates/helpers.jinja +3 -7
  65. openapi_python_client/templates/literal_enum.py.jinja +1 -1
  66. openapi_python_client/templates/model.py.jinja +18 -18
  67. openapi_python_client/templates/package_init.py.jinja +2 -2
  68. openapi_python_client/templates/property_templates/const_property.py.jinja +3 -3
  69. openapi_python_client/templates/property_templates/date_property.py.jinja +1 -1
  70. openapi_python_client/templates/property_templates/datetime_property.py.jinja +1 -1
  71. openapi_python_client/templates/property_templates/enum_property.py.jinja +1 -1
  72. openapi_python_client/templates/property_templates/file_property.py.jinja +1 -1
  73. openapi_python_client/templates/property_templates/list_property.py.jinja +3 -3
  74. openapi_python_client/templates/property_templates/literal_enum_property.py.jinja +2 -2
  75. openapi_python_client/templates/property_templates/model_property.py.jinja +1 -1
  76. openapi_python_client/templates/property_templates/property_macros.py.jinja +1 -1
  77. openapi_python_client/templates/property_templates/union_property.py.jinja +9 -9
  78. openapi_python_client/templates/property_templates/uuid_property.py.jinja +1 -1
  79. openapi_python_client/templates/pyproject_pdm.toml.jinja +3 -3
  80. openapi_python_client/templates/pyproject_poetry.toml.jinja +3 -3
  81. openapi_python_client/templates/pyproject_uv.toml.jinja +4 -4
  82. openapi_python_client/templates/setup.py.jinja +3 -3
  83. openapi_python_client/templates/str_enum.py.jinja +2 -2
  84. openapi_python_client/templates/types.py.jinja +2 -2
  85. {openapi_python_client-0.29.0.dist-info → openapi_python_client-0.30.0.dist-info}/METADATA +10 -9
  86. openapi_python_client-0.30.0.dist-info/RECORD +115 -0
  87. {openapi_python_client-0.29.0.dist-info → openapi_python_client-0.30.0.dist-info}/WHEEL +1 -1
  88. openapi_python_client/utils.py +0 -122
  89. openapi_python_client-0.29.0.dist-info/RECORD +0 -113
  90. {openapi_python_client-0.29.0.dist-info → openapi_python_client-0.30.0.dist-info}/entry_points.txt +0 -0
  91. {openapi_python_client-0.29.0.dist-info → openapi_python_client-0.30.0.dist-info}/licenses/LICENSE +0 -0
@@ -1,8 +1,8 @@
1
1
  import ssl
2
- from typing import Any
2
+ from typing import Any, Self
3
3
 
4
4
  from attrs import define, field, evolve
5
- import httpx
5
+ import httpx2
6
6
 
7
7
 
8
8
  {% set attrs_info = {
@@ -34,8 +34,8 @@ import httpx
34
34
  class Client:
35
35
  """A class for keeping track of data related to the API
36
36
 
37
- {% macro httpx_args_docstring() %}
38
- The following are accepted as keyword arguments and will be used to construct httpx Clients internally:
37
+ {% macro httpx2_args_docstring() %}
38
+ The following are accepted as keyword arguments and will be used to construct httpx2 Clients internally:
39
39
 
40
40
  ``base_url``: The base URL for the API, all requests are made to a relative path to this URL
41
41
 
@@ -44,16 +44,16 @@ class Client:
44
44
  ``headers``: A dictionary of headers to be sent with every request
45
45
 
46
46
  ``timeout``: The maximum amount of a time a request can take. API functions will raise
47
- httpx.TimeoutException if this is exceeded.
47
+ httpx2.TimeoutException if this is exceeded.
48
48
 
49
49
  ``verify_ssl``: Whether or not to verify the SSL certificate of the API server. This should be True in production,
50
50
  but can be set to False for testing purposes.
51
51
 
52
52
  ``follow_redirects``: Whether or not to follow redirects. Default value is False.
53
53
 
54
- ``httpx_args``: A dictionary of additional arguments to be passed to the ``httpx.Client`` and ``httpx.AsyncClient`` constructor.
54
+ ``httpx_args``: A dictionary of additional arguments to be passed to the ``httpx2.Client`` and ``httpx2.AsyncClient`` constructor.
55
55
  {% endmacro %}
56
- {{ httpx_args_docstring() }}
56
+ {{ httpx2_args_docstring() }}
57
57
  {% if not config.docstrings_on_attributes %}
58
58
 
59
59
  Attributes:
@@ -65,12 +65,12 @@ class Client:
65
65
  _base_url: str = field(alias="base_url")
66
66
  _cookies: dict[str, str] = field(factory=dict, kw_only=True, alias="cookies")
67
67
  _headers: dict[str, str] = field(factory=dict, kw_only=True, alias="headers")
68
- _timeout: httpx.Timeout | None = field(default=None, kw_only=True, alias="timeout")
68
+ _timeout: httpx2.Timeout | None = field(default=None, kw_only=True, alias="timeout")
69
69
  _verify_ssl: str | bool | ssl.SSLContext = field(default=True, kw_only=True, alias="verify_ssl")
70
70
  _follow_redirects: bool = field(default=False, kw_only=True, alias="follow_redirects")
71
71
  _httpx_args: dict[str, Any] = field(factory=dict, kw_only=True, alias="httpx_args")
72
- _client: httpx.Client | None = field(default=None, init=False)
73
- _async_client: httpx.AsyncClient | None = field(default=None, init=False)
72
+ _client: httpx2.Client | None = field(default=None, init=False)
73
+ _async_client: httpx2.AsyncClient | None = field(default=None, init=False)
74
74
  {% endmacro %}{{ attributes() }}
75
75
  {% macro builders(self) %}
76
76
  def with_headers(self, headers: dict[str, str]) -> "{{ self }}":
@@ -89,7 +89,7 @@ class Client:
89
89
  self._async_client.cookies.update(cookies)
90
90
  return evolve(self, cookies={**self._cookies, **cookies})
91
91
 
92
- def with_timeout(self, timeout: httpx.Timeout) -> "{{ self }}":
92
+ def with_timeout(self, timeout: httpx2.Timeout) -> "{{ self }}":
93
93
  """Get a new client matching this one with a new timeout configuration"""
94
94
  if self._client is not None:
95
95
  self._client.timeout = timeout
@@ -97,22 +97,22 @@ class Client:
97
97
  self._async_client.timeout = timeout
98
98
  return evolve(self, timeout=timeout)
99
99
  {% endmacro %}{{ builders("Client") }}
100
- {% macro httpx_stuff(name, custom_constructor=None) %}
101
- def set_httpx_client(self, client: httpx.Client) -> "{{ name }}":
102
- """Manually set the underlying httpx.Client
100
+ {% macro httpx2_stuff(name, custom_constructor=None) %}
101
+ def set_httpx_client(self, client: httpx2.Client) -> Self:
102
+ """Manually set the underlying httpx2.Client
103
103
 
104
104
  **NOTE**: This will override any other settings on the client, including cookies, headers, and timeout.
105
105
  """
106
106
  self._client = client
107
107
  return self
108
108
 
109
- def get_httpx_client(self) -> httpx.Client:
110
- """Get the underlying httpx.Client, constructing a new one if not previously set"""
109
+ def get_httpx_client(self) -> httpx2.Client:
110
+ """Get the underlying httpx2.Client, constructing a new one if not previously set"""
111
111
  if self._client is None:
112
112
  {% if custom_constructor %}
113
113
  {{ custom_constructor | indent(12) }}
114
114
  {% endif %}
115
- self._client = httpx.Client(
115
+ self._client = httpx2.Client(
116
116
  base_url=self._base_url,
117
117
  cookies=self._cookies,
118
118
  headers=self._headers,
@@ -123,30 +123,30 @@ class Client:
123
123
  )
124
124
  return self._client
125
125
 
126
- def __enter__(self) -> "{{ name }}":
127
- """Enter a context manager for self.client—you cannot enter twice (see httpx docs)"""
126
+ def __enter__(self) -> Self:
127
+ """Enter a context manager for self.client—you cannot enter twice (see httpx2 docs)"""
128
128
  self.get_httpx_client().__enter__()
129
129
  return self
130
130
 
131
131
  def __exit__(self, *args: Any, **kwargs: Any) -> None:
132
- """Exit a context manager for internal httpx.Client (see httpx docs)"""
132
+ """Exit a context manager for internal httpx2.Client (see httpx2 docs)"""
133
133
  self.get_httpx_client().__exit__(*args, **kwargs)
134
134
 
135
- def set_async_httpx_client(self, async_client: httpx.AsyncClient) -> "{{ name }}":
136
- """Manually set the underlying httpx.AsyncClient
135
+ def set_async_httpx_client(self, async_client: httpx2.AsyncClient) -> Self:
136
+ """Manually set the underlying httpx2.AsyncClient
137
137
 
138
138
  **NOTE**: This will override any other settings on the client, including cookies, headers, and timeout.
139
139
  """
140
140
  self._async_client = async_client
141
141
  return self
142
142
 
143
- def get_async_httpx_client(self) -> httpx.AsyncClient:
144
- """Get the underlying httpx.AsyncClient, constructing a new one if not previously set"""
143
+ def get_async_httpx_client(self) -> httpx2.AsyncClient:
144
+ """Get the underlying httpx2.AsyncClient, constructing a new one if not previously set"""
145
145
  if self._async_client is None:
146
146
  {% if custom_constructor %}
147
147
  {{ custom_constructor | indent(12) }}
148
148
  {% endif %}
149
- self._async_client = httpx.AsyncClient(
149
+ self._async_client = httpx2.AsyncClient(
150
150
  base_url=self._base_url,
151
151
  cookies=self._cookies,
152
152
  headers=self._headers,
@@ -157,21 +157,21 @@ class Client:
157
157
  )
158
158
  return self._async_client
159
159
 
160
- async def __aenter__(self) -> "{{ name }}":
161
- """Enter a context manager for underlying httpx.AsyncClient—you cannot enter twice (see httpx docs)"""
160
+ async def __aenter__(self) -> Self:
161
+ """Enter a context manager for underlying httpx2.AsyncClient—you cannot enter twice (see httpx2 docs)"""
162
162
  await self.get_async_httpx_client().__aenter__()
163
163
  return self
164
164
 
165
165
  async def __aexit__(self, *args: Any, **kwargs: Any) -> None:
166
- """Exit a context manager for underlying httpx.AsyncClient (see httpx docs)"""
166
+ """Exit a context manager for underlying httpx2.AsyncClient (see httpx2 docs)"""
167
167
  await self.get_async_httpx_client().__aexit__(*args, **kwargs)
168
- {% endmacro %}{{ httpx_stuff("Client") }}
168
+ {% endmacro %}{{ httpx2_stuff("Client") }}
169
169
 
170
170
  @define
171
171
  class AuthenticatedClient:
172
172
  """A Client which has been authenticated for use on secured endpoints
173
173
 
174
- {{ httpx_args_docstring() }}
174
+ {{ httpx2_args_docstring() }}
175
175
  {% if not config.docstrings_on_attributes %}
176
176
 
177
177
  Attributes:
@@ -188,4 +188,4 @@ class AuthenticatedClient:
188
188
  {{ declare_attr("auth_header_name") | indent(4) }}
189
189
 
190
190
  {{ builders("AuthenticatedClient") }}
191
- {{ httpx_stuff("AuthenticatedClient", "self._headers[self.auth_header_name] = f\"{self.prefix} {self.token}\" if self.prefix else self.token") }}
191
+ {{ httpx2_stuff("AuthenticatedClient", "self._headers[self.auth_header_name] = f\"{self.prefix} {self.token}\" if self.prefix else self.token") }}
@@ -1,5 +1,5 @@
1
1
  {% from "property_templates/helpers.jinja" import guarded_statement %}
2
- {% from "helpers.jinja" import safe_docstring %}
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["' + parameter.name + '"]' + " = " + expression %}
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 %}
@@ -184,28 +184,28 @@ Args:
184
184
  {% endif %}
185
185
  Raises:
186
186
  errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
187
- httpx.TimeoutException: If the request takes longer than Client.timeout.
187
+ httpx2.TimeoutException: If the request takes longer than Client.timeout.
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
- {{ safe_docstring(docstring_content(endpoint, return_string, is_detailed)) }}
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 %}
@@ -2,7 +2,7 @@ from http import HTTPStatus
2
2
  from typing import Any, cast
3
3
  from urllib.parse import quote
4
4
 
5
- import httpx
5
+ import httpx2
6
6
 
7
7
  from ...client import AuthenticatedClient, Client
8
8
  from ...types import Response, UNSET
@@ -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,21 +48,18 @@ 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
- {%- if body.content_type == "multipart/form-data" %}
54
- headers["Content-Type"] = "multipart/form-data; boundary=+++"
55
- {% else %}
56
- headers["Content-Type"] = "{{ body.content_type }}"
53
+ {#- httpx2 sets multipart/form-data with a random boundary itself, a fixed one could appear in the content #}
54
+ {%- if body.content_type != "multipart/form-data" %}
55
+ headers["Content-Type"] = "{{ body.content_type | in_double_quote_literal }}"
57
56
  {% endif %}
58
57
  {% endfor %}
59
58
  {% elif endpoint.bodies | length == 1 %}
60
59
  {% set body = endpoint.bodies[0] %}
61
60
  {{ body_to_kwarg(body) | indent(4) }}
62
- {%- if body.content_type == "multipart/form-data" %}
63
- headers["Content-Type"] = "multipart/form-data; boundary=+++"
64
- {% else %}
65
- headers["Content-Type"] = "{{ body.content_type }}"
61
+ {%- if body.content_type != "multipart/form-data" %}
62
+ headers["Content-Type"] = "{{ body.content_type | in_double_quote_literal }}"
66
63
  {% endif %}
67
64
  {% endif %}
68
65
 
@@ -72,13 +69,13 @@ def _get_kwargs(
72
69
  return _kwargs
73
70
 
74
71
  {% if endpoint.responses.default %}
75
- {% set return_type = return_string %}
72
+ {% set return_type = return_string | as_unembedded_code %}
76
73
  {% else %}
77
- {% set return_type = return_string + " | None" %}
74
+ {% set return_type = (return_string | as_unembedded_code) + " | None" %}
78
75
  {% endif %}
79
76
 
80
77
 
81
- def _parse_response(*, client: AuthenticatedClient | Client, response: httpx.Response) -> {{return_type}}:
78
+ def _parse_response(*, client: AuthenticatedClient | Client, response: httpx2.Response) -> {{return_type}}:
82
79
  {% for response in endpoint.responses.patterns %}
83
80
  {% set code_range = response.status_code.range %}
84
81
  {% if code_range[0] == code_range[1] %}
@@ -98,7 +95,7 @@ def _parse_response(*, client: AuthenticatedClient | Client, response: httpx.Res
98
95
  {% endif %}
99
96
 
100
97
 
101
- def _build_response(*, client: AuthenticatedClient | Client, response: httpx.Response) -> Response[{{ return_string }}]:
98
+ def _build_response(*, client: AuthenticatedClient | Client, response: httpx2.Response) -> Response[{{ return_string | as_unembedded_code }}]:
102
99
  return Response(
103
100
  status_code=HTTPStatus(response.status_code),
104
101
  content=response.content,
@@ -109,7 +106,7 @@ def _build_response(*, client: AuthenticatedClient | Client, response: httpx.Res
109
106
 
110
107
  def sync_detailed(
111
108
  {{ arguments(endpoint) | indent(4) }}
112
- ) -> Response[{{ return_string }}]:
109
+ ) -> Response[{{ return_string | as_unembedded_code }}]:
113
110
  {{ docstring(endpoint, return_string, is_detailed=true) | indent(4) }}
114
111
 
115
112
  kwargs = _get_kwargs(
@@ -125,7 +122,7 @@ def sync_detailed(
125
122
  {% if parsed_responses %}
126
123
  def sync(
127
124
  {{ arguments(endpoint) | indent(4) }}
128
- ) -> {{ return_string }} | None:
125
+ ) -> {{ return_string | as_unembedded_code }} | None:
129
126
  {{ docstring(endpoint, return_string, is_detailed=false) | indent(4) }}
130
127
 
131
128
  return sync_detailed(
@@ -135,7 +132,7 @@ def sync(
135
132
 
136
133
  async def asyncio_detailed(
137
134
  {{ arguments(endpoint) | indent(4) }}
138
- ) -> Response[{{ return_string }}]:
135
+ ) -> Response[{{ return_string | as_unembedded_code }}]:
139
136
  {{ docstring(endpoint, return_string, is_detailed=true) | indent(4) }}
140
137
 
141
138
  kwargs = _get_kwargs(
@@ -151,7 +148,7 @@ async def asyncio_detailed(
151
148
  {% if parsed_responses %}
152
149
  async def asyncio(
153
150
  {{ arguments(endpoint) | indent(4) }}
154
- ) -> {{ return_string }} | None:
151
+ ) -> {{ return_string | as_unembedded_code }} | None:
155
152
  {{ docstring(endpoint, return_string, is_detailed=false) | indent(4) }}
156
153
 
157
154
  return (await asyncio_detailed(
@@ -1,10 +1,6 @@
1
- {% macro safe_docstring(content, omit_if_empty=False) %}
2
- {# This macro returns the provided content as a docstring, set to a raw string if it contains a backslash #}
3
- {% if (not omit_if_empty) or (content | trim) %}
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 safe_docstring %}
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 | string | wordwrap(112) | indent(12) }}
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
- {{ safe_docstring(property.description, omit_if_empty=True) | wordwrap(112) }}
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
- {{ safe_docstring(class_docstring_content(model), omit_if_empty=config.docstrings_on_attributes) | indent(4) }}
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 safe_docstring %}
1
+ {% from "helpers.jinja" import docstring %}
2
2
 
3
- {{ safe_docstring(package_description) }}
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 %}