transcodely 0.3.2__tar.gz → 0.3.3__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.
Files changed (92) hide show
  1. {transcodely-0.3.2 → transcodely-0.3.3}/PKG-INFO +2 -2
  2. {transcodely-0.3.2 → transcodely-0.3.3}/pyproject.toml +2 -7
  3. {transcodely-0.3.2 → transcodely-0.3.3}/src/transcodely/_codec/json_codec.py +2 -3
  4. {transcodely-0.3.2 → transcodely-0.3.3}/src/transcodely/_transport/error_mapping.py +8 -7
  5. {transcodely-0.3.2 → transcodely-0.3.3}/src/transcodely/_transport/transport.py +26 -26
  6. {transcodely-0.3.2 → transcodely-0.3.3}/src/transcodely/client.py +24 -23
  7. {transcodely-0.3.2 → transcodely-0.3.3}/src/transcodely/errors.py +12 -12
  8. {transcodely-0.3.2 → transcodely-0.3.3}/src/transcodely/pagination.py +8 -8
  9. {transcodely-0.3.2 → transcodely-0.3.3}/src/transcodely/resources/_helpers.py +6 -5
  10. {transcodely-0.3.2 → transcodely-0.3.3}/src/transcodely/resources/api_keys.py +6 -6
  11. {transcodely-0.3.2 → transcodely-0.3.3}/src/transcodely/resources/apps.py +13 -15
  12. {transcodely-0.3.2 → transcodely-0.3.3}/src/transcodely/resources/billing.py +5 -7
  13. {transcodely-0.3.2 → transcodely-0.3.3}/src/transcodely/resources/events.py +10 -10
  14. {transcodely-0.3.2 → transcodely-0.3.3}/src/transcodely/resources/health.py +1 -3
  15. {transcodely-0.3.2 → transcodely-0.3.3}/src/transcodely/resources/jobs.py +29 -28
  16. {transcodely-0.3.2 → transcodely-0.3.3}/src/transcodely/resources/memberships.py +7 -7
  17. {transcodely-0.3.2 → transcodely-0.3.3}/src/transcodely/resources/organizations.py +6 -6
  18. {transcodely-0.3.2 → transcodely-0.3.3}/src/transcodely/resources/origins.py +6 -6
  19. {transcodely-0.3.2 → transcodely-0.3.3}/src/transcodely/resources/presets.py +7 -7
  20. {transcodely-0.3.2 → transcodely-0.3.3}/src/transcodely/resources/users.py +7 -7
  21. {transcodely-0.3.2 → transcodely-0.3.3}/src/transcodely/resources/videos.py +9 -8
  22. {transcodely-0.3.2 → transcodely-0.3.3}/src/transcodely/resources/webhook_endpoints.py +32 -31
  23. {transcodely-0.3.2 → transcodely-0.3.3}/src/transcodely/streaming.py +3 -2
  24. {transcodely-0.3.2 → transcodely-0.3.3}/src/transcodely/types.py +219 -227
  25. {transcodely-0.3.2 → transcodely-0.3.3}/src/transcodely/v1/job_pb2.py +52 -52
  26. {transcodely-0.3.2 → transcodely-0.3.3}/src/transcodely/v1/job_pb2.pyi +4 -2
  27. {transcodely-0.3.2 → transcodely-0.3.3}/src/transcodely/v1/subtitles_pb2.py +6 -6
  28. {transcodely-0.3.2 → transcodely-0.3.3}/src/transcodely/v1/subtitles_pb2.pyi +6 -2
  29. {transcodely-0.3.2 → transcodely-0.3.3}/src/transcodely/v1/thumbnails_pb2.py +6 -6
  30. {transcodely-0.3.2 → transcodely-0.3.3}/src/transcodely/v1/thumbnails_pb2.pyi +4 -2
  31. {transcodely-0.3.2 → transcodely-0.3.3}/src/transcodely/version.py +1 -1
  32. {transcodely-0.3.2 → transcodely-0.3.3}/src/transcodely/webhooks/construct_event.py +5 -4
  33. {transcodely-0.3.2 → transcodely-0.3.3}/src/transcodely/webhooks/signature.py +6 -6
  34. {transcodely-0.3.2 → transcodely-0.3.3}/src/transcodely/webhooks/types.py +6 -5
  35. {transcodely-0.3.2 → transcodely-0.3.3}/.gitignore +0 -0
  36. {transcodely-0.3.2 → transcodely-0.3.3}/LICENSE +0 -0
  37. {transcodely-0.3.2 → transcodely-0.3.3}/README.md +0 -0
  38. {transcodely-0.3.2 → transcodely-0.3.3}/src/buf/__init__.py +0 -0
  39. {transcodely-0.3.2 → transcodely-0.3.3}/src/buf/validate/__init__.py +0 -0
  40. {transcodely-0.3.2 → transcodely-0.3.3}/src/buf/validate/validate_pb2.py +0 -0
  41. {transcodely-0.3.2 → transcodely-0.3.3}/src/buf/validate/validate_pb2.pyi +0 -0
  42. {transcodely-0.3.2 → transcodely-0.3.3}/src/transcodely/__init__.py +21 -21
  43. {transcodely-0.3.2 → transcodely-0.3.3}/src/transcodely/_codec/__init__.py +0 -0
  44. {transcodely-0.3.2 → transcodely-0.3.3}/src/transcodely/_transport/__init__.py +0 -0
  45. {transcodely-0.3.2 → transcodely-0.3.3}/src/transcodely/_transport/headers.py +0 -0
  46. {transcodely-0.3.2 → transcodely-0.3.3}/src/transcodely/py.typed +0 -0
  47. {transcodely-0.3.2 → transcodely-0.3.3}/src/transcodely/resources/__init__.py +0 -0
  48. {transcodely-0.3.2 → transcodely-0.3.3}/src/transcodely/v1/api_key_pb2.py +0 -0
  49. {transcodely-0.3.2 → transcodely-0.3.3}/src/transcodely/v1/api_key_pb2.pyi +0 -0
  50. {transcodely-0.3.2 → transcodely-0.3.3}/src/transcodely/v1/app_pb2.py +0 -0
  51. {transcodely-0.3.2 → transcodely-0.3.3}/src/transcodely/v1/app_pb2.pyi +0 -0
  52. {transcodely-0.3.2 → transcodely-0.3.3}/src/transcodely/v1/billing_pb2.py +0 -0
  53. {transcodely-0.3.2 → transcodely-0.3.3}/src/transcodely/v1/billing_pb2.pyi +0 -0
  54. {transcodely-0.3.2 → transcodely-0.3.3}/src/transcodely/v1/codec_av1_pb2.py +0 -0
  55. {transcodely-0.3.2 → transcodely-0.3.3}/src/transcodely/v1/codec_av1_pb2.pyi +0 -0
  56. {transcodely-0.3.2 → transcodely-0.3.3}/src/transcodely/v1/codec_h264_pb2.py +0 -0
  57. {transcodely-0.3.2 → transcodely-0.3.3}/src/transcodely/v1/codec_h264_pb2.pyi +0 -0
  58. {transcodely-0.3.2 → transcodely-0.3.3}/src/transcodely/v1/codec_h265_pb2.py +0 -0
  59. {transcodely-0.3.2 → transcodely-0.3.3}/src/transcodely/v1/codec_h265_pb2.pyi +0 -0
  60. {transcodely-0.3.2 → transcodely-0.3.3}/src/transcodely/v1/codec_vp9_pb2.py +0 -0
  61. {transcodely-0.3.2 → transcodely-0.3.3}/src/transcodely/v1/codec_vp9_pb2.pyi +0 -0
  62. {transcodely-0.3.2 → transcodely-0.3.3}/src/transcodely/v1/common_pb2.py +0 -0
  63. {transcodely-0.3.2 → transcodely-0.3.3}/src/transcodely/v1/common_pb2.pyi +0 -0
  64. {transcodely-0.3.2 → transcodely-0.3.3}/src/transcodely/v1/content_aware_pb2.py +0 -0
  65. {transcodely-0.3.2 → transcodely-0.3.3}/src/transcodely/v1/content_aware_pb2.pyi +0 -0
  66. {transcodely-0.3.2 → transcodely-0.3.3}/src/transcodely/v1/drm_pb2.py +0 -0
  67. {transcodely-0.3.2 → transcodely-0.3.3}/src/transcodely/v1/drm_pb2.pyi +0 -0
  68. {transcodely-0.3.2 → transcodely-0.3.3}/src/transcodely/v1/hdr_pb2.py +0 -0
  69. {transcodely-0.3.2 → transcodely-0.3.3}/src/transcodely/v1/hdr_pb2.pyi +0 -0
  70. {transcodely-0.3.2 → transcodely-0.3.3}/src/transcodely/v1/health_pb2.py +0 -0
  71. {transcodely-0.3.2 → transcodely-0.3.3}/src/transcodely/v1/health_pb2.pyi +0 -0
  72. {transcodely-0.3.2 → transcodely-0.3.3}/src/transcodely/v1/media_pb2.py +0 -0
  73. {transcodely-0.3.2 → transcodely-0.3.3}/src/transcodely/v1/media_pb2.pyi +0 -0
  74. {transcodely-0.3.2 → transcodely-0.3.3}/src/transcodely/v1/membership_pb2.py +0 -0
  75. {transcodely-0.3.2 → transcodely-0.3.3}/src/transcodely/v1/membership_pb2.pyi +0 -0
  76. {transcodely-0.3.2 → transcodely-0.3.3}/src/transcodely/v1/organization_pb2.py +0 -0
  77. {transcodely-0.3.2 → transcodely-0.3.3}/src/transcodely/v1/organization_pb2.pyi +0 -0
  78. {transcodely-0.3.2 → transcodely-0.3.3}/src/transcodely/v1/origin_pb2.py +0 -0
  79. {transcodely-0.3.2 → transcodely-0.3.3}/src/transcodely/v1/origin_pb2.pyi +0 -0
  80. {transcodely-0.3.2 → transcodely-0.3.3}/src/transcodely/v1/preset_pb2.py +0 -0
  81. {transcodely-0.3.2 → transcodely-0.3.3}/src/transcodely/v1/preset_pb2.pyi +0 -0
  82. {transcodely-0.3.2 → transcodely-0.3.3}/src/transcodely/v1/streaming_pb2.py +0 -0
  83. {transcodely-0.3.2 → transcodely-0.3.3}/src/transcodely/v1/streaming_pb2.pyi +0 -0
  84. {transcodely-0.3.2 → transcodely-0.3.3}/src/transcodely/v1/user_pb2.py +0 -0
  85. {transcodely-0.3.2 → transcodely-0.3.3}/src/transcodely/v1/user_pb2.pyi +0 -0
  86. {transcodely-0.3.2 → transcodely-0.3.3}/src/transcodely/v1/video_pb2.py +0 -0
  87. {transcodely-0.3.2 → transcodely-0.3.3}/src/transcodely/v1/video_pb2.pyi +0 -0
  88. {transcodely-0.3.2 → transcodely-0.3.3}/src/transcodely/v1/watermark_pb2.py +0 -0
  89. {transcodely-0.3.2 → transcodely-0.3.3}/src/transcodely/v1/watermark_pb2.pyi +0 -0
  90. {transcodely-0.3.2 → transcodely-0.3.3}/src/transcodely/v1/webhook_pb2.py +0 -0
  91. {transcodely-0.3.2 → transcodely-0.3.3}/src/transcodely/v1/webhook_pb2.pyi +0 -0
  92. {transcodely-0.3.2 → transcodely-0.3.3}/src/transcodely/webhooks/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: transcodely
