spitch 1.27.1__tar.gz → 1.28.0__tar.gz

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.

Files changed (77) hide show
  1. {spitch-1.27.1 → spitch-1.28.0}/.gitignore +0 -1
  2. spitch-1.28.0/.release-please-manifest.json +3 -0
  3. {spitch-1.27.1 → spitch-1.28.0}/CHANGELOG.md +34 -0
  4. {spitch-1.27.1 → spitch-1.28.0}/PKG-INFO +42 -10
  5. {spitch-1.27.1 → spitch-1.28.0}/README.md +37 -9
  6. {spitch-1.27.1 → spitch-1.28.0}/bin/check-release-environment +1 -1
  7. {spitch-1.27.1 → spitch-1.28.0}/pyproject.toml +4 -1
  8. {spitch-1.27.1 → spitch-1.28.0}/requirements-dev.lock +32 -3
  9. {spitch-1.27.1 → spitch-1.28.0}/requirements.lock +31 -3
  10. {spitch-1.27.1 → spitch-1.28.0}/src/spitch/__init__.py +2 -1
  11. {spitch-1.27.1 → spitch-1.28.0}/src/spitch/_base_client.py +34 -2
  12. {spitch-1.27.1 → spitch-1.28.0}/src/spitch/_files.py +4 -4
  13. {spitch-1.27.1 → spitch-1.28.0}/src/spitch/_models.py +31 -7
  14. {spitch-1.27.1 → spitch-1.28.0}/src/spitch/_version.py +1 -1
  15. {spitch-1.27.1 → spitch-1.28.0}/src/spitch/resources/speech.py +50 -64
  16. {spitch-1.27.1 → spitch-1.28.0}/src/spitch/types/speech_generate_params.py +8 -7
  17. {spitch-1.27.1 → spitch-1.28.0}/src/spitch/types/speech_transcribe_params.py +4 -2
  18. spitch-1.28.0/src/spitch/types/speech_transcribe_response.py +23 -0
  19. {spitch-1.27.1 → spitch-1.28.0}/src/spitch/types/text_tone_mark_response.py +1 -3
  20. {spitch-1.27.1 → spitch-1.28.0}/src/spitch/types/text_translate_response.py +1 -3
  21. {spitch-1.27.1 → spitch-1.28.0}/tests/api_resources/test_speech.py +11 -7
  22. {spitch-1.27.1 → spitch-1.28.0}/tests/api_resources/test_text.py +3 -1
  23. {spitch-1.27.1 → spitch-1.28.0}/tests/conftest.py +37 -6
  24. {spitch-1.27.1 → spitch-1.28.0}/tests/test_client.py +4 -2
  25. {spitch-1.27.1 → spitch-1.28.0}/tests/test_models.py +73 -1
  26. spitch-1.27.1/.release-please-manifest.json +0 -3
  27. spitch-1.27.1/src/spitch/types/speech_transcribe_response.py +0 -25
  28. {spitch-1.27.1 → spitch-1.28.0}/CONTRIBUTING.md +0 -0
  29. {spitch-1.27.1 → spitch-1.28.0}/LICENSE +0 -0
  30. {spitch-1.27.1 → spitch-1.28.0}/SECURITY.md +0 -0
  31. {spitch-1.27.1 → spitch-1.28.0}/api.md +0 -0
  32. {spitch-1.27.1 → spitch-1.28.0}/bin/publish-pypi +0 -0
  33. {spitch-1.27.1 → spitch-1.28.0}/examples/.keep +0 -0
  34. {spitch-1.27.1 → spitch-1.28.0}/examples/example.py +0 -0
  35. {spitch-1.27.1 → spitch-1.28.0}/mypy.ini +0 -0
  36. {spitch-1.27.1 → spitch-1.28.0}/noxfile.py +0 -0
  37. {spitch-1.27.1 → spitch-1.28.0}/release-please-config.json +0 -0
  38. {spitch-1.27.1 → spitch-1.28.0}/src/spitch/_client.py +0 -0
  39. {spitch-1.27.1 → spitch-1.28.0}/src/spitch/_compat.py +0 -0
  40. {spitch-1.27.1 → spitch-1.28.0}/src/spitch/_constants.py +0 -0
  41. {spitch-1.27.1 → spitch-1.28.0}/src/spitch/_exceptions.py +0 -0
  42. {spitch-1.27.1 → spitch-1.28.0}/src/spitch/_qs.py +0 -0
  43. {spitch-1.27.1 → spitch-1.28.0}/src/spitch/_resource.py +0 -0
  44. {spitch-1.27.1 → spitch-1.28.0}/src/spitch/_response.py +0 -0
  45. {spitch-1.27.1 → spitch-1.28.0}/src/spitch/_streaming.py +0 -0
  46. {spitch-1.27.1 → spitch-1.28.0}/src/spitch/_types.py +0 -0
  47. {spitch-1.27.1 → spitch-1.28.0}/src/spitch/_utils/__init__.py +0 -0
  48. {spitch-1.27.1 → spitch-1.28.0}/src/spitch/_utils/_logs.py +0 -0
  49. {spitch-1.27.1 → spitch-1.28.0}/src/spitch/_utils/_proxy.py +0 -0
  50. {spitch-1.27.1 → spitch-1.28.0}/src/spitch/_utils/_reflection.py +0 -0
  51. {spitch-1.27.1 → spitch-1.28.0}/src/spitch/_utils/_resources_proxy.py +0 -0
  52. {spitch-1.27.1 → spitch-1.28.0}/src/spitch/_utils/_streams.py +0 -0
  53. {spitch-1.27.1 → spitch-1.28.0}/src/spitch/_utils/_sync.py +0 -0
  54. {spitch-1.27.1 → spitch-1.28.0}/src/spitch/_utils/_transform.py +0 -0
  55. {spitch-1.27.1 → spitch-1.28.0}/src/spitch/_utils/_typing.py +0 -0
  56. {spitch-1.27.1 → spitch-1.28.0}/src/spitch/_utils/_utils.py +0 -0
  57. {spitch-1.27.1 → spitch-1.28.0}/src/spitch/lib/.keep +0 -0
  58. {spitch-1.27.1 → spitch-1.28.0}/src/spitch/py.typed +0 -0
  59. {spitch-1.27.1 → spitch-1.28.0}/src/spitch/resources/__init__.py +0 -0
  60. {spitch-1.27.1 → spitch-1.28.0}/src/spitch/resources/text.py +0 -0
  61. {spitch-1.27.1 → spitch-1.28.0}/src/spitch/types/__init__.py +0 -0
  62. {spitch-1.27.1 → spitch-1.28.0}/src/spitch/types/text_tone_mark_params.py +0 -0
  63. {spitch-1.27.1 → spitch-1.28.0}/src/spitch/types/text_translate_params.py +0 -0
  64. {spitch-1.27.1 → spitch-1.28.0}/tests/__init__.py +0 -0
  65. {spitch-1.27.1 → spitch-1.28.0}/tests/api_resources/__init__.py +0 -0
  66. {spitch-1.27.1 → spitch-1.28.0}/tests/sample_file.txt +0 -0
  67. {spitch-1.27.1 → spitch-1.28.0}/tests/test_deepcopy.py +0 -0
  68. {spitch-1.27.1 → spitch-1.28.0}/tests/test_extract_files.py +0 -0
  69. {spitch-1.27.1 → spitch-1.28.0}/tests/test_files.py +0 -0
  70. {spitch-1.27.1 → spitch-1.28.0}/tests/test_qs.py +0 -0
  71. {spitch-1.27.1 → spitch-1.28.0}/tests/test_required_args.py +0 -0
  72. {spitch-1.27.1 → spitch-1.28.0}/tests/test_response.py +0 -0
  73. {spitch-1.27.1 → spitch-1.28.0}/tests/test_streaming.py +0 -0
  74. {spitch-1.27.1 → spitch-1.28.0}/tests/test_transform.py +0 -0
  75. {spitch-1.27.1 → spitch-1.28.0}/tests/test_utils/test_proxy.py +0 -0
  76. {spitch-1.27.1 → spitch-1.28.0}/tests/test_utils/test_typing.py +0 -0
  77. {spitch-1.27.1 → spitch-1.28.0}/tests/utils.py +0 -0
