runwayml 3.0.1__py3-none-any.whl → 3.0.3__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.
runwayml/_base_client.py CHANGED
@@ -98,7 +98,11 @@ _StreamT = TypeVar("_StreamT", bound=Stream[Any])
98
98
  _AsyncStreamT = TypeVar("_AsyncStreamT", bound=AsyncStream[Any])
99
99
 
100
100
  if TYPE_CHECKING:
101
- from httpx._config import DEFAULT_TIMEOUT_CONFIG as HTTPX_DEFAULT_TIMEOUT
101
+ from httpx._config import (
102
+ DEFAULT_TIMEOUT_CONFIG, # pyright: ignore[reportPrivateImportUsage]
103
+ )
104
+
105
+ HTTPX_DEFAULT_TIMEOUT = DEFAULT_TIMEOUT_CONFIG
102
106
  else:
103
107
  try:
104
108
  from httpx._config import DEFAULT_TIMEOUT_CONFIG as HTTPX_DEFAULT_TIMEOUT
@@ -115,6 +119,7 @@ class PageInfo:
115
119
 
116
120
  url: URL | NotGiven
117
121
  params: Query | NotGiven
122
+ json: Body | NotGiven
118
123
 
119
124
  @overload
120
125
  def __init__(
@@ -130,19 +135,30 @@ class PageInfo:
130
135
  params: Query,
131
136
  ) -> None: ...
132
137
 
138
+ @overload
139
+ def __init__(
140
+ self,
141
+ *,
142
+ json: Body,
143
+ ) -> None: ...
144
+
133
145
  def __init__(
134
146
  self,
135
147
  *,
136
148
  url: URL | NotGiven = NOT_GIVEN,
149
+ json: Body | NotGiven = NOT_GIVEN,
137
150
  params: Query | NotGiven = NOT_GIVEN,
138
151
  ) -> None:
139
152
  self.url = url
153
+ self.json = json
140
154
  self.params = params
141
155
 
142
156
  @override
143
157
  def __repr__(self) -> str:
144
158
  if self.url:
145
159
  return f"{self.__class__.__name__}(url={self.url})"
160
+ if self.json:
161
+ return f"{self.__class__.__name__}(json={self.json})"
146
162
  return f"{self.__class__.__name__}(params={self.params})"
147
163
 
148
164
 
@@ -191,6 +207,19 @@ class BasePage(GenericModel, Generic[_T]):
191
207
  options.url = str(url)
192
208
  return options
193
209
 
210
+ if not isinstance(info.json, NotGiven):
211
+ if not is_mapping(info.json):
212
+ raise TypeError("Pagination is only supported with mappings")
213
+
214
+ if not options.json_data:
215
+ options.json_data = {**info.json}
216
+ else:
217
+ if not is_mapping(options.json_data):
218
+ raise TypeError("Pagination is only supported with mappings")
219
+
220
+ options.json_data = {**options.json_data, **info.json}
221
+ return options
222
+
194
223
  raise ValueError("Unexpected PageInfo state")
195
224
 
196
225
 
@@ -408,8 +437,7 @@ class BaseClient(Generic[_HttpxClientT, _DefaultStreamT]):
408
437
  headers = httpx.Headers(headers_dict)
409
438
 
410
439
  idempotency_header = self._idempotency_header
411
- if idempotency_header and options.method.lower() != "get" and idempotency_header not in headers:
412
- options.idempotency_key = options.idempotency_key or self._idempotency_key()
440
+ if idempotency_header and options.idempotency_key and idempotency_header not in headers:
413
441
  headers[idempotency_header] = options.idempotency_key
414
442
 
415
443
  # Don't set these headers if they were already set or removed by the caller. We check
@@ -874,7 +902,6 @@ class SyncAPIClient(BaseClient[httpx.Client, Stream[Any]]):
874
902
  self,
875
903
  cast_to: Type[ResponseT],
876
904
  options: FinalRequestOptions,
877
- remaining_retries: Optional[int] = None,
878
905
  *,
879
906
  stream: Literal[True],
880
907
  stream_cls: Type[_StreamT],
@@ -885,7 +912,6 @@ class SyncAPIClient(BaseClient[httpx.Client, Stream[Any]]):
885
912
  self,
886
913
  cast_to: Type[ResponseT],
887
914
  options: FinalRequestOptions,
888
- remaining_retries: Optional[int] = None,
889
915
  *,
890
916
  stream: Literal[False] = False,
891
917
  ) -> ResponseT: ...
@@ -895,7 +921,6 @@ class SyncAPIClient(BaseClient[httpx.Client, Stream[Any]]):
895
921
  self,
896
922
  cast_to: Type[ResponseT],
897
923
  options: FinalRequestOptions,
898
- remaining_retries: Optional[int] = None,
899
924
  *,
