Skip to content

Commit a9e6d06

Browse files
(fix): remove pydantic warnings
1 parent 8110b5c commit a9e6d06

8 files changed

+330
-178
lines changed

poetry.lock

+97-97
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "elevenlabs"
3-
version = "1.8.1"
3+
version = "1.8.2"
44
description = ""
55
readme = "README.md"
66
authors = []

src/elevenlabs/core/__init__.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from .api_error import ApiError
44
from .client_wrapper import AsyncClientWrapper, BaseClientWrapper, SyncClientWrapper
55
from .datetime_utils import serialize_datetime
6-
from .file import File, convert_file_dict_to_httpx_tuples
6+
from .file import File, convert_file_dict_to_httpx_tuples, with_content_type
77
from .http_client import AsyncHttpClient, HttpClient
88
from .jsonable_encoder import jsonable_encoder
99
from .pydantic_utilities import (
@@ -47,4 +47,5 @@
4747
"universal_field_validator",
4848
"universal_root_validator",
4949
"update_forward_refs",
50+
"with_content_type",
5051
]

src/elevenlabs/core/client_wrapper.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ def get_headers(self) -> typing.Dict[str, str]:
1616
headers: typing.Dict[str, str] = {
1717
"X-Fern-Language": "Python",
1818
"X-Fern-SDK-Name": "elevenlabs",
19-
"X-Fern-SDK-Version": "1.8.1",
19+
"X-Fern-SDK-Version": "1.8.2",
2020
}
2121
if self._api_key is not None:
2222
headers["xi-api-key"] = self._api_key

src/elevenlabs/core/file.py

+30-11
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,30 @@
11
# This file was auto-generated by Fern from our API Definition.
22

3-
import typing
3+
from typing import IO, Dict, List, Mapping, Optional, Tuple, Union, cast
44

