runwayml 2.0.0__tar.gz → 2.1.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.
- runwayml-2.1.0/.release-please-manifest.json +3 -0
- {runwayml-2.0.0 → runwayml-2.1.0}/CHANGELOG.md +9 -0
- {runwayml-2.0.0 → runwayml-2.1.0}/PKG-INFO +4 -5
- {runwayml-2.0.0 → runwayml-2.1.0}/README.md +2 -2
- {runwayml-2.0.0 → runwayml-2.1.0}/pyproject.toml +5 -10
- {runwayml-2.0.0 → runwayml-2.1.0}/requirements-dev.lock +11 -14
- {runwayml-2.0.0 → runwayml-2.1.0}/requirements.lock +4 -4
- {runwayml-2.0.0 → runwayml-2.1.0}/src/runwayml/_base_client.py +9 -2
- {runwayml-2.0.0 → runwayml-2.1.0}/src/runwayml/_client.py +4 -4
- {runwayml-2.0.0 → runwayml-2.1.0}/src/runwayml/_compat.py +5 -3
- {runwayml-2.0.0 → runwayml-2.1.0}/src/runwayml/_models.py +11 -8
- {runwayml-2.0.0 → runwayml-2.1.0}/src/runwayml/_response.py +3 -0
- {runwayml-2.0.0 → runwayml-2.1.0}/src/runwayml/_types.py +4 -2
- {runwayml-2.0.0 → runwayml-2.1.0}/src/runwayml/_utils/__init__.py +1 -0
- {runwayml-2.0.0 → runwayml-2.1.0}/src/runwayml/_utils/_transform.py +7 -2
- {runwayml-2.0.0 → runwayml-2.1.0}/src/runwayml/_utils/_utils.py +17 -0
- {runwayml-2.0.0 → runwayml-2.1.0}/src/runwayml/_version.py +1 -1
- {runwayml-2.0.0 → runwayml-2.1.0}/src/runwayml/resources/image_to_video.py +13 -10
- {runwayml-2.0.0 → runwayml-2.1.0}/src/runwayml/types/image_to_video_create_params.py +23 -8
- {runwayml-2.0.0 → runwayml-2.1.0}/src/runwayml/types/image_to_video_create_response.py +4 -2
- {runwayml-2.0.0 → runwayml-2.1.0}/src/runwayml/types/task_retrieve_response.py +1 -0
- {runwayml-2.0.0 → runwayml-2.1.0}/tests/api_resources/test_image_to_video.py +2 -2
- {runwayml-2.0.0 → runwayml-2.1.0}/tests/conftest.py +8 -6
- {runwayml-2.0.0 → runwayml-2.1.0}/tests/test_client.py +21 -2
- {runwayml-2.0.0 → runwayml-2.1.0}/tests/test_models.py +8 -15
- {runwayml-2.0.0 → runwayml-2.1.0}/tests/test_response.py +50 -0
- {runwayml-2.0.0 → runwayml-2.1.0}/tests/test_transform.py +15 -0
- runwayml-2.0.0/.release-please-manifest.json +0 -3
- {runwayml-2.0.0 → runwayml-2.1.0}/.gitignore +0 -0
- {runwayml-2.0.0 → runwayml-2.1.0}/CONTRIBUTING.md +0 -0
- {runwayml-2.0.0 → runwayml-2.1.0}/LICENSE +0 -0
- {runwayml-2.0.0 → runwayml-2.1.0}/SECURITY.md +0 -0
- {runwayml-2.0.0 → runwayml-2.1.0}/api.md +0 -0
- {runwayml-2.0.0 → runwayml-2.1.0}/bin/check-release-environment +0 -0
- {runwayml-2.0.0 → runwayml-2.1.0}/bin/publish-pypi +0 -0
- {runwayml-2.0.0 → runwayml-2.1.0}/examples/.keep +0 -0
- {runwayml-2.0.0 → runwayml-2.1.0}/mypy.ini +0 -0
- {runwayml-2.0.0 → runwayml-2.1.0}/noxfile.py +0 -0
- {runwayml-2.0.0 → runwayml-2.1.0}/release-please-config.json +0 -0
- {runwayml-2.0.0 → runwayml-2.1.0}/src/runwayml/__init__.py +0 -0
- {runwayml-2.0.0 → runwayml-2.1.0}/src/runwayml/_constants.py +0 -0
- {runwayml-2.0.0 → runwayml-2.1.0}/src/runwayml/_exceptions.py +0 -0
- {runwayml-2.0.0 → runwayml-2.1.0}/src/runwayml/_files.py +0 -0
- {runwayml-2.0.0 → runwayml-2.1.0}/src/runwayml/_qs.py +0 -0
- {runwayml-2.0.0 → runwayml-2.1.0}/src/runwayml/_resource.py +0 -0
- {runwayml-2.0.0 → runwayml-2.1.0}/src/runwayml/_streaming.py +0 -0
- {runwayml-2.0.0 → runwayml-2.1.0}/src/runwayml/_utils/_logs.py +0 -0
- {runwayml-2.0.0 → runwayml-2.1.0}/src/runwayml/_utils/_proxy.py +0 -0
- {runwayml-2.0.0 → runwayml-2.1.0}/src/runwayml/_utils/_reflection.py +0 -0
- {runwayml-2.0.0 → runwayml-2.1.0}/src/runwayml/_utils/_streams.py +0 -0
- {runwayml-2.0.0 → runwayml-2.1.0}/src/runwayml/_utils/_sync.py +0 -0
- {runwayml-2.0.0 → runwayml-2.1.0}/src/runwayml/_utils/_typing.py +0 -0
- {runwayml-2.0.0 → runwayml-2.1.0}/src/runwayml/lib/.keep +0 -0
- {runwayml-2.0.0 → runwayml-2.1.0}/src/runwayml/py.typed +0 -0
- {runwayml-2.0.0 → runwayml-2.1.0}/src/runwayml/resources/__init__.py +0 -0
- {runwayml-2.0.0 → runwayml-2.1.0}/src/runwayml/resources/tasks.py +0 -0
- {runwayml-2.0.0 → runwayml-2.1.0}/src/runwayml/types/__init__.py +0 -0
- {runwayml-2.0.0 → runwayml-2.1.0}/tests/__init__.py +0 -0
- {runwayml-2.0.0 → runwayml-2.1.0}/tests/api_resources/__init__.py +0 -0
- {runwayml-2.0.0 → runwayml-2.1.0}/tests/api_resources/test_tasks.py +0 -0
- {runwayml-2.0.0 → runwayml-2.1.0}/tests/sample_file.txt +0 -0
- {runwayml-2.0.0 → runwayml-2.1.0}/tests/test_deepcopy.py +0 -0
- {runwayml-2.0.0 → runwayml-2.1.0}/tests/test_extract_files.py +0 -0
- {runwayml-2.0.0 → runwayml-2.1.0}/tests/test_files.py +0 -0
- {runwayml-2.0.0 → runwayml-2.1.0}/tests/test_qs.py +0 -0
- {runwayml-2.0.0 → runwayml-2.1.0}/tests/test_required_args.py +0 -0
- {runwayml-2.0.0 → runwayml-2.1.0}/tests/test_streaming.py +0 -0
- {runwayml-2.0.0 → runwayml-2.1.0}/tests/test_utils/test_proxy.py +0 -0
- {runwayml-2.0.0 → runwayml-2.1.0}/tests/test_utils/test_typing.py +0 -0
- {runwayml-2.0.0 → runwayml-2.1.0}/tests/utils.py +0 -0
@@ -1,5 +1,14 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## 2.1.0 (2024-11-06)
|
4
|
+
|
5
|
+
Full Changelog: [v2.0.0...v2.1.0](https://github.com/runwayml/sdk-python/compare/v2.0.0...v2.1.0)
|
6
|
+
|
7
|
+
### Features
|
8
|
+
|
9
|
+
* **api:** API version 2024-11-06 ([#15](https://github.com/runwayml/sdk-python/issues/15)) ([39c096f](https://github.com/runwayml/sdk-python/commit/39c096fdc4784cc726e8688dcc3263e6cb322607))
|
10
|
+
* **api:** Set latest default API version ([#17](https://github.com/runwayml/sdk-python/issues/17)) ([2ad66fd](https://github.com/runwayml/sdk-python/commit/2ad66fd753321eaae28fa09755bfdb6bbfc07949))
|
11
|
+
|
3
12
|
## 2.0.0 (2024-10-04)
|
4
13
|
|
5
14
|
Full Changelog: [v1.0.0...v2.0.0](https://github.com/runwayml/sdk-python/compare/v1.0.0...v2.0.0)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.3
|
2
2
|
Name: runwayml
|
3
|
-
Version: 2.
|
3
|
+
Version: 2.1.0
|
4
4
|
Summary: The official Python library for the runwayml API
|
5
5
|
Project-URL: Homepage, https://github.com/runwayml/sdk-python
|
6
6
|
Project-URL: Repository, https://github.com/runwayml/sdk-python
|
@@ -14,7 +14,6 @@ Classifier: Operating System :: Microsoft :: Windows
|
|
14
14
|
Classifier: Operating System :: OS Independent
|
15
15
|
Classifier: Operating System :: POSIX
|
16
16
|
Classifier: Operating System :: POSIX :: Linux
|
17
|
-
Classifier: Programming Language :: Python :: 3.7
|
18
17
|
Classifier: Programming Language :: Python :: 3.8
|
19
18
|
Classifier: Programming Language :: Python :: 3.9
|
20
19
|
Classifier: Programming Language :: Python :: 3.10
|
@@ -22,7 +21,7 @@ Classifier: Programming Language :: Python :: 3.11
|
|
22
21
|
Classifier: Programming Language :: Python :: 3.12
|
23
22
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
24
23
|
Classifier: Typing :: Typed
|
25
|
-
Requires-Python: >=3.
|
24
|
+
Requires-Python: >=3.8
|
26
25
|
Requires-Dist: anyio<5,>=3.5.0
|
27
26
|
Requires-Dist: cached-property; python_version < '3.8'
|
28
27
|
Requires-Dist: distro<2,>=1.7.0
|
@@ -36,7 +35,7 @@ Description-Content-Type: text/markdown
|
|
36
35
|
|
37
36
|
[](https://pypi.org/project/runwayml/)
|
38
37
|
|
39
|
-
The RunwayML Python library provides convenient access to the RunwayML REST API from any Python 3.
|
38
|
+
The RunwayML Python library provides convenient access to the RunwayML REST API from any Python 3.8+
|
40
39
|
application. The library includes type definitions for all request params and response fields,
|
41
40
|
and offers both synchronous and asynchronous clients powered by [httpx](https://github.com/encode/httpx).
|
42
41
|
|
@@ -376,7 +375,7 @@ print(runwayml.__version__)
|
|
376
375
|
|
377
376
|
## Requirements
|
378
377
|
|
379
|
-
Python 3.
|
378
|
+
Python 3.8 or higher.
|
380
379
|
|
381
380
|
## Contributing
|
382
381
|
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
[](https://pypi.org/project/runwayml/)
|
4
4
|
|
5
|
-
The RunwayML Python library provides convenient access to the RunwayML REST API from any Python 3.
|
5
|
+
The RunwayML Python library provides convenient access to the RunwayML REST API from any Python 3.8+
|
6
6
|
application. The library includes type definitions for all request params and response fields,
|
7
7
|
and offers both synchronous and asynchronous clients powered by [httpx](https://github.com/encode/httpx).
|
8
8
|
|
@@ -342,7 +342,7 @@ print(runwayml.__version__)
|
|
342
342
|
|
343
343
|
## Requirements
|
344
344
|
|
345
|
-
Python 3.
|
345
|
+
Python 3.8 or higher.
|
346
346
|
|
347
347
|
## Contributing
|
348
348
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
[project]
|
2
2
|
name = "runwayml"
|
3
|
-
version = "2.
|
3
|
+
version = "2.1.0"
|
4
4
|
description = "The official Python library for the runwayml API"
|
5
5
|
dynamic = ["readme"]
|
6
6
|
license = "Apache-2.0"
|
@@ -16,11 +16,10 @@ dependencies = [
|
|
16
16
|
"sniffio",
|
17
17
|
"cached-property; python_version < '3.8'",
|
18
18
|
]
|
19
|
-
requires-python = ">= 3.
|
19
|
+
requires-python = ">= 3.8"
|
20
20
|
classifiers = [
|
21
21
|
"Typing :: Typed",
|
22
22
|
"Intended Audience :: Developers",
|
23
|
-
"Programming Language :: Python :: 3.7",
|
24
23
|
"Programming Language :: Python :: 3.8",
|
25
24
|
"Programming Language :: Python :: 3.9",
|
26
25
|
"Programming Language :: Python :: 3.10",
|
@@ -63,11 +62,11 @@ format = { chain = [
|
|
63
62
|
"format:ruff",
|
64
63
|
"format:docs",
|
65
64
|
"fix:ruff",
|
65
|
+
# run formatting again to fix any inconsistencies when imports are stripped
|
66
|
+
"format:ruff",
|
66
67
|
]}
|
67
|
-
"format:black" = "black ."
|
68
68
|
"format:docs" = "python scripts/utils/ruffen-docs.py README.md api.md"
|
69
69
|
"format:ruff" = "ruff format"
|
70
|
-
"format:isort" = "isort ."
|
71
70
|
|
72
71
|
"lint" = { chain = [
|
73
72
|
"check:ruff",
|
@@ -125,10 +124,6 @@ path = "README.md"
|
|
125
124
|
pattern = '\[(.+?)\]\(((?!https?://)\S+?)\)'
|
126
125
|
replacement = '[\1](https://github.com/runwayml/sdk-python/tree/main/\g<2>)'
|
127
126
|
|
128
|
-
[tool.black]
|
129
|
-
line-length = 120
|
130
|
-
target-version = ["py37"]
|
131
|
-
|
132
127
|
[tool.pytest.ini_options]
|
133
128
|
testpaths = ["tests"]
|
134
129
|
addopts = "--tb=short"
|
@@ -143,7 +138,7 @@ filterwarnings = [
|
|
143
138
|
# there are a couple of flags that are still disabled by
|
144
139
|
# default in strict mode as they are experimental and niche.
|
145
140
|
typeCheckingMode = "strict"
|
146
|
-
pythonVersion = "3.
|
141
|
+
pythonVersion = "3.8"
|
147
142
|
|
148
143
|
exclude = [
|
149
144
|
"_dev",
|
@@ -16,8 +16,6 @@ anyio==4.4.0
|
|
16
16
|
# via runwayml
|
17
17
|
argcomplete==3.1.2
|
18
18
|
# via nox
|
19
|
-
attrs==23.1.0
|
20
|
-
# via pytest
|
21
19
|
certifi==2023.7.22
|
22
20
|
# via httpcore
|
23
21
|
# via httpx
|
@@ -28,8 +26,9 @@ distlib==0.3.7
|
|
28
26
|
# via virtualenv
|
29
27
|
distro==1.8.0
|
30
28
|
# via runwayml
|
31
|
-
exceptiongroup==1.
|
29
|
+
exceptiongroup==1.2.2
|
32
30
|
# via anyio
|
31
|
+
# via pytest
|
33
32
|
filelock==3.12.4
|
34
33
|
# via virtualenv
|
35
34
|
h11==0.14.0
|
@@ -49,7 +48,7 @@ markdown-it-py==3.0.0
|
|
49
48
|
# via rich
|
50
49
|
mdurl==0.1.2
|
51
50
|
# via markdown-it-py
|
52
|
-
mypy==1.
|
51
|
+
mypy==1.13.0
|
53
52
|
mypy-extensions==1.0.0
|
54
53
|
# via mypy
|
55
54
|
nodeenv==1.8.0
|
@@ -60,27 +59,25 @@ packaging==23.2
|
|
60
59
|
# via pytest
|
61
60
|
platformdirs==3.11.0
|
62
61
|
# via virtualenv
|
63
|
-
pluggy==1.
|
64
|
-
# via pytest
|
65
|
-
py==1.11.0
|
62
|
+
pluggy==1.5.0
|
66
63
|
# via pytest
|
67
|
-
pydantic==2.
|
64
|
+
pydantic==2.9.2
|
68
65
|
# via runwayml
|
69
|
-
pydantic-core==2.
|
66
|
+
pydantic-core==2.23.4
|
70
67
|
# via pydantic
|
71
68
|
pygments==2.18.0
|
72
69
|
# via rich
|
73
70
|
pyright==1.1.380
|
74
|
-
pytest==
|
71
|
+
pytest==8.3.3
|
75
72
|
# via pytest-asyncio
|
76
|
-
pytest-asyncio==0.
|
73
|
+
pytest-asyncio==0.24.0
|
77
74
|
python-dateutil==2.8.2
|
78
75
|
# via time-machine
|
79
76
|
pytz==2023.3.post1
|
80
77
|
# via dirty-equals
|
81
78
|
respx==0.20.2
|
82
79
|
rich==13.7.1
|
83
|
-
ruff==0.6.
|
80
|
+
ruff==0.6.9
|
84
81
|
setuptools==68.2.2
|
85
82
|
# via nodeenv
|
86
83
|
six==1.16.0
|
@@ -90,10 +87,10 @@ sniffio==1.3.0
|
|
90
87
|
# via httpx
|
91
88
|
# via runwayml
|
92
89
|
time-machine==2.9.0
|
93
|
-
tomli==2.0.
|
90
|
+
tomli==2.0.2
|
94
91
|
# via mypy
|
95
92
|
# via pytest
|
96
|
-
typing-extensions==4.
|
93
|
+
typing-extensions==4.12.2
|
97
94
|
# via anyio
|
98
95
|
# via mypy
|
99
96
|
# via pydantic
|
@@ -19,7 +19,7 @@ certifi==2023.7.22
|
|
19
19
|
# via httpx
|
20
20
|
distro==1.8.0
|
21
21
|
# via runwayml
|
22
|
-
exceptiongroup==1.
|
22
|
+
exceptiongroup==1.2.2
|
23
23
|
# via anyio
|
24
24
|
h11==0.14.0
|
25
25
|
# via httpcore
|
@@ -30,15 +30,15 @@ httpx==0.25.2
|
|
30
30
|
idna==3.4
|
31
31
|
# via anyio
|
32
32
|
# via httpx
|
33
|
-
pydantic==2.
|
33
|
+
pydantic==2.9.2
|
34
34
|
# via runwayml
|
35
|
-
pydantic-core==2.
|
35
|
+
pydantic-core==2.23.4
|
36
36
|
# via pydantic
|
37
37
|
sniffio==1.3.0
|
38
38
|
# via anyio
|
39
39
|
# via httpx
|
40
40
|
# via runwayml
|
41
|
-
typing-extensions==4.
|
41
|
+
typing-extensions==4.12.2
|
42
42
|
# via anyio
|
43
43
|
# via pydantic
|
44
44
|
# via pydantic-core
|
@@ -143,6 +143,12 @@ class PageInfo:
|
|
143
143
|
self.url = url
|
144
144
|
self.params = params
|
145
145
|
|
146
|
+
@override
|
147
|
+
def __repr__(self) -> str:
|
148
|
+
if self.url:
|
149
|
+
return f"{self.__class__.__name__}(url={self.url})"
|
150
|
+
return f"{self.__class__.__name__}(params={self.params})"
|
151
|
+
|
146
152
|
|
147
153
|
class BasePage(GenericModel, Generic[_T]):
|
148
154
|
"""
|
@@ -689,7 +695,8 @@ class BaseClient(Generic[_HttpxClientT, _DefaultStreamT]):
|
|
689
695
|
if retry_after is not None and 0 < retry_after <= 60:
|
690
696
|
return retry_after
|
691
697
|
|
692
|
-
|
698
|
+
# Also cap retry count to 1000 to avoid any potential overflows with `pow`
|
699
|
+
nb_retries = min(max_retries - remaining_retries, 1000)
|
693
700
|
|
694
701
|
# Apply exponential backoff, but not more than the max.
|
695
702
|
sleep_seconds = min(INITIAL_RETRY_DELAY * pow(2.0, nb_retries), MAX_RETRY_DELAY)
|
@@ -1568,7 +1575,7 @@ class AsyncAPIClient(BaseClient[httpx.AsyncClient, AsyncStream[Any]]):
|
|
1568
1575
|
except Exception as err:
|
1569
1576
|
log.debug("Encountered Exception", exc_info=True)
|
1570
1577
|
|
1571
|
-
if
|
1578
|
+
if remaining_retries > 0:
|
1572
1579
|
return await self._retry_request(
|
1573
1580
|
input_options,
|
1574
1581
|
cast_to,
|
@@ -59,7 +59,7 @@ class RunwayML(SyncAPIClient):
|
|
59
59
|
self,
|
60
60
|
*,
|
61
61
|
api_key: str | None = None,
|
62
|
-
runway_version: str | None = "2024-
|
62
|
+
runway_version: str | None = "2024-11-06",
|
63
63
|
base_url: str | httpx.URL | None = None,
|
64
64
|
timeout: Union[float, Timeout, None, NotGiven] = NOT_GIVEN,
|
65
65
|
max_retries: int = DEFAULT_MAX_RETRIES,
|
@@ -92,7 +92,7 @@ class RunwayML(SyncAPIClient):
|
|
92
92
|
self.api_key = api_key
|
93
93
|
|
94
94
|
if runway_version is None:
|
95
|
-
runway_version = "2024-
|
95
|
+
runway_version = "2024-11-06"
|
96
96
|
self.runway_version = runway_version
|
97
97
|
|
98
98
|
if base_url is None:
|
@@ -238,7 +238,7 @@ class AsyncRunwayML(AsyncAPIClient):
|
|
238
238
|
self,
|
239
239
|
*,
|
240
240
|
api_key: str | None = None,
|
241
|
-
runway_version: str | None = "2024-
|
241
|
+
runway_version: str | None = "2024-11-06",
|
242
242
|
base_url: str | httpx.URL | None = None,
|
243
243
|
timeout: Union[float, Timeout, None, NotGiven] = NOT_GIVEN,
|
244
244
|
max_retries: int = DEFAULT_MAX_RETRIES,
|
@@ -271,7 +271,7 @@ class AsyncRunwayML(AsyncAPIClient):
|
|
271
271
|
self.api_key = api_key
|
272
272
|
|
273
273
|
if runway_version is None:
|
274
|
-
runway_version = "2024-
|
274
|
+
runway_version = "2024-11-06"
|
275
275
|
self.runway_version = runway_version
|
276
276
|
|
277
277
|
if base_url is None:
|
@@ -2,7 +2,7 @@ from __future__ import annotations
|
|
2
2
|
|
3
3
|
from typing import TYPE_CHECKING, Any, Union, Generic, TypeVar, Callable, cast, overload
|
4
4
|
from datetime import date, datetime
|
5
|
-
from typing_extensions import Self
|
5
|
+
from typing_extensions import Self, Literal
|
6
6
|
|
7
7
|
import pydantic
|
8
8
|
from pydantic.fields import FieldInfo
|
@@ -133,13 +133,15 @@ def model_json(model: pydantic.BaseModel, *, indent: int | None = None) -> str:
|
|
133
133
|
def model_dump(
|
134
134
|
model: pydantic.BaseModel,
|
135
135
|
*,
|
136
|
-
exclude: IncEx = None,
|
136
|
+
exclude: IncEx | None = None,
|
137
137
|
exclude_unset: bool = False,
|
138
138
|
exclude_defaults: bool = False,
|
139
139
|
warnings: bool = True,
|
140
|
+
mode: Literal["json", "python"] = "python",
|
140
141
|
) -> dict[str, Any]:
|
141
|
-
if PYDANTIC_V2:
|
142
|
+
if PYDANTIC_V2 or hasattr(model, "model_dump"):
|
142
143
|
return model.model_dump(
|
144
|
+
mode=mode,
|
143
145
|
exclude=exclude,
|
144
146
|
exclude_unset=exclude_unset,
|
145
147
|
exclude_defaults=exclude_defaults,
|
@@ -37,6 +37,7 @@ from ._utils import (
|
|
37
37
|
PropertyInfo,
|
38
38
|
is_list,
|
39
39
|
is_given,
|
40
|
+
json_safe,
|
40
41
|
lru_cache,
|
41
42
|
is_mapping,
|
42
43
|
parse_date,
|
@@ -176,7 +177,7 @@ class BaseModel(pydantic.BaseModel):
|
|
176
177
|
# Based on https://github.com/samuelcolvin/pydantic/issues/1168#issuecomment-817742836.
|
177
178
|
@classmethod
|
178
179
|
@override
|
179
|
-
def construct(
|
180
|
+
def construct( # pyright: ignore[reportIncompatibleMethodOverride]
|
180
181
|
cls: Type[ModelT],
|
181
182
|
_fields_set: set[str] | None = None,
|
182
183
|
**values: object,
|
@@ -248,8 +249,8 @@ class BaseModel(pydantic.BaseModel):
|
|
248
249
|
self,
|
249
250
|
*,
|
250
251
|
mode: Literal["json", "python"] | str = "python",
|
251
|
-
include: IncEx = None,
|
252
|
-
exclude: IncEx = None,
|
252
|
+
include: IncEx | None = None,
|
253
|
+
exclude: IncEx | None = None,
|
253
254
|
by_alias: bool = False,
|
254
255
|
exclude_unset: bool = False,
|
255
256
|
exclude_defaults: bool = False,
|
@@ -279,8 +280,8 @@ class BaseModel(pydantic.BaseModel):
|
|
279
280
|
Returns:
|
280
281
|
A dictionary representation of the model.
|
281
282
|
"""
|
282
|
-
if mode
|
283
|
-
raise ValueError("mode
|
283
|
+
if mode not in {"json", "python"}:
|
284
|
+
raise ValueError("mode must be either 'json' or 'python'")
|
284
285
|
if round_trip != False:
|
285
286
|
raise ValueError("round_trip is only supported in Pydantic v2")
|
286
287
|
if warnings != True:
|
@@ -289,7 +290,7 @@ class BaseModel(pydantic.BaseModel):
|
|
289
290
|
raise ValueError("context is only supported in Pydantic v2")
|
290
291
|
if serialize_as_any != False:
|
291
292
|
raise ValueError("serialize_as_any is only supported in Pydantic v2")
|
292
|
-
|
293
|
+
dumped = super().dict( # pyright: ignore[reportDeprecated]
|
293
294
|
include=include,
|
294
295
|
exclude=exclude,
|
295
296
|
by_alias=by_alias,
|
@@ -298,13 +299,15 @@ class BaseModel(pydantic.BaseModel):
|
|
298
299
|
exclude_none=exclude_none,
|
299
300
|
)
|
300
301
|
|
302
|
+
return cast(dict[str, Any], json_safe(dumped)) if mode == "json" else dumped
|
303
|
+
|
301
304
|
@override
|
302
305
|
def model_dump_json(
|
303
306
|
self,
|
304
307
|
*,
|
305
308
|
indent: int | None = None,
|
306
|
-
include: IncEx = None,
|
307
|
-
exclude: IncEx = None,
|
309
|
+
include: IncEx | None = None,
|
310
|
+
exclude: IncEx | None = None,
|
308
311
|
by_alias: bool = False,
|
309
312
|
exclude_unset: bool = False,
|
310
313
|
exclude_defaults: bool = False,
|
@@ -192,6 +192,9 @@ class BaseAPIResponse(Generic[R]):
|
|
192
192
|
if cast_to == float:
|
193
193
|
return cast(R, float(response.text))
|
194
194
|
|
195
|
+
if cast_to == bool:
|
196
|
+
return cast(R, response.text.lower() == "true")
|
197
|
+
|
195
198
|
origin = get_origin(cast_to) or cast_to
|
196
199
|
|
197
200
|
if origin == APIResponse:
|
@@ -16,7 +16,7 @@ from typing import (
|
|
16
16
|
Optional,
|
17
17
|
Sequence,
|
18
18
|
)
|
19
|
-
from typing_extensions import Literal, Protocol, TypeAlias, TypedDict, override, runtime_checkable
|
19
|
+
from typing_extensions import Set, Literal, Protocol, TypeAlias, TypedDict, override, runtime_checkable
|
20
20
|
|
21
21
|
import httpx
|
22
22
|
import pydantic
|
@@ -193,7 +193,9 @@ StrBytesIntFloat = Union[str, bytes, int, float]
|
|
193
193
|
|
194
194
|
# Note: copied from Pydantic
|
195
195
|
# https://github.com/pydantic/pydantic/blob/32ea570bf96e84234d2992e1ddf40ab8a565925a/pydantic/main.py#L49
|
196
|
-
IncEx: TypeAlias =
|
196
|
+
IncEx: TypeAlias = Union[
|
197
|
+
Set[int], Set[str], Mapping[int, Union["IncEx", Literal[True]]], Mapping[str, Union["IncEx", Literal[True]]]
|
198
|
+
]
|
197
199
|
|
198
200
|
PostParser = Callable[[Any], Any]
|
199
201
|
|
@@ -173,6 +173,11 @@ def _transform_recursive(
|
|
173
173
|
# Iterable[T]
|
174
174
|
or (is_iterable_type(stripped_type) and is_iterable(data) and not isinstance(data, str))
|
175
175
|
):
|
176
|
+
# dicts are technically iterable, but it is an iterable on the keys of the dict and is not usually
|
177
|
+
# intended as an iterable, so we don't transform it.
|
178
|
+
if isinstance(data, dict):
|
179
|
+
return cast(object, data)
|
180
|
+
|
176
181
|
inner_type = extract_type_arg(stripped_type, 0)
|
177
182
|
return [_transform_recursive(d, annotation=annotation, inner_type=inner_type) for d in data]
|
178
183
|
|
@@ -186,7 +191,7 @@ def _transform_recursive(
|
|
186
191
|
return data
|
187
192
|
|
188
193
|
if isinstance(data, pydantic.BaseModel):
|
189
|
-
return model_dump(data, exclude_unset=True)
|
194
|
+
return model_dump(data, exclude_unset=True, mode="json")
|
190
195
|
|
191
196
|
annotated_type = _get_annotated_type(annotation)
|
192
197
|
if annotated_type is None:
|
@@ -324,7 +329,7 @@ async def _async_transform_recursive(
|
|
324
329
|
return data
|
325
330
|
|
326
331
|
if isinstance(data, pydantic.BaseModel):
|
327
|
-
return model_dump(data, exclude_unset=True)
|
332
|
+
return model_dump(data, exclude_unset=True, mode="json")
|
328
333
|
|
329
334
|
annotated_type = _get_annotated_type(annotation)
|
330
335
|
if annotated_type is None:
|
@@ -16,6 +16,7 @@ from typing import (
|
|
16
16
|
overload,
|
17
17
|
)
|
18
18
|
from pathlib import Path
|
19
|
+
from datetime import date, datetime
|
19
20
|
from typing_extensions import TypeGuard
|
20
21
|
|
21
22
|
import sniffio
|
@@ -395,3 +396,19 @@ def lru_cache(*, maxsize: int | None = 128) -> Callable[[CallableT], CallableT]:
|
|
395
396
|
maxsize=maxsize,
|
396
397
|
)
|
397
398
|
return cast(Any, wrapper) # type: ignore[no-any-return]
|
399
|
+
|
400
|
+
|
401
|
+
def json_safe(data: object) -> object:
|
402
|
+
"""Translates a mapping / sequence recursively in the same fashion
|
403
|
+
as `pydantic` v2's `model_dump(mode="json")`.
|
404
|
+
"""
|
405
|
+
if is_mapping(data):
|
406
|
+
return {json_safe(key): json_safe(value) for key, value in data.items()}
|
407
|
+
|
408
|
+
if is_iterable(data) and not isinstance(data, (str, bytes, bytearray)):
|
409
|
+
return [json_safe(item) for item in data]
|
410
|
+
|
411
|
+
if isinstance(data, (datetime, date)):
|
412
|
+
return data.isoformat()
|
413
|
+
|
414
|
+
return data
|
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
from __future__ import annotations
|
4
4
|
|
5
|
+
from typing import Union, Iterable
|
5
6
|
from typing_extensions import Literal
|
6
7
|
|
7
8
|
import httpx
|
@@ -50,10 +51,10 @@ class ImageToVideoResource(SyncAPIResource):
|
|
50
51
|
self,
|
51
52
|
*,
|
52
53
|
model: Literal["gen3a_turbo"],
|
53
|
-
prompt_image: str,
|
54
|
+
prompt_image: Union[str, Iterable[image_to_video_create_params.PromptImagePromptImage]],
|
54
55
|
duration: Literal[5, 10] | NotGiven = NOT_GIVEN,
|
55
56
|
prompt_text: str | NotGiven = NOT_GIVEN,
|
56
|
-
ratio: Literal["
|
57
|
+
ratio: Literal["1280:768", "768:1280"] | NotGiven = NOT_GIVEN,
|
57
58
|
seed: int | NotGiven = NOT_GIVEN,
|
58
59
|
watermark: bool | NotGiven = NOT_GIVEN,
|
59
60
|
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
|
@@ -69,14 +70,15 @@ class ImageToVideoResource(SyncAPIResource):
|
|
69
70
|
Args:
|
70
71
|
model: The model variant to use.
|
71
72
|
|
72
|
-
prompt_image: A HTTPS URL
|
73
|
-
|
73
|
+
prompt_image: A HTTPS URL or data URI containing an encoded image to be used as the first
|
74
|
+
frame of the generated video. See [our docs](/assets/inputs#images) on image
|
75
|
+
inputs for more information.
|
74
76
|
|
75
77
|
duration: The number of seconds of duration for the output video.
|
76
78
|
|
77
79
|
prompt_text
|
78
80
|
|
79
|
-
ratio
|
81
|
+
ratio
|
80
82
|
|
81
83
|
seed: If unspecified, a random number is chosen. Varying the seed integer is a way to
|
82
84
|
get different results for the same other request parameters. Using the same seed
|
@@ -138,10 +140,10 @@ class AsyncImageToVideoResource(AsyncAPIResource):
|
|
138
140
|
self,
|
139
141
|
*,
|
140
142
|
model: Literal["gen3a_turbo"],
|
141
|
-
prompt_image: str,
|
143
|
+
prompt_image: Union[str, Iterable[image_to_video_create_params.PromptImagePromptImage]],
|
142
144
|
duration: Literal[5, 10] | NotGiven = NOT_GIVEN,
|
143
145
|
prompt_text: str | NotGiven = NOT_GIVEN,
|
144
|
-
ratio: Literal["
|
146
|
+
ratio: Literal["1280:768", "768:1280"] | NotGiven = NOT_GIVEN,
|
145
147
|
seed: int | NotGiven = NOT_GIVEN,
|
146
148
|
watermark: bool | NotGiven = NOT_GIVEN,
|
147
149
|
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
|
@@ -157,14 +159,15 @@ class AsyncImageToVideoResource(AsyncAPIResource):
|
|
157
159
|
Args:
|
158
160
|
model: The model variant to use.
|
159
161
|
|
160
|
-
prompt_image: A HTTPS URL
|
161
|
-
|
162
|
+
prompt_image: A HTTPS URL or data URI containing an encoded image to be used as the first
|
163
|
+
frame of the generated video. See [our docs](/assets/inputs#images) on image
|
164
|
+
inputs for more information.
|
162
165
|
|
163
166
|
duration: The number of seconds of duration for the output video.
|
164
167
|
|
165
168
|
prompt_text
|
166
169
|
|
167
|
-
ratio
|
170
|
+
ratio
|
168
171
|
|
169
172
|
seed: If unspecified, a random number is chosen. Varying the seed integer is a way to
|
170
173
|
get different results for the same other request parameters. Using the same seed
|
@@ -2,22 +2,23 @@
|
|
2
2
|
|
3
3
|
from __future__ import annotations
|
4
4
|
|
5
|
+
from typing import Union, Iterable
|
5
6
|
from typing_extensions import Literal, Required, Annotated, TypedDict
|
6
7
|
|
7
8
|
from .._utils import PropertyInfo
|
8
9
|
|
9
|
-
__all__ = ["ImageToVideoCreateParams"]
|
10
|
+
__all__ = ["ImageToVideoCreateParams", "PromptImagePromptImage"]
|
10
11
|
|
11
12
|
|
12
13
|
class ImageToVideoCreateParams(TypedDict, total=False):
|
13
14
|
model: Required[Literal["gen3a_turbo"]]
|
14
15
|
"""The model variant to use."""
|
15
16
|
|
16
|
-
prompt_image: Required[Annotated[str, PropertyInfo(alias="promptImage")]]
|
17
|
-
"""
|
18
|
-
|
19
|
-
|
20
|
-
|
17
|
+
prompt_image: Required[Annotated[Union[str, Iterable[PromptImagePromptImage]], PropertyInfo(alias="promptImage")]]
|
18
|
+
"""
|
19
|
+
A HTTPS URL or data URI containing an encoded image to be used as the first
|
20
|
+
frame of the generated video. See [our docs](/assets/inputs#images) on image
|
21
|
+
inputs for more information.
|
21
22
|
"""
|
22
23
|
|
23
24
|
duration: Literal[5, 10]
|
@@ -25,8 +26,7 @@ class ImageToVideoCreateParams(TypedDict, total=False):
|
|
25
26
|
|
26
27
|
prompt_text: Annotated[str, PropertyInfo(alias="promptText")]
|
27
28
|
|
28
|
-
ratio: Literal["
|
29
|
-
"""The aspect ratio of the output video."""
|
29
|
+
ratio: Literal["1280:768", "768:1280"]
|
30
30
|
|
31
31
|
seed: int
|
32
32
|
"""If unspecified, a random number is chosen.
|
@@ -41,3 +41,18 @@ class ImageToVideoCreateParams(TypedDict, total=False):
|
|
41
41
|
A boolean indicating whether or not the output video will contain a Runway
|
42
42
|
watermark.
|
43
43
|
"""
|
44
|
+
|
45
|
+
|
46
|
+
class PromptImagePromptImage(TypedDict, total=False):
|
47
|
+
position: Required[Literal["first", "last"]]
|
48
|
+
"""The position of the image in the output video.
|
49
|
+
|
50
|
+
"first" will use the image as the first frame of the video, "last" will use the
|
51
|
+
image as the last frame of the video.
|
52
|
+
"""
|
53
|
+
|
54
|
+
uri: Required[str]
|
55
|
+
"""A HTTPS URL or data URI containing an encoded image.
|
56
|
+
|
57
|
+
See [our docs](/assets/inputs#images) on image inputs for more information.
|
58
|
+
"""
|
@@ -1,7 +1,6 @@
|
|
1
1
|
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
|
2
2
|
|
3
3
|
|
4
|
-
|
5
4
|
from .._models import BaseModel
|
6
5
|
|
7
6
|
__all__ = ["ImageToVideoCreateResponse"]
|
@@ -9,4 +8,7 @@ __all__ = ["ImageToVideoCreateResponse"]
|
|
9
8
|
|
10
9
|
class ImageToVideoCreateResponse(BaseModel):
|
11
10
|
id: str
|
12
|
-
"""The ID of the newly created task.
|
11
|
+
"""The ID of the newly created task.
|
12
|
+
|
13
|
+
Use this ID to query the task status and retrieve the generated video.
|
14
|
+
"""
|
@@ -32,7 +32,7 @@ class TestImageToVideo:
|
|
32
32
|
prompt_image="https://example.com",
|
33
33
|
duration=5,
|
34
34
|
prompt_text="promptText",
|
35
|
-
ratio="
|
35
|
+
ratio="1280:768",
|
36
36
|
seed=0,
|
37
37
|
watermark=True,
|
38
38
|
)
|
@@ -83,7 +83,7 @@ class TestAsyncImageToVideo:
|
|
83
83
|
prompt_image="https://example.com",
|
84
84
|
duration=5,
|
85
85
|
prompt_text="promptText",
|
86
|
-
ratio="
|
86
|
+
ratio="1280:768",
|
87
87
|
seed=0,
|
88
88
|
watermark=True,
|
89
89
|
)
|
@@ -1,11 +1,11 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
3
|
import os
|
4
|
-
import asyncio
|
5
4
|
import logging
|
6
5
|
from typing import TYPE_CHECKING, Iterator, AsyncIterator
|
7
6
|
|
8
7
|
import pytest
|
8
|
+
from pytest_asyncio import is_async_test
|
9
9
|
|
10
10
|
from runwayml import RunwayML, AsyncRunwayML
|
11
11
|
|
@@ -17,11 +17,13 @@ pytest.register_assert_rewrite("tests.utils")
|
|
17
17
|
logging.getLogger("runwayml").setLevel(logging.DEBUG)
|
18
18
|
|
19
19
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
20
|
+
# automatically add `pytest.mark.asyncio()` to all of our async tests
|
21
|
+
# so we don't have to add that boilerplate everywhere
|
22
|
+
def pytest_collection_modifyitems(items: list[pytest.Function]) -> None:
|
23
|
+
pytest_asyncio_tests = (item for item in items if is_async_test(item))
|
24
|
+
session_scope_marker = pytest.mark.asyncio(loop_scope="session")
|
25
|
+
for async_test in pytest_asyncio_tests:
|
26
|
+
async_test.add_marker(session_scope_marker, append=False)
|
25
27
|
|
26
28
|
|
27
29
|
base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
|
@@ -10,6 +10,7 @@ import inspect
|
|
10
10
|
import tracemalloc
|
11
11
|
from typing import Any, Union, cast
|
12
12
|
from unittest import mock
|
13
|
+
from typing_extensions import Literal
|
13
14
|
|
14
15
|
import httpx
|
15
16
|
import pytest
|
@@ -692,6 +693,7 @@ class TestRunwayML:
|
|
692
693
|
[3, "", 0.5],
|
693
694
|
[2, "", 0.5 * 2.0],
|
694
695
|
[1, "", 0.5 * 4.0],
|
696
|
+
[-1100, "", 8], # test large number potentially overflowing
|
695
697
|
],
|
696
698
|
)
|
697
699
|
@mock.patch("time.time", mock.MagicMock(return_value=1696004797))
|
@@ -750,7 +752,14 @@ class TestRunwayML:
|
|
750
752
|
@pytest.mark.parametrize("failures_before_success", [0, 2, 4])
|
751
753
|
@mock.patch("runwayml._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout)
|
752
754
|
@pytest.mark.respx(base_url=base_url)
|
753
|
-
|
755
|
+
@pytest.mark.parametrize("failure_mode", ["status", "exception"])
|
756
|
+
def test_retries_taken(
|
757
|
+
self,
|
758
|
+
client: RunwayML,
|
759
|
+
failures_before_success: int,
|
760
|
+
failure_mode: Literal["status", "exception"],
|
761
|
+
respx_mock: MockRouter,
|
762
|
+
) -> None:
|
754
763
|
client = client.with_options(max_retries=4)
|
755
764
|
|
756
765
|
nb_retries = 0
|
@@ -759,6 +768,8 @@ class TestRunwayML:
|
|
759
768
|
nonlocal nb_retries
|
760
769
|
if nb_retries < failures_before_success:
|
761
770
|
nb_retries += 1
|
771
|
+
if failure_mode == "exception":
|
772
|
+
raise RuntimeError("oops")
|
762
773
|
return httpx.Response(500)
|
763
774
|
return httpx.Response(200)
|
764
775
|
|
@@ -1477,6 +1488,7 @@ class TestAsyncRunwayML:
|
|
1477
1488
|
[3, "", 0.5],
|
1478
1489
|
[2, "", 0.5 * 2.0],
|
1479
1490
|
[1, "", 0.5 * 4.0],
|
1491
|
+
[-1100, "", 8], # test large number potentially overflowing
|
1480
1492
|
],
|
1481
1493
|
)
|
1482
1494
|
@mock.patch("time.time", mock.MagicMock(return_value=1696004797))
|
@@ -1537,8 +1549,13 @@ class TestAsyncRunwayML:
|
|
1537
1549
|
@mock.patch("runwayml._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout)
|
1538
1550
|
@pytest.mark.respx(base_url=base_url)
|
1539
1551
|
@pytest.mark.asyncio
|
1552
|
+
@pytest.mark.parametrize("failure_mode", ["status", "exception"])
|
1540
1553
|
async def test_retries_taken(
|
1541
|
-
self,
|
1554
|
+
self,
|
1555
|
+
async_client: AsyncRunwayML,
|
1556
|
+
failures_before_success: int,
|
1557
|
+
failure_mode: Literal["status", "exception"],
|
1558
|
+
respx_mock: MockRouter,
|
1542
1559
|
) -> None:
|
1543
1560
|
client = async_client.with_options(max_retries=4)
|
1544
1561
|
|
@@ -1548,6 +1565,8 @@ class TestAsyncRunwayML:
|
|
1548
1565
|
nonlocal nb_retries
|
1549
1566
|
if nb_retries < failures_before_success:
|
1550
1567
|
nb_retries += 1
|
1568
|
+
if failure_mode == "exception":
|
1569
|
+
raise RuntimeError("oops")
|
1551
1570
|
return httpx.Response(500)
|
1552
1571
|
return httpx.Response(200)
|
1553
1572
|
|
@@ -245,7 +245,7 @@ def test_nested_union_of_mixed_types() -> None:
|
|
245
245
|
assert m.foo is True
|
246
246
|
|
247
247
|
m = Model.construct(foo="CARD_HOLDER")
|
248
|
-
assert m.foo
|
248
|
+
assert m.foo == "CARD_HOLDER"
|
249
249
|
|
250
250
|
m = Model.construct(foo={"bar": False})
|
251
251
|
assert isinstance(m.foo, Submodel1)
|
@@ -520,19 +520,15 @@ def test_to_dict() -> None:
|
|
520
520
|
assert m3.to_dict(exclude_none=True) == {}
|
521
521
|
assert m3.to_dict(exclude_defaults=True) == {}
|
522
522
|
|
523
|
-
|
524
|
-
|
525
|
-
class Model2(BaseModel):
|
526
|
-
created_at: datetime
|
523
|
+
class Model2(BaseModel):
|
524
|
+
created_at: datetime
|
527
525
|
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
else:
|
533
|
-
with pytest.raises(ValueError, match="mode is only supported in Pydantic v2"):
|
534
|
-
m.to_dict(mode="json")
|
526
|
+
time_str = "2024-03-21T11:39:01.275859"
|
527
|
+
m4 = Model2.construct(created_at=time_str)
|
528
|
+
assert m4.to_dict(mode="python") == {"created_at": datetime.fromisoformat(time_str)}
|
529
|
+
assert m4.to_dict(mode="json") == {"created_at": time_str}
|
535
530
|
|
531
|
+
if not PYDANTIC_V2:
|
536
532
|
with pytest.raises(ValueError, match="warnings is only supported in Pydantic v2"):
|
537
533
|
m.to_dict(warnings=False)
|
538
534
|
|
@@ -558,9 +554,6 @@ def test_forwards_compat_model_dump_method() -> None:
|
|
558
554
|
assert m3.model_dump(exclude_none=True) == {}
|
559
555
|
|
560
556
|
if not PYDANTIC_V2:
|
561
|
-
with pytest.raises(ValueError, match="mode is only supported in Pydantic v2"):
|
562
|
-
m.model_dump(mode="json")
|
563
|
-
|
564
557
|
with pytest.raises(ValueError, match="round_trip is only supported in Pydantic v2"):
|
565
558
|
m.model_dump(round_trip=True)
|
566
559
|
|
@@ -190,6 +190,56 @@ async def test_async_response_parse_annotated_type(async_client: AsyncRunwayML)
|
|
190
190
|
assert obj.bar == 2
|
191
191
|
|
192
192
|
|
193
|
+
@pytest.mark.parametrize(
|
194
|
+
"content, expected",
|
195
|
+
[
|
196
|
+
("false", False),
|
197
|
+
("true", True),
|
198
|
+
("False", False),
|
199
|
+
("True", True),
|
200
|
+
("TrUe", True),
|
201
|
+
("FalSe", False),
|
202
|
+
],
|
203
|
+
)
|
204
|
+
def test_response_parse_bool(client: RunwayML, content: str, expected: bool) -> None:
|
205
|
+
response = APIResponse(
|
206
|
+
raw=httpx.Response(200, content=content),
|
207
|
+
client=client,
|
208
|
+
stream=False,
|
209
|
+
stream_cls=None,
|
210
|
+
cast_to=str,
|
211
|
+
options=FinalRequestOptions.construct(method="get", url="/foo"),
|
212
|
+
)
|
213
|
+
|
214
|
+
result = response.parse(to=bool)
|
215
|
+
assert result is expected
|
216
|
+
|
217
|
+
|
218
|
+
@pytest.mark.parametrize(
|
219
|
+
"content, expected",
|
220
|
+
[
|
221
|
+
("false", False),
|
222
|
+
("true", True),
|
223
|
+
("False", False),
|
224
|
+
("True", True),
|
225
|
+
("TrUe", True),
|
226
|
+
("FalSe", False),
|
227
|
+
],
|
228
|
+
)
|
229
|
+
async def test_async_response_parse_bool(client: AsyncRunwayML, content: str, expected: bool) -> None:
|
230
|
+
response = AsyncAPIResponse(
|
231
|
+
raw=httpx.Response(200, content=content),
|
232
|
+
client=client,
|
233
|
+
stream=False,
|
234
|
+
stream_cls=None,
|
235
|
+
cast_to=str,
|
236
|
+
options=FinalRequestOptions.construct(method="get", url="/foo"),
|
237
|
+
)
|
238
|
+
|
239
|
+
result = await response.parse(to=bool)
|
240
|
+
assert result is expected
|
241
|
+
|
242
|
+
|
193
243
|
class OtherModel(BaseModel):
|
194
244
|
a: str
|
195
245
|
|
@@ -177,17 +177,32 @@ class DateDict(TypedDict, total=False):
|
|
177
177
|
foo: Annotated[date, PropertyInfo(format="iso8601")]
|
178
178
|
|
179
179
|
|
180
|
+
class DatetimeModel(BaseModel):
|
181
|
+
foo: datetime
|
182
|
+
|
183
|
+
|
184
|
+
class DateModel(BaseModel):
|
185
|
+
foo: Optional[date]
|
186
|
+
|
187
|
+
|
180
188
|
@parametrize
|
181
189
|
@pytest.mark.asyncio
|
182
190
|
async def test_iso8601_format(use_async: bool) -> None:
|
183
191
|
dt = datetime.fromisoformat("2023-02-23T14:16:36.337692+00:00")
|
192
|
+
tz = "Z" if PYDANTIC_V2 else "+00:00"
|
184
193
|
assert await transform({"foo": dt}, DatetimeDict, use_async) == {"foo": "2023-02-23T14:16:36.337692+00:00"} # type: ignore[comparison-overlap]
|
194
|
+
assert await transform(DatetimeModel(foo=dt), Any, use_async) == {"foo": "2023-02-23T14:16:36.337692" + tz} # type: ignore[comparison-overlap]
|
185
195
|
|
186
196
|
dt = dt.replace(tzinfo=None)
|
187
197
|
assert await transform({"foo": dt}, DatetimeDict, use_async) == {"foo": "2023-02-23T14:16:36.337692"} # type: ignore[comparison-overlap]
|
198
|
+
assert await transform(DatetimeModel(foo=dt), Any, use_async) == {"foo": "2023-02-23T14:16:36.337692"} # type: ignore[comparison-overlap]
|
188
199
|
|
189
200
|
assert await transform({"foo": None}, DateDict, use_async) == {"foo": None} # type: ignore[comparison-overlap]
|
201
|
+
assert await transform(DateModel(foo=None), Any, use_async) == {"foo": None} # type: ignore
|
190
202
|
assert await transform({"foo": date.fromisoformat("2023-02-23")}, DateDict, use_async) == {"foo": "2023-02-23"} # type: ignore[comparison-overlap]
|
203
|
+
assert await transform(DateModel(foo=date.fromisoformat("2023-02-23")), DateDict, use_async) == {
|
204
|
+
"foo": "2023-02-23"
|
205
|
+
} # type: ignore[comparison-overlap]
|
191
206
|
|
192
207
|
|
193
208
|
@parametrize
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|