900
925
  stream: bool = False,
901
926
  stream_cls: Type[_StreamT] | None = None,
@@ -905,125 +930,109 @@ class SyncAPIClient(BaseClient[httpx.Client, Stream[Any]]):
905
930
  self,
906
931
  cast_to: Type[ResponseT],
907
932
  options: FinalRequestOptions,
908
- remaining_retries: Optional[int] = None,
909
933
  *,
910
934
  stream: bool = False,
911
935
  stream_cls: type[_StreamT] | None = None,
912
936
  ) -> ResponseT | _StreamT:
913
- if remaining_retries is not None:
914
- retries_taken = options.get_max_retries(self.max_retries) - remaining_retries
915
- else:
916
- retries_taken = 0
917
-
918
- return self._request(
919
- cast_to=cast_to,
920
- options=options,
921
- stream=stream,
922
- stream_cls=stream_cls,
923
- retries_taken=retries_taken,
924
- )
937
+ cast_to = self._maybe_override_cast_to(cast_to, options)
925
938
 
926
- def _request(
927
- self,
928
- *,
929
- cast_to: Type[ResponseT],
930
- options: FinalRequestOptions,
931
- retries_taken: int,
932
- stream: bool,
933
- stream_cls: type[_StreamT] | None,
934
- ) -> ResponseT | _StreamT:
935
939
  # create a copy of the options we were given so that if the
936
940
  # options are mutated later & we then retry, the retries are
937
941
  # given the original options
938
942
  input_options = model_copy(options)
939
-
940
- cast_to = self._maybe_override_cast_to(cast_to, options)
941
- options = self._prepare_options(options)
942
-
943
- remaining_retries = options.get_max_retries(self.max_retries) - retries_taken
944
- request = self._build_request(options, retries_taken=retries_taken)
945
- self._prepare_request(request)
946
-
947
- if options.idempotency_key:
943
+ if input_options.idempotency_key is None and input_options.method.lower() != "get":
948
944
  # ensure the idempotency key is reused between requests
949
- input_options.idempotency_key = options.idempotency_key
945
+ input_options.idempotency_key = self._idempotency_key()
950
946
 
951
- kwargs: HttpxSendArgs = {}
952
- if self.custom_auth is not None:
953
- kwargs["auth"] = self.custom_auth
947
+ response: httpx.Response | None = None
948
+ max_retries = input_options.get_max_retries(self.max_retries)
954
949
 
955
- log.debug("Sending HTTP Request: %s %s", request.method, request.url)
950
+ retries_taken = 0
951
+ for retries_taken in range(max_retries + 1):
952
+ options = model_copy(input_options)
953
+ options = self._prepare_options(options)
956
954
 
957
- try:
958
- response = self._client.send(
959
- request,
960
- stream=stream or self._should_stream_response_body(request=request),
961
- **kwargs,
962
- )
963
- except httpx.TimeoutException as err:
964
- log.debug("Encountered httpx.TimeoutException", exc_info=True)
955
+ remaining_retries = max_retries - retries_taken
956
+ request = self._build_request(options, retries_taken=retries_taken)
957
+ self._prepare_request(request)
965
958
 
966
- if remaining_retries > 0:
967
- return self._retry_request(
968
- input_options,
969
- cast_to,
970
- retries_taken=retries_taken,
971
- stream=stream,
972
- stream_cls=stream_cls,
973
- response_headers=None,
974
- )
959
+ kwargs: HttpxSendArgs = {}
960
+ if self.custom_auth is not None:
961
+ kwargs["auth"] = self.custom_auth
975
962
 
976
- log.debug("Raising timeout error")
977
- raise APITimeoutError(request=request) from err
978
- except Exception as err:
979
- log.debug("Encountered Exception", exc_info=True)
963
+ log.debug("Sending HTTP Request: %s %s", request.method, request.url)
980
964
 
