spitch 1.19.0__py3-none-any.whl → 1.21.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.
Potentially problematic release.
This version of spitch might be problematic. Click here for more details.
- spitch/_base_client.py +25 -65
- spitch/_client.py +2 -2
- spitch/_constants.py +1 -1
- spitch/_files.py +1 -1
- spitch/_models.py +16 -7
- spitch/_response.py +9 -3
- spitch/_utils/_sync.py +17 -2
- spitch/_utils/_transform.py +11 -1
- spitch/_version.py +1 -1
- spitch/resources/speech.py +6 -2
- spitch/resources/text.py +2 -2
- spitch/types/speech_generate_params.py +2 -0
- {spitch-1.19.0.dist-info → spitch-1.21.0.dist-info}/METADATA +34 -7
- {spitch-1.19.0.dist-info → spitch-1.21.0.dist-info}/RECORD +16 -16
- {spitch-1.19.0.dist-info → spitch-1.21.0.dist-info}/WHEEL +0 -0
- {spitch-1.19.0.dist-info → spitch-1.21.0.dist-info}/licenses/LICENSE +0 -0
spitch/_base_client.py
CHANGED
|
@@ -9,7 +9,6 @@ import asyncio
|
|
|
9
9
|
import inspect
|
|
10
10
|
import logging
|
|
11
11
|
import platform
|
|
12
|
-
import warnings
|
|
13
12
|
import email.utils
|
|
14
13
|
from types import TracebackType
|
|
15
14
|
from random import random
|
|
@@ -36,7 +35,7 @@ import anyio
|
|
|
36
35
|
import httpx
|
|
37
36
|
import distro
|
|
38
37
|
import pydantic
|
|
39
|
-
from httpx import URL
|
|
38
|
+
from httpx import URL
|
|
40
39
|
from pydantic import PrivateAttr
|
|
41
40
|
|
|
42
41
|
from . import _exceptions
|
|
@@ -51,19 +50,16 @@ from ._types import (
|
|
|
51
50
|
Timeout,
|
|
52
51
|
NotGiven,
|
|
53
52
|
ResponseT,
|
|
54
|
-
Transport,
|
|
55
53
|
AnyMapping,
|
|
56
54
|
PostParser,
|
|
57
|
-
ProxiesTypes,
|
|
58
55
|
RequestFiles,
|
|
59
56
|
HttpxSendArgs,
|
|
60
|
-
AsyncTransport,
|
|
61
57
|
RequestOptions,
|
|
62
58
|
HttpxRequestFiles,
|
|
63
59
|
ModelBuilderProtocol,
|
|
64
60
|
)
|
|
65
61
|
from ._utils import is_dict, is_list, asyncify, is_given, lru_cache, is_mapping
|
|
66
|
-
from ._compat import model_copy, model_dump
|
|
62
|
+
from ._compat import PYDANTIC_V2, model_copy, model_dump
|
|
67
63
|
from ._models import GenericModel, FinalRequestOptions, validate_type, construct_type
|
|
68
64
|
from ._response import (
|
|
69
65
|
APIResponse,
|
|
@@ -207,6 +203,9 @@ class BaseSyncPage(BasePage[_T], Generic[_T]):
|
|
|
207
203
|
model: Type[_T],
|
|
208
204
|
options: FinalRequestOptions,
|
|
209
205
|
) -> None:
|
|
206
|
+
if PYDANTIC_V2 and getattr(self, "__pydantic_private__", None) is None:
|
|
207
|
+
self.__pydantic_private__ = {}
|
|
208
|
+
|
|
210
209
|
self._model = model
|
|
211
210
|
self._client = client
|
|
212
211
|
self._options = options
|
|
@@ -292,6 +291,9 @@ class BaseAsyncPage(BasePage[_T], Generic[_T]):
|
|
|
292
291
|
client: AsyncAPIClient,
|
|
293
292
|
options: FinalRequestOptions,
|
|
294
293
|
) -> None:
|
|
294
|
+
if PYDANTIC_V2 and getattr(self, "__pydantic_private__", None) is None:
|
|
295
|
+
self.__pydantic_private__ = {}
|
|
296
|
+
|
|
295
297
|
self._model = model
|
|
296
298
|
self._client = client
|
|
297
299
|
self._options = options
|
|
@@ -331,9 +333,6 @@ class BaseClient(Generic[_HttpxClientT, _DefaultStreamT]):
|
|
|
331
333
|
_base_url: URL
|
|
332
334
|
max_retries: int
|
|
333
335
|
timeout: Union[float, Timeout, None]
|
|
334
|
-
_limits: httpx.Limits
|
|
335
|
-
_proxies: ProxiesTypes | None
|
|
336
|
-
_transport: Transport | AsyncTransport | None
|
|
337
336
|
_strict_response_validation: bool
|
|
338
337
|
_idempotency_header: str | None
|
|
339
338
|
_default_stream_cls: type[_DefaultStreamT] | None = None
|
|
@@ -346,9 +345,6 @@ class BaseClient(Generic[_HttpxClientT, _DefaultStreamT]):
|
|
|
346
345
|
_strict_response_validation: bool,
|
|
347
346
|
max_retries: int = DEFAULT_MAX_RETRIES,
|
|
348
347
|
timeout: float | Timeout | None = DEFAULT_TIMEOUT,
|
|
349
|
-
limits: httpx.Limits,
|
|
350
|
-
transport: Transport | AsyncTransport | None,
|
|
351
|
-
proxies: ProxiesTypes | None,
|
|
352
348
|
custom_headers: Mapping[str, str] | None = None,
|
|
353
349
|
custom_query: Mapping[str, object] | None = None,
|
|
354
350
|
) -> None:
|
|
@@ -356,9 +352,6 @@ class BaseClient(Generic[_HttpxClientT, _DefaultStreamT]):
|
|
|
356
352
|
self._base_url = self._enforce_trailing_slash(URL(base_url))
|
|
357
353
|
self.max_retries = max_retries
|
|
358
354
|
self.timeout = timeout
|
|
359
|
-
self._limits = limits
|
|
360
|
-
self._proxies = proxies
|
|
361
|
-
self._transport = transport
|
|
362
355
|
self._custom_headers = custom_headers or {}
|
|
363
356
|
self._custom_query = custom_query or {}
|
|
364
357
|
self._strict_response_validation = _strict_response_validation
|
|
@@ -418,10 +411,17 @@ class BaseClient(Generic[_HttpxClientT, _DefaultStreamT]):
|
|
|
418
411
|
if idempotency_header and options.method.lower() != "get" and idempotency_header not in headers:
|
|
419
412
|
headers[idempotency_header] = options.idempotency_key or self._idempotency_key()
|
|
420
413
|
|
|
421
|
-
# Don't set
|
|
414
|
+
# Don't set these headers if they were already set or removed by the caller. We check
|
|
422
415
|
# `custom_headers`, which can contain `Omit()`, instead of `headers` to account for the removal case.
|
|
423
|
-
|
|
416
|
+
lower_custom_headers = [header.lower() for header in custom_headers]
|
|
417
|
+
if "x-stainless-retry-count" not in lower_custom_headers:
|
|
424
418
|
headers["x-stainless-retry-count"] = str(retries_taken)
|
|
419
|
+
if "x-stainless-read-timeout" not in lower_custom_headers:
|
|
420
|
+
timeout = self.timeout if isinstance(options.timeout, NotGiven) else options.timeout
|
|
421
|
+
if isinstance(timeout, Timeout):
|
|
422
|
+
timeout = timeout.read
|
|
423
|
+
if timeout is not None:
|
|
424
|
+
headers["x-stainless-read-timeout"] = str(timeout)
|
|
425
425
|
|
|
426
426
|
return headers
|
|
427
427
|
|
|
@@ -511,7 +511,7 @@ class BaseClient(Generic[_HttpxClientT, _DefaultStreamT]):
|
|
|
511
511
|
# so that passing a `TypedDict` doesn't cause an error.
|
|
512
512
|
# https://github.com/microsoft/pyright/issues/3526#event-6715453066
|
|
513
513
|
params=self.qs.stringify(cast(Mapping[str, Any], params)) if params else None,
|
|
514
|
-
json=json_data,
|
|
514
|
+
json=json_data if is_given(json_data) else None,
|
|
515
515
|
files=files,
|
|
516
516
|
**kwargs,
|
|
517
517
|
)
|
|
@@ -767,6 +767,9 @@ else:
|
|
|
767
767
|
|
|
768
768
|
class SyncHttpxClientWrapper(DefaultHttpxClient):
|
|
769
769
|
def __del__(self) -> None:
|
|
770
|
+
if self.is_closed:
|
|
771
|
+
return
|
|
772
|
+
|
|
770
773
|
try:
|
|
771
774
|
self.close()
|
|
772
775
|
except Exception:
|
|
@@ -784,43 +787,11 @@ class SyncAPIClient(BaseClient[httpx.Client, Stream[Any]]):
|
|
|
784
787
|
base_url: str | URL,
|
|
785
788
|
max_retries: int = DEFAULT_MAX_RETRIES,
|
|
786
789
|
timeout: float | Timeout | None | NotGiven = NOT_GIVEN,
|
|
787
|
-
transport: Transport | None = None,
|
|
788
|
-
proxies: ProxiesTypes | None = None,
|
|
789
|
-
limits: Limits | None = None,
|
|
790
790
|
http_client: httpx.Client | None = None,
|
|
791
791
|
custom_headers: Mapping[str, str] | None = None,
|
|
792
792
|
custom_query: Mapping[str, object] | None = None,
|
|
793
793
|
_strict_response_validation: bool,
|
|
794
794
|
) -> None:
|
|
795
|
-
if limits is not None:
|
|
796
|
-
warnings.warn(
|
|
797
|
-
"The `connection_pool_limits` argument is deprecated. The `http_client` argument should be passed instead",
|
|
798
|
-
category=DeprecationWarning,
|
|
799
|
-
stacklevel=3,
|
|
800
|
-
)
|
|
801
|
-
if http_client is not None:
|
|
802
|
-
raise ValueError("The `http_client` argument is mutually exclusive with `connection_pool_limits`")
|
|
803
|
-
else:
|
|
804
|
-
limits = DEFAULT_CONNECTION_LIMITS
|
|
805
|
-
|
|
806
|
-
if transport is not None:
|
|
807
|
-
warnings.warn(
|
|
808
|
-
"The `transport` argument is deprecated. The `http_client` argument should be passed instead",
|
|
809
|
-
category=DeprecationWarning,
|
|
810
|
-
stacklevel=3,
|
|
811
|
-
)
|
|
812
|
-
if http_client is not None:
|
|
813
|
-
raise ValueError("The `http_client` argument is mutually exclusive with `transport`")
|
|
814
|
-
|
|
815
|
-
if proxies is not None:
|
|
816
|
-
warnings.warn(
|
|
817
|
-
"The `proxies` argument is deprecated. The `http_client` argument should be passed instead",
|
|
818
|
-
category=DeprecationWarning,
|
|
819
|
-
stacklevel=3,
|
|
820
|
-
)
|
|
821
|
-
if http_client is not None:
|
|
822
|
-
raise ValueError("The `http_client` argument is mutually exclusive with `proxies`")
|
|
823
|
-
|
|
824
795
|
if not is_given(timeout):
|
|
825
796
|
# if the user passed in a custom http client with a non-default
|
|
826
797
|
# timeout set then we use that timeout.
|
|
@@ -841,12 +812,9 @@ class SyncAPIClient(BaseClient[httpx.Client, Stream[Any]]):
|
|
|
841
812
|
|
|
842
813
|
super().__init__(
|
|
843
814
|
version=version,
|
|
844
|
-
limits=limits,
|
|
845
815
|
# cast to a valid type because mypy doesn't understand our type narrowing
|
|
846
816
|
timeout=cast(Timeout, timeout),
|
|
847
|
-
proxies=proxies,
|
|
848
817
|
base_url=base_url,
|
|
849
|
-
transport=transport,
|
|
850
818
|
max_retries=max_retries,
|
|
851
819
|
custom_query=custom_query,
|
|
852
820
|
custom_headers=custom_headers,
|
|
@@ -855,11 +823,7 @@ class SyncAPIClient(BaseClient[httpx.Client, Stream[Any]]):
|
|
|
855
823
|
self._client = http_client or SyncHttpxClientWrapper(
|
|
856
824
|
base_url=base_url,
|
|
857
825
|
# cast to a valid type because mypy doesn't understand our type narrowing
|
|
858
|
-
timeout=cast(Timeout, timeout)
|
|
859
|
-
proxies=proxies,
|
|
860
|
-
transport=transport,
|
|
861
|
-
limits=limits,
|
|
862
|
-
follow_redirects=True,
|
|
826
|
+
timeout=cast(Timeout, timeout)
|
|
863
827
|
)
|
|
864
828
|
|
|
865
829
|
def is_closed(self) -> bool:
|
|
@@ -1332,6 +1296,9 @@ else:
|
|
|
1332
1296
|
|
|
1333
1297
|
class AsyncHttpxClientWrapper(DefaultAsyncHttpxClient):
|
|
1334
1298
|
def __del__(self) -> None:
|
|
1299
|
+
if self.is_closed:
|
|
1300
|
+
return
|
|
1301
|
+
|
|
1335
1302
|
try:
|
|
1336
1303
|
# TODO(someday): support non asyncio runtimes here
|
|
1337
1304
|
asyncio.get_running_loop().create_task(self.aclose())
|
|
@@ -1351,9 +1318,6 @@ class AsyncAPIClient(BaseClient[httpx.AsyncClient, AsyncStream[Any]]):
|
|
|
1351
1318
|
_strict_response_validation: bool,
|
|
1352
1319
|
max_retries: int = DEFAULT_MAX_RETRIES,
|
|
1353
1320
|
timeout: float | Timeout | None | NotGiven = NOT_GIVEN,
|
|
1354
|
-
transport: AsyncTransport | None = None,
|
|
1355
|
-
proxies: ProxiesTypes | None = None,
|
|
1356
|
-
limits: Limits | None = None,
|
|
1357
1321
|
http_client: httpx.AsyncClient | None = None,
|
|
1358
1322
|
custom_headers: Mapping[str, str] | None = None,
|
|
1359
1323
|
custom_query: Mapping[str, object] | None = None,
|
|
@@ -1386,7 +1350,6 @@ class AsyncAPIClient(BaseClient[httpx.AsyncClient, AsyncStream[Any]]):
|
|
|
1386
1350
|
)
|
|
1387
1351
|
if http_client is not None:
|
|
1388
1352
|
raise ValueError("The `http_client` argument is mutually exclusive with `proxies`")
|
|
1389
|
-
|
|
1390
1353
|
if not is_given(timeout):
|
|
1391
1354
|
# if the user passed in a custom http client with a non-default
|
|
1392
1355
|
# timeout set then we use that timeout.
|
|
@@ -1408,11 +1371,8 @@ class AsyncAPIClient(BaseClient[httpx.AsyncClient, AsyncStream[Any]]):
|
|
|
1408
1371
|
super().__init__(
|
|
1409
1372
|
version=version,
|
|
1410
1373
|
base_url=base_url,
|
|
1411
|
-
limits=limits,
|
|
1412
1374
|
# cast to a valid type because mypy doesn't understand our type narrowing
|
|
1413
1375
|
timeout=cast(Timeout, timeout),
|
|
1414
|
-
proxies=proxies,
|
|
1415
|
-
transport=transport,
|
|
1416
1376
|
max_retries=max_retries,
|
|
1417
1377
|
custom_query=custom_query,
|
|
1418
1378
|
custom_headers=custom_headers,
|
spitch/_client.py
CHANGED
|
@@ -77,7 +77,7 @@ class Spitch(SyncAPIClient):
|
|
|
77
77
|
# part of our public interface in the future.
|
|
78
78
|
_strict_response_validation: bool = False,
|
|
79
79
|
) -> None:
|
|
80
|
-
"""Construct a new synchronous
|
|
80
|
+
"""Construct a new synchronous Spitch client instance.
|
|
81
81
|
|
|
82
82
|
This automatically infers the `api_key` argument from the `SPITCH_API_KEY` environment variable if it is not provided.
|
|
83
83
|
"""
|
|
@@ -247,7 +247,7 @@ class AsyncSpitch(AsyncAPIClient):
|
|
|
247
247
|
# part of our public interface in the future.
|
|
248
248
|
_strict_response_validation: bool = False,
|
|
249
249
|
) -> None:
|
|
250
|
-
"""Construct a new async
|
|
250
|
+
"""Construct a new async AsyncSpitch client instance.
|
|
251
251
|
|
|
252
252
|
This automatically infers the `api_key` argument from the `SPITCH_API_KEY` environment variable if it is not provided.
|
|
253
253
|
"""
|
spitch/_constants.py
CHANGED
|
@@ -6,7 +6,7 @@ RAW_RESPONSE_HEADER = "X-Stainless-Raw-Response"
|
|
|
6
6
|
OVERRIDE_CAST_TO_HEADER = "____stainless_override_cast_to"
|
|
7
7
|
|
|
8
8
|
# default timeout is 1 minute
|
|
9
|
-
DEFAULT_TIMEOUT = httpx.Timeout(timeout=60
|
|
9
|
+
DEFAULT_TIMEOUT = httpx.Timeout(timeout=60, connect=5.0)
|
|
10
10
|
DEFAULT_MAX_RETRIES = 2
|
|
11
11
|
DEFAULT_CONNECTION_LIMITS = httpx.Limits(max_connections=100, max_keepalive_connections=20)
|
|
12
12
|
|
spitch/_files.py
CHANGED
|
@@ -34,7 +34,7 @@ def assert_is_file_content(obj: object, *, key: str | None = None) -> None:
|
|
|
34
34
|
if not is_file_content(obj):
|
|
35
35
|
prefix = f"Expected entry at `{key}`" if key is not None else f"Expected file input `{obj!r}`"
|
|
36
36
|
raise RuntimeError(
|
|
37
|
-
f"{prefix} to be bytes, an io.IOBase instance, PathLike or a tuple but received {type(obj)} instead."
|
|
37
|
+
f"{prefix} to be bytes, an io.IOBase instance, PathLike or a tuple but received {type(obj)} instead. See https://github.com/spi-tch/spitch-python/tree/main#file-uploads"
|
|
38
38
|
) from None
|
|
39
39
|
|
|
40
40
|
|
spitch/_models.py
CHANGED
|
@@ -171,21 +171,21 @@ class BaseModel(pydantic.BaseModel):
|
|
|
171
171
|
@override
|
|
172
172
|
def __str__(self) -> str:
|
|
173
173
|
# mypy complains about an invalid self arg
|
|
174
|
-
return f
|
|
174
|
+
return f"{self.__repr_name__()}({self.__repr_str__(', ')})" # type: ignore[misc]
|
|
175
175
|
|
|
176
176
|
# Override the 'construct' method in a way that supports recursive parsing without validation.
|
|
177
177
|
# Based on https://github.com/samuelcolvin/pydantic/issues/1168#issuecomment-817742836.
|
|
178
178
|
@classmethod
|
|
179
179
|
@override
|
|
180
180
|
def construct( # pyright: ignore[reportIncompatibleMethodOverride]
|
|
181
|
-
|
|
181
|
+
__cls: Type[ModelT],
|
|
182
182
|
_fields_set: set[str] | None = None,
|
|
183
183
|
**values: object,
|
|
184
184
|
) -> ModelT:
|
|
185
|
-
m =
|
|
185
|
+
m = __cls.__new__(__cls)
|
|
186
186
|
fields_values: dict[str, object] = {}
|
|
187
187
|
|
|
188
|
-
config = get_model_config(
|
|
188
|
+
config = get_model_config(__cls)
|
|
189
189
|
populate_by_name = (
|
|
190
190
|
config.allow_population_by_field_name
|
|
191
191
|
if isinstance(config, _ConfigProtocol)
|
|
@@ -195,7 +195,7 @@ class BaseModel(pydantic.BaseModel):
|
|
|
195
195
|
if _fields_set is None:
|
|
196
196
|
_fields_set = set()
|
|
197
197
|
|
|
198
|
-
model_fields = get_model_fields(
|
|
198
|
+
model_fields = get_model_fields(__cls)
|
|
199
199
|
for name, field in model_fields.items():
|
|
200
200
|
key = field.alias
|
|
201
201
|
if key is None or (key not in values and populate_by_name):
|
|
@@ -425,6 +425,11 @@ def construct_type(*, value: object, type_: object) -> object:
|
|
|
425
425
|
|
|
426
426
|
If the given value does not match the expected type then it is returned as-is.
|
|
427
427
|
"""
|
|
428
|
+
|
|
429
|
+
# store a reference to the original type we were given before we extract any inner
|
|
430
|
+
# types so that we can properly resolve forward references in `TypeAliasType` annotations
|
|
431
|
+
original_type = None
|
|
432
|
+
|
|
428
433
|
# we allow `object` as the input type because otherwise, passing things like
|
|
429
434
|
# `Literal['value']` will be reported as a type error by type checkers
|
|
430
435
|
type_ = cast("type[object]", type_)
|
|
@@ -443,7 +448,7 @@ def construct_type(*, value: object, type_: object) -> object:
|
|
|
443
448
|
|
|
444
449
|
if is_union(origin):
|
|
445
450
|
try:
|
|
446
|
-
return validate_type(type_=cast("type[object]", type_), value=value)
|
|
451
|
+
return validate_type(type_=cast("type[object]", original_type or type_), value=value)
|
|
447
452
|
except Exception:
|
|
448
453
|
pass
|
|
449
454
|
|
|
@@ -485,7 +490,11 @@ def construct_type(*, value: object, type_: object) -> object:
|
|
|
485
490
|
_, items_type = get_args(type_) # Dict[_, items_type]
|
|
486
491
|
return {key: construct_type(value=item, type_=items_type) for key, item in value.items()}
|
|
487
492
|
|
|
488
|
-
if
|
|
493
|
+
if (
|
|
494
|
+
not is_literal_type(type_)
|
|
495
|
+
and inspect.isclass(origin)
|
|
496
|
+
and (issubclass(origin, BaseModel) or issubclass(origin, GenericModel))
|
|
497
|
+
):
|
|
489
498
|
if is_list(value):
|
|
490
499
|
return [cast(Any, type_).construct(**entry) if is_mapping(entry) else entry for entry in value]
|
|
491
500
|
|
spitch/_response.py
CHANGED
|
@@ -130,6 +130,8 @@ class BaseAPIResponse(Generic[R]):
|
|
|
130
130
|
if to and is_annotated_type(to):
|
|
131
131
|
to = extract_type_arg(to, 0)
|
|
132
132
|
|
|
133
|
+
origin = get_origin(cast_to) or cast_to
|
|
134
|
+
|
|
133
135
|
if self._is_sse_stream:
|
|
134
136
|
if to:
|
|
135
137
|
if not is_stream_class_type(to):
|
|
@@ -195,8 +197,6 @@ class BaseAPIResponse(Generic[R]):
|
|
|
195
197
|
if cast_to == bool:
|
|
196
198
|
return cast(R, response.text.lower() == "true")
|
|
197
199
|
|
|
198
|
-
origin = get_origin(cast_to) or cast_to
|
|
199
|
-
|
|
200
200
|
if origin == APIResponse:
|
|
201
201
|
raise RuntimeError("Unexpected state - cast_to is `APIResponse`")
|
|
202
202
|
|
|
@@ -210,7 +210,13 @@ class BaseAPIResponse(Generic[R]):
|
|
|
210
210
|
raise ValueError(f"Subclasses of httpx.Response cannot be passed to `cast_to`")
|
|
211
211
|
return cast(R, response)
|
|
212
212
|
|
|
213
|
-
if
|
|
213
|
+
if (
|
|
214
|
+
inspect.isclass(
|
|
215
|
+
origin # pyright: ignore[reportUnknownArgumentType]
|
|
216
|
+
)
|
|
217
|
+
and not issubclass(origin, BaseModel)
|
|
218
|
+
and issubclass(origin, pydantic.BaseModel)
|
|
219
|
+
):
|
|
214
220
|
raise TypeError("Pydantic models must subclass our base model type, e.g. `from spitch import BaseModel`")
|
|
215
221
|
|
|
216
222
|
if (
|
spitch/_utils/_sync.py
CHANGED
|
@@ -7,16 +7,20 @@ import contextvars
|
|
|
7
7
|
from typing import Any, TypeVar, Callable, Awaitable
|
|
8
8
|
from typing_extensions import ParamSpec
|
|
9
9
|
|
|
10
|
+
import anyio
|
|
11
|
+
import sniffio
|
|
12
|
+
import anyio.to_thread
|
|
13
|
+
|
|
10
14
|
T_Retval = TypeVar("T_Retval")
|
|
11
15
|
T_ParamSpec = ParamSpec("T_ParamSpec")
|
|
12
16
|
|
|
13
17
|
|
|
14
18
|
if sys.version_info >= (3, 9):
|
|
15
|
-
|
|
19
|
+
_asyncio_to_thread = asyncio.to_thread
|
|
16
20
|
else:
|
|
17
21
|
# backport of https://docs.python.org/3/library/asyncio-task.html#asyncio.to_thread
|
|
18
22
|
# for Python 3.8 support
|
|
19
|
-
async def
|
|
23
|
+
async def _asyncio_to_thread(
|
|
20
24
|
func: Callable[T_ParamSpec, T_Retval], /, *args: T_ParamSpec.args, **kwargs: T_ParamSpec.kwargs
|
|
21
25
|
) -> Any:
|
|
22
26
|
"""Asynchronously run function *func* in a separate thread.
|
|
@@ -34,6 +38,17 @@ else:
|
|
|
34
38
|
return await loop.run_in_executor(None, func_call)
|
|
35
39
|
|
|
36
40
|
|
|
41
|
+
async def to_thread(
|
|
42
|
+
func: Callable[T_ParamSpec, T_Retval], /, *args: T_ParamSpec.args, **kwargs: T_ParamSpec.kwargs
|
|
43
|
+
) -> T_Retval:
|
|
44
|
+
if sniffio.current_async_library() == "asyncio":
|
|
45
|
+
return await _asyncio_to_thread(func, *args, **kwargs)
|
|
46
|
+
|
|
47
|
+
return await anyio.to_thread.run_sync(
|
|
48
|
+
functools.partial(func, *args, **kwargs),
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
|
|
37
52
|
# inspired by `asyncer`, https://github.com/tiangolo/asyncer
|
|
38
53
|
def asyncify(function: Callable[T_ParamSpec, T_Retval]) -> Callable[T_ParamSpec, Awaitable[T_Retval]]:
|
|
39
54
|
"""
|
spitch/_utils/_transform.py
CHANGED
|
@@ -25,7 +25,7 @@ from ._typing import (
|
|
|
25
25
|
is_annotated_type,
|
|
26
26
|
strip_annotated_type,
|
|
27
27
|
)
|
|
28
|
-
from .._compat import model_dump, is_typeddict
|
|
28
|
+
from .._compat import get_origin, model_dump, is_typeddict
|
|
29
29
|
|
|
30
30
|
_T = TypeVar("_T")
|
|
31
31
|
|
|
@@ -164,9 +164,14 @@ def _transform_recursive(
|
|
|
164
164
|
inner_type = annotation
|
|
165
165
|
|
|
166
166
|
stripped_type = strip_annotated_type(inner_type)
|
|
167
|
+
origin = get_origin(stripped_type) or stripped_type
|
|
167
168
|
if is_typeddict(stripped_type) and is_mapping(data):
|
|
168
169
|
return _transform_typeddict(data, stripped_type)
|
|
169
170
|
|
|
171
|
+
if origin == dict and is_mapping(data):
|
|
172
|
+
items_type = get_args(stripped_type)[1]
|
|
173
|
+
return {key: _transform_recursive(value, annotation=items_type) for key, value in data.items()}
|
|
174
|
+
|
|
170
175
|
if (
|
|
171
176
|
# List[T]
|
|
172
177
|
(is_list_type(stripped_type) and is_list(data))
|
|
@@ -307,9 +312,14 @@ async def _async_transform_recursive(
|
|
|
307
312
|
inner_type = annotation
|
|
308
313
|
|
|
309
314
|
stripped_type = strip_annotated_type(inner_type)
|
|
315
|
+
origin = get_origin(stripped_type) or stripped_type
|
|
310
316
|
if is_typeddict(stripped_type) and is_mapping(data):
|
|
311
317
|
return await _async_transform_typeddict(data, stripped_type)
|
|
312
318
|
|
|
319
|
+
if origin == dict and is_mapping(data):
|
|
320
|
+
items_type = get_args(stripped_type)[1]
|
|
321
|
+
return {key: _transform_recursive(value, annotation=items_type) for key, value in data.items()}
|
|
322
|
+
|
|
313
323
|
if (
|
|
314
324
|
# List[T]
|
|
315
325
|
(is_list_type(stripped_type) and is_list(data))
|
spitch/_version.py
CHANGED
spitch/resources/speech.py
CHANGED
|
@@ -41,7 +41,7 @@ class SpeechResource(SyncAPIResource):
|
|
|
41
41
|
@cached_property
|
|
42
42
|
def with_raw_response(self) -> SpeechResourceWithRawResponse:
|
|
43
43
|
"""
|
|
44
|
-
This property can be used as a prefix for any HTTP method call to return
|
|
44
|
+
This property can be used as a prefix for any HTTP method call to return
|
|
45
45
|
the raw response object instead of the parsed content.
|
|
46
46
|
|
|
47
47
|
For more information, see https://www.github.com/spi-tch/spitch-python#accessing-raw-response-data-eg-headers
|
|
@@ -79,6 +79,8 @@ class SpeechResource(SyncAPIResource):
|
|
|
79
79
|
"lina",
|
|
80
80
|
"john",
|
|
81
81
|
"jude",
|
|
82
|
+
"henry",
|
|
83
|
+
"kani",
|
|
82
84
|
],
|
|
83
85
|
stream: bool | NotGiven = NOT_GIVEN,
|
|
84
86
|
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
|
|
@@ -177,7 +179,7 @@ class AsyncSpeechResource(AsyncAPIResource):
|
|
|
177
179
|
@cached_property
|
|
178
180
|
def with_raw_response(self) -> AsyncSpeechResourceWithRawResponse:
|
|
179
181
|
"""
|
|
180
|
-
This property can be used as a prefix for any HTTP method call to return
|
|
182
|
+
This property can be used as a prefix for any HTTP method call to return
|
|
181
183
|
the raw response object instead of the parsed content.
|
|
182
184
|
|
|
183
185
|
For more information, see https://www.github.com/spi-tch/spitch-python#accessing-raw-response-data-eg-headers
|
|
@@ -215,6 +217,8 @@ class AsyncSpeechResource(AsyncAPIResource):
|
|
|
215
217
|
"lina",
|
|
216
218
|
"john",
|
|
217
219
|
"jude",
|
|
220
|
+
"henry",
|
|
221
|
+
"kani",
|
|
218
222
|
],
|
|
219
223
|
stream: bool | NotGiven = NOT_GIVEN,
|
|
220
224
|
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
|
spitch/resources/text.py
CHANGED
|
@@ -31,7 +31,7 @@ class TextResource(SyncAPIResource):
|
|
|
31
31
|
@cached_property
|
|
32
32
|
def with_raw_response(self) -> TextResourceWithRawResponse:
|
|
33
33
|
"""
|
|
34
|
-
This property can be used as a prefix for any HTTP method call to return
|
|
34
|
+
This property can be used as a prefix for any HTTP method call to return
|
|
35
35
|
the raw response object instead of the parsed content.
|
|
36
36
|
|
|
37
37
|
For more information, see https://www.github.com/spi-tch/spitch-python#accessing-raw-response-data-eg-headers
|
|
@@ -132,7 +132,7 @@ class AsyncTextResource(AsyncAPIResource):
|
|
|
132
132
|
@cached_property
|
|
133
133
|
def with_raw_response(self) -> AsyncTextResourceWithRawResponse:
|
|
134
134
|
"""
|
|
135
|
-
This property can be used as a prefix for any HTTP method call to return
|
|
135
|
+
This property can be used as a prefix for any HTTP method call to return
|
|
136
136
|
the raw response object instead of the parsed content.
|
|
137
137
|
|
|
138
138
|
For more information, see https://www.github.com/spi-tch/spitch-python#accessing-raw-response-data-eg-headers
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: spitch
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.21.0
|
|
4
4
|
Summary: The official Python library for the spitch API
|
|
5
5
|
Project-URL: Homepage, https://github.com/spi-tch/spitch-python
|
|
6
6
|
Project-URL: Repository, https://github.com/spi-tch/spitch-python
|
|
7
|
-
Author-email: Spitch <
|
|
7
|
+
Author-email: Spitch <hello@spi-tch.com>
|
|
8
8
|
License-Expression: Apache-2.0
|
|
9
9
|
License-File: LICENSE
|
|
10
10
|
Classifier: Intended Audience :: Developers
|
|
@@ -39,7 +39,7 @@ The Spitch Python library provides convenient access to the Spitch REST API from
|
|
|
39
39
|
application. The library includes type definitions for all request params and response fields,
|
|
40
40
|
and offers both synchronous and asynchronous clients powered by [httpx](https://github.com/encode/httpx).
|
|
41
41
|
|
|
42
|
-
It is generated with [Stainless](https://www.
|
|
42
|
+
It is generated with [Stainless](https://www.stainless.com/).
|
|
43
43
|
|
|
44
44
|
## Documentation
|
|
45
45
|
|
|
@@ -112,6 +112,24 @@ Nested request parameters are [TypedDicts](https://docs.python.org/3/library/typ
|
|
|
112
112
|
|
|
113
113
|
Typed requests and responses provide autocomplete and documentation within your editor. If you would like to see type errors in VS Code to help catch bugs earlier, set `python.analysis.typeCheckingMode` to `basic`.
|
|
114
114
|
|
|
115
|
+
## File uploads
|
|
116
|
+
|
|
117
|
+
Request parameters that correspond to file uploads can be passed as `bytes`, a [`PathLike`](https://docs.python.org/3/library/os.html#os.PathLike) instance or a tuple of `(filename, contents, media type)`.
|
|
118
|
+
|
|
119
|
+
```python
|
|
120
|
+
from pathlib import Path
|
|
121
|
+
from spitch import Spitch
|
|
122
|
+
|
|
123
|
+
client = Spitch()
|
|
124
|
+
|
|
125
|
+
client.speech.transcribe(
|
|
126
|
+
language="yo",
|
|
127
|
+
content=Path("/path/to/file"),
|
|
128
|
+
)
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
The async client uses the exact same interface. If you pass a [`PathLike`](https://docs.python.org/3/library/os.html#os.PathLike) instance, the file contents will be read asynchronously automatically.
|
|
132
|
+
|
|
115
133
|
## Handling errors
|
|
116
134
|
|
|
117
135
|
When the library is unable to connect to the API (for example, due to network connection problems or a timeout), a subclass of `spitch.APIConnectionError` is raised.
|
|
@@ -144,7 +162,7 @@ except spitch.APIStatusError as e:
|
|
|
144
162
|
print(e.response)
|
|
145
163
|
```
|
|
146
164
|
|
|
147
|
-
Error codes are as
|
|
165
|
+
Error codes are as follows:
|
|
148
166
|
|
|
149
167
|
| Status Code | Error Type |
|
|
150
168
|
| ----------- | -------------------------- |
|
|
@@ -289,8 +307,7 @@ If you need to access undocumented endpoints, params, or response properties, th
|
|
|
289
307
|
#### Undocumented endpoints
|
|
290
308
|
|
|
291
309
|
To make requests to undocumented endpoints, you can make requests using `client.get`, `client.post`, and other
|
|
292
|
-
http verbs. Options on the client will be respected (such as retries)
|
|
293
|
-
request.
|
|
310
|
+
http verbs. Options on the client will be respected (such as retries) when making this request.
|
|
294
311
|
|
|
295
312
|
```py
|
|
296
313
|
import httpx
|
|
@@ -346,12 +363,22 @@ client.with_options(http_client=DefaultHttpxClient(...))
|
|
|
346
363
|
|
|
347
364
|
By default the library closes underlying HTTP connections whenever the client is [garbage collected](https://docs.python.org/3/reference/datamodel.html#object.__del__). You can manually close the client using the `.close()` method if desired, or with a context manager that closes when exiting.
|
|
348
365
|
|
|
366
|
+
```py
|
|
367
|
+
from spitch import Spitch
|
|
368
|
+
|
|
369
|
+
with Spitch() as client:
|
|
370
|
+
# make requests here
|
|
371
|
+
...
|
|
372
|
+
|
|
373
|
+
# HTTP client is now closed
|
|
374
|
+
```
|
|
375
|
+
|
|
349
376
|
## Versioning
|
|
350
377
|
|
|
351
378
|
This package generally follows [SemVer](https://semver.org/spec/v2.0.0.html) conventions, though certain backwards-incompatible changes may be released as minor versions:
|
|
352
379
|
|
|
353
380
|
1. Changes that only affect static types, without breaking runtime behavior.
|
|
354
|
-
2. Changes to library internals which are technically public but not intended or documented for external use. _(Please open a GitHub issue to let us know if you are relying on such internals)_
|
|
381
|
+
2. Changes to library internals which are technically public but not intended or documented for external use. _(Please open a GitHub issue to let us know if you are relying on such internals.)_
|
|
355
382
|
3. Changes that we do not expect to impact the vast majority of users in practice.
|
|
356
383
|
|
|
357
384
|
We take backwards-compatibility seriously and work hard to ensure you can rely on a smooth upgrade experience.
|
|
@@ -1,40 +1,40 @@
|
|
|
1
1
|
spitch/__init__.py,sha256=mBQhmNu88PeR66Zmw9atnzuU8f1mpSPcW7vn-EeSikE,2399
|
|
2
|
-
spitch/_base_client.py,sha256=
|
|
3
|
-
spitch/_client.py,sha256=
|
|
2
|
+
spitch/_base_client.py,sha256=_0Ke1uTQh6OVX9isEL3Nu-3Ui8cK8P6hUNGitUc9KBM,66369
|
|
3
|
+
spitch/_client.py,sha256=53FDDffn_8d-95rmM7-awLEUo9h_K2563fOmQm6qL-I,15451
|
|
4
4
|
spitch/_compat.py,sha256=fQkXUY7reJc8m_yguMWSjHBfO8lNzw4wOAxtkhP9d1Q,6607
|
|
5
|
-
spitch/_constants.py,sha256=
|
|
5
|
+
spitch/_constants.py,sha256=S14PFzyN9-I31wiV7SmIlL5Ga0MLHxdvegInGdXH7tM,462
|
|
6
6
|
spitch/_exceptions.py,sha256=xsQtKJTiIdz2X1bQDQFZcSW7WBofLazdQm9nMCyPEVM,3220
|
|
7
|
-
spitch/_files.py,sha256=
|
|
8
|
-
spitch/_models.py,sha256=
|
|
7
|
+
spitch/_files.py,sha256=wV8OmI8oHeNVRtF-7aAEu22jtRG4FzjOioE8lBp-jNA,3617
|
|
8
|
+
spitch/_models.py,sha256=l6MV_1qor7alt-wmx365DEIjAUYGxkrbwc2slEkyJ-I,28765
|
|
9
9
|
spitch/_qs.py,sha256=AOkSz4rHtK4YI3ZU_kzea-zpwBUgEY8WniGmTPyEimc,4846
|
|
10
10
|
spitch/_resource.py,sha256=TLFPcOOmtxZOQLh3XCNPB_BdrQpp0MIYoKoH52aRAu8,1100
|
|
11
|
-
spitch/_response.py,sha256=
|
|
11
|
+
spitch/_response.py,sha256=F9KG5S6W8E3U7Rg9Arb0PqDYly_VlaC1ul6W1UgInQo,28733
|
|
12
12
|
spitch/_streaming.py,sha256=5SpId2EIfF8Ee8UUYmJxqgHUGP1ZdHCUHhHCdNJREFA,10100
|
|
13
13
|
spitch/_types.py,sha256=uuSZot9wXgdAMJzfF3raLmt3DvhThG7skqUC98_Dm1k,6167
|
|
14
|
-
spitch/_version.py,sha256=
|
|
14
|
+
spitch/_version.py,sha256=EwbxZFxfUDlM0URRPdYxSuB2HC_NTV-B3R6sJdzV8bs,159
|
|
15
15
|
spitch/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
16
16
|
spitch/_utils/__init__.py,sha256=k266EatJr88V8Zseb7xUimTlCeno9SynRfLwadHP1b4,2016
|
|
17
17
|
spitch/_utils/_logs.py,sha256=ApRyYK_WgZfEr_ygBUBXWMlTgeMr2tdNOGlH8jE4oJc,774
|
|
18
18
|
spitch/_utils/_proxy.py,sha256=z3zsateHtb0EARTWKk8QZNHfPkqJbqwd1lM993LBwGE,1902
|
|
19
19
|
spitch/_utils/_reflection.py,sha256=ZmGkIgT_PuwedyNBrrKGbxoWtkpytJNU1uU4QHnmEMU,1364
|
|
20
20
|
spitch/_utils/_streams.py,sha256=SMC90diFFecpEg_zgDRVbdR3hSEIgVVij4taD-noMLM,289
|
|
21
|
-
spitch/_utils/_sync.py,sha256=
|
|
22
|
-
spitch/_utils/_transform.py,sha256=
|
|
21
|
+
spitch/_utils/_sync.py,sha256=TpGLrrhRNWTJtODNE6Fup3_k7zrWm1j2RlirzBwre-0,2862
|
|
22
|
+
spitch/_utils/_transform.py,sha256=tsSFOIZ7iczaUsMSGBD_iSFOOdUyT2xtkcq1xyF0L9o,13986
|
|
23
23
|
spitch/_utils/_typing.py,sha256=tFbktdpdHCQliwzGsWysgn0P5H0JRdagkZdb_LegGkY,3838
|
|
24
24
|
spitch/_utils/_utils.py,sha256=8UmbPOy_AAr2uUjjFui-VZSrVBHRj6bfNEKRp5YZP2A,12004
|
|
25
25
|
spitch/lib/.keep,sha256=wuNrz-5SXo3jJaJOJgz4vFHM41YH_g20F5cRQo0vLes,224
|
|
26
26
|
spitch/resources/__init__.py,sha256=KT6rAvIlWHQk9QdM4Jp8ABziKILaBrrtiO7LCB5Wa5E,976
|
|
27
|
-
spitch/resources/speech.py,sha256=
|
|
28
|
-
spitch/resources/text.py,sha256=
|
|
27
|
+
spitch/resources/speech.py,sha256=LpI8rtw6xCuqEM_xBec9_owQAsPDRfeqH4azPzwnYWU,13011
|
|
28
|
+
spitch/resources/text.py,sha256=0C2v6i5nawuwHkPbUo-J0RSrmMvAbKPmCPVwuLQTJmY,9657
|
|
29
29
|
spitch/types/__init__.py,sha256=xBZbzXwV3WlBdawp4Mb4IqoQG4VfaLbi-4FMqPbwqlE,704
|
|
30
|
-
spitch/types/speech_generate_params.py,sha256=
|
|
30
|
+
spitch/types/speech_generate_params.py,sha256=dBCEBNvo1f08VfT3zutoUEHwYGe5mSrr3-6qipTGJ_Y,799
|
|
31
31
|
spitch/types/speech_transcribe_params.py,sha256=2nUgsYIURWTKx8qBYE0WfIHh6hXVcdgCz-UC1wIaXpA,515
|
|
32
32
|
spitch/types/speech_transcribe_response.py,sha256=mGNIIK-A_YOTi78tlYMRCKykPiFjSWm4gsRjPk1IvF8,516
|
|
33
33
|
spitch/types/text_tone_mark_params.py,sha256=63P5VElxanYkDP1ZLEuQt97JSgVpMCaAo4WRWLDvhlY,349
|
|
34
34
|
spitch/types/text_tone_mark_response.py,sha256=WGxZsBxLceZ03VM5dafZshp6azdDxpNHcJHhBX7A5DY,277
|
|
35
35
|
spitch/types/text_translate_params.py,sha256=rU5hbTyBaP5L_akmgswHoNLcTKWN0Gz1kL1yt3EQL44,404
|
|
36
36
|
spitch/types/text_translate_response.py,sha256=Az3QSpvarlCNTiB7uVzMH21YoWHWJMBEvgdKgVJZW4M,279
|
|
37
|
-
spitch-1.
|
|
38
|
-
spitch-1.
|
|
39
|
-
spitch-1.
|
|
40
|
-
spitch-1.
|
|
37
|
+
spitch-1.21.0.dist-info/METADATA,sha256=YD7zPYj171CtAb90f99txFhlLs5MpxHVkLvVfd1ZUWk,13288
|
|
38
|
+
spitch-1.21.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
39
|
+
spitch-1.21.0.dist-info/licenses/LICENSE,sha256=C0lDWY-no8IxmnqzQA9BA7Z8jeh_bogVPfeWSgeDDcc,11336
|
|
40
|
+
spitch-1.21.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|