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 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, Limits
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 the retry count header if it was already set or removed by the caller. We check
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
- if "x-stainless-retry-count" not in (header.lower() for header in custom_headers):
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 spitch client instance.
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 spitch client instance.
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.0, connect=5.0)
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'{self.__repr_name__()}({self.__repr_str__(", ")})' # type: ignore[misc]
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
- cls: Type[ModelT],
181
+ __cls: Type[ModelT],
182
182
  _fields_set: set[str] | None = None,
183
183
  **values: object,
184
184
  ) -> ModelT:
185
- m = cls.__new__(cls)
185
+ m = __cls.__new__(__cls)
186
186
  fields_values: dict[str, object] = {}
187
187
 
188
- config = get_model_config(cls)
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(cls)
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 not is_literal_type(type_) and (issubclass(origin, BaseModel) or issubclass(origin, GenericModel)):
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 inspect.isclass(origin) and not issubclass(origin, BaseModel) and issubclass(origin, pydantic.BaseModel):
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
- to_thread = asyncio.to_thread
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 to_thread(
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
  """
@@ -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
@@ -1,4 +1,4 @@
1
1
  # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
2
2
 
3
3
  __title__ = "spitch"
4
- __version__ = "1.19.0" # x-release-please-version
4
+ __version__ = "1.21.0" # x-release-please-version
@@ -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 the
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 the
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 the
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 the
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
@@ -30,6 +30,8 @@ class SpeechGenerateParams(TypedDict, total=False):
30
30
  "lina",
31
31
  "john",
32
32
  "jude",
33
+ "henry",
34
+ "kani",
33
35
  ]
34
36
  ]
35
37
 
@@ -1,10 +1,10 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: spitch
3
- Version: 1.19.0
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 <dev@spi-tch.com>
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.stainlessapi.com/).
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 followed:
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) will be respected when making this
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=LlWEnS9IoWcUjk98wigur4NAaHIXv5wyW6TfyILoFnQ,67844
3
- spitch/_client.py,sha256=wnexAmnaiafFtJM7PC9LyThw3aIkI6eLn7ZReCL2iLY,15446
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=JE8kyZa2Q4NK_i4fO--8siEYTzeHnT0fYbOFDgDP4uk,464
5
+ spitch/_constants.py,sha256=S14PFzyN9-I31wiV7SmIlL5Ga0MLHxdvegInGdXH7tM,462
6
6
  spitch/_exceptions.py,sha256=xsQtKJTiIdz2X1bQDQFZcSW7WBofLazdQm9nMCyPEVM,3220
7
- spitch/_files.py,sha256=mf4dOgL4b0ryyZlbqLhggD3GVgDf6XxdGFAgce01ugE,3549
8
- spitch/_models.py,sha256=uhxvXZC0JO7HuGR_GWXH-zYKuptF2rwiGVJfMMfg3fw,28470
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=aN6swtguiZ7CC_hWFrwoUa53FgSbjRfZXxYHvfDNGeo,28609
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=2P2TWGb-BQsOSxDDZLmCtv9hhUkFUgw7TgaIlHN8xAQ,159
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=jJl-iCEaZZUAkq4IUtzN1-aMsKTUFaNoNbeYnnpQjIQ,2438
22
- spitch/_utils/_transform.py,sha256=Dkkyr7OveGmOolepcvXmVJWE3kqim4b0nM0h7yWbgeY,13468
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=FXV0ld21gf6368siL679DV3d2_2EgG7jRtWsuHQVm4I,12937
28
- spitch/resources/text.py,sha256=Gno8dQg7jeL8rslqqURyu4t-5_lLwziYgCRvgY8V9XM,9665
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=gLIzNBaevzSLv9-m-chhA3dl7lZg3M1VQ7CPnLD5leU,758
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.19.0.dist-info/METADATA,sha256=ZeVk4pSqWhI51rE7ccHCoK8bpBTPNiwe_aOU0URCt48,12593
38
- spitch-1.19.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
39
- spitch-1.19.0.dist-info/licenses/LICENSE,sha256=C0lDWY-no8IxmnqzQA9BA7Z8jeh_bogVPfeWSgeDDcc,11336
40
- spitch-1.19.0.dist-info/RECORD,,
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,,