3
- Version: 0.3.2
3
+ Version: 0.3.3
4
4
  Summary: Official Python SDK for the Transcodely video transcoding API
5
5
  Project-URL: Homepage, https://transcodely.com
6
6
  Project-URL: Documentation, https://transcodely.com/docs
@@ -30,7 +30,7 @@ Provides-Extra: dev
30
30
  Requires-Dist: mypy>=1.10; extra == 'dev'
31
31
  Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
32
32
  Requires-Dist: pytest>=8.0; extra == 'dev'
33
- Requires-Dist: ruff<0.16,>=0.5; extra == 'dev'
33
+ Requires-Dist: ruff>=0.16; extra == 'dev'
34
34
  Provides-Extra: fast
35
35
  Requires-Dist: orjson>=3.10; extra == 'fast'
36
36
  Description-Content-Type: text/markdown
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "transcodely"
7
- version = "0.3.2"
7
+ version = "0.3.3"
8
8
  description = "Official Python SDK for the Transcodely video transcoding API"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -37,12 +37,7 @@ fast = ["orjson>=3.10"]
37
37
  dev = [
38
38
  "pytest>=8.0",
39
39
  "pytest-asyncio>=0.23",
40
- # Upper bound is deliberate: ruff 0.16 promoted UP007/UP045 (and others) into
41
- # its default rule set, which flags the Optional[...]/Union[...] style used
42
- # throughout this repo — 257 findings on an untouched master. Adopting the
43
- # new rules is a mechanical `ruff check --fix` sweep that belongs in its own
44
- # PR, not in whatever change happens to run CI first after a ruff release.
45
- "ruff>=0.5,<0.16",
40
+ "ruff>=0.16",
46
41
  "mypy>=1.10",
47
42
  ]