981
- if remaining_retries > 0:
982
- return self._retry_request(
983
- input_options,
984
- cast_to,
985
- retries_taken=retries_taken,
986
- stream=stream,
987
- stream_cls=stream_cls,
988
- response_headers=None,
965
+ response = None
966
+ try:
967
+ response = self._client.send(
968
+ request,
969
+ stream=stream or self._should_stream_response_body(request=request),
970
+ **kwargs,
989
971
  )
972
+ except httpx.TimeoutException as err:
973
+ log.debug("Encountered httpx.TimeoutException", exc_info=True)
974
+
975
+ if remaining_retries > 0:
976
+ self._sleep_for_retry(
977
+ retries_taken=retries_taken,
978
+ max_retries=max_retries,
979
+ options=input_options,
980
+ response=None,
981
+ )
982
+ continue
983
+
984
+ log.debug("Raising timeout error")
985
+ raise APITimeoutError(request=request) from err
986
+ except Exception as err:
987
+ log.debug("Encountered Exception", exc_info=True)
988
+
989
+ if remaining_retries > 0:
990
+ self._sleep_for_retry(
991
+ retries_taken=retries_taken,
992
+ max_retries=max_retries,
993
+ options=input_options,
994
+ response=None,
995
+ )
996
+ continue
997
+
998
+ log.debug("Raising connection error")
999
+ raise APIConnectionError(request=request) from err
1000
+
1001
+ log.debug(
1002
+ 'HTTP Response: %s %s "%i %s" %s',
1003
+ request.method,
1004
+ request.url,
1005
+ response.status_code,
1006
+ response.reason_phrase,
1007
+ response.headers,
1008
+ )
990
1009
 
991
- log.debug("Raising connection error")
992
- raise APIConnectionError(request=request) from err
993
-
994
- log.debug(
995
- 'HTTP Response: %s %s "%i %s" %s',
996
- request.method,
997
- request.url,
998
- response.status_code,
999
- response.reason_phrase,
1000
- response.headers,
1001
- )
1010
+ try:
1011
+ response.raise_for_status()
1012
+ except httpx.HTTPStatusError as err: # thrown on 4xx and 5xx status code
1013
+ log.debug("Encountered httpx.HTTPStatusError", exc_info=True)
1014
+
1015
+ if remaining_retries > 0 and self._should_retry(err.response):
1016
+ err.response.close()
1017
+ self._sleep_for_retry(
1018
+ retries_taken=retries_taken,
1019
+ max_retries=max_retries,
1020
+ options=input_options,
1021
+ response=response,
1022
+ )
1023
+ continue
1002
1024
 
1003
- try:
1004
- response.raise_for_status()
1005
- except httpx.HTTPStatusError as err: # thrown on 4xx and 5xx status code
1006
- log.debug("Encountered httpx.HTTPStatusError", exc_info=True)
1007
-
1008
- if remaining_retries > 0 and self._should_retry(err.response):
1009
- err.response.close()
1010
- return self._retry_request(
1011
- input_options,
1012
- cast_to,
1013
- retries_taken=retries_taken,
1014
- response_headers=err.response.headers,
1015
- stream=stream,
1016
- stream_cls=stream_cls,
1017
- )
1025
+ # If the response is streamed then we need to explicitly read the response
1026
+ # to completion before attempting to access the response text.
1027
+ if not err.response.is_closed:
1028
+ err.response.read()
1018
1029
 
1019
- # If the response is streamed then we need to explicitly read the response
1020
- # to completion before attempting to access the response text.
1021
- if not err.response.is_closed:
1022
- err.response.read()
1030
+ log.debug("Re-raising status error")
1031
+ raise self._make_status_error_from_response(err.response) from None
1023
1032
 
1024
- log.debug("Re-raising status error")
1025
- raise self._make_status_error_from_response(err.response) from None
1033
+ break
1026
1034
 
1035
+ assert response is not None, "could not resolve response (should never happen)"
1027
1036
  return self._process_response(
1028
1037
  cast_to=cast_to,
1029
1038
  options=options,
@@ -1033,37 +1042,20 @@ class SyncAPIClient(BaseClient[httpx.Client, Stream[Any]]):
1033
1042
  retries_taken=retries_taken,
1034
1043
  )
1035
1044
 
1036
- def _retry_request(
1037
- self,
1038
- options: FinalRequestOptions,
1039
- cast_to: Type[ResponseT],
1040
- *,
1041
- retries_taken: int,
1042
- response_headers: httpx.Headers | None,
1043
- stream: bool,
1044
- stream_cls: type[_StreamT] | None,
1045
- ) -> ResponseT | _StreamT:
1046
- remaining_retries = options.get_max_retries(self.max_retries) - retries_taken
1045
+ def _sleep_for_retry(
1046
+ self, *, retries_taken: int, max_retries: int, options: FinalRequestOptions, response: httpx.Response | None
1047
+ ) -> None:
1048
+ remaining_retries = max_retries - retries_taken
1047
1049
  if remaining_retries == 1:
1048
1050
  log.debug("1 retry left")
1049
1051
  else:
1050
1052
  log.debug("%i retries left", remaining_retries)
1051
1053
 
1052
- timeout = self._calculate_retry_timeout(remaining_retries, options, response_headers)
1054
+ timeout = self._calculate_retry_timeout(remaining_retries, options, response.headers if response else None)
1053
1055
  log.info("Retrying request to %s in %f seconds", options.url, timeout)
1054
1056
 
1055
- # In a synchronous context we are blocking the entire thread. Up to the library user to run the client in a
1056
- # different thread if necessary.
1057
1057
  time.sleep(timeout)
1058
1058
 
1059
- return self._request(
1060
- options=options,
1061
- cast_to=cast_to,
1062
- retries_taken=retries_taken + 1,
1063
- stream=stream,
1064
- stream_cls=stream_cls,
1065
- )
1066
-
1067
1059
  def _process_response(
1068
1060
  self,
1069
1061
  *,
@@ -1407,7 +1399,6 @@ class AsyncAPIClient(BaseClient[httpx.AsyncClient, AsyncStream[Any]]):
1407
1399
  options: FinalRequestOptions,
1408
1400
  *,
1409
1401
  stream: Literal[False] = False,
1410
- remaining_retries: Optional[int] = None,
1411
1402
  ) -> ResponseT: ...
1412
1403
 
1413
1404
  @overload
@@ -1418,7 +1409,6 @@ class AsyncAPIClient(BaseClient[httpx.AsyncClient, AsyncStream[Any]]):
1418
1409
  *,
1419
1410
  stream: Literal[True],
1420
1411
  stream_cls: type[_AsyncStreamT],
1421
- remaining_retries: Optional[int] = None,
1422
1412
  ) -> _AsyncStreamT: ...