@@ -1,5 +1,4 @@
1
1
  .prism.log
2
- .vscode
3
2
  _dev
4
3
 
5
4
  __pycache__
@@ -0,0 +1,3 @@
1
+ {
2
+ ".": "1.28.0"
3
+ }
@@ -1,5 +1,39 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.28.0 (2025-08-01)
4
+
5
+ Full Changelog: [v1.27.1...v1.28.0](https://github.com/spi-tch/spitch-python/compare/v1.27.1...v1.28.0)
6
+
7
+ ### Features
8
+
9
+ * **api:** update via SDK Studio ([9db6264](https://github.com/spi-tch/spitch-python/commit/9db6264b632e315387983917ab7b42387e7c40c3))
10
+ * **api:** update via SDK Studio ([8fff307](https://github.com/spi-tch/spitch-python/commit/8fff307f77fee14c52877ff581338f3ec08a173e))
11
+ * clean up environment call outs ([fd8997e](https://github.com/spi-tch/spitch-python/commit/fd8997e1a67c568cf3f6e7061124ac27ded0a742))
12
+ * **client:** add support for aiohttp ([716f429](https://github.com/spi-tch/spitch-python/commit/716f4298354f2018155a9fd0a22b5446116081e3))
13
+ * **client:** support file upload requests ([60d3ce3](https://github.com/spi-tch/spitch-python/commit/60d3ce3ebc58ab4c0639b19c8e36078123ac7cc3))
14
+
15
+
16
+ ### Bug Fixes
17
+
18
+ * **ci:** correct conditional ([1734cf4](https://github.com/spi-tch/spitch-python/commit/1734cf46a42f752642f04df64c701dd2ca7d60f5))
19
+ * **ci:** release-doctor — report correct token name ([1a09341](https://github.com/spi-tch/spitch-python/commit/1a09341c5dc0adc06a53824a7effcb987c1a3f33))
20
+ * **client:** don't send Content-Type header on GET requests ([b5a10cf](https://github.com/spi-tch/spitch-python/commit/b5a10cf567f59d90c259c8c6400b0cdf06af4ab3))
21
+ * **parsing:** correctly handle nested discriminated unions ([3e1a25b](https://github.com/spi-tch/spitch-python/commit/3e1a25b7953ed4611ca17c7ff4c84ffd032b5e4c))
22
+ * **parsing:** ignore empty metadata ([fc15e0e](https://github.com/spi-tch/spitch-python/commit/fc15e0ec9875c16f7aa8e00d6d8d8d4c60a866a8))
23
+ * **parsing:** parse extra field types ([8be1102](https://github.com/spi-tch/spitch-python/commit/8be11026e3d93b62aa77b170dd90acb5f246203a))
24
+
25
+
26
+ ### Chores
27
+
28
+ * **ci:** change upload type ([d9bf40f](https://github.com/spi-tch/spitch-python/commit/d9bf40f2b21338b7e3e1201b01ccd652417acfe4))
29
+ * **ci:** only run for pushes and fork pull requests ([bcba93e](https://github.com/spi-tch/spitch-python/commit/bcba93e8a69c3dec830e9d8040c76ebdda50cecf))
30
+ * **internal:** bump pinned h11 dep ([bd7d84b](https://github.com/spi-tch/spitch-python/commit/bd7d84b66fd468aabbc7dd6943579a330598bb98))
31
+ * **internal:** codegen related update ([8c23505](https://github.com/spi-tch/spitch-python/commit/8c235055fa37c950b3559e57a70ecd925ddaf518))
32
+ * **package:** mark python 3.13 as supported ([12c842b](https://github.com/spi-tch/spitch-python/commit/12c842bc7b4d6dbdd4376b79bf1581398363258e))
33
+ * **project:** add settings file for vscode ([8b0274c](https://github.com/spi-tch/spitch-python/commit/8b0274cb3d4c369550a823600bb4e493eb7b3638))
34
+ * **readme:** fix version rendering on pypi ([bf4b720](https://github.com/spi-tch/spitch-python/commit/bf4b720b71d72a1073179e68a3e55f2208bd0649))
35
+ * **tests:** skip some failing tests on the latest python versions ([9f60f27](https://github.com/spi-tch/spitch-python/commit/9f60f27835eccba43797fab6d033cdb116ec2a59))
36
+
3
37
  ## 1.27.1 (2025-06-19)
4
38
 
5
39
  Full Changelog: [v1.27.0...v1.27.1](https://github.com/spi-tch/spitch-python/compare/v1.27.0...v1.27.1)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: spitch
3
- Version: 1.27.1
3
+ Version: 1.28.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
@@ -18,6 +18,7 @@ Classifier: Programming Language :: Python :: 3.9
18
18
  Classifier: Programming Language :: Python :: 3.10
19
19
  Classifier: Programming Language :: Python :: 3.11
20
20
  Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Programming Language :: Python :: 3.13
21
22
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
22
23
  Classifier: Typing :: Typed
23
24
  Requires-Python: >=3.8
@@ -28,11 +29,15 @@ Requires-Dist: httpx<0.28.0,>=0.23.0
28
29
  Requires-Dist: pydantic<3,>=1.9.0
29
30
  Requires-Dist: sniffio
30
31
  Requires-Dist: typing-extensions<5,>=4.7
32
+ Provides-Extra: aiohttp
33
+ Requires-Dist: aiohttp; extra == 'aiohttp'
34
+ Requires-Dist: httpx-aiohttp>=0.1.8; extra == 'aiohttp'
31
35
  Description-Content-Type: text/markdown
32
36
 
33
37
  # Spitch Python API library
34
38
 
35
- [![PyPI version](https://github.com/spi-tch/spitch-python/tree/main/<https://img.shields.io/pypi/v/spitch.svg?label=pypi%20(stable)>)](https://pypi.org/project/spitch/)
39
+ <!-- prettier-ignore -->
40
+ [![PyPI version](https://img.shields.io/pypi/v/spitch.svg?label=pypi%20(stable))](https://pypi.org/project/spitch/)
36
41
 
37
42
  The Spitch Python library provides convenient access to the Spitch REST API from any Python 3.8+
38
43
  application. The library includes type definitions for all request params and response fields,
@@ -56,12 +61,9 @@ pip install spitch
56
61
  The full API of this library can be found in [api.md](https://github.com/spi-tch/spitch-python/tree/main/api.md).
57
62
 
58
63
  ```python
59
- import os
60
64
  from spitch import Spitch
61
65
 
62
- client = Spitch(
63
- api_key=os.environ.get("SPITCH_API_KEY"), # This is the default and can be omitted
64
- )
66
+ client = Spitch()
65
67
 
66
68
  response = client.speech.generate(
67
69
  language="yo",
@@ -80,13 +82,10 @@ so that your API Key is not stored in source control.
80
82
  Simply import `AsyncSpitch` instead of `Spitch` and use `await` with each API call:
81
83
 
82
84
  ```python
83
- import os
84
85
  import asyncio
85
86
  from spitch import AsyncSpitch
86
87
 
87
- client = AsyncSpitch(
88
- api_key=os.environ.get("SPITCH_API_KEY"), # This is the default and can be omitted
89
- )
88
+ client = AsyncSpitch()
90
89
 
91
90
 
92
91
  async def main() -> None:
@@ -102,6 +101,39 @@ asyncio.run(main())
102
101
 
103
102
  Functionality between the synchronous and asynchronous clients is otherwise identical.
104
103
 
104
+ ### With aiohttp
105
+
106
+ By default, the async client uses `httpx` for HTTP requests. However, for improved concurrency performance you may also use `aiohttp` as the HTTP backend.
107
+
108
+ You can enable this by installing `aiohttp`:
109
+
110
+ ```sh
111
+ # install from PyPI
112
+ pip install spitch[aiohttp]
113
+ ```
114
+
115
+ Then you can enable it by instantiating the client with `http_client=DefaultAioHttpClient()`:
116
+
117
+ ```python
118
+ import asyncio
119
+ from spitch import DefaultAioHttpClient
120
+ from spitch import AsyncSpitch
121
+
122
+
123
+ async def main() -> None:
124
+ async with AsyncSpitch(
125
+ http_client=DefaultAioHttpClient(),
126
+ ) as client:
127
+ response = await client.speech.generate(
128
+ language="yo",
129
+ text="text",
130
+ voice="sade",
131
+ )
132
+
133
+
134
+ asyncio.run(main())
135
+ ```
136
+
105
137
  ## Using types
106
138
 
107
139
  Nested request parameters are [TypedDicts](https://docs.python.org/3/library/typing.html#typing.TypedDict). Responses are [Pydantic models](https://docs.pydantic.dev) which also provide helper methods for things like:
@@ -1,6 +1,7 @@
1
1
  # Spitch Python API library
2
2
 
3
- [![PyPI version](<https://img.shields.io/pypi/v/spitch.svg?label=pypi%20(stable)>)](https://pypi.org/project/spitch/)
3
+ <!-- prettier-ignore -->
4
+ [![PyPI version](https://img.shields.io/pypi/v/spitch.svg?label=pypi%20(stable))](https://pypi.org/project/spitch/)
4
5
 
5
6
  The Spitch Python library provides convenient access to the Spitch REST API from any Python 3.8+
6
7
  application. The library includes type definitions for all request params and response fields,
@@ -24,12 +25,9 @@ pip install spitch
24
25
  The full API of this library can be found in [api.md](api.md).
25
26
 
26
27
  ```python
27
- import os
28
28
  from spitch import Spitch
29
29
 
30
- client = Spitch(
31
- api_key=os.environ.get("SPITCH_API_KEY"), # This is the default and can be omitted
32
- )
30
+ client = Spitch()
33
31
 
34
32
  response = client.speech.generate(
35
33
  language="yo",
@@ -48,13 +46,10 @@ so that your API Key is not stored in source control.
48
46
  Simply import `AsyncSpitch` instead of `Spitch` and use `await` with each API call:
49
47
 
50
48
  ```python
51
- import os
52
49
  import asyncio
53
50
  from spitch import AsyncSpitch
54
51
 
55
- client = AsyncSpitch(
56
- api_key=os.environ.get("SPITCH_API_KEY"), # This is the default and can be omitted
57
- )
52
+ client = AsyncSpitch()
58
53
 
59
54
 
60
55
  async def main() -> None:
@@ -70,6 +65,39 @@ asyncio.run(main())
70
65
 
71
66
  Functionality between the synchronous and asynchronous clients is otherwise identical.
72
67
 
68
+ ### With aiohttp
69
+
70
+ By default, the async client uses `httpx` for HTTP requests. However, for improved concurrency performance you may also use `aiohttp` as the HTTP backend.
71
+
72
+ You can enable this by installing `aiohttp`:
73
+
74
+ ```sh
75
+ # install from PyPI
76
+ pip install spitch[aiohttp]
77
+ ```
78
+
79
+ Then you can enable it by instantiating the client with `http_client=DefaultAioHttpClient()`:
80
+
81
+ ```python
82
+ import asyncio
83
+ from spitch import DefaultAioHttpClient
84
+ from spitch import AsyncSpitch
85
+
86
+
87
+ async def main() -> None:
88
+ async with AsyncSpitch(
89
+ http_client=DefaultAioHttpClient(),
90
+ ) as client:
91
+ response = await client.speech.generate(
92
+ language="yo",
93
+ text="text",
94
+ voice="sade",
95
+ )
96
+
97
+
98
+ asyncio.run(main())
99
+ ```
100
+
73
101
  ## Using types
74
102
 
75
103
  Nested request parameters are [TypedDicts](https://docs.python.org/3/library/typing.html#typing.TypedDict). Responses are [Pydantic models](https://docs.pydantic.dev) which also provide helper methods for things like:
@@ -3,7 +3,7 @@
3
3
  errors=()
4
4
 
5
5
  if [ -z "${PYPI_TOKEN}" ]; then
6
- errors+=("The SPITCH_PYPI_TOKEN secret has not been set. Please set it in either this repository's secrets or your organization secrets.")
6
+ errors+=("The PYPI_TOKEN secret has not been set. Please set it in either this repository's secrets or your organization secrets.")
7
7
  fi
8
8
 
9
9
  lenErrors=${#errors[@]}
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "spitch"
3
- version = "1.27.1"
3
+ version = "1.28.0"
4
4
  description = "The official Python library for the spitch API"
5
5
  dynamic = ["readme"]
6
6
  license = "Apache-2.0"
@@ -25,6 +25,7 @@ classifiers = [
25
25
  "Programming Language :: Python :: 3.10",
26
26
  "Programming Language :: Python :: 3.11",
27
27
  "Programming Language :: Python :: 3.12",
28
+ "Programming Language :: Python :: 3.13",
28
29
  "Operating System :: OS Independent",
29
30
  "Operating System :: POSIX",
30
31
  "Operating System :: MacOS",
@@ -38,6 +39,8 @@ classifiers = [
38
39
  Homepage = "https://github.com/spi-tch/spitch-python"
39
40
  Repository = "https://github.com/spi-tch/spitch-python"
40
41
 
42
+ [project.optional-dependencies]
43
+ aiohttp = ["aiohttp", "httpx_aiohttp>=0.1.8"]
41
44
 
42
45
  [tool.rye]
43
46
  managed = true
@@ -10,6 +10,13 @@
10
10
  # universal: false
11
11
 
12
12
  -e file:.
13
+ aiohappyeyeballs==2.6.1
14
+ # via aiohttp
15
+ aiohttp==3.12.13
16
+ # via httpx-aiohttp
17
+ # via spitch
18
+ aiosignal==1.3.2
19
+ # via aiohttp
13
20
  annotated-types==0.6.0
14
21
  # via pydantic
15
22
  anyio==4.4.0
@@ -17,6 +24,10 @@ anyio==4.4.0
17
24
  # via spitch
18
25
  argcomplete==3.1.2
19
26
  # via nox
27
+ async-timeout==5.0.1
28
+ # via aiohttp
29
+ attrs==25.3.0
30
+ # via aiohttp
20
31
  certifi==2023.7.22
21
32
  # via httpcore
22
33
  # via httpx
@@ -34,16 +45,23 @@ execnet==2.1.1
34
45
  # via pytest-xdist
35
46
  filelock==3.12.4
36
47
  # via virtualenv
37
- h11==0.14.0
48
+ frozenlist==1.7.0
49
+ # via aiohttp
50
+ # via aiosignal
51
+ h11==0.16.0
38
52
  # via httpcore
39
- httpcore==1.0.2
53
+ httpcore==1.0.9
40
54
  # via httpx
41
- httpx==0.28.1
55
+ httpx==0.27.2
56
+ # via httpx-aiohttp
42
57
  # via respx
43
58
  # via spitch
59
+ httpx-aiohttp==0.1.8
60
+ # via spitch
44
61
  idna==3.4
45
62
  # via anyio
46
63
  # via httpx
64
+ # via yarl
47
65
  importlib-metadata==7.0.0
48
66
  iniconfig==2.0.0
49
67
  # via pytest
@@ -51,6 +69,9 @@ markdown-it-py==3.0.0
51
69
  # via rich
52
70
  mdurl==0.1.2
53
71
  # via markdown-it-py
72
+ multidict==6.5.0
73
+ # via aiohttp
74
+ # via yarl
54
75
  mypy==1.14.1
55
76
  mypy-extensions==1.0.0
56
77
  # via mypy
@@ -65,6 +86,9 @@ platformdirs==3.11.0
65
86
  # via virtualenv
66
87
  pluggy==1.5.0
67
88
  # via pytest
89
+ propcache==0.3.2
90
+ # via aiohttp
91
+ # via yarl
68
92
  pydantic==2.9.2
69
93
  # via spitch
70
94
  pydantic-core==2.23.4
@@ -90,6 +114,7 @@ six==1.16.0
90
114
  # via python-dateutil
91
115
  sniffio==1.3.0
92
116
  # via anyio
117
+ # via httpx
93
118
  # via spitch
94
119
  time-machine==2.9.0
95
120
  tomli==2.0.2
@@ -97,11 +122,15 @@ tomli==2.0.2
97
122
  # via pytest
98
123
  typing-extensions==4.12.2
99
124
  # via anyio
125
+ # via multidict
100
126
  # via mypy
101
127
  # via pydantic
102
128
  # via pydantic-core
129
+ # via pyright
103
130
  # via spitch
104
131
  virtualenv==20.24.5
105
132
  # via nox
133
+ yarl==1.20.1
134
+ # via aiohttp
106
135
  zipp==3.17.0
107
136
  # via importlib-metadata
@@ -10,11 +10,22 @@
10
10
  # universal: false
11
11
 
12
12
  -e file:.
13
+ aiohappyeyeballs==2.6.1
14
+ # via aiohttp
15
+ aiohttp==3.12.13
16
+ # via httpx-aiohttp
17
+ # via spitch
18
+ aiosignal==1.3.2
19
+ # via aiohttp
13
20
  annotated-types==0.6.0
14
21
  # via pydantic
15
22
  anyio==4.4.0
16
23
  # via httpx
17
24
  # via spitch
25
+ async-timeout==5.0.1
26
+ # via aiohttp
27
+ attrs==25.3.0
28
+ # via aiohttp
18
29
  certifi==2023.7.22
19
30
  # via httpcore
20
31
  # via httpx
@@ -22,24 +33,41 @@ distro==1.8.0
22
33
  # via spitch
23
34
  exceptiongroup==1.2.2
24
35
  # via anyio
25
- h11==0.14.0
36
+ frozenlist==1.7.0
37
+ # via aiohttp
38
+ # via aiosignal
39
+ h11==0.16.0
26
40
  # via httpcore
27
- httpcore==1.0.2
41
+ httpcore==1.0.9
28
42
  # via httpx
29
- httpx==0.28.1
43
+ httpx==0.27.2
44
+ # via httpx-aiohttp
45
+ # via spitch
46
+ httpx-aiohttp==0.1.8
30
47
  # via spitch
31
48
  idna==3.4
32
49
  # via anyio
33
50
  # via httpx
51
+ # via yarl
52
+ multidict==6.5.0
53
+ # via aiohttp
54
+ # via yarl
55
+ propcache==0.3.2
56
+ # via aiohttp
57
+ # via yarl
34
58
  pydantic==2.9.2
35
59
  # via spitch
36
60
  pydantic-core==2.23.4
37
61
  # via pydantic
38
62
  sniffio==1.3.0
39
63
  # via anyio
64
+ # via httpx
40
65
  # via spitch
41
66
  typing-extensions==4.12.2
42
67
  # via anyio
68
+ # via multidict
43
69
  # via pydantic
44
70
  # via pydantic-core
45
71
  # via spitch
72
+ yarl==1.20.1
73
+ # via aiohttp
@@ -26,7 +26,7 @@ from ._exceptions import (
26
26
  UnprocessableEntityError,
27
27
  APIResponseValidationError,
28
28
  )
29
- from ._base_client import DefaultHttpxClient, DefaultAsyncHttpxClient
29
+ from ._base_client import DefaultHttpxClient, DefaultAioHttpClient, DefaultAsyncHttpxClient
30
30
  from ._utils._logs import setup_logging as _setup_logging
31
31
 
32
32
  __all__ = [
@@ -67,6 +67,7 @@ __all__ = [
67
67
  "DEFAULT_CONNECTION_LIMITS",
68
68
  "DefaultHttpxClient",
69
69
  "DefaultAsyncHttpxClient",
70
+ "DefaultAioHttpClient",
70
71
  ]
71
72
 
72
73
  if not _t.TYPE_CHECKING:
@@ -529,6 +529,18 @@ class BaseClient(Generic[_HttpxClientT, _DefaultStreamT]):
529
529
  # work around https://github.com/encode/httpx/discussions/2880
530
530
  kwargs["extensions"] = {"sni_hostname": prepared_url.host.replace("_", "-")}
531
531
 
532
+ is_body_allowed = options.method.lower() != "get"
533
+
534
+ if is_body_allowed:
535
+ if isinstance(json_data, bytes):
536
+ kwargs["content"] = json_data
537
+ else:
538
+ kwargs["json"] = json_data if is_given(json_data) else None
539
+ kwargs["files"] = files
540
+ else:
541
+ headers.pop("Content-Type", None)
542
+ kwargs.pop("data", None)
543
+
532
544
  # TODO: report this error to httpx
533
545
  return self._client.build_request( # pyright: ignore[reportUnknownMemberType]
534
546
  headers=headers,
@@ -540,8 +552,6 @@ class BaseClient(Generic[_HttpxClientT, _DefaultStreamT]):
540
552
  # so that passing a `TypedDict` doesn't cause an error.
541
553
  # https://github.com/microsoft/pyright/issues/3526#event-6715453066
542
554
  params=self.qs.stringify(cast(Mapping[str, Any], params)) if params else None,
543
- json=json_data if is_given(json_data) else None,
544
- files=files,
545
555
  **kwargs,
546
556
  )
547
557
 
@@ -1289,6 +1299,24 @@ class _DefaultAsyncHttpxClient(httpx.AsyncClient):
1289
1299
  super().__init__(**kwargs)
1290
1300
 
1291
1301
 
1302
+ try:
1303
+ import httpx_aiohttp
1304
+ except ImportError:
1305
+
1306
+ class _DefaultAioHttpClient(httpx.AsyncClient):
1307
+ def __init__(self, **_kwargs: Any) -> None:
1308
+ raise RuntimeError("To use the aiohttp client you must have installed the package with the `aiohttp` extra")
1309
+ else:
1310
+
1311
+ class _DefaultAioHttpClient(httpx_aiohttp.HttpxAiohttpClient): # type: ignore
1312
+ def __init__(self, **kwargs: Any) -> None:
1313
+ kwargs.setdefault("timeout", DEFAULT_TIMEOUT)
1314
+ kwargs.setdefault("limits", DEFAULT_CONNECTION_LIMITS)
1315
+ kwargs.setdefault("follow_redirects", True)
1316
+
1317
+ super().__init__(**kwargs)
1318
+
1319
+
1292
1320
  if TYPE_CHECKING:
1293
1321
  DefaultAsyncHttpxClient = httpx.AsyncClient
1294
1322
  """An alias to `httpx.AsyncClient` that provides the same defaults that this SDK
@@ -1297,8 +1325,12 @@ if TYPE_CHECKING:
1297
1325
  This is useful because overriding the `http_client` with your own instance of
1298
1326
  `httpx.AsyncClient` will result in httpx's defaults being used, not ours.
1299
1327
  """
1328
+
1329
+ DefaultAioHttpClient = httpx.AsyncClient
1330
+ """An alias to `httpx.AsyncClient` that changes the default HTTP transport to `aiohttp`."""
1300
1331
  else:
1301
1332
  DefaultAsyncHttpxClient = _DefaultAsyncHttpxClient
1333
+ DefaultAioHttpClient = _DefaultAioHttpClient
1302
1334
 
1303
1335
 
1304
1336
  class AsyncHttpxClientWrapper(DefaultAsyncHttpxClient):
@@ -69,12 +69,12 @@ def _transform_file(file: FileTypes) -> HttpxFileTypes:
69
69
  return file
70
70
 
71
71
  if is_tuple_t(file):
72
- return (file[0], _read_file_content(file[1]), *file[2:])
72
+ return (file[0], read_file_content(file[1]), *file[2:])
73
73
 
74
74
  raise TypeError(f"Expected file types input to be a FileContent type or to be a tuple")
75
75
 
76
76
 
77
- def _read_file_content(file: FileContent) -> HttpxFileContent:
77
+ def read_file_content(file: FileContent) -> HttpxFileContent:
78
78
  if isinstance(file, os.PathLike):
79
79
  return pathlib.Path(file).read_bytes()
80
80
  return file
@@ -111,12 +111,12 @@ async def _async_transform_file(file: FileTypes) -> HttpxFileTypes:
111
111
  return file
112
112
 
113
113
  if is_tuple_t(file):
114
- return (file[0], await _async_read_file_content(file[1]), *file[2:])
114
+ return (file[0], await async_read_file_content(file[1]), *file[2:])
115
115
 
116
116
  raise TypeError(f"Expected file types input to be a FileContent type or to be a tuple")
117
117
 
118
118
 
119
- async def _async_read_file_content(file: FileContent) -> HttpxFileContent:
119
+ async def async_read_file_content(file: FileContent) -> HttpxFileContent:
120
120
  if isinstance(file, os.PathLike):
121
121
  return await anyio.Path(file).read_bytes()
122
122
 
@@ -2,9 +2,10 @@ from __future__ import annotations
2
2
 
3
3
  import os
4
4
  import inspect
5
- from typing import TYPE_CHECKING, Any, Type, Union, Generic, TypeVar, Callable, cast
5
+ from typing import TYPE_CHECKING, Any, Type, Union, Generic, TypeVar, Callable, Optional, cast
6
6
  from datetime import date, datetime
7
7
  from typing_extensions import (
8
+ List,
8
9
  Unpack,
9
10
  Literal,
10
11
  ClassVar,
@@ -206,14 +207,18 @@ class BaseModel(pydantic.BaseModel):
206
207
  else:
207
208
  fields_values[name] = field_get_default(field)
208
209
 
210
+ extra_field_type = _get_extra_fields_type(__cls)
211
+
209
212
  _extra = {}
210
213
  for key, value in values.items():
211
214
  if key not in model_fields:
215
+ parsed = construct_type(value=value, type_=extra_field_type) if extra_field_type is not None else value
216
+
212
217
  if PYDANTIC_V2:
213
- _extra[key] = value
218
+ _extra[key] = parsed
214
219
  else:
215
220
  _fields_set.add(key)
216
- fields_values[key] = value
221
+ fields_values[key] = parsed
217
222
 
218
223
  object.__setattr__(m, "__dict__", fields_values)
219
224
 
@@ -365,7 +370,24 @@ def _construct_field(value: object, field: FieldInfo, key: str) -> object:
365
370
  if type_ is None:
366
371
  raise RuntimeError(f"Unexpected field type is None for {key}")
367
372
 
368
- return construct_type(value=value, type_=type_)
373
+ return construct_type(value=value, type_=type_, metadata=getattr(field, "metadata", None))
374
+
375
+
376
+ def _get_extra_fields_type(cls: type[pydantic.BaseModel]) -> type | None:
377
+ if not PYDANTIC_V2:
378
+ # TODO
379
+ return None
380
+
381
+ schema = cls.__pydantic_core_schema__
382
+ if schema["type"] == "model":
383
+ fields = schema["schema"]
384
+ if fields["type"] == "model-fields":
385
+ extras = fields.get("extras_schema")
386
+ if extras and "cls" in extras:
387
+ # mypy can't narrow the type
388
+ return extras["cls"] # type: ignore[no-any-return]
389
+
390
+ return None
369
391
 
370
392
 
371
393
  def is_basemodel(type_: type) -> bool:
@@ -419,7 +441,7 @@ def construct_type_unchecked(*, value: object, type_: type[_T]) -> _T:
419
441
  return cast(_T, construct_type(value=value, type_=type_))
420
442
 
421
443
 
422
- def construct_type(*, value: object, type_: object) -> object:
444
+ def construct_type(*, value: object, type_: object, metadata: Optional[List[Any]] = None) -> object:
423
445
  """Loose coercion to the expected type with construction of nested values.
424
446
 
425
447
  If the given value does not match the expected type then it is returned as-is.
@@ -434,8 +456,10 @@ def construct_type(*, value: object, type_: object) -> object:
434
456
  type_ = cast("type[object]", type_)
435
457
 
436
458
  # unwrap `Annotated[T, ...]` -> `T`
437
- if is_annotated_type(type_):
438
- meta: tuple[Any, ...] = get_args(type_)[1:]
459
+ if metadata is not None and len(metadata) > 0:
460
+ meta: tuple[Any, ...] = tuple(metadata)
461
+ elif is_annotated_type(type_):
462
+ meta = get_args(type_)[1:]
439
463
  type_ = extract_type_arg(type_, 0)
440
464
  else:
441
465
  meta = tuple()
@@ -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.27.1" # x-release-please-version
4
+ __version__ = "1.28.0" # x-release-please-version