48
43
 
@@ -132,9 +132,8 @@ def _transform_field(
132
132
  parent[key] = _apply_enum(v, field.enum_type, mode)
133
133
  return
134
134
 
135
- if field.type == FieldDescriptor.TYPE_MESSAGE:
136
- if isinstance(v, dict):
137
- transform_enums_in_dict(v, field.message_type, mode=mode)
135
+ if field.type == FieldDescriptor.TYPE_MESSAGE and isinstance(v, dict):
136
+ transform_enums_in_dict(v, field.message_type, mode=mode)
138
137
 
139
138
 
140
139
  def _apply_enum(value: str, enum_desc: EnumDescriptor, mode: str) -> str:
@@ -3,7 +3,8 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  import json
6
- from typing import Any, Mapping, Optional
6
+ from collections.abc import Mapping
7
+ from typing import Any
7
8
 
8
9
  from ..errors import (
9
10
  APIConnectionError,
@@ -55,7 +56,7 @@ def map_http_error(*, status: int, body: Any, headers: Mapping[str, str]) -> Tra
55
56
  return InvalidRequestError(message, **kwargs)
56
57
  if status == 429:
57
58
  retry_after = _header(headers, "retry-after")
58
- retry_after_ms: Optional[int] = None
59
+ retry_after_ms: int | None = None
59
60
  if retry_after:
60
61
  try:
61
62
  retry_after_ms = max(0, int(retry_after) * 1000)
@@ -69,7 +70,7 @@ def map_http_error(*, status: int, body: Any, headers: Mapping[str, str]) -> Tra
69
70
  return APIError(message, **kwargs)
70
71
 
71
72
 
72
- def connection_error(cause: BaseException, message: Optional[str] = None) -> APIConnectionError:
73
+ def connection_error(cause: BaseException, message: str | None = None) -> APIConnectionError:
73
74
  msg = message or str(cause) or "network request failed"
74
75
  err = APIConnectionError(msg)
75
76
  err.__cause__ = cause
@@ -82,21 +83,21 @@ def _parse_body(body: Any) -> dict[str, Any]:
82
83
  if isinstance(body, (bytes, bytearray)):
83
84
  try:
84
85
  return json.loads(body.decode("utf-8"))
85
- except Exception:
86
+ except (UnicodeDecodeError, json.JSONDecodeError):
86
87
  return {"message": body.decode("utf-8", errors="replace")}
87
88
  if isinstance(body, str):
88
89
  try:
89
90
  return json.loads(body)
90
- except Exception:
91
+ except json.JSONDecodeError:
91
92
  return {"message": body}
92
93
  return {}
93
94
 
94
95
 
95
- def _header(headers: Mapping[str, str], key: str) -> Optional[str]:
96
+ def _header(headers: Mapping[str, str], key: str) -> str | None:
96
97
  # httpx headers are case-insensitive but expose .get with the lowercase form.
97
98
  if hasattr(headers, "get"):
98
99
  try:
99
100
  return headers.get(key) or headers.get(key.title())
100
- except Exception:
101
+ except Exception: # noqa: BLE001 — headers may be any duck-typed mapping
101
102
  return None
102
103
  return None
@@ -19,11 +19,13 @@ from __future__ import annotations
19
19
  import random
20
20
  import struct
21
21
  import time
22
+ from collections.abc import Callable, Generator, Iterable
22
23
  from dataclasses import dataclass, field
23
- from typing import Any, Callable, Generator, Iterable, Optional, TypeVar
24
+ from typing import Any, TypeVar
24
25
 
25
26
  import httpx
26
27
  from google.protobuf.message import Message
28
+ from typing_extensions import Self
27
29
 
28
30
  from .._codec import json_codec
29
31
  from ..errors import RateLimitError, TranscodelyError
@@ -39,10 +41,10 @@ TRes = TypeVar("TRes", bound=Message)
39
41
  class CallOptions:
40
42
  """Per-call overrides for timeout, retries, idempotency, headers."""
41
43
 
42
- timeout: Optional[float] = None
43
- max_retries: Optional[int] = None
44
- idempotency_key: Optional[str] = None
45
- api_version: Optional[str] = None
44
+ timeout: float | None = None
45
+ max_retries: int | None = None
46
+ idempotency_key: str | None = None
47
+ api_version: str | None = None
46
48
  headers: dict[str, str] = field(default_factory=dict)
47
49
 
48
50
 
@@ -52,9 +54,9 @@ class LogEvent:
52
54
  method: str
53
55
  duration_ms: int
54
56
  attempt: int
55
- status: Optional[int] = None
56
- request_id: Optional[str] = None
57
- error: Optional[TranscodelyError] = None
57
+ status: int | None = None
58
+ request_id: str | None = None
59
+ error: TranscodelyError | None = None
58
60
 
59
61
 
60
62
  _WRITE_PREFIXES = ("Create", "Update", "Delete", "Cancel", "Confirm", "Revoke", "Archive", "Enable")
@@ -71,14 +73,14 @@ class Transport:
71
73
  self,
72
74
  api_key: str,
73
75
  *,
74
- organization_id: Optional[str] = None,
75
- base_url: Optional[str] = None,
76
+ organization_id: str | None = None,
77
+ base_url: str | None = None,
76
78
  timeout: float = 30.0,
77
79
  max_retries: int = 3,
78
- api_version: Optional[str] = None,
79
- default_headers: Optional[dict[str, str]] = None,
80
- http_client: Optional[httpx.Client] = None,
81
- logger: Optional[Callable[[LogEvent], None]] = None,
80
+ api_version: str | None = None,
81
+ default_headers: dict[str, str] | None = None,
82
+ http_client: httpx.Client | None = None,
83
+ logger: Callable[[LogEvent], None] | None = None,
82
84
  ) -> None:
83
85
  if not api_key:
84
86
  raise ValueError("Transcodely: api_key is required")
@@ -93,16 +95,16 @@ class Transport:
93
95
  self._owns_client = http_client is None
94
96
  self._logger = logger
95
97
  #: ID of the most recent successful or failed request, Stripe-style.
96
- self.last_request_id: Optional[str] = None
98
+ self.last_request_id: str | None = None
97
99
 
98
100
  def close(self) -> None:
99
101
  if self._owns_client:
100
102
  self._client.close()
101
103
 
102
- def __enter__(self) -> "Transport":
104
+ def __enter__(self) -> Self:
103
105
  return self
104
106
 
105
- def __exit__(self, *args: Any) -> None:
107
+ def __exit__(self, *args: object) -> None:
106
108
  self.close()
107
109
 
108
110
  # ---------- unary ----------
@@ -113,7 +115,7 @@ class Transport:
113
115
  method_name: str,
114
116
  request: Message,
115
117
  response: TRes,
116
- opts: Optional[CallOptions] = None,
118
+ opts: CallOptions | None = None,
117
119
  ) -> TRes:
118
120
  opts = opts or CallOptions()
119
121
  url = f"{self.base_url}/{service_name}/{method_name}"
@@ -163,7 +165,7 @@ class Transport:
163
165
  method_name: str,
164
166
  request: Message,
165
167
  response_factory: Callable[[], TRes],
166
- opts: Optional[CallOptions] = None,
168
+ opts: CallOptions | None = None,
167
169
  ) -> Generator[TRes, None, None]:
168
170
  opts = opts or CallOptions()
169
171
  url = f"{self.base_url}/{service_name}/{method_name}"
@@ -209,7 +211,7 @@ class Transport:
209
211
  self,
210
212
  opts: CallOptions,
211
213
  content_type: str,
212
- idempotency_key: Optional[str] = None,
214
+ idempotency_key: str | None = None,
213
215
  ) -> dict[str, str]:
214
216
  headers = {
215
217
  "content-type": content_type,
@@ -232,7 +234,7 @@ class Transport:
232
234
  if self._logger is not None:
233
235
  try:
234
236
  self._logger(event)
235
- except Exception:
237
+ except Exception: # noqa: BLE001, S110 — a caller's logger must never break requests
236
238
  pass
237
239
 
238
240
  def _with_retry(
@@ -243,7 +245,7 @@ class Transport:
243
245
  fn: Callable[[int], TRes],
244
246
  ) -> TRes:
245
247
  max_retries = opts.max_retries if opts.max_retries is not None else self.max_retries
246
- last_error: Optional[BaseException] = None
248
+ last_error: BaseException | None = None
247
249
  for attempt in range(1, max_retries + 2):
248
250
  try:
249
251
  return fn(attempt)
@@ -264,9 +266,7 @@ def _is_retryable(exc: BaseException) -> bool:
264
266
  return True
265
267
  if exc.__class__.__name__ == "APIConnectionError":
266
268
  return True
267
- if exc.http_status is not None and exc.http_status >= 500:
268
- return True
269
- return False
269
+ return exc.http_status is not None and exc.http_status >= 500
270
270
 
271
271
 
272
272
  def _sleep_backoff(attempt: int, exc: BaseException) -> None:
@@ -312,5 +312,5 @@ def _try_json(data: bytes) -> Any:
312
312
  import json
313
313
 
314
314
  return json.loads(data.decode("utf-8"))
315
- except Exception:
315
+ except (UnicodeDecodeError, json.JSONDecodeError):
316
316
  return data.decode("utf-8", errors="replace")
@@ -2,9 +2,10 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- from typing import Any, Callable, Optional
5
+ from collections.abc import Callable
6
6
 
7
7
  import httpx
8
+ from typing_extensions import Self
8
9
 
9
10
  from ._transport.transport import LogEvent, Transport
10
11
  from .resources.api_keys import ApiKeys
@@ -38,14 +39,14 @@ class Transcodely:
38
39
  self,
39
40
  api_key: str,
40
41
  *,
41
- organization_id: Optional[str] = None,
42
- base_url: Optional[str] = None,
42
+ organization_id: str | None = None,
43
+ base_url: str | None = None,
43
44
  timeout: float = 30.0,
44
45
  max_retries: int = 3,
45
- api_version: Optional[str] = None,
46
- default_headers: Optional[dict[str, str]] = None,
47
- http_client: Optional[httpx.Client] = None,
48
- logger: Optional[Callable[[LogEvent], None]] = None,
46
+ api_version: str | None = None,
47
+ default_headers: dict[str, str] | None = None,
48
+ http_client: httpx.Client | None = None,
49
+ logger: Callable[[LogEvent], None] | None = None,
49
50
  ) -> None:
50
51
  self._transport = Transport(
51
52
  api_key,
@@ -58,31 +59,31 @@ class Transcodely:
58
59
  http_client=http_client,
59
60
  logger=logger,
60
61
  )
61
- self._jobs: Optional[Jobs] = None
62
- self._videos: Optional[Videos] = None
63
- self._presets: Optional[Presets] = None
64
- self._origins: Optional[Origins] = None
65
- self._apps: Optional[Apps] = None
66
- self._api_keys: Optional[ApiKeys] = None
67
- self._organizations: Optional[Organizations] = None
68
- self._memberships: Optional[Memberships] = None
69
- self._users: Optional[Users] = None
70
- self._health: Optional[Health] = None
71
- self._webhook_endpoints: Optional[WebhookEndpoints] = None
72
- self._events: Optional[Events] = None
73
- self._billing: Optional[Billing] = None
62
+ self._jobs: Jobs | None = None
63
+ self._videos: Videos | None = None
64
+ self._presets: Presets | None = None
65
+ self._origins: Origins | None = None
66
+ self._apps: Apps | None = None
67
+ self._api_keys: ApiKeys | None = None
68
+ self._organizations: Organizations | None = None
69
+ self._memberships: Memberships | None = None
70
+ self._users: Users | None = None
71
+ self._health: Health | None = None
72
+ self._webhook_endpoints: WebhookEndpoints | None = None
73
+ self._events: Events | None = None
74
+ self._billing: Billing | None = None
74
75
 