1423
1413
 
1424
1414
  @overload
@@ -1429,7 +1419,6 @@ class AsyncAPIClient(BaseClient[httpx.AsyncClient, AsyncStream[Any]]):
1429
1419
  *,
1430
1420
  stream: bool,
1431
1421
  stream_cls: type[_AsyncStreamT] | None = None,
1432
- remaining_retries: Optional[int] = None,
1433
1422
  ) -> ResponseT | _AsyncStreamT: ...
1434
1423
 
1435
1424
  async def request(
@@ -1439,120 +1428,111 @@ class AsyncAPIClient(BaseClient[httpx.AsyncClient, AsyncStream[Any]]):
1439
1428
  *,
1440
1429
  stream: bool = False,
1441
1430
  stream_cls: type[_AsyncStreamT] | None = None,
1442
- remaining_retries: Optional[int] = None,
1443
- ) -> ResponseT | _AsyncStreamT:
1444
- if remaining_retries is not None:
1445
- retries_taken = options.get_max_retries(self.max_retries) - remaining_retries
1446
- else:
1447
- retries_taken = 0
1448
-
1449
- return await self._request(
1450
- cast_to=cast_to,
1451
- options=options,
1452
- stream=stream,
1453
- stream_cls=stream_cls,
1454
- retries_taken=retries_taken,
1455
- )
1456
-
1457
- async def _request(
1458
- self,
1459
- cast_to: Type[ResponseT],
1460
- options: FinalRequestOptions,
1461
- *,
1462
- stream: bool,
1463
- stream_cls: type[_AsyncStreamT] | None,
1464
- retries_taken: int,
1465
1431
  ) -> ResponseT | _AsyncStreamT:
1466
1432
  if self._platform is None:
1467
1433
  # `get_platform` can make blocking IO calls so we
1468
1434
  # execute it earlier while we are in an async context
1469
1435
  self._platform = await asyncify(get_platform)()
1470
1436
 
1437
+ cast_to = self._maybe_override_cast_to(cast_to, options)
1438
+
1471
1439
  # create a copy of the options we were given so that if the
1472
1440
  # options are mutated later & we then retry, the retries are
1473
1441
  # given the original options
1474
1442
  input_options = model_copy(options)
1475
-
1476
- cast_to = self._maybe_override_cast_to(cast_to, options)
1477
- options = await self._prepare_options(options)
1478
-
1479
- remaining_retries = options.get_max_retries(self.max_retries) - retries_taken
1480
- request = self._build_request(options, retries_taken=retries_taken)
1481
- await self._prepare_request(request)
1482
-
1483
- if options.idempotency_key:
1443
+ if input_options.idempotency_key is None and input_options.method.lower() != "get":
1484
1444
  # ensure the idempotency key is reused between requests
1485
- input_options.idempotency_key = options.idempotency_key
1445
+ input_options.idempotency_key = self._idempotency_key()
1486
1446
 
1487
- kwargs: HttpxSendArgs = {}
1488
- if self.custom_auth is not None:
1489
- kwargs["auth"] = self.custom_auth
1447
+ response: httpx.Response | None = None
1448
+ max_retries = input_options.get_max_retries(self.max_retries)
1490
1449
 
1491
- try:
1492
- response = await self._client.send(
1493
- request,
1494
- stream=stream or self._should_stream_response_body(request=request),
1495
- **kwargs,
1496
- )
1497
- except httpx.TimeoutException as err:
1498
- log.debug("Encountered httpx.TimeoutException", exc_info=True)
1450
+ retries_taken = 0
1451
+ for retries_taken in range(max_retries + 1):
1452
+ options = model_copy(input_options)
1453
+ options = await self._prepare_options(options)
1499
1454
 
