openapi-python-client 0.29.1__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 (31) hide show
  1. openapi_python_client/__init__.py +4 -4
  2. openapi_python_client/cli.py +9 -1
  3. openapi_python_client/config.py +1 -0
  4. openapi_python_client/parser/properties/__init__.py +2 -0
  5. openapi_python_client/parser/properties/any.py +1 -0
  6. openapi_python_client/parser/properties/boolean.py +1 -0
  7. openapi_python_client/parser/properties/date.py +1 -0
  8. openapi_python_client/parser/properties/datetime.py +1 -0
  9. openapi_python_client/parser/properties/file.py +1 -0
  10. openapi_python_client/parser/properties/float.py +1 -0
  11. openapi_python_client/parser/properties/int.py +1 -0
  12. openapi_python_client/parser/properties/model_property.py +1 -1
  13. openapi_python_client/parser/properties/none.py +1 -0
  14. openapi_python_client/parser/properties/string.py +1 -0
  15. openapi_python_client/parser/properties/uuid.py +1 -0
  16. openapi_python_client/parser/responses.py +1 -1
  17. openapi_python_client/schema/openapi_schema_pydantic/schema.py +2 -2
  18. openapi_python_client/templates/README.md.jinja +6 -6
  19. openapi_python_client/templates/client.py.jinja +28 -28
  20. openapi_python_client/templates/endpoint_macros.py.jinja +1 -1
  21. openapi_python_client/templates/endpoint_module.py.jinja +6 -9
  22. openapi_python_client/templates/pyproject_pdm.toml.jinja +1 -1
  23. openapi_python_client/templates/pyproject_poetry.toml.jinja +1 -1
  24. openapi_python_client/templates/pyproject_uv.toml.jinja +2 -2
  25. openapi_python_client/templates/setup.py.jinja +1 -1
  26. openapi_python_client/templates/types.py.jinja +2 -2
  27. {openapi_python_client-0.29.1.dist-info → openapi_python_client-0.30.0.dist-info}/METADATA +7 -5
  28. {openapi_python_client-0.29.1.dist-info → openapi_python_client-0.30.0.dist-info}/RECORD +31 -31
  29. {openapi_python_client-0.29.1.dist-info → openapi_python_client-0.30.0.dist-info}/WHEEL +1 -1
  30. {openapi_python_client-0.29.1.dist-info → openapi_python_client-0.30.0.dist-info}/entry_points.txt +0 -0
  31. {openapi_python_client-0.29.1.dist-info → openapi_python_client-0.30.0.dist-info}/licenses/LICENSE +0 -0
@@ -10,8 +10,8 @@ from pathlib import Path
10
10
  from subprocess import CalledProcessError
11
11
  from typing import Any
12
12
 
13
- import httpcore
14
- import httpx
13
+ import httpcore2
14
+ import httpx2
15
15
  from jinja2 import BaseLoader, ChoiceLoader, Environment, FileSystemLoader, PackageLoader
16
16
  from ruamel.yaml import YAML
17
17
  from ruamel.yaml.error import YAMLError
@@ -355,14 +355,14 @@ def _get_document(*, source: str | Path, timeout: int) -> dict[str, Any] | Gener
355
355
  content_type: str | None
356
356
  if isinstance(source, str):
357
357
  try:
358
- response = httpx.get(source, timeout=timeout)
358
+ response = httpx2.get(source, timeout=timeout)
359
359
  yaml_bytes = response.content
360
360
  if "content-type" in response.headers:
361
361
  content_type = response.headers["content-type"].split(";")[0]
362
362
  else: # pragma: no cover
363
363
  content_type = mimetypes.guess_type(source, strict=True)[0]
364
364
 
365
- except (httpx.HTTPError, httpcore.NetworkError):
365
+ except (httpx2.HTTPError, httpcore2.NetworkError):
366
366
  return GeneratorError(header="Could not get OpenAPI document from provided URL")
367
367
  else:
368
368
  yaml_bytes = source.read_bytes()