75
76
  def close(self) -> None:
76
77
  self._transport.close()
77
78
 
78
- def __enter__(self) -> "Transcodely":
79
+ def __enter__(self) -> Self:
79
80
  return self
80
81
 
81
- def __exit__(self, *args: Any) -> None:
82
+ def __exit__(self, *args: object) -> None:
82
83
  self.close()
83
84
 
84
85
  @property
85
- def last_request_id(self) -> Optional[str]:
86
+ def last_request_id(self) -> str | None:
86
87
  """Stripe-style: ID of the most recent successful or failed request."""
87
88
  return self._transport.last_request_id
88
89
 
@@ -8,7 +8,7 @@ error handling. Mirrors the TypeScript and Go SDK error classes.
8
8
  from __future__ import annotations
9
9
 
10
10
  from dataclasses import dataclass
11
- from typing import Any, Optional
11
+ from typing import Any
12
12
 
13
13
 
14
14
  @dataclass(frozen=True)
@@ -26,19 +26,19 @@ class TranscodelyError(Exception):
26
26
  self,
27
27
  message: str,
28
28
  *,
29
- code: Optional[str] = None,
30
- type: Optional[str] = None,
31
- errors: Optional[list[FieldViolation]] = None,
32
- http_status: Optional[int] = None,
33
- request_id: Optional[str] = None,
29
+ code: str | None = None,
30
+ type: str | None = None,
31
+ errors: list[FieldViolation] | None = None,
32
+ http_status: int | None = None,
33
+ request_id: str | None = None,
34
34
  raw: Any = None,
35
35
  ) -> None:
36
36
  super().__init__(message)
37
- self.code: Optional[str] = code
38
- self.type: Optional[str] = type
37
+ self.code: str | None = code
38
+ self.type: str | None = type
39
39
  self.errors: list[FieldViolation] = errors or []
40
- self.http_status: Optional[int] = http_status
41
- self.request_id: Optional[str] = request_id
40
+ self.http_status: int | None = http_status
41
+ self.request_id: str | None = request_id
42
42
  self.raw: Any = raw
43
43
 
44
44
  def __repr__(self) -> str:
@@ -79,11 +79,11 @@ class RateLimitError(TranscodelyError):
79
79
  self,
80
80
  message: str,
81
81
  *,
82
- retry_after_ms: Optional[int] = None,
82
+ retry_after_ms: int | None = None,
83
83
  **kwargs: Any,
84
84
  ) -> None:
85
85
  super().__init__(message, **kwargs)
86
- self.retry_after_ms: Optional[int] = retry_after_ms
86
+ self.retry_after_ms: int | None = retry_after_ms
87
87
 
88
88
 
89
89
  class InvalidRequestError(TranscodelyError):
@@ -6,8 +6,9 @@ Every ``list`` method returns a :class:`Page` you can iterate manually or via
6
6
 
7
7
  from __future__ import annotations
8
8
 
9
+ from collections.abc import Callable, Generator, Iterator
9
10
  from dataclasses import dataclass
10
- from typing import Callable, Generator, Generic, Iterator, Optional, TypeVar
11
+ from typing import Generic, TypeVar
11
12
 
12
13
  T = TypeVar("T")
13
14
 
@@ -15,22 +16,22 @@ T = TypeVar("T")
15
16
  @dataclass
16
17
  class PageContents(Generic[T]):
17
18
  items: list[T]
18
- next_cursor: Optional[str]
19
+ next_cursor: str | None
19
20
 
20
21
 
21
22
  class Page(Generic[T]):
22
23
  """A single page of results plus an opt-in iterator across all pages."""
23
24
 
24
- def __init__(self, fetcher: Callable[[Optional[str]], PageContents[T]]) -> None:
25
+ def __init__(self, fetcher: Callable[[str | None], PageContents[T]]) -> None:
25
26
  self._fetcher = fetcher
26
- self._first: Optional[PageContents[T]] = None
27
+ self._first: PageContents[T] | None = None
27
28
 
28
29
  @property
29
30
  def items(self) -> list[T]:
30
31
  return self._first_page().items
31
32
 
32
33
  @property
33
- def next_cursor(self) -> Optional[str]:
34
+ def next_cursor(self) -> str | None:
34
35
  return self._first_page().next_cursor
35
36
 
36
37
  def __iter__(self) -> Iterator[T]:
@@ -38,11 +39,10 @@ class Page(Generic[T]):
38
39
 
39
40
  def auto_paging_iter(self) -> Generator[T, None, None]:
40
41
  """Yield every item, transparently fetching subsequent pages."""
41
- cursor: Optional[str] = None
42
+ cursor: str | None = None
42
43
  while True:
43
44
  page = self._fetcher(cursor)
44
- for item in page.items:
45
- yield item
45
+ yield from page.items
46
46
  if not page.next_cursor:
47
47
  return
48
48
  cursor = page.next_cursor
@@ -2,7 +2,8 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- from typing import Any, Mapping, Optional, Union
5
+ from collections.abc import Mapping
6
+ from typing import Any
6
7
 
7
8
  from google.protobuf import json_format