1500
- if remaining_retries > 0:
1501
- return await self._retry_request(
1502
- input_options,
1503
- cast_to,
1504
- retries_taken=retries_taken,
1505
- stream=stream,
1506
- stream_cls=stream_cls,
1507
- response_headers=None,
1508
- )
1455
+ remaining_retries = max_retries - retries_taken
1456
+ request = self._build_request(options, retries_taken=retries_taken)
1457
+ await self._prepare_request(request)
1509
1458
 
1510
- log.debug("Raising timeout error")
1511
- raise APITimeoutError(request=request) from err
1512
- except Exception as err:
1513
- log.debug("Encountered Exception", exc_info=True)
1459
+ kwargs: HttpxSendArgs = {}
1460
+ if self.custom_auth is not None:
1461
+ kwargs["auth"] = self.custom_auth
1514
1462
 
1515
- if remaining_retries > 0:
1516
- return await self._retry_request(
1517
- input_options,
1518
- cast_to,
1519
- retries_taken=retries_taken,
1520
- stream=stream,
1521
- stream_cls=stream_cls,
1522
- response_headers=None,
1523
- )
1463
+ log.debug("Sending HTTP Request: %s %s", request.method, request.url)
1524
1464
 
1525
- log.debug("Raising connection error")
1526
- raise APIConnectionError(request=request) from err
1465
+ response = None
1466
+ try:
1467
+ response = await self._client.send(
1468
+ request,
1469
+ stream=stream or self._should_stream_response_body(request=request),
1470
+ **kwargs,
1471
+ )
1472
+ except httpx.TimeoutException as err:
1473
+ log.debug("Encountered httpx.TimeoutException", exc_info=True)
1474
+
1475
+ if remaining_retries > 0:
1476
+ await self._sleep_for_retry(
1477
+ retries_taken=retries_taken,
1478
+ max_retries=max_retries,
1479
+ options=input_options,
1480
+ response=None,
1481
+ )
1482
+ continue
1483
+
1484
+ log.debug("Raising timeout error")
1485
+ raise APITimeoutError(request=request) from err
1486
+ except Exception as err:
1487
+ log.debug("Encountered Exception", exc_info=True)
1488
+
1489
+ if remaining_retries > 0:
1490
+ await self._sleep_for_retry(
1491
+ retries_taken=retries_taken,
1492
+ max_retries=max_retries,
1493
+ options=input_options,
1494
+ response=None,
1495
+ )
1496
+ continue
1497
+
1498
+ log.debug("Raising connection error")
1499
+ raise APIConnectionError(request=request) from err
1500
+
1501
+ log.debug(
1502
+ 'HTTP Response: %s %s "%i %s" %s',
1503
+ request.method,
1504
+ request.url,
1505
+ response.status_code,
1506
+ response.reason_phrase,
1507
+ response.headers,
1508
+ )
1527
1509
 
1528
- log.debug(
1529
- 'HTTP Request: %s %s "%i %s"', request.method, request.url, response.status_code, response.reason_phrase
1530
- )
1510
+ try:
1511
+ response.raise_for_status()
1512
+ except httpx.HTTPStatusError as err: # thrown on 4xx and 5xx status code
1513
+ log.debug("Encountered httpx.HTTPStatusError", exc_info=True)
1514
+
1515
+ if remaining_retries > 0 and self._should_retry(err.response):
1516
+ await err.response.aclose()
1517
+ await self._sleep_for_retry(
1518
+ retries_taken=retries_taken,
1519
+ max_retries=max_retries,
1520
+ options=input_options,
1521
+ response=response,
1522
+ )
1523
+ continue
1531
1524
 
1532
- try:
1533
- response.raise_for_status()
1534
- except httpx.HTTPStatusError as err: # thrown on 4xx and 5xx status code
1535
- log.debug("Encountered httpx.HTTPStatusError", exc_info=True)
1536
-
1537
- if remaining_retries > 0 and self._should_retry(err.response):
1538
- await err.response.aclose()
1539
- return await self._retry_request(
1540
- input_options,
1541
- cast_to,
1542
- retries_taken=retries_taken,
1543
- response_headers=err.response.headers,
1544
- stream=stream,
1545
- stream_cls=stream_cls,
1546
- )
1525
+ # If the response is streamed then we need to explicitly read the response
1526
+ # to completion before attempting to access the response text.
1527
+ if not err.response.is_closed:
1528
+ await err.response.aread()
1547
1529
 
1548
- # If the response is streamed then we need to explicitly read the response
1549
- # to completion before attempting to access the response text.
1550
- if not err.response.is_closed:
1551
- await err.response.aread()
1530
+ log.debug("Re-raising status error")
1531
+ raise self._make_status_error_from_response(err.response) from None
1552
1532
 
1553
- log.debug("Re-raising status error")
1554
- raise self._make_status_error_from_response(err.response) from None
1533
+ break
1555
1534
 
