sg-python-api 0.1.1__py3-none-any.whl → 0.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {sg_python_api-0.1.1.dist-info → sg_python_api-0.2.0.dist-info}/METADATA +21 -24
- sg_python_api-0.2.0.dist-info/RECORD +58 -0
- shotgun_api3/__init__.py +4 -0
- shotgun_api3/_async_client.py +404 -316
- shotgun_api3/_async_driver.py +25 -55
- shotgun_api3/_async_httpx2.py +92 -59
- shotgun_api3/_async_transport.py +38 -3
- shotgun_api3/_capabilities.py +4 -3
- shotgun_api3/_client.py +354 -462
- shotgun_api3/_config.py +245 -120
- shotgun_api3/_connections.py +144 -0
- shotgun_api3/_crud.py +25 -250
- shotgun_api3/_driver.py +29 -54
- shotgun_api3/_execution.py +1 -3
- shotgun_api3/_files.py +215 -123
- shotgun_api3/_methods.py +153 -412
- shotgun_api3/_protocol.py +27 -146
- shotgun_api3/_transport.py +82 -407
- shotgun_api3/_version.py +7 -2
- shotgun_api3/lib/mockgun/__init__.py +20 -7
- shotgun_api3/lib/mockgun/_activity.py +510 -0
- shotgun_api3/lib/mockgun/_async_client.py +196 -0
- shotgun_api3/lib/mockgun/_dates.py +164 -0
- shotgun_api3/lib/mockgun/_errors.py +18 -0
- shotgun_api3/lib/mockgun/_events.py +1031 -0
- shotgun_api3/lib/mockgun/_follows.py +217 -0
- shotgun_api3/lib/mockgun/_handlers.py +558 -0
- shotgun_api3/lib/mockgun/_links.py +440 -0
- shotgun_api3/lib/mockgun/_new_client.py +92 -0
- shotgun_api3/lib/mockgun/_presets.py +492 -0
- shotgun_api3/lib/mockgun/_query.py +2163 -0
- shotgun_api3/lib/mockgun/_retirement.py +368 -0
- shotgun_api3/lib/mockgun/_schema.py +172 -0
- shotgun_api3/lib/mockgun/_schema_writes.py +425 -0
- shotgun_api3/lib/mockgun/_search.py +378 -0
- shotgun_api3/lib/mockgun/_server.py +169 -0
- shotgun_api3/lib/mockgun/_sessions.py +206 -0
- shotgun_api3/lib/mockgun/_site.py +150 -0
- shotgun_api3/lib/mockgun/_site_data.py +584 -0
- shotgun_api3/lib/mockgun/_storage.py +208 -0
- shotgun_api3/lib/mockgun/_store.py +1446 -0
- shotgun_api3/lib/mockgun/_summaries.py +964 -0
- shotgun_api3/lib/mockgun/_sync_client.py +190 -0
- shotgun_api3/lib/mockgun/_threads.py +226 -0
- shotgun_api3/lib/mockgun/_thumbnails.py +146 -0
- shotgun_api3/lib/mockgun/_transport.py +145 -0
- shotgun_api3/lib/mockgun/_uploads.py +417 -0
- shotgun_api3/lib/mockgun/_wire.py +120 -0
- shotgun_api3/lib/mockgun/mockgun.py +14 -0
- shotgun_api3/lib/sgtimezone.py +24 -1
- sg_python_api-0.1.1.dist-info/RECORD +0 -31
- shotgun_api3/_signatures.py +0 -53
- shotgun_api3/lib/mockgun/_async.py +0 -315
- shotgun_api3/lib/mockgun/_mockgun.py +0 -1666
- {sg_python_api-0.1.1.dist-info → sg_python_api-0.2.0.dist-info}/WHEEL +0 -0
- {sg_python_api-0.1.1.dist-info → sg_python_api-0.2.0.dist-info}/licenses/LICENSE +0 -0
shotgun_api3/_async_client.py
CHANGED
|
@@ -1,18 +1,13 @@
|
|
|
1
1
|
"""Native-async Shotgun client and lifecycle."""
|
|
2
2
|
|
|
3
|
-
# Same-file collaboration follows module-boundary privacy.
|
|
4
|
-
# pyright: reportPrivateUsage=false
|
|
5
|
-
|
|
6
3
|
import asyncio
|
|
7
4
|
import copy
|
|
8
5
|
import dataclasses
|
|
9
6
|
import datetime as _dt_module
|
|
10
7
|
import importlib
|
|
11
|
-
import inspect
|
|
12
8
|
import os
|
|
13
9
|
import threading
|
|
14
10
|
import urllib.request
|
|
15
|
-
import uuid
|
|
16
11
|
from collections.abc import AsyncGenerator, AsyncIterator, Callable, Mapping
|
|
17
12
|
from contextlib import ExitStack, asynccontextmanager, suppress
|
|
18
13
|
from pathlib import Path
|
|
@@ -21,6 +16,7 @@ from typing import (
|
|
|
21
16
|
TYPE_CHECKING,
|
|
22
17
|
Any,
|
|
23
18
|
BinaryIO,
|
|
19
|
+
ClassVar,
|
|
24
20
|
Literal,
|
|
25
21
|
Never,
|
|
26
22
|
ParamSpec,
|
|
@@ -43,12 +39,8 @@ from ._async_driver import (
|
|
|
43
39
|
from ._async_transport import AsyncStreamingResponse, AsyncTransport
|
|
44
40
|
from ._capabilities import ClientCapabilities, ServerCapabilities
|
|
45
41
|
from ._client import (
|
|
46
|
-
CONSTRUCTOR_SIGNATURE,
|
|
47
42
|
ConstructorOptions,
|
|
48
|
-
Shotgun,
|
|
49
|
-
bind_constructor,
|
|
50
43
|
build_config,
|
|
51
|
-
expose_constructor_signature,
|
|
52
44
|
)
|
|
53
45
|
from ._config import (
|
|
54
46
|
Config,
|
|
@@ -59,9 +51,7 @@ from ._config import (
|
|
|
59
51
|
server_host,
|
|
60
52
|
server_page_limit,
|
|
61
53
|
)
|
|
62
|
-
from ._errors import ShotgunError
|
|
63
54
|
from ._execution import SuppressedExceptionCapture, release_lease, retire_transport
|
|
64
|
-
from ._signatures import bind_arguments, expose_signature
|
|
65
55
|
from ._transport import (
|
|
66
56
|
ReadTimeoutError,
|
|
67
57
|
SendError,
|
|
@@ -81,11 +71,6 @@ _FilesystemParams = ParamSpec('_FilesystemParams')
|
|
|
81
71
|
_FilesystemResult = TypeVar('_FilesystemResult')
|
|
82
72
|
_UploadResult = TypeVar('_UploadResult')
|
|
83
73
|
|
|
84
|
-
_METHOD_UPLOAD_SIGNATURE = inspect.signature(Shotgun.upload)
|
|
85
|
-
_UPLOAD_SIGNATURE = inspect.signature(Shotgun.upload).replace(
|
|
86
|
-
parameters=tuple(inspect.signature(Shotgun.upload).parameters.values())[1:]
|
|
87
|
-
)
|
|
88
|
-
|
|
89
74
|
|
|
90
75
|
class _AsyncDownloadCancellationScope:
|
|
91
76
|
"""Own and join every filesystem worker started by one transfer."""
|
|
@@ -95,12 +80,17 @@ class _AsyncDownloadCancellationScope:
|
|
|
95
80
|
self._captured_cancellation: asyncio.CancelledError | None = None
|
|
96
81
|
|
|
97
82
|
@property
|
|
98
|
-
def
|
|
83
|
+
def cancellation(self) -> asyncio.CancelledError | None:
|
|
99
84
|
"""Return the first cancellation captured while joining a worker."""
|
|
100
85
|
return self._captured_cancellation
|
|
101
86
|
|
|
102
|
-
def
|
|
103
|
-
"""Retain one actively delivered caller cancellation.
|
|
87
|
+
def capture_cancellation(self, error: BaseException) -> None:
|
|
88
|
+
"""Retain one actively delivered caller cancellation.
|
|
89
|
+
|
|
90
|
+
Args:
|
|
91
|
+
error: Failure to retain if it is an active caller cancellation;
|
|
92
|
+
other failures are ignored.
|
|
93
|
+
"""
|
|
104
94
|
current = asyncio.current_task()
|
|
105
95
|
if (
|
|
106
96
|
not isinstance(error, asyncio.CancelledError)
|
|
@@ -112,28 +102,45 @@ class _AsyncDownloadCancellationScope:
|
|
|
112
102
|
self._captured_cancellation = error
|
|
113
103
|
current.uncancel()
|
|
114
104
|
|
|
115
|
-
def
|
|
116
|
-
"""Propagate a captured cancellation only between worker phases.
|
|
105
|
+
def raise_if_cancelled(self) -> None:
|
|
106
|
+
"""Propagate a captured cancellation only between worker phases.
|
|
107
|
+
|
|
108
|
+
Raises:
|
|
109
|
+
asyncio.CancelledError: If caller cancellation was captured.
|
|
110
|
+
"""
|
|
117
111
|
if self._captured_cancellation is not None:
|
|
118
112
|
raise self._captured_cancellation
|
|
119
113
|
|
|
120
|
-
async def
|
|
114
|
+
async def run(
|
|
121
115
|
self,
|
|
122
116
|
function: Callable[_FilesystemParams, _FilesystemResult],
|
|
123
117
|
*args: _FilesystemParams.args,
|
|
124
118
|
**kwargs: _FilesystemParams.kwargs,
|
|
125
119
|
) -> _FilesystemResult:
|
|
126
|
-
"""Run one worker to completion while retaining cancellation.
|
|
120
|
+
"""Run one worker to completion while retaining cancellation.
|
|
121
|
+
|
|
122
|
+
Args:
|
|
123
|
+
function: Blocking operation to execute off-loop.
|
|
124
|
+
*args: Positional arguments forwarded to the operation.
|
|
125
|
+
**kwargs: Keyword arguments forwarded to the operation.
|
|
126
|
+
|
|
127
|
+
Returns:
|
|
128
|
+
The operation's result after its worker finishes.
|
|
129
|
+
|
|
130
|
+
Raises:
|
|
131
|
+
BaseException: If the worker fails. A captured caller cancellation
|
|
132
|
+
takes precedence over the worker's failure.
|
|
133
|
+
"""
|
|
127
134
|
task = asyncio.create_task(asyncio.to_thread(function, *args, **kwargs))
|
|
128
135
|
while not task.done():
|
|
129
136
|
try:
|
|
130
137
|
await asyncio.wait({task})
|
|
131
138
|
except asyncio.CancelledError as error:
|
|
132
|
-
self.
|
|
139
|
+
self.capture_cancellation(error)
|
|
133
140
|
try:
|
|
134
141
|
return task.result()
|
|
135
142
|
except BaseException:
|
|
136
|
-
self.
|
|
143
|
+
self.raise_if_cancelled()
|
|
137
144
|
raise
|
|
138
145
|
|
|
139
146
|
|
|
@@ -156,7 +163,7 @@ class _AsyncDownloadCleanupCapture:
|
|
|
156
163
|
_traceback: TracebackType | None,
|
|
157
164
|
) -> bool:
|
|
158
165
|
"""Capture and suppress one async download-cleanup exception."""
|
|
159
|
-
self.exception = self._workers.
|
|
166
|
+
self.exception = self._workers.cancellation or exception
|
|
160
167
|
return exception is not None
|
|
161
168
|
|
|
162
169
|
|
|
@@ -194,35 +201,38 @@ class _AsyncDownloadExchange:
|
|
|
194
201
|
workers = _AsyncDownloadCancellationScope()
|
|
195
202
|
try:
|
|
196
203
|
try:
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
204
|
+
with _files.download_destination_io(destination):
|
|
205
|
+
stack, handle, temporary = await workers.run(
|
|
206
|
+
_open_download_temporary,
|
|
207
|
+
destination,
|
|
208
|
+
)
|
|
209
|
+
workers.raise_if_cancelled()
|
|
202
210
|
opened_handle = handle
|
|
203
211
|
|
|
204
212
|
async def write(chunk: bytes) -> None:
|
|
205
|
-
|
|
206
|
-
|
|
213
|
+
with _files.download_destination_io(destination):
|
|
214
|
+
await workers.run(opened_handle.write, chunk)
|
|
215
|
+
workers.raise_if_cancelled()
|
|
207
216
|
|
|
208
217
|
await control.consume_into(chunks, write, total_bytes=total_bytes)
|
|
209
|
-
|
|
210
|
-
|
|
218
|
+
with _files.download_destination_io(destination):
|
|
219
|
+
await workers.run(handle.flush)
|
|
220
|
+
workers.raise_if_cancelled()
|
|
211
221
|
close_exception = await _capture_download_cleanup(workers, stack.close)
|
|
212
|
-
stack = None
|
|
213
|
-
|
|
214
|
-
workers._raise_if_cancelled()
|
|
222
|
+
stack, handle = None, None
|
|
223
|
+
workers.raise_if_cancelled()
|
|
215
224
|
if close_exception is not None:
|
|
216
225
|
_raise_download_cleanup_error()
|
|
217
226
|
# This is the final cancellation checkpoint before atomic commit.
|
|
218
227
|
# Once `replace` starts, its worker is joined before cancellation
|
|
219
228
|
# propagates, so the destination cannot change after return.
|
|
220
229
|
await asyncio.sleep(0)
|
|
221
|
-
|
|
222
|
-
|
|
230
|
+
with _files.download_destination_io(destination):
|
|
231
|
+
await workers.run(temporary.replace, destination)
|
|
232
|
+
workers.raise_if_cancelled()
|
|
223
233
|
except BaseException as error:
|
|
224
234
|
operation_exception = error
|
|
225
|
-
workers.
|
|
235
|
+
workers.capture_cancellation(error)
|
|
226
236
|
raise
|
|
227
237
|
finally:
|
|
228
238
|
close_exception: BaseException | None = None
|
|
@@ -235,7 +245,7 @@ class _AsyncDownloadExchange:
|
|
|
235
245
|
temporary.unlink,
|
|
236
246
|
missing_ok=True,
|
|
237
247
|
)
|
|
238
|
-
workers.
|
|
248
|
+
workers.raise_if_cancelled()
|
|
239
249
|
if operation_exception is None:
|
|
240
250
|
if close_exception is not None:
|
|
241
251
|
raise _AsyncDownloadCleanupError from None
|
|
@@ -259,12 +269,23 @@ class _AsyncDownloadExchange:
|
|
|
259
269
|
chunks = streaming.aiter_chunks(_ASYNC_FILE_BATCH_SIZE)
|
|
260
270
|
return await self._write_file(chunks, control, total_bytes)
|
|
261
271
|
|
|
262
|
-
async def
|
|
272
|
+
async def execute(
|
|
263
273
|
self,
|
|
264
274
|
driver: AsyncDriver,
|
|
265
275
|
operation: _protocol.OperationDescriptor,
|
|
266
276
|
) -> bytes | str:
|
|
267
|
-
"""Execute the workflow through the common async attempt loop.
|
|
277
|
+
"""Execute the workflow through the common async attempt loop.
|
|
278
|
+
|
|
279
|
+
Returns:
|
|
280
|
+
Downloaded bytes, or the destination path for a file download.
|
|
281
|
+
|
|
282
|
+
Raises:
|
|
283
|
+
SendError: If required download cleanup fails.
|
|
284
|
+
TransportError: If the attempt loop cannot complete the transfer.
|
|
285
|
+
ShotgunFileDownloadError: If the response or redirect is invalid,
|
|
286
|
+
or creating, writing, flushing, or publishing the destination
|
|
287
|
+
file fails.
|
|
288
|
+
"""
|
|
268
289
|
try:
|
|
269
290
|
return await driver.execute(
|
|
270
291
|
operation,
|
|
@@ -311,36 +332,6 @@ class _TokenExchangeProgress:
|
|
|
311
332
|
token: str | None = None
|
|
312
333
|
|
|
313
334
|
|
|
314
|
-
def _bind_upload(
|
|
315
|
-
args: tuple[object, ...],
|
|
316
|
-
kwargs: dict[str, object],
|
|
317
|
-
) -> _files.UploadArguments:
|
|
318
|
-
"""Bind runtime arguments with upload-specific errors."""
|
|
319
|
-
bound = bind_arguments(_UPLOAD_SIGNATURE, args, kwargs, 'AsyncShotgun.upload()')
|
|
320
|
-
arguments = cast('Callable[..., _files.UploadArguments]', _files.UploadArguments)
|
|
321
|
-
return arguments(**bound.arguments)
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
async def _capture_download_cleanup(
|
|
325
|
-
workers: _AsyncDownloadCancellationScope,
|
|
326
|
-
function: Callable[_FilesystemParams, _FilesystemResult],
|
|
327
|
-
*args: _FilesystemParams.args,
|
|
328
|
-
**kwargs: _FilesystemParams.kwargs,
|
|
329
|
-
) -> BaseException | None:
|
|
330
|
-
"""Run one joined cleanup operation and capture its failure."""
|
|
331
|
-
capture = _AsyncDownloadCleanupCapture(workers)
|
|
332
|
-
with capture:
|
|
333
|
-
await workers._run(function, *args, **kwargs)
|
|
334
|
-
return capture.exception
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
def _expose_upload_signature(function: Callable[..., object]) -> Callable[..., object]:
|
|
338
|
-
"""Attach the implemented sync upload signature and annotations."""
|
|
339
|
-
expose_signature(function, _METHOD_UPLOAD_SIGNATURE, Shotgun.upload.__doc__)
|
|
340
|
-
object.__setattr__(function, '__annotations__', Shotgun.upload.__annotations__)
|
|
341
|
-
return function
|
|
342
|
-
|
|
343
|
-
|
|
344
335
|
class AsyncShotgun:
|
|
345
336
|
"""Provide the asynchronous Flow Production Tracking API client surface.
|
|
346
337
|
|
|
@@ -357,12 +348,8 @@ class AsyncShotgun:
|
|
|
357
348
|
server_info: Cached raw metadata for the configured origin.
|
|
358
349
|
"""
|
|
359
350
|
|
|
360
|
-
|
|
351
|
+
_in_process: ClassVar[bool] = False
|
|
361
352
|
|
|
362
|
-
@overload
|
|
363
|
-
def __init__(self, base_url: Never) -> None: ...
|
|
364
|
-
|
|
365
|
-
@overload
|
|
366
353
|
def __init__(
|
|
367
354
|
self,
|
|
368
355
|
base_url: str,
|
|
@@ -377,10 +364,7 @@ class AsyncShotgun:
|
|
|
377
364
|
sudo_as_login: str | None = None,
|
|
378
365
|
session_token: str | None = None,
|
|
379
366
|
auth_token: str | None = None,
|
|
380
|
-
) -> None:
|
|
381
|
-
|
|
382
|
-
@expose_constructor_signature
|
|
383
|
-
def __init__(self, *args: object, **kwargs: object) -> None:
|
|
367
|
+
) -> None:
|
|
384
368
|
"""Initialize a client for one site and authentication mode.
|
|
385
369
|
|
|
386
370
|
Supply exactly one authentication mode: `script_name` with `api_key`,
|
|
@@ -392,6 +376,8 @@ class AsyncShotgun:
|
|
|
392
376
|
`http_proxy` accepts `[user:pass@]host[:port]`, with port 8080 when
|
|
393
377
|
omitted. A truthy `ca_certs` selects a CA bundle; otherwise
|
|
394
378
|
`SHOTGUN_API_CACERTS` is consulted before using system trust roots.
|
|
379
|
+
`SHOTGUN_API_RETRY_INTERVAL` supplies a nonnegative integer retry
|
|
380
|
+
interval in milliseconds, defaulting to 3000.
|
|
395
381
|
|
|
396
382
|
Constructor validation is synchronous; readiness and authentication
|
|
397
383
|
exchange I/O begins only through an operation, `connect()`, or an async
|
|
@@ -399,20 +385,57 @@ class AsyncShotgun:
|
|
|
399
385
|
I/O.
|
|
400
386
|
|
|
401
387
|
Args:
|
|
402
|
-
|
|
403
|
-
|
|
388
|
+
base_url: Flow Production Tracking site URL.
|
|
389
|
+
script_name: Script name for script authentication.
|
|
390
|
+
api_key: API key for script authentication.
|
|
391
|
+
convert_datetimes_to_utc: Whether to convert datetime values to
|
|
392
|
+
UTC.
|
|
393
|
+
http_proxy: Optional proxy authority in `[user:pass@]host[:port]`
|
|
394
|
+
form.
|
|
395
|
+
connect: Whether to request eager readiness during asynchronous
|
|
396
|
+
context entry.
|
|
397
|
+
ca_certs: Optional certificate-authority bundle path.
|
|
398
|
+
login: Login for human-user authentication.
|
|
399
|
+
password: Password for human-user authentication.
|
|
400
|
+
sudo_as_login: Optional user login for sudo impersonation.
|
|
401
|
+
session_token: Existing session token for authentication.
|
|
402
|
+
auth_token: Optional two-factor authentication token.
|
|
404
403
|
|
|
405
404
|
Raises:
|
|
406
|
-
|
|
407
|
-
|
|
405
|
+
ValueError: If authentication, URL, or proxy values are invalid,
|
|
406
|
+
the configured CA bundle is not a readable file, or
|
|
407
|
+
`SHOTGUN_API_RETRY_INTERVAL` is not a nonnegative integer.
|
|
408
408
|
"""
|
|
409
|
-
self._initialize(
|
|
409
|
+
self._initialize(
|
|
410
|
+
ConstructorOptions(
|
|
411
|
+
base_url,
|
|
412
|
+
script_name,
|
|
413
|
+
api_key,
|
|
414
|
+
convert_datetimes_to_utc,
|
|
415
|
+
http_proxy,
|
|
416
|
+
connect,
|
|
417
|
+
ca_certs,
|
|
418
|
+
login,
|
|
419
|
+
password,
|
|
420
|
+
sudo_as_login,
|
|
421
|
+
session_token,
|
|
422
|
+
auth_token,
|
|
423
|
+
)
|
|
424
|
+
)
|
|
410
425
|
|
|
411
426
|
async def __aenter__(self) -> Self:
|
|
412
427
|
"""Enter this client and perform any configured eager connection.
|
|
413
428
|
|
|
414
429
|
Returns:
|
|
415
430
|
This client.
|
|
431
|
+
|
|
432
|
+
Raises:
|
|
433
|
+
ShotgunError: If eager connection encounters an RPC fault or
|
|
434
|
+
terminal transport failure.
|
|
435
|
+
ProtocolError: If an eager connection request returns an HTTP
|
|
436
|
+
error.
|
|
437
|
+
ValueError: If eager authenticated HTTP violates the configured
|
|
438
|
+
origin policy.
|
|
416
439
|
"""
|
|
417
440
|
if not self._connect_on_enter:
|
|
418
441
|
return self
|
|
@@ -428,6 +451,10 @@ class AsyncShotgun:
|
|
|
428
451
|
|
|
429
452
|
Args:
|
|
430
453
|
*exc: Context-manager exception details, if any.
|
|
454
|
+
|
|
455
|
+
Raises:
|
|
456
|
+
ShotgunError: If the built-in transport cannot finish cleanup.
|
|
457
|
+
Exception: If a replacement transport's cleanup fails.
|
|
431
458
|
"""
|
|
432
459
|
await self.close()
|
|
433
460
|
|
|
@@ -483,10 +510,14 @@ class AsyncShotgun:
|
|
|
483
510
|
def _initialize(self, options: ConstructorOptions) -> None:
|
|
484
511
|
"""Initialize network-free state from validated constructor options."""
|
|
485
512
|
self._base_url = options.base_url.rstrip('/')
|
|
486
|
-
self._ca_certs =
|
|
513
|
+
self._ca_certs = (
|
|
514
|
+
options.ca_certs or os.environ.get('SHOTGUN_API_CACERTS') or None
|
|
515
|
+
if self._in_process
|
|
516
|
+
else config_module.constructor_ca_certs(options.ca_certs)
|
|
517
|
+
)
|
|
487
518
|
self._client_caps = ClientCapabilities()
|
|
488
519
|
self._connect_on_enter = options.connect
|
|
489
|
-
self._config = build_config(options)
|
|
520
|
+
self._config = build_config(options, in_process=self._in_process)
|
|
490
521
|
|
|
491
522
|
self._driver: AsyncDriver | None = None
|
|
492
523
|
self._driver_generation: int | None = None
|
|
@@ -547,7 +578,11 @@ class AsyncShotgun:
|
|
|
547
578
|
raise cancellation
|
|
548
579
|
|
|
549
580
|
def _new_transport(self, context: RequestContext) -> AsyncTransport:
|
|
550
|
-
"""Create
|
|
581
|
+
"""Create a transport from one context; mockgun overrides this package-internal hook."""
|
|
582
|
+
if context.in_process:
|
|
583
|
+
raise RuntimeError('an in-process client must supply its own transport')
|
|
584
|
+
if context.pool_maxsize < 1:
|
|
585
|
+
raise ValueError('pool_maxsize must be at least 1')
|
|
551
586
|
module = importlib.import_module(f'{__package__}._async_httpx2')
|
|
552
587
|
factory = cast(
|
|
553
588
|
'type[Httpx2Transport]',
|
|
@@ -558,6 +593,8 @@ class AsyncShotgun:
|
|
|
558
593
|
proxy_headers=dict(context.proxy_headers) or None,
|
|
559
594
|
proxy_url=context.proxy_url,
|
|
560
595
|
pool_maxsize=context.pool_maxsize,
|
|
596
|
+
max_connection_idle_secs=context.max_connection_idle_secs,
|
|
597
|
+
tcp_keepalive_timeout_secs=context.tcp_keepalive_timeout_secs,
|
|
561
598
|
)
|
|
562
599
|
|
|
563
600
|
def _acquire_driver(
|
|
@@ -567,7 +604,14 @@ class AsyncShotgun:
|
|
|
567
604
|
"""Lease one generation's driver and return idle transports to close."""
|
|
568
605
|
close_after_unlock: list[AsyncTransport] = []
|
|
569
606
|
with self._transport_lock:
|
|
570
|
-
|
|
607
|
+
decision = _files.choose_driver_lease(
|
|
608
|
+
self._known_transport,
|
|
609
|
+
self._transport,
|
|
610
|
+
self._driver_generation,
|
|
611
|
+
context.transport_generation,
|
|
612
|
+
self._transport_owned,
|
|
613
|
+
)
|
|
614
|
+
if decision.known_changed:
|
|
571
615
|
if self._known_transport is not None:
|
|
572
616
|
retired = retire_transport(
|
|
573
617
|
self._lease_counts, self._retired_transports, self._known_transport
|
|
@@ -585,16 +629,14 @@ class AsyncShotgun:
|
|
|
585
629
|
self._transport = transport
|
|
586
630
|
self._known_transport = transport
|
|
587
631
|
self._transport_owned = True
|
|
588
|
-
elif
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
self._transport = transport
|
|
597
|
-
self._known_transport = transport
|
|
632
|
+
elif decision.replace_owned_transport:
|
|
633
|
+
retired = retire_transport(self._lease_counts, self._retired_transports, transport)
|
|
634
|
+
if retired is not None:
|
|
635
|
+
close_after_unlock.append(retired)
|
|
636
|
+
transport = self._new_transport(context)
|
|
637
|
+
self._transport = transport
|
|
638
|
+
self._known_transport = transport
|
|
639
|
+
if decision.reset_driver:
|
|
598
640
|
self._driver = None
|
|
599
641
|
|
|
600
642
|
if self._driver is None:
|
|
@@ -952,7 +994,9 @@ class AsyncShotgun:
|
|
|
952
994
|
driver,
|
|
953
995
|
context,
|
|
954
996
|
exchange,
|
|
955
|
-
lambda text, _headers: _files.parse_link_completion(
|
|
997
|
+
lambda text, _headers: _files.parse_link_completion(
|
|
998
|
+
text, operation='share_thumbnail', entities=entities
|
|
999
|
+
),
|
|
956
1000
|
)
|
|
957
1001
|
|
|
958
1002
|
async def _thumbnail_source_ready(
|
|
@@ -1026,7 +1070,7 @@ class AsyncShotgun:
|
|
|
1026
1070
|
_files.StorageRange(
|
|
1027
1071
|
length=prepared.file_size,
|
|
1028
1072
|
offset=0,
|
|
1029
|
-
path=prepared.
|
|
1073
|
+
path=prepared.path,
|
|
1030
1074
|
),
|
|
1031
1075
|
chunk_size=_ASYNC_FILE_BATCH_SIZE,
|
|
1032
1076
|
)
|
|
@@ -1037,29 +1081,6 @@ class AsyncShotgun:
|
|
|
1037
1081
|
lambda _text, _headers: None,
|
|
1038
1082
|
)
|
|
1039
1083
|
|
|
1040
|
-
@staticmethod
|
|
1041
|
-
async def _upload_form(
|
|
1042
|
-
driver: AsyncDriver,
|
|
1043
|
-
context: RequestContext,
|
|
1044
|
-
prepared: _files.PreparedUpload,
|
|
1045
|
-
) -> Never:
|
|
1046
|
-
"""Send the observed form request without guessing a success result."""
|
|
1047
|
-
if context.canonical_origin is None:
|
|
1048
|
-
raise ValueError('server parameters must be configured before upload I/O')
|
|
1049
|
-
exchange = _files.build_form_upload_exchange(
|
|
1050
|
-
context.canonical_origin,
|
|
1051
|
-
context.user_agent,
|
|
1052
|
-
prepared,
|
|
1053
|
-
context.auth,
|
|
1054
|
-
chunk_size=_ASYNC_FILE_BATCH_SIZE,
|
|
1055
|
-
)
|
|
1056
|
-
|
|
1057
|
-
def reject_success(_text: str, _headers: Mapping[str, str]) -> Never:
|
|
1058
|
-
raise ShotgunError('form upload success response is not established')
|
|
1059
|
-
|
|
1060
|
-
await _execute_upload_exchange(driver, context, exchange, reject_success)
|
|
1061
|
-
raise AssertionError('form upload response parser returned')
|
|
1062
|
-
|
|
1063
1084
|
@staticmethod
|
|
1064
1085
|
async def _upload_link(
|
|
1065
1086
|
driver: AsyncDriver,
|
|
@@ -1086,7 +1107,16 @@ class AsyncShotgun:
|
|
|
1086
1107
|
driver,
|
|
1087
1108
|
context,
|
|
1088
1109
|
exchange,
|
|
1089
|
-
lambda text, _headers: _files.parse_link_completion(
|
|
1110
|
+
lambda text, _headers: _files.parse_link_completion(
|
|
1111
|
+
text,
|
|
1112
|
+
operation='upload link',
|
|
1113
|
+
entities=[
|
|
1114
|
+
{
|
|
1115
|
+
'type': prepared.options.arguments.entity_type,
|
|
1116
|
+
'id': prepared.options.arguments.entity_id,
|
|
1117
|
+
}
|
|
1118
|
+
],
|
|
1119
|
+
),
|
|
1090
1120
|
)
|
|
1091
1121
|
|
|
1092
1122
|
@staticmethod
|
|
@@ -1159,17 +1189,9 @@ class AsyncShotgun:
|
|
|
1159
1189
|
prepared: _files.PreparedUpload,
|
|
1160
1190
|
) -> int:
|
|
1161
1191
|
"""Execute one prepared upload through a captured driver lease."""
|
|
1162
|
-
arguments = prepared.options.arguments
|
|
1163
1192
|
capabilities = await self._ensure_readiness(context, driver)
|
|
1164
1193
|
operation_context = await self._authenticated_context(context, driver)
|
|
1165
|
-
upload_path = _files.
|
|
1166
|
-
capabilities.server_info,
|
|
1167
|
-
arguments.entity_type,
|
|
1168
|
-
arguments.field_name,
|
|
1169
|
-
prepared.file_size,
|
|
1170
|
-
)
|
|
1171
|
-
if upload_path == 'form':
|
|
1172
|
-
return await self._upload_form(driver, operation_context, prepared)
|
|
1194
|
+
upload_path = _files.choose_prepared_upload_path(capabilities.server_info, prepared)
|
|
1173
1195
|
multipart = upload_path == 'multipart'
|
|
1174
1196
|
upload_info = await self._upload_allocation(
|
|
1175
1197
|
driver,
|
|
@@ -1208,6 +1230,10 @@ class AsyncShotgun:
|
|
|
1208
1230
|
prepared: _files.PreparedUpload,
|
|
1209
1231
|
) -> int:
|
|
1210
1232
|
"""Execute the exact direct-storage thumbnail workflow."""
|
|
1233
|
+
capabilities = await self._ensure_readiness(context, driver)
|
|
1234
|
+
_files.choose_prepared_upload_path(
|
|
1235
|
+
capabilities.server_info, prepared, sharing_thumbnail=True
|
|
1236
|
+
)
|
|
1211
1237
|
upload_info = await self._upload_allocation(
|
|
1212
1238
|
driver,
|
|
1213
1239
|
context,
|
|
@@ -1239,10 +1265,6 @@ class AsyncShotgun:
|
|
|
1239
1265
|
result.update({field_name: current.get(field_name) for field_name in thumbnails})
|
|
1240
1266
|
return result
|
|
1241
1267
|
|
|
1242
|
-
@overload
|
|
1243
|
-
async def activity_stream_read(self, entity_type: Never) -> dict[str, object]: ...
|
|
1244
|
-
|
|
1245
|
-
@overload
|
|
1246
1268
|
async def activity_stream_read(
|
|
1247
1269
|
self,
|
|
1248
1270
|
entity_type: str,
|
|
@@ -1251,34 +1273,30 @@ class AsyncShotgun:
|
|
|
1251
1273
|
min_id: int | None = None,
|
|
1252
1274
|
max_id: int | None = None,
|
|
1253
1275
|
limit: int | None = None,
|
|
1254
|
-
) -> dict[str, object]: ...
|
|
1255
|
-
|
|
1256
|
-
@_methods.expose_activity_stream_read_signature
|
|
1257
|
-
async def activity_stream_read(
|
|
1258
|
-
self,
|
|
1259
|
-
*args: object,
|
|
1260
|
-
**kwargs: object,
|
|
1261
1276
|
) -> dict[str, object]:
|
|
1262
1277
|
"""Read the activity stream for one entity.
|
|
1263
1278
|
|
|
1264
1279
|
Args:
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1280
|
+
entity_type: Entity type whose activity stream to read.
|
|
1281
|
+
entity_id: Numeric identifier of the entity.
|
|
1282
|
+
entity_fields: Optional fields to return for linked entity types.
|
|
1283
|
+
min_id: Optional minimum activity identifier.
|
|
1284
|
+
max_id: Optional maximum activity identifier.
|
|
1285
|
+
limit: Optional maximum number of activity records.
|
|
1269
1286
|
|
|
1270
1287
|
Returns:
|
|
1271
1288
|
Activity stream data returned by the server.
|
|
1272
1289
|
|
|
1273
1290
|
Raises:
|
|
1274
|
-
TypeError: If arguments do not match the documented signature.
|
|
1275
1291
|
ShotgunError: If an RPC fault or terminal transport failure occurs,
|
|
1276
1292
|
or if the server result has the wrong type.
|
|
1277
1293
|
ProtocolError: If a server request returns an HTTP error.
|
|
1278
1294
|
ValueError: If authenticated HTTP violates the configured origin
|
|
1279
1295
|
policy.
|
|
1280
1296
|
"""
|
|
1281
|
-
options = _methods.
|
|
1297
|
+
options = _methods.ActivityStreamReadOptions(
|
|
1298
|
+
entity_type, entity_id, entity_fields, min_id, max_id, limit
|
|
1299
|
+
)
|
|
1282
1300
|
result = await self._call(
|
|
1283
1301
|
'activity_stream',
|
|
1284
1302
|
_methods.build_activity_stream_read_params(options),
|
|
@@ -1362,7 +1380,10 @@ class AsyncShotgun:
|
|
|
1362
1380
|
type, or if the server result is not a list.
|
|
1363
1381
|
ProtocolError: If a server request returns an HTTP error.
|
|
1364
1382
|
ValueError: If authenticated HTTP violates the configured origin
|
|
1365
|
-
policy.
|
|
1383
|
+
policy, or a naive datetime cannot be converted to UTC.
|
|
1384
|
+
OverflowError: If converting an aware datetime to UTC exceeds the
|
|
1385
|
+
supported datetime range.
|
|
1386
|
+
OSError: If the platform cannot resolve a local datetime.
|
|
1366
1387
|
"""
|
|
1367
1388
|
calls = _crud.build_batch_calls(requests)
|
|
1368
1389
|
if not calls:
|
|
@@ -1439,7 +1460,15 @@ class AsyncShotgun:
|
|
|
1439
1460
|
)
|
|
1440
1461
|
|
|
1441
1462
|
async def close(self) -> None:
|
|
1442
|
-
"""Swap out and drain the current transport without aborting work.
|
|
1463
|
+
"""Swap out and drain the current transport without aborting work.
|
|
1464
|
+
|
|
1465
|
+
Attempt cleanup of the remaining retired transports if one close fails,
|
|
1466
|
+
then propagate the first failure.
|
|
1467
|
+
|
|
1468
|
+
Raises:
|
|
1469
|
+
ShotgunError: If the built-in transport cannot finish cleanup.
|
|
1470
|
+
Exception: If a replacement transport's cleanup fails.
|
|
1471
|
+
"""
|
|
1443
1472
|
close_after_unlock: list[AsyncTransport] = []
|
|
1444
1473
|
with self._transport_lock:
|
|
1445
1474
|
advance_transport_generation(self._config)
|
|
@@ -1558,7 +1587,10 @@ class AsyncShotgun:
|
|
|
1558
1587
|
file. A thumbnail failure occurs after the entity is created.
|
|
1559
1588
|
ProtocolError: If a server request returns an HTTP error.
|
|
1560
1589
|
ValueError: If authenticated HTTP violates the configured origin
|
|
1561
|
-
policy.
|
|
1590
|
+
policy, or a naive datetime cannot be converted to UTC.
|
|
1591
|
+
OverflowError: If converting an aware datetime to UTC exceeds the
|
|
1592
|
+
supported datetime range.
|
|
1593
|
+
OSError: If the platform cannot resolve a local datetime.
|
|
1562
1594
|
"""
|
|
1563
1595
|
field_data, thumbnails = _files.split_thumbnail_uploads(data, keep_image_none=False)
|
|
1564
1596
|
params = _crud.build_create_params(entity_type, field_data, return_fields)
|
|
@@ -1626,13 +1658,13 @@ class AsyncShotgun:
|
|
|
1626
1658
|
|
|
1627
1659
|
Raises:
|
|
1628
1660
|
TypeError: If no attachment is given or the reference is a string.
|
|
1629
|
-
ValueError: If an entity dictionary references a different type
|
|
1661
|
+
ValueError: If an entity dictionary references a different type,
|
|
1662
|
+
server settings are incomplete, or authenticated HTTP violates
|
|
1663
|
+
the configured origin security policy.
|
|
1630
1664
|
ShotgunError: If another attachment reference is unsupported or a
|
|
1631
1665
|
setup RPC reports a fault; the fault keeps its specific type.
|
|
1632
1666
|
ShotgunFileDownloadError: If the resolved URL is invalid or setup,
|
|
1633
1667
|
transfer, or destination I/O fails.
|
|
1634
|
-
ValueError: If server settings are incomplete or authenticated HTTP
|
|
1635
|
-
violates the configured origin security policy.
|
|
1636
1668
|
"""
|
|
1637
1669
|
if attachment is False:
|
|
1638
1670
|
if attachment_id is None:
|
|
@@ -1667,7 +1699,7 @@ class AsyncShotgun:
|
|
|
1667
1699
|
headers=headers,
|
|
1668
1700
|
url=url,
|
|
1669
1701
|
)
|
|
1670
|
-
return await exchange.
|
|
1702
|
+
return await exchange.execute(driver, operation)
|
|
1671
1703
|
except _errors.ShotgunFileDownloadError:
|
|
1672
1704
|
raise
|
|
1673
1705
|
except (TransportError, _errors.ProtocolError, OSError):
|
|
@@ -1682,54 +1714,39 @@ class AsyncShotgun:
|
|
|
1682
1714
|
"""
|
|
1683
1715
|
raise _methods.entity_types_alias_error()
|
|
1684
1716
|
|
|
1685
|
-
@overload
|
|
1686
1717
|
async def export_page(
|
|
1687
1718
|
self,
|
|
1688
1719
|
page_id: int,
|
|
1689
|
-
|
|
1690
|
-
/,
|
|
1720
|
+
format: str, # noqa: A002
|
|
1691
1721
|
layout_name: str | None = None,
|
|
1692
|
-
) -> str:
|
|
1693
|
-
|
|
1694
|
-
@overload
|
|
1695
|
-
async def export_page(
|
|
1696
|
-
self,
|
|
1697
|
-
page_id: int,
|
|
1698
|
-
**kwargs: Unpack[_methods.ExportPageKeywordArguments],
|
|
1699
|
-
) -> str: ...
|
|
1700
|
-
|
|
1701
|
-
@_methods.expose_export_page_signature
|
|
1702
|
-
async def export_page(self, *args: object, **kwargs: object) -> str:
|
|
1722
|
+
) -> str:
|
|
1703
1723
|
"""Export a page using the documented arguments.
|
|
1704
1724
|
|
|
1725
|
+
The Page or its layout must be marked API Exportable in the web UI, or
|
|
1726
|
+
the server rejects the export.
|
|
1727
|
+
|
|
1705
1728
|
Args:
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
signature.
|
|
1729
|
+
page_id: Numeric page identifier.
|
|
1730
|
+
format: Required nonempty export format name.
|
|
1731
|
+
layout_name: Optional layout name, including an empty name.
|
|
1710
1732
|
|
|
1711
1733
|
Returns:
|
|
1712
1734
|
Exported page text returned by the server.
|
|
1713
1735
|
|
|
1714
1736
|
Raises:
|
|
1715
|
-
TypeError: If arguments do not match the documented signature.
|
|
1716
1737
|
ShotgunError: If an RPC fault or terminal transport failure occurs,
|
|
1717
|
-
or if
|
|
1738
|
+
or if `page_id`, `format`, or the server result is invalid.
|
|
1718
1739
|
ProtocolError: If a server request returns an HTTP error.
|
|
1719
1740
|
ValueError: If authenticated HTTP violates the configured origin
|
|
1720
1741
|
policy.
|
|
1721
1742
|
"""
|
|
1722
|
-
options = _methods.
|
|
1743
|
+
options = _methods.ExportPageOptions(page_id, format, layout_name)
|
|
1723
1744
|
result = await self._call(
|
|
1724
1745
|
'export_page',
|
|
1725
1746
|
_methods.build_export_page_params(options),
|
|
1726
1747
|
)
|
|
1727
1748
|
return _methods.require_string_result(result, 'export_page')
|
|
1728
1749
|
|
|
1729
|
-
@overload
|
|
1730
|
-
async def find(self, entity_type: Never) -> list[dict[str, object]]: ...
|
|
1731
|
-
|
|
1732
|
-
@overload
|
|
1733
1750
|
async def find(
|
|
1734
1751
|
self,
|
|
1735
1752
|
entity_type: str,
|
|
@@ -1742,33 +1759,50 @@ class AsyncShotgun:
|
|
|
1742
1759
|
page: int = 0,
|
|
1743
1760
|
include_archived_projects: bool = True,
|
|
1744
1761
|
additional_filter_presets: object = None,
|
|
1745
|
-
) -> list[dict[str, object]]:
|
|
1746
|
-
|
|
1747
|
-
@_crud.expose_find_signature
|
|
1748
|
-
async def find(self, *args: object, **kwargs: object) -> list[dict[str, object]]:
|
|
1762
|
+
) -> list[dict[str, object]]:
|
|
1749
1763
|
"""Find entities matching the supplied filters.
|
|
1750
1764
|
|
|
1751
1765
|
Args:
|
|
1752
|
-
|
|
1753
|
-
|
|
1766
|
+
entity_type: Entity type to find.
|
|
1767
|
+
filters: Filters in list or dict syntax.
|
|
1768
|
+
fields: Optional fields to return; defaults to `id`.
|
|
1769
|
+
order: Optional field and direction sort specifications.
|
|
1770
|
+
filter_operator: `'all'` or `'any'` for list-syntax filters.
|
|
1771
|
+
limit: Maximum entities to return, or `0` for no limit.
|
|
1772
|
+
retired_only: Return retired entities instead of active entities.
|
|
1773
|
+
page: Specific page to return, or `0` to read all pages.
|
|
1774
|
+
include_archived_projects: Include entities from archived projects.
|
|
1775
|
+
additional_filter_presets: Optional server-side filter presets.
|
|
1754
1776
|
|
|
1755
1777
|
Returns:
|
|
1756
1778
|
Matching entities in server order.
|
|
1757
1779
|
|
|
1758
1780
|
Raises:
|
|
1759
|
-
TypeError: If arguments do not match the documented signature.
|
|
1760
1781
|
ShotgunError: If an RPC fault or terminal transport failure occurs,
|
|
1761
|
-
or if filters, paging, ordering, or a server result is
|
|
1782
|
+
or if filters, fields, paging, ordering, or a server result is
|
|
1783
|
+
invalid.
|
|
1762
1784
|
ProtocolError: If a server request returns an HTTP error.
|
|
1763
1785
|
ValueError: If authenticated HTTP violates the configured origin
|
|
1764
|
-
policy.
|
|
1786
|
+
policy, or a naive datetime cannot be converted to UTC.
|
|
1787
|
+
OverflowError: If converting an aware datetime to UTC exceeds the
|
|
1788
|
+
supported datetime range.
|
|
1789
|
+
OSError: If the platform cannot resolve a local datetime.
|
|
1765
1790
|
"""
|
|
1766
|
-
return await self._find(
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1791
|
+
return await self._find(
|
|
1792
|
+
_crud.FindOptions(
|
|
1793
|
+
entity_type,
|
|
1794
|
+
filters,
|
|
1795
|
+
fields,
|
|
1796
|
+
order,
|
|
1797
|
+
filter_operator,
|
|
1798
|
+
limit,
|
|
1799
|
+
retired_only,
|
|
1800
|
+
page,
|
|
1801
|
+
include_archived_projects,
|
|
1802
|
+
additional_filter_presets,
|
|
1803
|
+
)
|
|
1804
|
+
)
|
|
1770
1805
|
|
|
1771
|
-
@overload
|
|
1772
1806
|
async def find_one(
|
|
1773
1807
|
self,
|
|
1774
1808
|
entity_type: str,
|
|
@@ -1779,28 +1813,43 @@ class AsyncShotgun:
|
|
|
1779
1813
|
retired_only: bool = False,
|
|
1780
1814
|
include_archived_projects: bool = True,
|
|
1781
1815
|
additional_filter_presets: object = None,
|
|
1782
|
-
) -> dict[str, object] | None:
|
|
1783
|
-
|
|
1784
|
-
@_crud.expose_find_one_signature
|
|
1785
|
-
async def find_one(self, *args: object, **kwargs: object) -> dict[str, object] | None:
|
|
1816
|
+
) -> dict[str, object] | None:
|
|
1786
1817
|
"""Return the first entity matching the supplied filters.
|
|
1787
1818
|
|
|
1788
1819
|
Args:
|
|
1789
|
-
|
|
1790
|
-
|
|
1820
|
+
entity_type: Entity type to find.
|
|
1821
|
+
filters: Filters in list or dict syntax.
|
|
1822
|
+
fields: Optional fields to return; defaults to `id`.
|
|
1823
|
+
order: Optional field and direction sort specifications.
|
|
1824
|
+
filter_operator: `'all'` or `'any'` for list-syntax filters.
|
|
1825
|
+
retired_only: Return a retired entity instead of an active entity.
|
|
1826
|
+
include_archived_projects: Include entities from archived projects.
|
|
1827
|
+
additional_filter_presets: Optional server-side filter presets.
|
|
1791
1828
|
|
|
1792
1829
|
Returns:
|
|
1793
1830
|
The first matching entity, or `None` when no entity matches.
|
|
1794
1831
|
|
|
1795
1832
|
Raises:
|
|
1796
|
-
TypeError: If arguments do not match the documented signature.
|
|
1797
1833
|
ShotgunError: If an RPC fault or terminal transport failure occurs,
|
|
1798
|
-
or if filters, paging, ordering, or a server result is
|
|
1834
|
+
or if filters, fields, paging, ordering, or a server result is
|
|
1835
|
+
invalid.
|
|
1799
1836
|
ProtocolError: If a server request returns an HTTP error.
|
|
1800
1837
|
ValueError: If authenticated HTTP violates the configured origin
|
|
1801
|
-
policy.
|
|
1838
|
+
policy, or a naive datetime cannot be converted to UTC.
|
|
1839
|
+
OverflowError: If converting an aware datetime to UTC exceeds the
|
|
1840
|
+
supported datetime range.
|
|
1841
|
+
OSError: If the platform cannot resolve a local datetime.
|
|
1802
1842
|
"""
|
|
1803
|
-
options = _crud.
|
|
1843
|
+
options = _crud.FindOneOptions(
|
|
1844
|
+
entity_type,
|
|
1845
|
+
filters,
|
|
1846
|
+
fields,
|
|
1847
|
+
order,
|
|
1848
|
+
filter_operator,
|
|
1849
|
+
retired_only,
|
|
1850
|
+
include_archived_projects,
|
|
1851
|
+
additional_filter_presets,
|
|
1852
|
+
)
|
|
1804
1853
|
return _crud.first_result(await self._find(_crud.find_options_from_one(options)))
|
|
1805
1854
|
|
|
1806
1855
|
async def follow(
|
|
@@ -2022,7 +2071,11 @@ class AsyncShotgun:
|
|
|
2022
2071
|
entity,
|
|
2023
2072
|
seed_entity_field,
|
|
2024
2073
|
)
|
|
2025
|
-
result = await self._call(
|
|
2074
|
+
result = await self._call(
|
|
2075
|
+
'nav_search',
|
|
2076
|
+
params,
|
|
2077
|
+
_InvocationOptions(logical_method_name='nav_search_entity'),
|
|
2078
|
+
)
|
|
2026
2079
|
return _methods.require_dictionary_list_result(result, 'nav_search_entity')
|
|
2027
2080
|
|
|
2028
2081
|
async def nav_search_string(
|
|
@@ -2053,7 +2106,11 @@ class AsyncShotgun:
|
|
|
2053
2106
|
search_string,
|
|
2054
2107
|
seed_entity_field,
|
|
2055
2108
|
)
|
|
2056
|
-
result = await self._call(
|
|
2109
|
+
result = await self._call(
|
|
2110
|
+
'nav_search',
|
|
2111
|
+
params,
|
|
2112
|
+
_InvocationOptions(logical_method_name='nav_search_string'),
|
|
2113
|
+
)
|
|
2057
2114
|
return _methods.require_dictionary_list_result(result, 'nav_search_string')
|
|
2058
2115
|
|
|
2059
2116
|
async def note_thread_read(
|
|
@@ -2078,7 +2135,11 @@ class AsyncShotgun:
|
|
|
2078
2135
|
policy.
|
|
2079
2136
|
"""
|
|
2080
2137
|
params = _methods.build_note_thread_read_params(note_id, entity_fields)
|
|
2081
|
-
result = await self._call(
|
|
2138
|
+
result = await self._call(
|
|
2139
|
+
'note_thread_contents',
|
|
2140
|
+
params,
|
|
2141
|
+
_InvocationOptions(logical_method_name='note_thread_read'),
|
|
2142
|
+
)
|
|
2082
2143
|
return _methods.require_dictionary_list_result(result, 'note_thread_read')
|
|
2083
2144
|
|
|
2084
2145
|
async def preferences_read(self, prefs: list[str] | None = None) -> dict[str, object]:
|
|
@@ -2185,7 +2246,10 @@ class AsyncShotgun:
|
|
|
2185
2246
|
or if the server result is invalid.
|
|
2186
2247
|
ProtocolError: If a server request returns an HTTP error.
|
|
2187
2248
|
ValueError: If authenticated HTTP violates the configured origin
|
|
2188
|
-
policy.
|
|
2249
|
+
policy, or a naive datetime cannot be converted to UTC.
|
|
2250
|
+
OverflowError: If converting an aware datetime to UTC exceeds the
|
|
2251
|
+
supported datetime range.
|
|
2252
|
+
OSError: If the platform cannot resolve a local datetime.
|
|
2189
2253
|
"""
|
|
2190
2254
|
params = _methods.build_schema_field_create_params(
|
|
2191
2255
|
entity_type,
|
|
@@ -2273,7 +2337,10 @@ class AsyncShotgun:
|
|
|
2273
2337
|
or if the server result is invalid.
|
|
2274
2338
|
ProtocolError: If a server request returns an HTTP error.
|
|
2275
2339
|
ValueError: If authenticated HTTP violates the configured origin
|
|
2276
|
-
policy.
|
|
2340
|
+
policy, or a naive datetime cannot be converted to UTC.
|
|
2341
|
+
OverflowError: If converting an aware datetime to UTC exceeds the
|
|
2342
|
+
supported datetime range.
|
|
2343
|
+
OSError: If the platform cannot resolve a local datetime.
|
|
2277
2344
|
"""
|
|
2278
2345
|
params = _methods.build_schema_field_update_params(
|
|
2279
2346
|
entity_type,
|
|
@@ -2357,7 +2424,8 @@ class AsyncShotgun:
|
|
|
2357
2424
|
|
|
2358
2425
|
Supply exactly one of `thumbnail_path` and `source_entity`. A local
|
|
2359
2426
|
thumbnail is uploaded to the first target before it is shared with the
|
|
2360
|
-
remaining targets.
|
|
2427
|
+
remaining targets. A local thumbnail is rejected before any upload
|
|
2428
|
+
request if the server requires legacy form uploads.
|
|
2361
2429
|
|
|
2362
2430
|
Args:
|
|
2363
2431
|
entities: Nonempty list of target entities with `type` and `id`.
|
|
@@ -2374,7 +2442,8 @@ class AsyncShotgun:
|
|
|
2374
2442
|
TypeError: If forwarded arguments do not match the upload signature
|
|
2375
2443
|
or include `field_name`.
|
|
2376
2444
|
ShotgunError: If an RPC fault or terminal transport failure occurs,
|
|
2377
|
-
or if the source selection, entities, or transport fails
|
|
2445
|
+
or if the source selection, entities, or transport fails, or a
|
|
2446
|
+
local upload requires the legacy form route.
|
|
2378
2447
|
ShotgunThumbnailNotReady: If an uploaded source stays transient.
|
|
2379
2448
|
ProtocolError: If an upload endpoint returns an HTTP error.
|
|
2380
2449
|
ValueError: If authenticated HTTP violates the configured origin
|
|
@@ -2386,30 +2455,26 @@ class AsyncShotgun:
|
|
|
2386
2455
|
source_entity,
|
|
2387
2456
|
filmstrip_thumbnail,
|
|
2388
2457
|
)
|
|
2389
|
-
if share.thumbnail_path is not None and 'field_name' in kwargs:
|
|
2390
|
-
raise TypeError("AsyncShotgun.upload() got multiple values for argument 'field_name'")
|
|
2391
2458
|
prepared: _files.PreparedUpload | None = None
|
|
2392
2459
|
if share.thumbnail_path is not None:
|
|
2393
|
-
|
|
2394
|
-
(
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2460
|
+
for keyword in kwargs:
|
|
2461
|
+
if keyword not in ('display_name', 'tag_list', 'created_at'):
|
|
2462
|
+
raise TypeError(
|
|
2463
|
+
'AsyncShotgun.share_thumbnail() got an unexpected keyword '
|
|
2464
|
+
f"argument '{keyword}'"
|
|
2465
|
+
)
|
|
2466
|
+
arguments = _files.UploadArguments(
|
|
2467
|
+
entity_type=share.source_entity_type,
|
|
2468
|
+
entity_id=share.source_entity_id,
|
|
2469
|
+
path=share.thumbnail_path,
|
|
2470
|
+
field_name=_files.thumbnail_fields(filmstrip_thumbnail)[0],
|
|
2471
|
+
display_name=cast('str | None', kwargs.get('display_name')),
|
|
2472
|
+
tag_list=cast('str | None', kwargs.get('tag_list')),
|
|
2473
|
+
created_at=cast('_dt_module.datetime | None', kwargs.get('created_at')),
|
|
2405
2474
|
)
|
|
2406
2475
|
prepared = await asyncio.to_thread(
|
|
2407
2476
|
_files.prepare_upload,
|
|
2408
|
-
_files.
|
|
2409
|
-
arguments=arguments,
|
|
2410
|
-
upload_type='Thumbnail',
|
|
2411
|
-
filmstrip=filmstrip_thumbnail,
|
|
2412
|
-
),
|
|
2477
|
+
_files.upload_options(arguments),
|
|
2413
2478
|
)
|
|
2414
2479
|
context = config_module.snapshot(self._config)
|
|
2415
2480
|
require_secure_rpc(context, include_auth=True)
|
|
@@ -2424,9 +2489,7 @@ class AsyncShotgun:
|
|
|
2424
2489
|
)
|
|
2425
2490
|
if len(share.entities) == 1:
|
|
2426
2491
|
return attachment_id
|
|
2427
|
-
field_name
|
|
2428
|
-
'filmstrip_image' if filmstrip_thumbnail else 'image'
|
|
2429
|
-
)
|
|
2492
|
+
field_name = _files.thumbnail_fields(filmstrip_thumbnail)[1]
|
|
2430
2493
|
ready = await driver.poll(
|
|
2431
2494
|
lambda: self._thumbnail_source_ready(
|
|
2432
2495
|
driver,
|
|
@@ -2455,10 +2518,6 @@ class AsyncShotgun:
|
|
|
2455
2518
|
filmstrip_thumbnail,
|
|
2456
2519
|
)
|
|
2457
2520
|
|
|
2458
|
-
@overload
|
|
2459
|
-
async def summarize(self, entity_type: Never) -> dict[str, object]: ...
|
|
2460
|
-
|
|
2461
|
-
@overload
|
|
2462
2521
|
async def summarize(
|
|
2463
2522
|
self,
|
|
2464
2523
|
entity_type: str,
|
|
@@ -2467,28 +2526,39 @@ class AsyncShotgun:
|
|
|
2467
2526
|
filter_operator: str | None = None,
|
|
2468
2527
|
grouping: list[dict[str, Any]] | None = None,
|
|
2469
2528
|
include_archived_projects: bool = True,
|
|
2470
|
-
) -> dict[str, object]:
|
|
2471
|
-
|
|
2472
|
-
@_methods.expose_summarize_signature
|
|
2473
|
-
async def summarize(self, *args: object, **kwargs: object) -> dict[str, object]:
|
|
2529
|
+
) -> dict[str, object]:
|
|
2474
2530
|
"""Summarize field data returned by a query.
|
|
2475
2531
|
|
|
2476
2532
|
Args:
|
|
2477
|
-
|
|
2478
|
-
|
|
2533
|
+
entity_type: Entity type to summarize.
|
|
2534
|
+
filters: Filters in list or dict syntax.
|
|
2535
|
+
summary_fields: List of `{'field': ..., 'type': ...}` summaries.
|
|
2536
|
+
filter_operator: `'all'` or `'any'` for list-syntax filters.
|
|
2537
|
+
grouping: Optional grouping specs.
|
|
2538
|
+
include_archived_projects: Include archived projects when `True`.
|
|
2479
2539
|
|
|
2480
2540
|
Returns:
|
|
2481
|
-
Dict with `summaries` and
|
|
2541
|
+
Dict with `summaries` and (when grouped) `groups`.
|
|
2482
2542
|
|
|
2483
2543
|
Raises:
|
|
2484
|
-
TypeError: If arguments do not match the documented signature.
|
|
2485
2544
|
ShotgunError: If an RPC fault or terminal transport failure occurs,
|
|
2486
|
-
or if
|
|
2545
|
+
or if `filters`, `summary_fields`, or the server result has the
|
|
2546
|
+
wrong type.
|
|
2487
2547
|
ProtocolError: If a server request returns an HTTP error.
|
|
2488
2548
|
ValueError: If authenticated HTTP violates the configured origin
|
|
2489
|
-
policy.
|
|
2549
|
+
policy, or a naive datetime cannot be converted to UTC.
|
|
2550
|
+
OverflowError: If converting an aware datetime to UTC exceeds the
|
|
2551
|
+
supported datetime range.
|
|
2552
|
+
OSError: If the platform cannot resolve a local datetime.
|
|
2490
2553
|
"""
|
|
2491
|
-
options = _methods.
|
|
2554
|
+
options = _methods.SummarizeOptions(
|
|
2555
|
+
entity_type,
|
|
2556
|
+
filters,
|
|
2557
|
+
summary_fields,
|
|
2558
|
+
filter_operator,
|
|
2559
|
+
grouping,
|
|
2560
|
+
include_archived_projects,
|
|
2561
|
+
)
|
|
2492
2562
|
params = _methods.build_summarize_params(options)
|
|
2493
2563
|
result = await self._call('summarize', params)
|
|
2494
2564
|
return _crud.require_dictionary_result(result, 'summarize')
|
|
@@ -2516,7 +2586,10 @@ class AsyncShotgun:
|
|
|
2516
2586
|
or if arguments or the server result are invalid.
|
|
2517
2587
|
ProtocolError: If a server request returns an HTTP error.
|
|
2518
2588
|
ValueError: If authenticated HTTP violates the configured origin
|
|
2519
|
-
policy.
|
|
2589
|
+
policy, or a naive datetime cannot be converted to UTC.
|
|
2590
|
+
OverflowError: If converting an aware datetime to UTC exceeds the
|
|
2591
|
+
supported datetime range.
|
|
2592
|
+
OSError: If the platform cannot resolve a local datetime.
|
|
2520
2593
|
"""
|
|
2521
2594
|
params = _methods.build_text_search_params(
|
|
2522
2595
|
text,
|
|
@@ -2586,7 +2659,10 @@ class AsyncShotgun:
|
|
|
2586
2659
|
nonempty file. A thumbnail failure occurs after the update.
|
|
2587
2660
|
ProtocolError: If a server request returns an HTTP error.
|
|
2588
2661
|
ValueError: If authenticated HTTP violates the configured origin
|
|
2589
|
-
policy.
|
|
2662
|
+
policy, or a naive datetime cannot be converted to UTC.
|
|
2663
|
+
OverflowError: If converting an aware datetime to UTC exceeds the
|
|
2664
|
+
supported datetime range.
|
|
2665
|
+
OSError: If the platform cannot resolve a local datetime.
|
|
2590
2666
|
"""
|
|
2591
2667
|
field_data, thumbnails = _files.split_thumbnail_uploads(data, keep_image_none=True)
|
|
2592
2668
|
params = _crud.build_update_params(
|
|
@@ -2617,7 +2693,8 @@ class AsyncShotgun:
|
|
|
2617
2693
|
user: Optional user entity to attribute the access to.
|
|
2618
2694
|
|
|
2619
2695
|
Raises:
|
|
2620
|
-
ShotgunError: If
|
|
2696
|
+
ShotgunError: If a project or user ID is invalid, or an RPC fault
|
|
2697
|
+
or terminal transport failure occurs.
|
|
2621
2698
|
ProtocolError: If a server request returns an HTTP error.
|
|
2622
2699
|
ValueError: If authenticated HTTP violates the configured origin
|
|
2623
2700
|
policy.
|
|
@@ -2628,10 +2705,6 @@ class AsyncShotgun:
|
|
|
2628
2705
|
_InvocationOptions(logical_method_name='update_project_last_accessed'),
|
|
2629
2706
|
)
|
|
2630
2707
|
|
|
2631
|
-
@overload
|
|
2632
|
-
async def upload(self, entity_type: Never) -> int: ...
|
|
2633
|
-
|
|
2634
|
-
@overload
|
|
2635
2708
|
async def upload(
|
|
2636
2709
|
self,
|
|
2637
2710
|
entity_type: str,
|
|
@@ -2641,49 +2714,37 @@ class AsyncShotgun:
|
|
|
2641
2714
|
display_name: str | None = None,
|
|
2642
2715
|
tag_list: str | None = None,
|
|
2643
2716
|
created_at: _dt_module.datetime | None = None,
|
|
2644
|
-
) -> int:
|
|
2645
|
-
|
|
2646
|
-
@_expose_upload_signature
|
|
2647
|
-
async def upload(self, *args: object, **kwargs: object) -> int:
|
|
2717
|
+
) -> int:
|
|
2648
2718
|
"""Upload a file or thumbnail and return its Attachment ID.
|
|
2649
2719
|
|
|
2650
|
-
A
|
|
2651
|
-
|
|
2652
|
-
The form request has already been sent when this error is raised.
|
|
2720
|
+
A server requiring legacy form uploads raises `ShotgunError` before any
|
|
2721
|
+
upload request is sent. Server capability reads may occur.
|
|
2653
2722
|
|
|
2654
2723
|
Args:
|
|
2655
|
-
|
|
2656
|
-
|
|
2724
|
+
entity_type: Entity type receiving the upload.
|
|
2725
|
+
entity_id: Numeric identifier of the receiving entity.
|
|
2726
|
+
path: Existing non-empty file path as text or filesystem-encoded
|
|
2727
|
+
bytes.
|
|
2728
|
+
field_name: Optional file field receiving an Attachment.
|
|
2729
|
+
display_name: Optional Attachment display name.
|
|
2730
|
+
tag_list: Optional comma-delimited Attachment tags.
|
|
2731
|
+
created_at: Optional Attachment creation timestamp.
|
|
2657
2732
|
|
|
2658
2733
|
Returns:
|
|
2659
2734
|
Created Attachment ID.
|
|
2660
2735
|
|
|
2661
2736
|
Raises:
|
|
2662
|
-
TypeError: If arguments do not match the documented signature.
|
|
2663
2737
|
ShotgunError: If the file or an upload response is invalid, an RPC
|
|
2664
|
-
fault or terminal transport failure occurs, or the
|
|
2665
|
-
form
|
|
2738
|
+
fault or terminal transport failure occurs, or the server
|
|
2739
|
+
requires legacy form uploads.
|
|
2666
2740
|
ProtocolError: If an upload endpoint returns an HTTP error.
|
|
2667
2741
|
ValueError: If authenticated HTTP violates the configured origin
|
|
2668
2742
|
policy.
|
|
2669
2743
|
"""
|
|
2670
|
-
arguments =
|
|
2671
|
-
|
|
2672
|
-
'filmstrip_image',
|
|
2673
|
-
'filmstrip_thumb_image',
|
|
2674
|
-
'image',
|
|
2675
|
-
'thumb_image',
|
|
2676
|
-
}
|
|
2677
|
-
upload_type: Literal['Attachment', 'Thumbnail'] = (
|
|
2678
|
-
'Thumbnail' if arguments.field_name in thumbnail_fields else 'Attachment'
|
|
2679
|
-
)
|
|
2680
|
-
return await self._upload(
|
|
2681
|
-
_files.UploadOptions(
|
|
2682
|
-
arguments=arguments,
|
|
2683
|
-
upload_type=upload_type,
|
|
2684
|
-
filmstrip=arguments.field_name in {'filmstrip_image', 'filmstrip_thumb_image'},
|
|
2685
|
-
)
|
|
2744
|
+
arguments = _files.UploadArguments(
|
|
2745
|
+
entity_type, entity_id, path, field_name, display_name, tag_list, created_at
|
|
2686
2746
|
)
|
|
2747
|
+
return await self._upload(_files.upload_options(arguments))
|
|
2687
2748
|
|
|
2688
2749
|
async def upload_filmstrip_thumbnail(
|
|
2689
2750
|
self,
|
|
@@ -2694,6 +2755,9 @@ class AsyncShotgun:
|
|
|
2694
2755
|
) -> int:
|
|
2695
2756
|
"""Upload a filmstrip thumbnail through `upload`.
|
|
2696
2757
|
|
|
2758
|
+
Servers requiring legacy form uploads are rejected before any upload
|
|
2759
|
+
request is sent.
|
|
2760
|
+
|
|
2697
2761
|
Args:
|
|
2698
2762
|
entity_type: Entity type receiving the thumbnail.
|
|
2699
2763
|
entity_id: Numeric identifier of the receiving entity.
|
|
@@ -2706,7 +2770,8 @@ class AsyncShotgun:
|
|
|
2706
2770
|
Raises:
|
|
2707
2771
|
TypeError: If forwarded arguments do not match `upload`.
|
|
2708
2772
|
ShotgunError: If an RPC fault or terminal transport failure occurs,
|
|
2709
|
-
or if the file, upload response, or transport fails
|
|
2773
|
+
or if the file, upload response, or transport fails, or the
|
|
2774
|
+
server requires legacy form uploads.
|
|
2710
2775
|
ProtocolError: If an upload endpoint returns an HTTP error.
|
|
2711
2776
|
ValueError: If authenticated HTTP violates the configured origin
|
|
2712
2777
|
policy.
|
|
@@ -2728,6 +2793,9 @@ class AsyncShotgun:
|
|
|
2728
2793
|
) -> int:
|
|
2729
2794
|
"""Upload a static thumbnail through `upload`.
|
|
2730
2795
|
|
|
2796
|
+
Servers requiring legacy form uploads are rejected before any upload
|
|
2797
|
+
request is sent.
|
|
2798
|
+
|
|
2731
2799
|
Args:
|
|
2732
2800
|
entity_type: Entity type receiving the thumbnail.
|
|
2733
2801
|
entity_id: Numeric identifier of the receiving entity.
|
|
@@ -2740,7 +2808,8 @@ class AsyncShotgun:
|
|
|
2740
2808
|
Raises:
|
|
2741
2809
|
TypeError: If forwarded arguments do not match `upload`.
|
|
2742
2810
|
ShotgunError: If an RPC fault or terminal transport failure occurs,
|
|
2743
|
-
or if the file, upload response, or transport fails
|
|
2811
|
+
or if the file, upload response, or transport fails, or the
|
|
2812
|
+
server requires legacy form uploads.
|
|
2744
2813
|
ProtocolError: If an upload endpoint returns an HTTP error.
|
|
2745
2814
|
ValueError: If authenticated HTTP violates the configured origin
|
|
2746
2815
|
policy.
|
|
@@ -2756,28 +2825,40 @@ class AsyncShotgun:
|
|
|
2756
2825
|
async def user_subscriptions_create(self, users: list[dict[str, Any]]) -> bool:
|
|
2757
2826
|
"""Assign subscriptions to users.
|
|
2758
2827
|
|
|
2828
|
+
The server accepts this call from scripts and from human users with
|
|
2829
|
+
permission to manage subscriptions, such as members of the Admin
|
|
2830
|
+
permission group. It refuses other human users.
|
|
2831
|
+
|
|
2759
2832
|
Args:
|
|
2760
|
-
users: Subscription dictionaries
|
|
2833
|
+
users: Subscription dictionaries with a string or integer
|
|
2834
|
+
`humanUserId`; integers become decimal string keys and booleans
|
|
2835
|
+
are not accepted. Each user may appear only once.
|
|
2836
|
+
`subscription` is `None`, a string, or a list of strings.
|
|
2837
|
+
Lists are copied in order. Other entry keys are ignored.
|
|
2761
2838
|
|
|
2762
2839
|
Returns:
|
|
2763
|
-
|
|
2840
|
+
`True` when every per-user report is `None`, meaning the server
|
|
2841
|
+
reported no problem; `False` when any user has a non-`None` report.
|
|
2764
2842
|
|
|
2765
2843
|
Raises:
|
|
2766
2844
|
ShotgunError: If an RPC fault or terminal transport failure occurs,
|
|
2767
|
-
or if
|
|
2845
|
+
or if `users` contains invalid or duplicate entries, or the
|
|
2846
|
+
server result is not a mapping with string keys.
|
|
2768
2847
|
ProtocolError: If a server request returns an HTTP error.
|
|
2769
2848
|
ValueError: If authenticated HTTP violates the configured origin
|
|
2770
2849
|
policy.
|
|
2771
2850
|
"""
|
|
2772
2851
|
params = _methods.build_user_subscriptions_create_params(users)
|
|
2773
2852
|
result = await self._call('user_subscriptions_create', params)
|
|
2774
|
-
return
|
|
2853
|
+
return _methods.require_user_subscriptions_create_result(result)
|
|
2775
2854
|
|
|
2776
2855
|
async def user_subscriptions_read(self) -> list[dict[str, object]]:
|
|
2777
|
-
"""List
|
|
2856
|
+
"""List user subscriptions in server order.
|
|
2778
2857
|
|
|
2779
2858
|
Returns:
|
|
2780
|
-
|
|
2859
|
+
Subscription entries in server order, each with an unchanged string
|
|
2860
|
+
`humanUserId` and a `subscription` of `None`, a string, or a copied
|
|
2861
|
+
list of strings.
|
|
2781
2862
|
|
|
2782
2863
|
Raises:
|
|
2783
2864
|
ShotgunError: If an RPC fault or terminal transport failure occurs,
|
|
@@ -2787,7 +2868,7 @@ class AsyncShotgun:
|
|
|
2787
2868
|
policy.
|
|
2788
2869
|
"""
|
|
2789
2870
|
result = await self._call('user_subscriptions_read', None)
|
|
2790
|
-
return _methods.
|
|
2871
|
+
return _methods.require_user_subscriptions_result(result)
|
|
2791
2872
|
|
|
2792
2873
|
async def work_schedule_read(
|
|
2793
2874
|
self,
|
|
@@ -2823,10 +2904,6 @@ class AsyncShotgun:
|
|
|
2823
2904
|
result = await self._call('work_schedule_read', params)
|
|
2824
2905
|
return _crud.require_dictionary_result(result, 'work_schedule_read')
|
|
2825
2906
|
|
|
2826
|
-
@overload
|
|
2827
|
-
async def work_schedule_update(self, date: Never) -> dict[str, object]: ...
|
|
2828
|
-
|
|
2829
|
-
@overload
|
|
2830
2907
|
async def work_schedule_update(
|
|
2831
2908
|
self,
|
|
2832
2909
|
date: _dt_module.date | str,
|
|
@@ -2835,37 +2912,49 @@ class AsyncShotgun:
|
|
|
2835
2912
|
project: dict[str, Any] | None = None,
|
|
2836
2913
|
user: dict[str, Any] | None = None,
|
|
2837
2914
|
recalculate_field: str | None = None,
|
|
2838
|
-
) -> dict[str, object]: ...
|
|
2839
|
-
|
|
2840
|
-
@_methods.expose_work_schedule_update_signature
|
|
2841
|
-
async def work_schedule_update(
|
|
2842
|
-
self,
|
|
2843
|
-
*args: object,
|
|
2844
|
-
**kwargs: object,
|
|
2845
2915
|
) -> dict[str, object]:
|
|
2846
|
-
"""Update a work schedule
|
|
2916
|
+
"""Update a work schedule.
|
|
2847
2917
|
|
|
2848
2918
|
Args:
|
|
2849
|
-
|
|
2850
|
-
|
|
2919
|
+
date: Date or exact `YYYY-MM-DD` string to update.
|
|
2920
|
+
working: Whether the date is a working day.
|
|
2921
|
+
description: Optional schedule description.
|
|
2922
|
+
project: Optional project entity restriction.
|
|
2923
|
+
user: Optional user entity restriction.
|
|
2924
|
+
recalculate_field: Optional field to recalculate.
|
|
2851
2925
|
|
|
2852
2926
|
Returns:
|
|
2853
2927
|
Updated work schedule data returned by the server.
|
|
2854
2928
|
|
|
2855
2929
|
Raises:
|
|
2856
|
-
TypeError: If arguments do not match the documented signature.
|
|
2857
2930
|
ShotgunError: If an RPC fault or terminal transport failure occurs,
|
|
2858
|
-
or if
|
|
2931
|
+
or if the date, project/user exclusivity, or server result is
|
|
2932
|
+
invalid.
|
|
2859
2933
|
ProtocolError: If a server request returns an HTTP error.
|
|
2860
2934
|
ValueError: If authenticated HTTP violates the configured origin
|
|
2861
2935
|
policy.
|
|
2862
2936
|
"""
|
|
2863
|
-
options = _methods.
|
|
2937
|
+
options = _methods.WorkScheduleUpdateOptions(
|
|
2938
|
+
date, working, description, project, user, recalculate_field
|
|
2939
|
+
)
|
|
2864
2940
|
params = _methods.build_work_schedule_update_params(options)
|
|
2865
2941
|
result = await self._call('work_schedule_update', params)
|
|
2866
2942
|
return _crud.require_dictionary_result(result, 'work_schedule_update')
|
|
2867
2943
|
|
|
2868
2944
|
|
|
2945
|
+
async def _capture_download_cleanup(
|
|
2946
|
+
workers: _AsyncDownloadCancellationScope,
|
|
2947
|
+
function: Callable[_FilesystemParams, _FilesystemResult],
|
|
2948
|
+
*args: _FilesystemParams.args,
|
|
2949
|
+
**kwargs: _FilesystemParams.kwargs,
|
|
2950
|
+
) -> BaseException | None:
|
|
2951
|
+
"""Run one joined cleanup operation and capture its failure."""
|
|
2952
|
+
capture = _AsyncDownloadCleanupCapture(workers)
|
|
2953
|
+
with capture:
|
|
2954
|
+
await workers.run(function, *args, **kwargs)
|
|
2955
|
+
return capture.exception
|
|
2956
|
+
|
|
2957
|
+
|
|
2869
2958
|
def _open_download_temporary(destination: Path) -> tuple[ExitStack, BinaryIO, Path]:
|
|
2870
2959
|
"""Open one adjacent download temporary file."""
|
|
2871
2960
|
stack = ExitStack()
|
|
@@ -2898,7 +2987,6 @@ async def _execute_upload_exchange(
|
|
|
2898
2987
|
headers=dict(exchange.headers),
|
|
2899
2988
|
body=exchange.body_factory(),
|
|
2900
2989
|
timeout=context.timeout,
|
|
2901
|
-
token=uuid.uuid4().hex,
|
|
2902
2990
|
)
|
|
2903
2991
|
|
|
2904
2992
|
async def consume(
|