55
# File typing inspired by the flexibility of types within the httpx library
66
# https://github.com/encode/httpx/blob/master/httpx/_types.py
7-
FileContent = typing.Union[typing.IO[bytes], bytes, str]
8-
File = typing.Union[
7+
FileContent = Union[IO[bytes], bytes, str]
8+
File = Union[
99
# file (or bytes)
1010
FileContent,
1111
# (filename, file (or bytes))
12-
typing.Tuple[typing.Optional[str], FileContent],
12+
Tuple[Optional[str], FileContent],
1313
# (filename, file (or bytes), content_type)
14-
typing.Tuple[typing.Optional[str], FileContent, typing.Optional[str]],
14+
Tuple[Optional[str], FileContent, Optional[str]],
1515
# (filename, file (or bytes), content_type, headers)
16-
typing.Tuple[
17-
typing.Optional[str],
16+
Tuple[
17+
Optional[str],
1818
FileContent,
19-
typing.Optional[str],
20-
typing.Mapping[str, str],
19+
Optional[str],
20+
Mapping[str, str],
2121
],
2222
]
2323

2424

2525
def convert_file_dict_to_httpx_tuples(
26-
d: typing.Dict[str, typing.Union[File, typing.List[File]]],
27-
) -> typing.List[typing.Tuple[str, File]]:
26+
d: Dict[str, Union[File, List[File]]],
27+
) -> List[Tuple[str, File]]:
2828
"""
2929
The format we use is a list of tuples, where the first element is the
3030
name of the file and the second is the file object. Typically HTTPX wants
@@ -41,3 +41,22 @@ def convert_file_dict_to_httpx_tuples(
4141
else:
4242
httpx_tuples.append((key, file_like))
4343
return httpx_tuples
44+
45+
46+
def with_content_type(*, file: File, content_type: str) -> File:
47+
""" """
48+
if isinstance(file, tuple):
49+
if len(file) == 2:
50+
filename, content = cast(Tuple[Optional[str], FileContent], file) # type: ignore
51+
return (filename, content, content_type)
52+
elif len(file) == 3:
53+
filename, content, _ = cast(Tuple[Optional[str], FileContent, Optional[str]], file) # type: ignore
54+
return (filename, content, content_type)
55+
elif len(file) == 4:
56+
filename, content, _, headers = cast( # type: ignore
57+
Tuple[Optional[str], FileContent, Optional[str], Mapping[str, str]], file
58+
)
59+
return (filename, content, content_type, headers)
60+
else:
61+
raise ValueError(f"Unexpected tuple length: {len(file)}")
62+
return (None, file, content_type)

src/elevenlabs/core/pydantic_utilities.py

+58-24
Original file line numberDiff line numberDiff line change
@@ -99,30 +99,64 @@ def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]:
9999
Override the default dict method to `exclude_unset` by default. This function patches
100100
`exclude_unset` to work include fields within non-None default values.
101101
"""
102-
_fields_set = self.__fields_set__
103-
104-
fields = _get_model_fields(self.__class__)
105-
for name, field in fields.items():
106-
if name not in _fields_set:
107-
default = _get_field_default(field)
108-
109-
# If the default values are non-null act like they've been set
110-
# This effectively allows exclude_unset to work like exclude_none where
111-
# the latter passes through intentionally set none values.
112-
if default != None:
113-
_fields_set.add(name)
114-
115-
kwargs_with_defaults_exclude_unset: typing.Any = {
116-
"by_alias": True,
117-
"exclude_unset": True,
118-
"include": _fields_set,
119-
**kwargs,
120-
}
121-
102+
# Note: the logic here is multi-plexed given the levers exposed in Pydantic V1 vs V2
103+
# Pydantic V1's .dict can be extremely slow, so we do not want to call it twice.
104+
#
105+
# We'd ideally do the same for Pydantic V2, but it shells out to a library to serialize models
106+
# that we have less control over, and this is less intrusive than custom serializers for now.
122107
if IS_PYDANTIC_V2:
123-
return super().model_dump(**kwargs_with_defaults_exclude_unset) # type: ignore # Pydantic v2
108+
kwargs_with_defaults_exclude_unset: typing.Any = {
109+
**kwargs,
110+
"by_alias": True,
111+
"exclude_unset": True,
112+
"exclude_none": False,
113+
}
114+
kwargs_with_defaults_exclude_none: typing.Any = {
115+
**kwargs,
116+
"by_alias": True,
117+
"exclude_none": True,
118+
"exclude_unset": False,
119+
}
120+
return deep_union_pydantic_dicts(
121+
super().model_dump(**kwargs_with_defaults_exclude_unset), # type: ignore # Pydantic v2
122+
super().model_dump(**kwargs_with_defaults_exclude_none), # type: ignore # Pydantic v2
123+
)
124+
124125
else:
125-
return super().dict(**kwargs_with_defaults_exclude_unset)
126+
_fields_set = self.__fields_set__
127+
128+
fields = _get_model_fields(self.__class__)
129+
for name, field in fields.items():
130+
if name not in _fields_set:
131+
default = _get_field_default(field)
132+
133+
# If the default values are non-null act like they've been set
134+
# This effectively allows exclude_unset to work like exclude_none where
135+
# the latter passes through intentionally set none values.
136+
if default != None:
137+
_fields_set.add(name)
138+
139+
kwargs_with_defaults_exclude_unset_include_fields: typing.Any = {
140+
"by_alias": True,
141+
"exclude_unset": True,
142+
"include": _fields_set,
143+
**kwargs,
144+
}
145+
146+
return super().dict(**kwargs_with_defaults_exclude_unset_include_fields)
147+
148+
149+
def deep_union_pydantic_dicts(
150+
source: typing.Dict[str, typing.Any], destination: typing.Dict[str, typing.Any]
151+
) -> typing.Dict[str, typing.Any]:
152+
for key, value in source.items():
153+
if isinstance(value, dict):
154+
node = destination.setdefault(key, {})
155+
deep_union_pydantic_dicts(value, node)
156+
else:
157+
destination[key] = value
158+
159+
return destination
126160

127161

128162
if IS_PYDANTIC_V2:
@@ -149,11 +183,11 @@ def encode_by_type(o: typing.Any) -> typing.Any:
149183
return encoder(o)
150184

151185

152-
def update_forward_refs(model: typing.Type["Model"]) -> None:
186+
def update_forward_refs(model: typing.Type["Model"], **localns: typing.Any) -> None:
153187
if IS_PYDANTIC_V2:
154188
model.model_rebuild(raise_errors=False) # type: ignore # Pydantic v2
155189
else:
156-
model.update_forward_refs()
190+
model.update_forward_refs(**localns)
157191

158192

159193
# Mirrors Pydantic's internal typing

0 commit comments

Comments
 (0)