1535
+ assert response is not None, "could not resolve response (should never happen)"
1556
1536
  return await self._process_response(
1557
1537
  cast_to=cast_to,
1558
1538
  options=options,
@@ -1562,35 +1542,20 @@ class AsyncAPIClient(BaseClient[httpx.AsyncClient, AsyncStream[Any]]):
1562
1542
  retries_taken=retries_taken,
1563
1543
  )
1564
1544
 
1565
- async def _retry_request(
1566
- self,
1567
- options: FinalRequestOptions,
1568
- cast_to: Type[ResponseT],
1569
- *,
1570
- retries_taken: int,
1571
- response_headers: httpx.Headers | None,
1572
- stream: bool,
1573
- stream_cls: type[_AsyncStreamT] | None,
1574
- ) -> ResponseT | _AsyncStreamT:
1575
- remaining_retries = options.get_max_retries(self.max_retries) - retries_taken
1545
+ async def _sleep_for_retry(
1546
+ self, *, retries_taken: int, max_retries: int, options: FinalRequestOptions, response: httpx.Response | None
1547
+ ) -> None:
1548
+ remaining_retries = max_retries - retries_taken
1576
1549
  if remaining_retries == 1:
1577
1550
  log.debug("1 retry left")
1578
1551
  else:
1579
1552
  log.debug("%i retries left", remaining_retries)
1580
1553
 
1581
- timeout = self._calculate_retry_timeout(remaining_retries, options, response_headers)
1554
+ timeout = self._calculate_retry_timeout(remaining_retries, options, response.headers if response else None)
1582
1555
  log.info("Retrying request to %s in %f seconds", options.url, timeout)
1583
1556
 
1584
1557
  await anyio.sleep(timeout)
1585
1558
 