@@ -53,7 +53,14 @@ def _process_config(
53
53
  except Exception as err:
54
54
  raise typer.BadParameter("Unable to parse config") from err
55
55
 
56
- return Config.from_sources(config_file, meta_type, source, file_encoding, overwrite, output_path=output_path)
56
+ return Config.from_sources(
57
+ config_file=config_file,
58
+ meta_type=meta_type,
59
+ document_source=source,
60
+ file_encoding=file_encoding,
61
+ overwrite=overwrite,
62
+ output_path=output_path,
63
+ )
57
64
 
58
65
 
59
66
  # noinspection PyUnusedLocal
@@ -123,6 +130,7 @@ def handle_errors(errors: Sequence[GeneratorError], fail_on_warning: bool = Fals
123
130
 
124
131
  @app.command()
125
132
  def generate(
133
+ *,
126
134
  url: str | None = typer.Option(None, help="A URL to read the OpenAPI document from"),
127
135
  path: Path | None = typer.Option(None, help="A path to the OpenAPI document"),
128
136
  custom_template_path: Path | None = typer.Option(
@@ -86,6 +86,7 @@ class Config:
86
86
 
87
87
  @staticmethod
88
88
  def from_sources(
89
+ *,
89
90
  config_file: ConfigFile,
90
91
  meta_type: MetaType,
91
92
  document_source: Path | str,
@@ -105,6 +105,7 @@ def _string_based_property(
105
105
 
106
106
 
107
107
  def _property_from_ref(
108
+ *,
108
109
  name: UntrustedString,
109
110
  required: bool,
110
111
  parent: oai.Schema | None,
@@ -141,6 +142,7 @@ def _property_from_ref(
141
142
 
142
143
 
143
144
  def property_from_data( # noqa: PLR0911, PLR0912
145
+ *,
144
146
  name: UntrustedString,
145
147
  required: bool,
146
148
  data: oai.Reference | oai.Schema,
@@ -16,6 +16,7 @@ class AnyProperty(PropertyProtocol):
16
16
  @classmethod
17
17
  def build(
18
18
  cls,
19
+ *,
19
20
  name: UntrustedString,
20
21
  required: bool,
21
22
  default: Any,
@@ -34,6 +34,7 @@ class BooleanProperty(PropertyProtocol):
34
34
  @classmethod
35
35
  def build(
36
36
  cls,
37
+ *,
37
38
  name: oai.UntrustedString,
38
39
  required: bool,
39
40
  default: Any,
@@ -29,6 +29,7 @@ class DateProperty(PropertyProtocol):
29
29
  @classmethod
30
30
  def build(
31
31
  cls,
32
+ *,
32
33
  name: oai.UntrustedString,
33
34
  required: bool,
34
35
  default: Any,
@@ -31,6 +31,7 @@ class DateTimeProperty(PropertyProtocol):
31
31
  @classmethod
32
32
  def build(
33
33
  cls,
34
+ *,
34
35
  name: oai.UntrustedString,
35
36
  required: bool,
36
37
  default: Any,
@@ -29,6 +29,7 @@ class FileProperty(PropertyProtocol):
29
29
  @classmethod
30
30
  def build(
31
31
  cls,
32
+ *,
32
33
  name: oai.UntrustedString,
33
34
  required: bool,
34
35
  default: Any,
@@ -34,6 +34,7 @@ class FloatProperty(PropertyProtocol):
34
34
  @classmethod
35
35
  def build(
36
36
  cls,
37
+ *,
37
38
  name: oai.UntrustedString,
38
39
  required: bool,
39
40
  default: Any,
@@ -34,6 +34,7 @@ class IntProperty(PropertyProtocol):
34
34
  @classmethod
35
35
  def build(
36
36
  cls,
37
+ *,
37
38
  name: oai.UntrustedString,
38
39
  required: bool,
39
40
  default: Any,
@@ -347,7 +347,7 @@ ANY_ADDITIONAL_PROPERTY = AnyProperty.build(
347
347
 
348
348
  def _get_additional_properties(
349
349
  *,
350
- schema_additional: None | (bool | (oai.Reference | oai.Schema)),
350
+ schema_additional: bool | oai.Reference | oai.Schema | None,
351
351
  schemas: Schemas,
352
352
  class_name: strings.ClassName,
353
353
  config: Config,
@@ -32,6 +32,7 @@ class NoneProperty(PropertyProtocol):
32
32
  @classmethod
33
33
  def build(
34
34
  cls,
35
+ *,
35
36
  name: oai.UntrustedString,
36
37
  required: bool,
37
38
  default: Any,
@@ -32,6 +32,7 @@ class StringProperty(PropertyProtocol):
32
32
  @classmethod
33
33
  def build(
34
34
  cls,
35
+ *,
35
36
  name: oai.UntrustedString,
36
37
  required: bool,
37
38
  default: Any,
@@ -35,6 +35,7 @@ class UuidProperty(PropertyProtocol):
35
35
  @classmethod
36
36
  def build(
37
37
  cls,
38
+ *,
38
39
  name: oai.UntrustedString,
39
40
  required: bool,
40
41
  default: Any,
@@ -31,7 +31,7 @@ class Responses:
31
31
 
32
32
 
33
33
  class _ResponseSource(TypedDict):
34
- """What data should be pulled from the httpx Response object"""
34
+ """What data should be pulled from the httpx2 Response object"""
35
35
 
36
36
  attribute: str
37
37
  return_type: str
@@ -36,8 +36,8 @@ class Schema(BaseModel):
36
36
  maxProperties: int | None = Field(default=None, ge=0)
37
37
  minProperties: int | None = Field(default=None, ge=0)
38
38
  required: list[UntrustedString] | None = Field(default=None)
39
- enum: None | list[Any] = Field(default=None, min_length=1)
40
- const: None | StrictStr | StrictInt | StrictFloat | StrictBool = None
39
+ enum: list[Any] | None = Field(default=None, min_length=1)
40
+ const: StrictStr | StrictInt | StrictFloat | StrictBool | None = None
41
41
  type: DataType | list[DataType] | None = Field(default=None)
42
42
  allOf: list[ReferenceOr["Schema"]] = Field(default_factory=list)
43
43
  oneOf: list[ReferenceOr["Schema"]] = Field(default_factory=list)
@@ -76,7 +76,7 @@ Things to know:
76
76
 
77
77
  ## Advanced customizations
78
78
 
79
- 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. You can also customize the underlying `httpx.Client` or `httpx.AsyncClient` (depending on your use-case):
79
+ 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. You can also customize the underlying `httpx2.Client` or `httpx2.AsyncClient` (depending on your use-case):
80
80
 
81
81
  ```python
82
82
  from {{ package_name }} import Client
@@ -93,20 +93,20 @@ client = Client(
93
93
  httpx_args={"event_hooks": {"request": [log_request], "response": [log_response]}},
94
94
  )
95
95
 
96
- # Or get the underlying httpx client to modify directly with client.get_httpx_client() or client.get_async_httpx_client()
96
+ # Or get the underlying httpx2 client to modify directly with client.get_httpx_client() or client.get_async_httpx_client()
97
97
  ```
98
98
 
99
- You can even set the httpx client directly, but beware that this will override any existing settings (e.g., base_url):
99
+ You can even set the httpx2 client directly, but beware that this will override any existing settings (e.g., base_url):
100
100
 
101
101
  ```python
102
- import httpx
102
+ import httpx2
103
103
  from {{ package_name }} import Client
104
104
 
105
105
  client = Client(
106
106
  base_url="https://api.example.com",
107
107
  )
108
108
  # Note that base_url needs to be re-set, as would any shared cookies, headers, etc.
109
- client.set_httpx_client(httpx.Client(base_url="https://api.example.com", proxies="http://localhost:8030"))
109
+ client.set_httpx_client(httpx2.Client(base_url="https://api.example.com", proxy="http://localhost:8030"))
110
110
  ```
111
111
 
112
112
  {% if meta == "poetry" %}
@@ -136,4 +136,4 @@ If you want to install this client into another project without publishing it (e
136
136
  1. If that project is not using uv:
137
137
  1. Build a wheel with `uv build --wheel`.
138
138
  1. Install that wheel from the other project `pip install <path-to-wheel>`.
139
- {% endif %}
139
+ {% endif %}
@@ -2,7 +2,7 @@ import ssl
2
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) -> Self:
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,
@@ -124,29 +124,29 @@ class Client:
124
124
  return self._client
125
125
 
126
126
  def __enter__(self) -> Self:
127
- """Enter a context manager for self.client—you cannot enter twice (see httpx docs)"""
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) -> Self:
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,
@@ -158,20 +158,20 @@ class Client:
158
158
  return self._async_client
159
159
 
160
160
  async def __aenter__(self) -> Self:
161
- """Enter a context manager for underlying httpx.AsyncClient—you cannot enter twice (see httpx docs)"""
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") }}
@@ -184,7 +184,7 @@ 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 %}
@@ -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
@@ -50,18 +50,15 @@ def _get_kwargs(
50
50
  {% for body in endpoint.bodies %}
51
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 %}
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" %}
56
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 %}
61
+ {%- if body.content_type != "multipart/form-data" %}
65
62
  headers["Content-Type"] = "{{ body.content_type | in_double_quote_literal }}"
66
63
  {% endif %}
67
64
  {% endif %}
@@ -78,7 +75,7 @@ def _get_kwargs(
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 | as_unembedded_code }}]:
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,
@@ -6,7 +6,7 @@ authors = []
6
6
  readme = "README.md"
7
7
  requires-python = ">=3.11"
8
8
  dependencies = [
9
- "httpx>=0.23.1,<0.29.0",
9
+ "httpx2>=2.13.1,<3",
10
10
  "attrs>=22.2.0",
11
11
  ]
12
12
 
@@ -11,7 +11,7 @@ include = ["{{ package_name }}/py.typed"]
11
11
 
12
12
  [tool.poetry.dependencies]
13
13
  python = "^3.11"
14
- httpx = ">=0.23.1,<0.29.0"
14
+ httpx2 = ">=2.13.1,<3"
15
15
  attrs = ">=22.2.0"
16
16
 
17
17
  [build-system]
@@ -6,7 +6,7 @@ authors = []
6
6
  requires-python = ">=3.11"
7
7
  readme = "README.md"
8
8
  dependencies = [
9
- "httpx>=0.23.1,<0.29.0",
9
+ "httpx2>=2.13.1,<3",
10
10
  "attrs>=22.2.0",
11
11
  ]
12
12
 
@@ -15,5 +15,5 @@ module-name = "{{ package_name }}"
15
15
  module-root = ""
16
16
 
17
17
  [build-system]
18
- requires = ["uv_build>=0.12.0,<0.13.0"]
18
+ requires = ["uv_build>=0.13.0,<0.14.0"]
19
19
  build-backend = "uv_build"
@@ -13,6 +13,6 @@ setup(
13
13
  long_description_content_type="text/markdown",
14
14
  packages=find_packages(),
15
15
  python_requires=">=3.11, <4",
16
- install_requires=["httpx >= 0.23.1, < 0.29.0", "attrs >= 22.2.0"],
16
+ install_requires=["httpx2 >= 2.13.1, < 3", "attrs >= 22.2.0"],
17
17
  package_data={"{{ package_name }}": ["py.typed"]},
18
18
  )
@@ -14,7 +14,7 @@ class Unset:
14
14
 
15
15
  UNSET: Unset = Unset()
16
16
 
17
- # The types that `httpx.Client(files=)` can accept, copied from that library.
17
+ # The types that `httpx2.Client(files=)` can accept, copied from that library.
18
18
  FileContent = IO[bytes] | bytes | str
19
19
  FileTypes = (
20
20
  # (filename, file (or bytes), content_type)
@@ -33,7 +33,7 @@ class File:
33
33
  mime_type: str | None = None
34
34
 
35
35
  def to_tuple(self) -> FileTypes:
36
- """ Return a tuple representation that httpx will accept for multipart/form-data """
36
+ """ Return a tuple representation that httpx2 will accept for multipart/form-data """
37
37
  return self.file_name, self.payload, self.mime_type
38
38
 
39
39
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: openapi-python-client
3
- Version: 0.29.1
3
+ Version: 0.30.0
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>
@@ -19,7 +19,7 @@ Classifier: Typing :: Typed
19
19
  Requires-Python: <4.0,>=3.11
20
20
  Requires-Dist: attrs>=22.2.0
21
21
  Requires-Dist: colorama>=0.4.3; sys_platform == 'win32'
22
- Requires-Dist: httpx<0.29.0,>=0.23.1
22
+ Requires-Dist: httpx2<3,>=2.13.1
23
23
  Requires-Dist: jinja2<4.0.0,>=3.0.0
24
24
  Requires-Dist: pydantic<3.0.0,>=2.10
25
25
  Requires-Dist: ruamel-yaml<0.20.0,>=0.18.6
@@ -91,7 +91,8 @@ _Be forewarned, this is a beta-level feature in the sense that the API exposed i
91
91
 
92
92
  ## What You Get
93
93
 
94
- 1. A `pyproject.toml` file, optionally with [Poetry] metadata (default), [PDM] (with `--meta=pdm`), or only [Ruff] config.
94
+ 1. A `pyproject.toml` file, optionally with [Poetry] metadata (default), [uv] (with `--meta=uv`), [PDM]
95
+ (with `--meta=pdm`), or only [Ruff] config.
95
96
  2. A `README.md` you'll most definitely need to update with your project's details
96
97
  3. A Python module named just like the auto-generated project name (e.g. "my_api_client") which contains:
97
98
  1. A `client` module which will have both a `Client` class and an `AuthenticatedClient` class. You'll need these
@@ -230,7 +231,7 @@ This extension has been adopted by similar projects such as [OpenAPI Tools](http
230
231
  It is intended to provide user-friendly names for integer Enum members that get generated.
231
232
  It is critical that the length of the array matches that of the enum values.
232
233
 
233
- ```
234
+ ```json
234
235
  "Colors": {
235
236
  "type": "integer",
236
237
  "format": "int32",
@@ -248,7 +249,7 @@ It is critical that the length of the array matches that of the enum values.
248
249
  ```
249
250
 
250
251
  Results in:
251
- ```
252
+ ```python
252
253
  class Color(IntEnum):
253
254
  RED = 0
254
255
  GREEN = 1
@@ -257,5 +258,6 @@ class Color(IntEnum):
257
258
 
258
259
  [changelog.md]: CHANGELOG.md
259
260
  [poetry]: https://python-poetry.org/
261
+ [uv]: https://docs.astral.sh/uv/
260
262
  [PDM]: https://pdm-project.org/latest/
261
263
  [Ruff]: https://docs.astral.sh/ruff/
@@ -1,35 +1,35 @@
1
- openapi_python_client/__init__.py,sha256=AFRoY0c5ZOt73eX1oXjNcPYi_c7XBGI6JvShLH-_8x4,14557
1
+ openapi_python_client/__init__.py,sha256=wyb-Efn6ZGhZmpaVtBsfTlPFzhqOguyDTKwCIgt6xpc,14562
2
2
  openapi_python_client/__main__.py,sha256=wcCrL4PjG51r5wVKqJhcoJPTLfHW0wNbD31DrUN0MWI,28
3
- openapi_python_client/cli.py,sha256=573RDIB1-FDk3hm8tGykfYusl6GrdtqlNjo29iiOxRk,5513
4
- openapi_python_client/config.py,sha256=fd_kyBWtPl77rVvpiTrSwJ6z8OJhEILKBhHggLvjY8A,4236
3
+ openapi_python_client/cli.py,sha256=9lGfusepcXWa-1Z6rQef2RsDZt3m8pSCTzYH3-rd9nY,5637
4
+ openapi_python_client/config.py,sha256=FQXr4nHBopytMtSpjvhxn1rTJiFpgZnNXkP93QD0wT0,4247
5
5
  openapi_python_client/py.typed,sha256=8PjyZ1aVoQpRVvt71muvuq5qE-jTFZkK-GLHkhdebmc,26
6
6
  openapi_python_client/strings.py,sha256=1Oo4Y7bgDw-Y8v8BSpbHiSMV-kWcs1XXhp-5pGS1F-U,9702
7
7
  openapi_python_client/parser/__init__.py,sha256=dpY1jYmWFPBubtjKCZIlg2uHC_DLC-KpfuMNdQF8RUc,177
8
8
  openapi_python_client/parser/bodies.py,sha256=M2uRhnyfK904mzsAb6bxDb6fE1oCDYGgc9HLU_a6FDY,4664
9
9
  openapi_python_client/parser/errors.py,sha256=COPpNRWjPGUWlgebwJetBhuVy2NkHQ63WWKJ9Skcimw,1191
10
10
  openapi_python_client/parser/openapi.py,sha256=Up0JwnFHNU-v78BHZ9OwR1nwfAWabiNGqtsnhY6jzLU,23877
11
- openapi_python_client/parser/responses.py,sha256=3sRpb5bZnkPvzqjWVFOiiMBDa7bObDINll_VfwFiocI,9016
12
- openapi_python_client/parser/properties/__init__.py,sha256=IGxFol11Y_cX9JyfdDjBnSmAnklqWFAjikMzJ3EMt6w,17551
13
- openapi_python_client/parser/properties/any.py,sha256=_O4Ku6UhGkgRekkso6xxl9GyajjVPjHNpL7f4GgEaBI,1532
14
- openapi_python_client/parser/properties/boolean.py,sha256=fTvw8XJ_RPiDeRoQZ_TvMEJK7u_Jjb1y4VVxt8jnlFw,2224
11
+ openapi_python_client/parser/responses.py,sha256=lXG0Y3nKBkQCr4RA12U5uzqnHShgNJwrQI1qi-uDBDI,9017
12
+ openapi_python_client/parser/properties/__init__.py,sha256=cpm7BTL0g6rtylTElRH4YDNL-dkbgZpvxV4Q7N3MqbM,17565
13
+ openapi_python_client/parser/properties/any.py,sha256=dSYBV8rDcodubsovnpHCSIzodeSA9Mgy8nV8V8Gm3cU,1543
14
+ openapi_python_client/parser/properties/boolean.py,sha256=zCSiJ5q3fx2dy1FHGxirMkU1ZC6APPiyVPH8IVJXq0g,2235
15
15
  openapi_python_client/parser/properties/const.py,sha256=saY5coCj49gNmUKc3-NmFZtCJG0uny0zovtseyaORLw,3910
16
- openapi_python_client/parser/properties/date.py,sha256=dEQsjbdiRnRvd0iL47NW3pDlQpWwqaFppaRvRylQ_ZY,2505
17
- openapi_python_client/parser/properties/datetime.py,sha256=p63RPd-V_3xHkoQbtpzWPwnL3KyY0oJ2WwjSYyyzAeA,2555
16
+ openapi_python_client/parser/properties/date.py,sha256=9eoXtLM743ewCylwUVfXTqVNCEuZ6pu3gYU4JOtdSvY,2516
17
+ openapi_python_client/parser/properties/datetime.py,sha256=PlWYzuNplsyrSgM4rkTNMF0kMQrG6Rf6kPHiipyFZ5Q,2566
18
18
  openapi_python_client/parser/properties/enum_property.py,sha256=6uScve1WE10yl1Jb15TUtLohsm_NZlqi4FM5-4Ix8Jc,9107
19
- openapi_python_client/parser/properties/file.py,sha256=iZGU0hBVLodl1nHbYCneV1JJzeS8-Rzt57Z0rPxeX60,2158
20
- openapi_python_client/parser/properties/float.py,sha256=jjQKAOZyUwvZwz3nF_ellwHrYq6HtTM-inL0LuSVMB8,2383
21
- openapi_python_client/parser/properties/int.py,sha256=ZVVm1had2iOXaro9ASlCfO88RZogrPKGQYWZQwxVU5U,2358
19
+ openapi_python_client/parser/properties/file.py,sha256=N3GFQVYsVX2EYtmlvDkxmenaItcj5OHtJrMblV8QCjc,2169
20
+ openapi_python_client/parser/properties/float.py,sha256=3XxqD6yrsWXbDhABvN_YViTeBqCMPEk5kcD_5Gk-SlQ,2394
21
+ openapi_python_client/parser/properties/int.py,sha256=3FfAMCWnno0F894uC8D_b_dat3Pr75L9n1rlr9gd5oQ,2369
22
22
  openapi_python_client/parser/properties/list_property.py,sha256=z2N2rS_08f_AsSL3KO7gMv8l7HzZkNVqkgtuMlJRYys,5967
23
23
  openapi_python_client/parser/properties/literal_enum_property.py,sha256=QfALmZ3Q05uoHl68aYyQ0TJXsv7p7QkF0RxfwNeKTw8,7712
24
24
  openapi_python_client/parser/properties/merge_properties.py,sha256=x4TDQUXB6D6Ae1r27yZldN9q-lz56tEnObHlnZiEY0E,9773
25
- openapi_python_client/parser/properties/model_property.py,sha256=m7cFzHCOKBP4evnps6XpHdBO4-HFLT9ZrSlTTfCWgFg,17349
26
- openapi_python_client/parser/properties/none.py,sha256=8jG-73Hhbk0Leav94HdEDAHTiLL5nQQy6rd9qruIY6Y,1912
25
+ openapi_python_client/parser/properties/model_property.py,sha256=m_0YC3Spij6Wf7ADtjwdMQUdFP48Hcf_E4dx3OeSeGQ,17345
26
+ openapi_python_client/parser/properties/none.py,sha256=dKw-GVlqASMDmo3SYUZVWNdBQfHb6dtx7wavQyM3gLQ,1923
27
27
  openapi_python_client/parser/properties/property.py,sha256=tUNaC-96EKSqpuqW_4NeEp9LG7_PMUeE0DOkIXU89M0,971
28
28
  openapi_python_client/parser/properties/protocol.py,sha256=rY2QbKysLiOrmODoh76gcMmZxxanj8BrCFMtv4yzBQk,7181
29
29
  openapi_python_client/parser/properties/schemas.py,sha256=Ohhw3YsluuO-VKOBjMj-uk0GoJJXDWmfX-UWLLrODLM,9129
30
- openapi_python_client/parser/properties/string.py,sha256=-ocGBWrWRKT7HR8GkSm-lU2ygJcQeKIXnYFEsq7LlRk,2048
30
+ openapi_python_client/parser/properties/string.py,sha256=LkeVmi_J06h1p6hqkFlk2IjlYxJpUCGpfgZuO5tQSrY,2059
31
31
  openapi_python_client/parser/properties/union.py,sha256=4VTvSwgXXaWQdjkVZkB_FpktpQh3BI826DiOs7Wgx9s,9097
32
- openapi_python_client/parser/properties/uuid.py,sha256=msBsFK4xp8AeTMrBA4nt7FkqTO0R2BbE-VjVQE-bnOE,2610
32
+ openapi_python_client/parser/properties/uuid.py,sha256=_3rI6XrsZw5TphW3CdZh97HcKJrVMoARggJZjr4ed-M,2621
33
33
  openapi_python_client/schema/3.0.3.md,sha256=0a25-q_HFDVcsxOnMOGeTo67K4FtLHqMfI5U8VwneJg,126244
34
34
  openapi_python_client/schema/3.1.0.md,sha256=O4SlQ2wux5Sa68Q9jrCcZYEJ5RcqXniJ9pSixtR15Rc,131397
35
35
  openapi_python_client/schema/__init__.py,sha256=BR4j82a9atqOI14sUEvO27CtcO8e-O4MKj5z2cbl8DY,559
@@ -63,7 +63,7 @@ openapi_python_client/schema/openapi_schema_pydantic/reference.py,sha256=V_cJ60q
63
63
  openapi_python_client/schema/openapi_schema_pydantic/request_body.py,sha256=DOVbvez6UXjwKXpMb9gVPpxfzWVYnMA8Wq7qv4JozJw,2821
64
64
  openapi_python_client/schema/openapi_schema_pydantic/response.py,sha256=AQYlgdDnqUpLWk4Gu73oiL8TpGrbgDqfq-PO0EGuyXE,2517
65
65
  openapi_python_client/schema/openapi_schema_pydantic/responses.py,sha256=VlnFD1Uz8DWbdNVO-DxDyv8EI-7O5_D6RTda2Btv-Y8,868
66
- openapi_python_client/schema/openapi_schema_pydantic/schema.py,sha256=Syyjiu9S4msShQFzLy9x7sF3F7TUeOaeE466prGQ5uI,8954
66
+ openapi_python_client/schema/openapi_schema_pydantic/schema.py,sha256=vwTDoYxR8GGO_BNxBQwMtcNoiaMfHRC2iLBJKihWCGs,8954
67
67
  openapi_python_client/schema/openapi_schema_pydantic/security_requirement.py,sha256=89WK-8Zlfyx4P_-ySSXdzsOMqqDFWCwt-UitCt-EZ90,942
68
68
  openapi_python_client/schema/openapi_schema_pydantic/security_scheme.py,sha256=5i4o0Kky-5qpVQf9CweHGondjFoprTgDj68HkXh4_pc,1952
69
69
  openapi_python_client/schema/openapi_schema_pydantic/server.py,sha256=NX_0Dqp51OFjRroMj6vJxA0TW2DE2Kt1zn2ny64tkps,1436
@@ -71,12 +71,12 @@ openapi_python_client/schema/openapi_schema_pydantic/server_variable.py,sha256=6
71
71
  openapi_python_client/schema/openapi_schema_pydantic/tag.py,sha256=99HECCPoDxgHvvD1_7s5lnCrJnaXy9eUO1UJv6eZHc0,829
72
72
  openapi_python_client/schema/openapi_schema_pydantic/xml.py,sha256=8OePi0SrUiv9PhJqiVS34RgvhoeFWjECsc9XPECHJYE,1054
73
73
  openapi_python_client/templates/.gitignore.jinja,sha256=SwZvl9qyKaHN9YjwaQrBsmpAclN11usf9Gohz1SFVg8,195
74
- openapi_python_client/templates/README.md.jinja,sha256=21eRjE07I9Km8ZBLEk_euQ-tC68qeL7Hv6BhfUj9Yjw,5976
74
+ openapi_python_client/templates/README.md.jinja,sha256=DDErcO12YzXqzi2N7OUbl3CHW9Dof4QD1xrskh1ifFs,5981
75
75
  openapi_python_client/templates/api_init.py.jinja,sha256=87ApBzKyGb5zsgTMOkQXDqsLZCmaSFoJMwbGzCDQZMw,47
76
- openapi_python_client/templates/client.py.jinja,sha256=Tqxldi8hAUp_v1nnMPG-bSjJyF3cHEC0WLb_X2emTJ4,8295
76
+ openapi_python_client/templates/client.py.jinja,sha256=gLlBoj7XFhXLwZtv6Z3TBqV0VXgULCIkTL44Te6ibwY,8327
77
77
  openapi_python_client/templates/endpoint_init.py.jinja,sha256=wYN6A3_bvYW13B1J9YvYCmcv5Els-q-E07kZJwJ7Vto,58
78
- openapi_python_client/templates/endpoint_macros.py.jinja,sha256=WO24tJE9tu1IY8Yz-zk2U2RJQ5jIFWx3FRxmdvGyj-4,7708
79
- openapi_python_client/templates/endpoint_module.py.jinja,sha256=wbJigFPrSHW6WVZrqlrpB2ZvnL-vowjZP5cKRAS3zVI,5319
78
+ openapi_python_client/templates/endpoint_macros.py.jinja,sha256=6-IMqumJz2kUiRN4mD-WqQk3GqRgoXEzMVJZU-oW0mk,7709
79
+ openapi_python_client/templates/endpoint_module.py.jinja,sha256=6oX1DyrWCn7Ecq0NiLY32Lnr3yqTIs99e3hUCzpz9N4,5270
80
80
  openapi_python_client/templates/errors.py.jinja,sha256=trp-p5qn1_JLRxGZhdHtICaNPaCrcDCe4TgIihBravk,546
81
81
  openapi_python_client/templates/helpers.jinja,sha256=Tmrae3qIBuc5-qex8zDB2yuSQYCjAD3wpOBI7ctUnwI,210
82
82
  openapi_python_client/templates/int_enum.py.jinja,sha256=z4d2hVsdgmlxUB1fbVs4w8tJaq2SdmhLjs4mjbZ5GLw,224
@@ -85,13 +85,13 @@ openapi_python_client/templates/model.py.jinja,sha256=_Rx5aA1RwGz7uJgJrRI3ULqa3Z
85
85
  openapi_python_client/templates/models_init.py.jinja,sha256=T2Sj1uGxWlYb5Vc8G3cC65KMDk-mtnEH0g5Xp0xE1Ro,233
86
86
  openapi_python_client/templates/package_init.py.jinja,sha256=rBWQfjLcOb2bg-pvIAL7h1FPIYK5f4gjG1qcRzIfYhA,207
87
87
  openapi_python_client/templates/pyproject.toml.jinja,sha256=rBaFq0oqlGeW-NesqPeF_BGCnEEB4P2Qq95vIrGOFQQ,255
88
- openapi_python_client/templates/pyproject_pdm.toml.jinja,sha256=BDTI3pY2DaDiKaWspfEVWS1v8hXBzLetifQLKWciXxs,401
89
- openapi_python_client/templates/pyproject_poetry.toml.jinja,sha256=BJJNN3-fDA3n6ccnQpNwUu7uCc7CvsjL-u02-6SjaJM,491
88
+ openapi_python_client/templates/pyproject_pdm.toml.jinja,sha256=hjI2PbsAuT8jSQDDwlcfHhyTsBsVyaX4GpIDos01C3Q,397
89
+ openapi_python_client/templates/pyproject_poetry.toml.jinja,sha256=8hvtbGz_DZn6qN5PjtpydkpH86GZNKwEzW8cA0tLcxo,487
90
90
  openapi_python_client/templates/pyproject_ruff.toml.jinja,sha256=OgFfCd9Ca3YsU8WdCxqJoJdCJEtzADpxAxfMghqzn80,74
91
- openapi_python_client/templates/pyproject_uv.toml.jinja,sha256=_kUJMCl75NweMM_3P7xzQmSgR9q5hMslbtr8WqHcMDM,456
92
- openapi_python_client/templates/setup.py.jinja,sha256=kTXHYDiGgjE2sRTqCGLLqwpJgNwpBQQcmsW3hDR8RKY,631
91
+ openapi_python_client/templates/pyproject_uv.toml.jinja,sha256=iukty-eMzWEcHxRO2y34MucZuHgI6k6Dqme8H1ZugSQ,452
92
+ openapi_python_client/templates/setup.py.jinja,sha256=vCqtCLQ_Hj0IHjpo3VomVrrQogSVYe2ssU8TJkYjnAM,627
93
93
  openapi_python_client/templates/str_enum.py.jinja,sha256=5JeaXmszg1HtqL2sU0AXz761GoCbSU_TxjLbOUFIF_w,233
94
- openapi_python_client/templates/types.py.jinja,sha256=k9-ddYg6Tq-sd90hvGkH8-9hLj94QeCGVmGNz2xM5JQ,1350
94
+ openapi_python_client/templates/types.py.jinja,sha256=1YN_lyNzGx5BDFJTg-TaiVRAXij-DDvHnrmBa9B-D8M,1352
95
95
  openapi_python_client/templates/property_templates/any_property.py.jinja,sha256=H1-aMl9Mxs9z-gXQUw9MCNNrGYewG1p0s43flY4YHtA,135
96
96
  openapi_python_client/templates/property_templates/boolean_property.py.jinja,sha256=3xghrbqSeUh-Qk1vdaWb_RxGOBNjhfKFf9EBOmMOBzU,225
97
97
  openapi_python_client/templates/property_templates/const_property.py.jinja,sha256=0paITBHzqvrWdy2Mtx3go9CK02ya0ZfA5ycIL4bzJlk,651
@@ -108,8 +108,8 @@ openapi_python_client/templates/property_templates/model_property.py.jinja,sha25
108
108
  openapi_python_client/templates/property_templates/property_macros.py.jinja,sha256=x5qZVFeZAqIU18rs9bfACRLR8guUEjjSkAmfywJyM_k,601
109
109
  openapi_python_client/templates/property_templates/union_property.py.jinja,sha256=A3kPJxDerrIZwY5RiuGDrUpB7aH3jPYl0Tnt_3VGzSY,4324
110
110
  openapi_python_client/templates/property_templates/uuid_property.py.jinja,sha256=5WURNZ-NPGiFjH-AcYKZzMS8lmO1zLgCtf1HLMiD2aI,1096
111
- openapi_python_client-0.29.1.dist-info/METADATA,sha256=Jx0RNtkQxQ3rMMEpEBbt5Bhjd25z-XcigeyCB0ozuCc,11551
112
- openapi_python_client-0.29.1.dist-info/WHEEL,sha256=zOwg4jB6zX2kU910N-cMawjivD6tO8NEWvE12je1bVk,87
113
- openapi_python_client-0.29.1.dist-info/entry_points.txt,sha256=iRfSdN2foASZXr8GghkYa6KRXkDgO0EFfLut5IXoBTU,72
114
- openapi_python_client-0.29.1.dist-info/licenses/LICENSE,sha256=4dpxQYqY0DB3aTauRrqYRuu6BVNsPSJdYeUT3sH6pQY,1075
115
- openapi_python_client-0.29.1.dist-info/RECORD,,
111
+ openapi_python_client-0.30.0.dist-info/METADATA,sha256=eagnZ6tNiaJJ7ES4ZsotOnb_dhrNgg-ZuniFkr13zl4,11618
112
+ openapi_python_client-0.30.0.dist-info/WHEEL,sha256=2iZrJkDIkeDh0k9SikKMqFuRP-ua6mb4Ir1AsER4BEw,87
113
+ openapi_python_client-0.30.0.dist-info/entry_points.txt,sha256=iRfSdN2foASZXr8GghkYa6KRXkDgO0EFfLut5IXoBTU,72
114
+ openapi_python_client-0.30.0.dist-info/licenses/LICENSE,sha256=4dpxQYqY0DB3aTauRrqYRuu6BVNsPSJdYeUT3sH6pQY,1075
115
+ openapi_python_client-0.30.0.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: hatchling 1.32.0
2
+ Generator: hatchling 1.33.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any