8
9
  from google.protobuf.descriptor import EnumDescriptor
@@ -23,7 +24,7 @@ def fill_from_dict(msg: Message, data: Mapping[str, Any]) -> Message:
23
24
  return msg
24
25
 
25
26
 
26
- def resolve_enum(value: Union[int, str], enum_desc: EnumDescriptor) -> int:
27
+ def resolve_enum(value: int | str, enum_desc: EnumDescriptor) -> int:
27
28
  """Resolve an enum value from either the raw proto int or a string.
28
29
 
29
30
  Strings may be the simplified lowercase wire form (``"standard"``,
@@ -54,9 +55,9 @@ def resolve_enum(value: Union[int, str], enum_desc: EnumDescriptor) -> int:
54
55
  def assign_pagination(
55
56
  req_pagination: Any,
56
57
  *,
57
- limit: Optional[int] = None,
58
- cursor: Optional[str] = None,
59
- offset: Optional[int] = None,
58
+ limit: int | None = None,
59
+ cursor: str | None = None,
60
+ offset: int | None = None,
60
61
  ) -> None:
61
62
  if limit is not None:
62
63
  req_pagination.limit = limit
@@ -2,7 +2,7 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- from typing import Any, Optional
5
+ from typing import Any
6
6
 
7
7
  from .._transport.transport import CallOptions, Transport
8
8
  from ..pagination import Page, PageContents
@@ -21,17 +21,17 @@ class ApiKeys:
21
21
  req = fill_from_dict(api_key_pb2.CreateAPIKeyRequest(), kwargs)
22
22
  return self._t.unary(_SERVICE, "Create", req, api_key_pb2.CreateAPIKeyResponse())
23
23
 
24
- def get(self, id: str, opts: Optional[CallOptions] = None) -> api_key_pb2.APIKey:
24
+ def get(self, id: str, opts: CallOptions | None = None) -> api_key_pb2.APIKey:
25
25
  req = api_key_pb2.GetAPIKeyRequest(id=id)
26
26
  return self._t.unary(_SERVICE, "Get", req, api_key_pb2.GetAPIKeyResponse(), opts).api_key
27
27
 
28
28
  def list(
29
29
  self,
30
30
  *,
31
- limit: Optional[int] = None,
32
- opts: Optional[CallOptions] = None,
31
+ limit: int | None = None,
32
+ opts: CallOptions | None = None,
33
33
  ) -> Page[api_key_pb2.APIKey]:
34
- def fetch(cursor: Optional[str]) -> PageContents[api_key_pb2.APIKey]:
34
+ def fetch(cursor: str | None) -> PageContents[api_key_pb2.APIKey]:
35
35
  req = api_key_pb2.ListAPIKeysRequest()
36
36
  assign_pagination(req.pagination, limit=limit, cursor=cursor)
37
37
  res = self._t.unary(_SERVICE, "List", req, api_key_pb2.ListAPIKeysResponse(), opts)
@@ -41,7 +41,7 @@ class ApiKeys:
41
41
 
42
42
  return Page(fetch)
43
43
 
44
- def revoke(self, id: str, opts: Optional[CallOptions] = None) -> api_key_pb2.APIKey:
44
+ def revoke(self, id: str, opts: CallOptions | None = None) -> api_key_pb2.APIKey:
45
45
  req = api_key_pb2.RevokeAPIKeyRequest(id=id)
46
46
  return self._t.unary(
47
47
  _SERVICE, "Revoke", req, api_key_pb2.RevokeAPIKeyResponse(), opts
@@ -2,7 +2,7 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- from typing import Any, Optional
5
+ from typing import Any
6
6
 
7
7
  from .._transport.transport import CallOptions, Transport
8
8
  from ..pagination import Page, PageContents
@@ -20,7 +20,7 @@ class Apps:
20
20
  req = fill_from_dict(app_pb2.CreateAppRequest(), kwargs)
21
21
  return self._t.unary(_SERVICE, "Create", req, app_pb2.CreateAppResponse()).app
22
22
 
23
- def get(self, id: str, opts: Optional[CallOptions] = None) -> app_pb2.App:
23
+ def get(self, id: str, opts: CallOptions | None = None) -> app_pb2.App:
24
24
  req = app_pb2.GetAppRequest(id=id)
25
25
  return self._t.unary(_SERVICE, "Get", req, app_pb2.GetAppResponse(), opts).app
26
26
 
@@ -31,10 +31,10 @@ class Apps:
31
31
  def list(
32
32
  self,
33
33
  *,
34
- org_id: Optional[str] = None,
35
- limit: Optional[int] = None,
36
- include_archived: Optional[bool] = None,
37
- opts: Optional[CallOptions] = None,
34
+ org_id: str | None = None,
35
+ limit: int | None = None,
36
+ include_archived: bool | None = None,
37
+ opts: CallOptions | None = None,
38
38
  ) -> Page[app_pb2.App]:
39
39
  """List apps in an organization.
40
40
 
@@ -42,7 +42,7 @@ class Apps:
42
42
  by the API. Pass ``include_archived=True`` to include archived apps.