1586
- return await self._request(
1587
- options=options,
1588
- cast_to=cast_to,
1589
- retries_taken=retries_taken + 1,
1590
- stream=stream,
1591
- stream_cls=stream_cls,
1592
- )
1593
-
1594
1559
  async def _process_response(
1595
1560
  self,
1596
1561
  *,
runwayml/_client.py CHANGED
@@ -19,10 +19,7 @@ from ._types import (
19
19
  ProxiesTypes,
20
20
  RequestOptions,
21
21
  )
22
- from ._utils import (
23
- is_given,
24
- get_async_library,
25
- )
22
+ from ._utils import is_given, get_async_library
26
23
  from ._version import __version__
27
24
  from .resources import tasks, organization, image_to_video
28
25
  from ._streaming import Stream as Stream, AsyncStream as AsyncStream
runwayml/_models.py CHANGED
@@ -19,7 +19,6 @@ from typing_extensions import (
19
19
  )
20
20
 
21
21
  import pydantic
22
- import pydantic.generics
23
22
  from pydantic.fields import FieldInfo
24
23
 
25
24
  from ._types import (
@@ -627,8 +626,8 @@ def _build_discriminated_union_meta(*, union: type, meta_annotations: tuple[Any,
627
626
  # Note: if one variant defines an alias then they all should
628
627
  discriminator_alias = field_info.alias
629
628
 
630
- if field_info.annotation and is_literal_type(field_info.annotation):
631
- for entry in get_args(field_info.annotation):
629
+ if (annotation := getattr(field_info, "annotation", None)) and is_literal_type(annotation):
630
+ for entry in get_args(annotation):
632
631
  if isinstance(entry, str):
633
632
  mapping[entry] = variant
634
633
 
runwayml/_response.py CHANGED
@@ -233,7 +233,7 @@ class BaseAPIResponse(Generic[R]):
233
233
  # split is required to handle cases where additional information is included
234
234
  # in the response, e.g. application/json; charset=utf-8
235
235
  content_type, *_ = response.headers.get("content-type", "*").split(";")
236
- if content_type != "application/json":
236
+ if not content_type.endswith("json"):
237
237
  if is_basemodel(cast_to):
238
238
  try:
239
239
  data = response.json()
@@ -110,7 +110,7 @@ def extract_type_var_from_base(
110
110
  ```
111
111
  """
112
112
  cls = cast(object, get_origin(typ) or typ)
113
- if cls in generic_bases:
113
+ if cls in generic_bases: # pyright: ignore[reportUnnecessaryContains]
114
114
  # we're given the class directly
115
115
  return extract_type_arg(typ, index)
116
116
 
runwayml/_utils/_utils.py CHANGED
@@ -72,8 +72,16 @@ def _extract_items(
72
72
  from .._files import assert_is_file_content
73
73
 
74
74
  # We have exhausted the path, return the entry we found.
75
- assert_is_file_content(obj, key=flattened_key)
76
75
  assert flattened_key is not None
76
+
77
+ if is_list(obj):
78
+ files: list[tuple[str, FileTypes]] = []
79
+ for entry in obj:
80
+ assert_is_file_content(entry, key=flattened_key + "[]" if flattened_key else "")
81
+ files.append((flattened_key + "[]", cast(FileTypes, entry)))
82
+ return files
83
+
84
+ assert_is_file_content(obj, key=flattened_key)
77
85
  return [(flattened_key, cast(FileTypes, obj))]
78
86
 
79
87
  index += 1
runwayml/_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__ = "runwayml"
4
- __version__ = "3.0.1" # x-release-please-version
4
+ __version__ = "3.0.3" # x-release-please-version
@@ -9,10 +9,7 @@ import httpx
9
9
 
10
10
  from ..types import image_to_video_create_params
11
11
  from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven
12
- from .._utils import (
13
- maybe_transform,
14
- async_maybe_transform,
15
- )
12
+ from .._utils import maybe_transform, async_maybe_transform
16
13
  from .._compat import cached_property
17
14
  from .._resource import SyncAPIResource, AsyncAPIResource
18
15
  from .._response import (
@@ -1,6 +1,5 @@
1
1
  # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
2
2
 
3
-
4
3
  from .._models import BaseModel
5
4
 
6
5
  __all__ = ["ImageToVideoCreateResponse"]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: runwayml
3
- Version: 3.0.1
3
+ Version: 3.0.3
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
@@ -1,17 +1,17 @@
1
1
  runwayml/__init__.py,sha256=iXnJfH73wbj9IxfCHpwfWBxgOa9C4FRrrbBZM5f3biw,2476
2
- runwayml/_base_client.py,sha256=BY_2p5vumkAeyhc7M3PjWeX9IlxfaDu1HIOSPDmT4X0,65367
3
- runwayml/_client.py,sha256=oUOh2gdY1YtVefv51GTssitaCuDQIncvVlxTvzDJyec,17136
2
+ runwayml/_base_client.py,sha256=IHjCRq-jze0tVzSAP_zkZLHYG9lsvg4j_xwy2hv0ryQ,64846
3
+ runwayml/_client.py,sha256=RmchptAkzAmgp1RC40KQ0ZKqpdxfV9f8bHin3Bbmcmg,17123
4
4
  runwayml/_compat.py,sha256=VWemUKbj6DDkQ-O4baSpHVLJafotzeXmCQGJugfVTIw,6580
5
5
  runwayml/_constants.py,sha256=S14PFzyN9-I31wiV7SmIlL5Ga0MLHxdvegInGdXH7tM,462
6
6
  runwayml/_exceptions.py,sha256=p2Q8kywHCVQzArLQL4Ht-HetTBhAvevU6yDvEq7PpIE,3224
7
7
  runwayml/_files.py,sha256=mf4dOgL4b0ryyZlbqLhggD3GVgDf6XxdGFAgce01ugE,3549
8
- runwayml/_models.py,sha256=Bg-k8-T1kDWURAYXrbDF5FSAyLEy7k90Jrvne-dF4Wc,29070
8
+ runwayml/_models.py,sha256=mB2r2VWQq49jG-F0RIXDrBxPp3v-Eg12wMOtVTNxtv4,29057
9
9
  runwayml/_qs.py,sha256=AOkSz4rHtK4YI3ZU_kzea-zpwBUgEY8WniGmTPyEimc,4846
10
10
  runwayml/_resource.py,sha256=BF-j3xY5eRTKmuTxg8eDhLtLP4MLB1phDh_B6BKipKA,1112
11
- runwayml/_response.py,sha256=3Tf7pmDYDMv5BJuF0ljEBtMMk5Q9T7jcWn7I6P-hbdM,28801
11
+ runwayml/_response.py,sha256=WxjSEXX-j01ZhlSxYyMCVSEKxo20pgy40RA7iyski8M,28800
12
12
  runwayml/_streaming.py,sha256=NSVuAgknVQWU1cgZEjQn01IdZKKynb5rOeYp5Lo-OEQ,10108
13
13
  runwayml/_types.py,sha256=oHct1QQY_lI8bepCgfWDZm2N5VNi0e6o1iLeiTh4Y_0,6145
14
- runwayml/_version.py,sha256=CPiXNPVxUqxftw-dZ-WnMk2SLbX6jS2ERS5um080dS8,160
14
+ runwayml/_version.py,sha256=pGXM62kJDoRlS4DF-R0nEBEHwvLS7qbmigH11wCxE_c,160
15
15
  runwayml/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
16
  runwayml/_utils/__init__.py,sha256=PNZ_QJuzZEgyYXqkO1HVhGkj5IU9bglVUcw7H-Knjzw,2062
17
17
  runwayml/_utils/_logs.py,sha256=ZfS5W59hdqEBVV86lNrk28PhvUxtHOzs9JqiLhSu0pI,780
@@ -20,19 +20,19 @@ runwayml/_utils/_reflection.py,sha256=ZmGkIgT_PuwedyNBrrKGbxoWtkpytJNU1uU4QHnmEM
20
20
  runwayml/_utils/_streams.py,sha256=SMC90diFFecpEg_zgDRVbdR3hSEIgVVij4taD-noMLM,289
21
21
  runwayml/_utils/_sync.py,sha256=TpGLrrhRNWTJtODNE6Fup3_k7zrWm1j2RlirzBwre-0,2862
22
22
  runwayml/_utils/_transform.py,sha256=n7kskEWz6o__aoNvhFoGVyDoalNe6mJwp-g7BWkdj88,15617
23
- runwayml/_utils/_typing.py,sha256=nOFiIH5faG-h5Ha-Ky2aZxw5kmR6iX8KzNtsn3JEWoA,4556
24
- runwayml/_utils/_utils.py,sha256=8UmbPOy_AAr2uUjjFui-VZSrVBHRj6bfNEKRp5YZP2A,12004
23
+ runwayml/_utils/_typing.py,sha256=D0DbbNu8GnYQTSICnTSHDGsYXj8TcAKyhejb0XcnjtY,4602
24
+ runwayml/_utils/_utils.py,sha256=ts4CiiuNpFiGB6YMdkQRh2SZvYvsl7mAF-JWHCcLDf4,12312
25
25
  runwayml/lib/.keep,sha256=wuNrz-5SXo3jJaJOJgz4vFHM41YH_g20F5cRQo0vLes,224
26
26
  runwayml/resources/__init__.py,sha256=SqcC1MLwxPaz2c7gRRBlOn9-2pDPMKTXD2gFbG5FJ2E,1597
27
- runwayml/resources/image_to_video.py,sha256=A0nOe7KwLcJ_UiqHLk6Gm_h2iWbr7V37a3B7m1mBOxc,9141
27
+ runwayml/resources/image_to_video.py,sha256=tyYdfxytBufHIP-iOq24SfjZ5uqdIOxS9n7Q2qJfDHE,9128
28
28
  runwayml/resources/organization.py,sha256=XBg5nhkycPU3rllRvf9aaeHuZNtzGDKHlLPrPqDCAsw,5419
29
29
  runwayml/resources/tasks.py,sha256=-VT3qetYcaqn4FskekxhN_fCTozMl1GqxGpGwxV8M60,9673
30
30
  runwayml/types/__init__.py,sha256=xfq4RirwNpSBy5xXra7CB8wa0029vKUH0DB6Zg02hFs,505
31
31
  runwayml/types/image_to_video_create_params.py,sha256=VNWGDEdqkhp-Br-19t8YYfaYMaXxHEmADwXZ1CUC4So,1882
32
- runwayml/types/image_to_video_create_response.py,sha256=l5GszzUSItV-ZYHCB8hH_GSVibUZEkzfRLrAhXkd8O4,346
32
+ runwayml/types/image_to_video_create_response.py,sha256=WvZHbZxxJz8KerRNogzb1RYBrxa1x0iCPDi9-LCpHyE,345
33
33
  runwayml/types/organization_retrieve_response.py,sha256=DV46yEIRjmL05uISc2-PpM5BGWu8gniA9TQ056abWLA,2721
34
34
  runwayml/types/task_retrieve_response.py,sha256=v8y2bLxsW6srzScW-B3Akv72q_PI_NQmduGrGRQMHds,2139
35
- runwayml-3.0.1.dist-info/METADATA,sha256=1fj9_9szjNroqdi0Dsw0HuaswObIWDdLx8Avngy3trI,13516
36
- runwayml-3.0.1.dist-info/WHEEL,sha256=C2FUgwZgiLbznR-k0b_5k3Ai_1aASOXDss3lzCUsUug,87
37
- runwayml-3.0.1.dist-info/licenses/LICENSE,sha256=baeFj6izBWIm6A5_7N3-WAsy_VYpDF05Dd4zS1zsfZI,11338
38
- runwayml-3.0.1.dist-info/RECORD,,
35
+ runwayml-3.0.3.dist-info/METADATA,sha256=o1N-tQ-cwDdAQCLdVZsiOOb3PkzqdiMkiEr91tHzNnY,13516
36
+ runwayml-3.0.3.dist-info/WHEEL,sha256=C2FUgwZgiLbznR-k0b_5k3Ai_1aASOXDss3lzCUsUug,87
37
+ runwayml-3.0.3.dist-info/licenses/LICENSE,sha256=baeFj6izBWIm6A5_7N3-WAsy_VYpDF05Dd4zS1zsfZI,11338
38
+ runwayml-3.0.3.dist-info/RECORD,,