43
43
  """
44
44
 
45
- def fetch(cursor: Optional[str]) -> PageContents[app_pb2.App]:
45
+ def fetch(cursor: str | None) -> PageContents[app_pb2.App]:
46
46
  req = app_pb2.ListAppsRequest()
47
47
  if org_id is not None:
48
48
  req.org_id = org_id
@@ -56,7 +56,7 @@ class Apps:
56
56
 
57
57
  return Page(fetch)
58
58
 
59
- def archive(self, id: str, opts: Optional[CallOptions] = None) -> app_pb2.App:
59
+ def archive(self, id: str, opts: CallOptions | None = None) -> app_pb2.App:
60
60
  req = app_pb2.ArchiveAppRequest(id=id)
61
61
  return self._t.unary(_SERVICE, "Archive", req, app_pb2.ArchiveAppResponse(), opts).app
62
62
 
@@ -73,8 +73,8 @@ class Apps:
73
73
  def update_spend_limit(
74
74
  self,
75
75
  app_id: str,
76
- monthly_spend_limit_eur: Optional[float] = None,
77
- opts: Optional[CallOptions] = None,
76
+ monthly_spend_limit_eur: float | None = None,
77
+ opts: CallOptions | None = None,
78
78
  ) -> app_pb2.App:
79
79
  """Set or clear an app's monthly transcoding spend cap.
80
80
 
@@ -92,7 +92,7 @@ class Apps:
92
92
  ).app
93
93
 
94
94
  def set_spend_limit(
95
- self, app_id: str, monthly_spend_limit_eur: float, opts: Optional[CallOptions] = None
95
+ self, app_id: str, monthly_spend_limit_eur: float, opts: CallOptions | None = None
96
96
  ) -> app_pb2.App:
97
97
  """Set the app's monthly transcoding spend cap in EUR (must be > 0).
98
98
 
@@ -102,13 +102,11 @@ class Apps:
102
102
  """
103
103
  return self.update_spend_limit(app_id, monthly_spend_limit_eur, opts)
104
104
 
105
- def clear_spend_limit(self, app_id: str, opts: Optional[CallOptions] = None) -> app_pb2.App:
105
+ def clear_spend_limit(self, app_id: str, opts: CallOptions | None = None) -> app_pb2.App:
106
106
  """Clear the app's monthly spend cap, returning it to unlimited (the default)."""
107
107
  return self.update_spend_limit(app_id, None, opts)
108
108
 
109
- def get_spend(
110
- self, app_id: str, opts: Optional[CallOptions] = None
111
- ) -> app_pb2.GetSpendResponse:
109
+ def get_spend(self, app_id: str, opts: CallOptions | None = None) -> app_pb2.GetSpendResponse:
112
110
  """Get the app's current-period transcoding spend against its limit.
113
111
 
114
112
  Returns the billing-period bounds, EUR spent so far, the cap (if set), and
@@ -2,8 +2,6 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- from typing import Optional
6
-
7
5
  from .._transport.transport import CallOptions, Transport
8
6
  from ..pagination import Page, PageContents
9
7
  from ..v1 import billing_pb2
@@ -39,8 +37,8 @@ class Billing:
39
37
  def list_invoices(
40
38
  self,
41
39
  *,
42
- limit: Optional[int] = None,
43
- opts: Optional[CallOptions] = None,
40
+ limit: int | None = None,
41
+ opts: CallOptions | None = None,
44
42
  ) -> Page[billing_pb2.Invoice]:
45
43
  """Page through finalized invoices, newest period first.
46
44
 
@@ -50,7 +48,7 @@ class Billing:
50
48
  :meth:`retrieve_upcoming`.
51
49
  """
52
50
 
53
- def fetch(cursor: Optional[str]) -> PageContents[billing_pb2.Invoice]:
51
+ def fetch(cursor: str | None) -> PageContents[billing_pb2.Invoice]:
54
52
  req = billing_pb2.ListInvoicesRequest()
55
53
  assign_pagination(req.pagination, limit=limit, cursor=cursor)
56
54
  res = self._t.unary(
@@ -63,7 +61,7 @@ class Billing:
63
61
 
64
62
  return Page(fetch)
65
63
 
66
- def retrieve(self, invoice_id: str, opts: Optional[CallOptions] = None) -> billing_pb2.Invoice:
64
+ def retrieve(self, invoice_id: str, opts: CallOptions | None = None) -> billing_pb2.Invoice:
67
65
  """Retrieve one invoice by ID (``inv_*``), including its line items."""
68
66
  req = billing_pb2.GetInvoiceRequest(id=invoice_id)
69
67
  return self._t.unary(
@@ -74,7 +72,7 @@ class Billing:
74
72
  opts,
75
73
  ).invoice
76
74
 
77
- def retrieve_upcoming(self, opts: Optional[CallOptions] = None) -> billing_pb2.Invoice:
75
+ def retrieve_upcoming(self, opts: CallOptions | None = None) -> billing_pb2.Invoice:
78
76
  """Retrieve the statement for the period currently accruing.
79
77
 
80
78
  Computed live from settled jobs rather than stored, so its ``id`` is