sg-python-api 0.1.0__py3-none-any.whl → 0.1.2__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.
@@ -15,7 +15,6 @@ from typing import Protocol, TypeVar, cast
15
15
  from . import _protocol, _transport
16
16
  from ._async_transport import AsyncTransport
17
17
  from ._config import (
18
- CompatibilityWarningCode,
19
18
  EventRoute,
20
19
  ObserverCallback,
21
20
  Outcome,
@@ -200,14 +199,11 @@ class AsyncExecutionOptions:
200
199
  """Control async executor transport choreography.
201
200
 
202
201
  Attributes:
203
- compatibility_warning_codes: Compatibility warnings emitted before
204
- attempts.
205
202
  logical_method_name: Optional observer-facing operation name.
206
203
  redirect_factory: Optional request factory for redirect responses.
207
204
  streaming: Whether the response body is consumed as a stream.
208
205
  """
209
206
 
210
- compatibility_warning_codes: tuple[CompatibilityWarningCode, ...] = ()
211
207
  logical_method_name: str | None = None
212
208
  redirect_factory: Callable[['AsyncLiveResponse'], TransportRequest] | None = None
213
209
  streaming: bool = False
@@ -486,10 +482,7 @@ class AsyncDriver:
486
482
  consumption, or cleanup fails.
487
483
  """
488
484
  execution_options = options or AsyncExecutionOptions()
489
- for event in compatibility_warning_events(
490
- execution_options.compatibility_warning_codes,
491
- context.timeout,
492
- ):
485
+ for event in compatibility_warning_events(context.timeout):
493
486
  notify_observer(context.on_compatibility_warning, event)
494
487
  route = event_route(operation)
495
488
  self._transport.checkout()
@@ -590,7 +583,6 @@ class AsyncDriver:
590
583
  params: object,
591
584
  context: RequestContext,
592
585
  include_auth: bool = True,
593
- compatibility_warning_codes: tuple[CompatibilityWarningCode, ...] = (),
594
586
  *,
595
587
  _logical_method_name: str | None = None,
596
588
  ) -> object:
@@ -601,7 +593,6 @@ class AsyncDriver:
601
593
  params: Normalized method parameters.
602
594
  context: Immutable operation configuration.
603
595
  include_auth: Whether to include captured authentication.
604
- compatibility_warning_codes: Sanitized compatibility notices.
605
596
  _logical_method_name: Optional logical operation name when it
606
597
  differs from the wire method.
607
598
 
@@ -615,10 +606,7 @@ class AsyncDriver:
615
606
  consumption, or cleanup fails.
616
607
  """
617
608
  normalized_params = copy.deepcopy(params)
618
- options = AsyncExecutionOptions(
619
- compatibility_warning_codes=compatibility_warning_codes,
620
- logical_method_name=_logical_method_name,
621
- )
609
+ options = AsyncExecutionOptions(logical_method_name=_logical_method_name)
622
610
  return await self._execute_rpc(
623
611
  method_name,
624
612
  normalized_params,
shotgun_api3/_client.py CHANGED
@@ -26,7 +26,6 @@ from . import _config as config_module
26
26
  from . import _crud, _errors, _files, _methods, _protocol, _transport
27
27
  from ._capabilities import ClientCapabilities, ServerCapabilities
28
28
  from ._config import (
29
- CompatibilityWarningCode,
30
29
  Config,
31
30
  RequestContext,
32
31
  advance_transport_generation,
@@ -137,10 +136,8 @@ _CONSTRUCTOR_PARAMETERS = (
137
136
  )
138
137
  _UPLOAD_DOCSTRING = """Upload a file or thumbnail and return its Attachment ID.
139
138
 
140
- A successful HTTP response from the legacy form-upload route still
141
- raises `ShotgunError`; no Attachment ID is established for that
142
- route. The form request has already been sent when this error is
143
- raised.
139
+ A server requiring legacy form uploads raises `ShotgunError` before
140
+ any upload request is sent. Server capability reads may occur.
144
141
 
145
142
  Args:
146
143
  entity_type: Entity type receiving the upload.
@@ -157,8 +154,8 @@ Returns:
157
154
  Raises:
158
155
  TypeError: If arguments do not match the documented signature.
159
156
  ShotgunError: If the file or an upload response is invalid, an RPC
160
- fault or terminal transport failure occurs, or the legacy
161
- form-upload route succeeds.
157
+ fault or terminal transport failure occurs, or the server
158
+ requires legacy form uploads.
162
159
  ProtocolError: If an upload endpoint returns an HTTP error.
163
160
  ValueError: If authenticated HTTP violates the configured origin policy.
164
161
  """
@@ -348,7 +345,6 @@ class _InvocationOptions:
348
345
  """Collect non-payload controls for one client invocation."""
349
346
 
350
347
  include_auth: bool = True
351
- compatibility_warning_codes: tuple[CompatibilityWarningCode, ...] = ()
352
348
  logical_method_name: str | None = None
353
349
  auth_spec: _protocol.AuthSpec | None = None
354
350
 
@@ -570,7 +566,6 @@ class Shotgun:
570
566
  outbound,
571
567
  context,
572
568
  include_auth=options.include_auth,
573
- compatibility_warning_codes=options.compatibility_warning_codes,
574
569
  _logical_method_name=options.logical_method_name,
575
570
  )
576
571
  return _protocol.transform_inbound(result, context.convert_datetimes_to_utc)
@@ -884,28 +879,6 @@ class Shotgun:
884
879
  lambda _text, _headers: None,
885
880
  )
886
881
 
887
- @staticmethod
888
- def _upload_form(
889
- driver: Driver,
890
- context: RequestContext,
891
- prepared: _files.PreparedUpload,
892
- ) -> Never:
893
- """Send the observed form request without guessing a success result."""
894
- if context.canonical_origin is None:
895
- raise ValueError('server parameters must be configured before upload I/O')
896
- exchange = _files.build_form_upload_exchange(
897
- context.canonical_origin,
898
- context.user_agent,
899
- prepared,
900
- context.auth,
901
- )
902
-
903
- def reject_success(_text: str, _headers: Mapping[str, str]) -> Never:
904
- raise ShotgunError('form upload success response is not established')
905
-
906
- _execute_upload_exchange(driver, context, exchange, reject_success)
907
- raise AssertionError('form upload response parser returned')
908
-
909
882
  @staticmethod
910
883
  def _upload_link(
911
884
  driver: Driver,
@@ -1002,14 +975,14 @@ class Shotgun:
1002
975
  """Execute one prepared upload through a captured driver lease."""
1003
976
  arguments = prepared.options.arguments
1004
977
  capabilities = self._ensure_readiness(context, driver)
1005
- upload_path = _files.choose_upload_path(
1006
- capabilities.server_info,
1007
- arguments.entity_type,
1008
- arguments.field_name,
1009
- prepared.file_size,
978
+ upload_path = _files.require_supported_upload_path(
979
+ _files.choose_upload_path(
980
+ capabilities.server_info,
981
+ arguments.entity_type,
982
+ arguments.field_name,
983
+ prepared.file_size,
984
+ )
1010
985
  )
1011
- if upload_path == 'form':
1012
- return self._upload_form(driver, context, prepared)
1013
986
  multipart = upload_path == 'multipart'
1014
987
  upload_info = self._upload_allocation(driver, context, prepared, multipart)
1015
988
  if multipart:
@@ -1033,11 +1006,42 @@ class Shotgun:
1033
1006
  prepared: _files.PreparedUpload,
1034
1007
  ) -> int:
1035
1008
  """Execute the exact direct-storage thumbnail workflow."""
1036
- self._ensure_readiness(context, driver)
1009
+ capabilities = self._ensure_readiness(context, driver)
1010
+ arguments = prepared.options.arguments
1011
+ _files.require_supported_upload_path(
1012
+ _files.choose_upload_path(
1013
+ capabilities.server_info,
1014
+ arguments.entity_type,
1015
+ arguments.field_name,
1016
+ prepared.file_size,
1017
+ )
1018
+ )
1037
1019
  upload_info = self._upload_allocation(driver, context, prepared, multipart=False)
1038
1020
  self._upload_direct(driver, context, prepared, upload_info)
1039
1021
  return self._upload_link(driver, context, prepared, upload_info)
1040
1022
 
1023
+ def _upload_thumbnails_after_write(
1024
+ self,
1025
+ entity_type: str,
1026
+ entity_id: int,
1027
+ thumbnails: dict[str, object],
1028
+ result: dict[str, object],
1029
+ ) -> dict[str, object]:
1030
+ """Upload separated thumbnail paths and merge their current field values."""
1031
+ if not thumbnails:
1032
+ return result
1033
+ for field_name, path in thumbnails.items():
1034
+ upload = (
1035
+ self.upload_filmstrip_thumbnail
1036
+ if field_name == 'filmstrip_image'
1037
+ else self.upload_thumbnail
1038
+ )
1039
+ upload(entity_type, entity_id, cast('str', path))
1040
+ current = self.find_one(entity_type, [['id', 'is', entity_id]], list(thumbnails))
1041
+ if current is not None:
1042
+ result.update({field_name: current.get(field_name) for field_name in thumbnails})
1043
+ return result
1044
+
1041
1045
  @overload
1042
1046
  def activity_stream_read(self, entity_type: Never) -> dict[str, object]: ...
1043
1047
 
@@ -1298,6 +1302,10 @@ class Shotgun:
1298
1302
  ) -> dict[str, object]:
1299
1303
  """Create an entity with the supplied field values.
1300
1304
 
1305
+ Non-empty `image` and `filmstrip_image` values are file paths. They are
1306
+ uploaded as thumbnails after the entity is created, and the result
1307
+ includes their current field values.
1308
+
1301
1309
  Args:
1302
1310
  entity_type: Entity type to create.
1303
1311
  data: Field values for the new entity.
@@ -1308,14 +1316,20 @@ class Shotgun:
1308
1316
 
1309
1317
  Raises:
1310
1318
  ShotgunError: If an RPC fault or terminal transport failure occurs,
1311
- or if `data`, its field names, `return_fields`, or the server
1312
- result is invalid.
1319
+ if `data`, its field names, `return_fields`, or the server
1320
+ result is invalid, or if a thumbnail path is not a nonempty
1321
+ file. A thumbnail failure occurs after the entity is created.
1313
1322
  ProtocolError: If a server request returns an HTTP error.
1314
1323
  ValueError: If authenticated HTTP violates the configured origin
1315
1324
  policy.
1316
1325
  """
1317
- params = _crud.build_create_params(entity_type, data, return_fields)
1318
- return _crud.require_dictionary_result(self._call('create', params), 'create')
1326
+ field_data, thumbnails = _files.split_thumbnail_uploads(data, keep_image_none=False)
1327
+ params = _crud.build_create_params(entity_type, field_data, return_fields)
1328
+ result = _crud.require_dictionary_result(self._call('create', params), 'create')
1329
+ if not thumbnails:
1330
+ return result
1331
+ entity_id = _crud.require_entity_id(result.get('id'), 'create did not return an entity id')
1332
+ return self._upload_thumbnails_after_write(entity_type, entity_id, thumbnails, result)
1319
1333
 
1320
1334
  def delete(self, entity_type: str, entity_id: int) -> bool:
1321
1335
  """Retire an entity.
@@ -1422,6 +1436,15 @@ class Shotgun:
1422
1436
  except (TransportError, _errors.ProtocolError, OSError):
1423
1437
  raise _errors.ShotgunFileDownloadError('attachment download failed') from None
1424
1438
 
1439
+ def entity_types(self) -> Never:
1440
+ """Reject the deprecated alias of `schema_entity_read()` without I/O.
1441
+
1442
+ Raises:
1443
+ ShotgunError: Always, naming `schema_entity_read()` as the
1444
+ replacement.
1445
+ """
1446
+ raise _methods.entity_types_alias_error()
1447
+
1425
1448
  @overload
1426
1449
  def export_page(
1427
1450
  self,
@@ -1442,6 +1465,9 @@ class Shotgun:
1442
1465
  def export_page(self, *args: object, **kwargs: object) -> str:
1443
1466
  """Export a page using the documented arguments.
1444
1467
 
1468
+ The Page or its layout must be marked API Exportable in the web UI, or
1469
+ the server rejects the export.
1470
+
1445
1471
  Args:
1446
1472
  *args: Positional values from the documented `export_page`
1447
1473
  signature.
@@ -1789,7 +1815,11 @@ class Shotgun:
1789
1815
  seed_entity_field,
1790
1816
  )
1791
1817
  return _methods.require_dictionary_list_result(
1792
- self._call('nav_search_entity', params),
1818
+ self._call(
1819
+ 'nav_search',
1820
+ params,
1821
+ invocation_options=_InvocationOptions(logical_method_name='nav_search_entity'),
1822
+ ),
1793
1823
  'nav_search_entity',
1794
1824
  )
1795
1825
 
@@ -1822,7 +1852,11 @@ class Shotgun:
1822
1852
  seed_entity_field,
1823
1853
  )
1824
1854
  return _methods.require_dictionary_list_result(
1825
- self._call('nav_search_string', params),
1855
+ self._call(
1856
+ 'nav_search',
1857
+ params,
1858
+ invocation_options=_InvocationOptions(logical_method_name='nav_search_string'),
1859
+ ),
1826
1860
  'nav_search_string',
1827
1861
  )
1828
1862
 
@@ -1849,7 +1883,11 @@ class Shotgun:
1849
1883
  """
1850
1884
  params = _methods.build_note_thread_read_params(note_id, entity_fields)
1851
1885
  return _methods.require_dictionary_list_result(
1852
- self._call('note_thread_read', params),
1886
+ self._call(
1887
+ 'note_thread_contents',
1888
+ params,
1889
+ invocation_options=_InvocationOptions(logical_method_name='note_thread_read'),
1890
+ ),
1853
1891
  'note_thread_read',
1854
1892
  )
1855
1893
 
@@ -1900,6 +1938,18 @@ class Shotgun:
1900
1938
  'revive',
1901
1939
  )
1902
1940
 
1941
+ def schema(self, entity_type: str) -> Never:
1942
+ """Reject the deprecated alias of `schema_field_read()` without I/O.
1943
+
1944
+ Args:
1945
+ entity_type: Entity type named in the replacement suggestion.
1946
+
1947
+ Raises:
1948
+ ShotgunError: Always, naming the equivalent `schema_field_read()`
1949
+ call.
1950
+ """
1951
+ raise _methods.schema_alias_error(entity_type)
1952
+
1903
1953
  def schema_entity_read(
1904
1954
  self,
1905
1955
  project_entity: dict[str, Any] | None = None,
@@ -1923,33 +1973,10 @@ class Shotgun:
1923
1973
  self._call(
1924
1974
  'schema_entity_read',
1925
1975
  _methods.build_schema_entity_read_params(project_entity),
1926
- _InvocationOptions(
1927
- compatibility_warning_codes=_methods.consume_compatibility_warnings(
1928
- _methods.SCHEMA_ENTITY_READ_WARNINGS,
1929
- self,
1930
- ),
1931
- ),
1932
1976
  ),
1933
1977
  'schema_entity_read',
1934
1978
  )
1935
1979
 
1936
- def entity_types(self) -> dict[str, object]:
1937
- """Return entity schema through the deprecated alias.
1938
-
1939
- Returns:
1940
- Entity-type schema data returned by the server.
1941
-
1942
- Raises:
1943
- ShotgunError: If an RPC fault or terminal transport failure occurs,
1944
- or if the server result is not a dictionary.
1945
- ProtocolError: If a server request returns an HTTP error.
1946
- ValueError: If authenticated HTTP violates the configured origin
1947
- policy.
1948
- """
1949
- _methods.warn_entity_types_alias()
1950
- with _methods.entity_types_alias(self):
1951
- return self.schema_entity_read()
1952
-
1953
1980
  def schema_field_create(
1954
1981
  self,
1955
1982
  entity_type: str,
@@ -2042,36 +2069,10 @@ class Shotgun:
2042
2069
  field_name,
2043
2070
  project_entity,
2044
2071
  ),
2045
- _InvocationOptions(
2046
- compatibility_warning_codes=_methods.consume_compatibility_warnings(
2047
- _methods.SCHEMA_FIELD_READ_WARNINGS,
2048
- self,
2049
- ),
2050
- ),
2051
2072
  ),
2052
2073
  'schema_field_read',
2053
2074
  )
2054
2075
 
2055
- def schema(self, entity_type: str) -> dict[str, object]:
2056
- """Return field schema through the deprecated alias.
2057
-
2058
- Args:
2059
- entity_type: Entity type whose fields to return.
2060
-
2061
- Returns:
2062
- Field schema data returned by the server.
2063
-
2064
- Raises:
2065
- ShotgunError: If an RPC fault or terminal transport failure occurs,
2066
- or if the server result is invalid.
2067
- ProtocolError: If a server request returns an HTTP error.
2068
- ValueError: If authenticated HTTP violates the configured origin
2069
- policy.
2070
- """
2071
- _methods.warn_schema_alias()
2072
- with _methods.schema_alias(self):
2073
- return self.schema_field_read(entity_type)
2074
-
2075
2076
  def schema_field_update(
2076
2077
  self,
2077
2078
  entity_type: str,
@@ -2184,7 +2185,8 @@ class Shotgun:
2184
2185
 
2185
2186
  Supply exactly one of `thumbnail_path` and `source_entity`. A local
2186
2187
  thumbnail is uploaded to the first target before it is shared with the
2187
- remaining targets.
2188
+ remaining targets. A local thumbnail is rejected before any upload
2189
+ request if the server requires legacy form uploads.
2188
2190
 
2189
2191
  Args:
2190
2192
  entities: Nonempty list of target entities with `type` and `id`.
@@ -2201,7 +2203,8 @@ class Shotgun:
2201
2203
  TypeError: If forwarded arguments do not match the upload signature
2202
2204
  or include `field_name`.
2203
2205
  ShotgunError: If an RPC fault or terminal transport failure occurs,
2204
- or if the source selection, entities, or transport fails.
2206
+ or if the source selection, entities, or transport fails, or a
2207
+ local upload requires the legacy form route.
2205
2208
  ShotgunThumbnailNotReady: If an uploaded source stays transient.
2206
2209
  ProtocolError: If an upload endpoint returns an HTTP error.
2207
2210
  ValueError: If authenticated HTTP violates the configured origin
@@ -2384,6 +2387,12 @@ class Shotgun:
2384
2387
  ) -> dict[str, object]:
2385
2388
  """Update fields on an existing entity.
2386
2389
 
2390
+ Non-empty `image` and `filmstrip_image` values are file paths. They are
2391
+ uploaded as thumbnails after the other fields are updated, and the
2392
+ result includes their current field values. An `image` of `None` clears
2393
+ both thumbnails. When no other fields remain, no update request is sent
2394
+ and the result contains only `id` and `type`.
2395
+
2387
2396
  Args:
2388
2397
  entity_type: Entity type to update.
2389
2398
  entity_id: Numeric entity identifier.
@@ -2395,19 +2404,27 @@ class Shotgun:
2395
2404
 
2396
2405
  Raises:
2397
2406
  ShotgunError: If an RPC fault or terminal transport failure occurs,
2398
- or if `data`, its field names, `multi_entity_update_modes`, or
2399
- the server result is invalid.
2407
+ if `data`, its field names, `multi_entity_update_modes`, or the
2408
+ server result is invalid, or if a thumbnail path is not a
2409
+ nonempty file. A thumbnail failure occurs after the update.
2400
2410
  ProtocolError: If a server request returns an HTTP error.
2401
2411
  ValueError: If authenticated HTTP violates the configured origin
2402
2412
  policy.
2403
2413
  """
2414
+ field_data, thumbnails = _files.split_thumbnail_uploads(data, keep_image_none=True)
2404
2415
  params = _crud.build_update_params(
2405
2416
  entity_type,
2406
2417
  entity_id,
2407
- data,
2418
+ field_data,
2408
2419
  multi_entity_update_modes,
2409
2420
  )
2410
- return _crud.require_dictionary_result(self._call('update', params), 'update')
2421
+ result: dict[str, object] = {'id': entity_id, 'type': entity_type}
2422
+ if params['fields']:
2423
+ result = _crud.require_dictionary_result(
2424
+ self._call('update', params),
2425
+ 'update',
2426
+ )
2427
+ return self._upload_thumbnails_after_write(entity_type, entity_id, thumbnails, result)
2411
2428
 
2412
2429
  def update_project_last_accessed(
2413
2430
  self,
@@ -2453,9 +2470,8 @@ class Shotgun:
2453
2470
  def upload(self, *args: object, **kwargs: object) -> int:
2454
2471
  """Upload a file or thumbnail and return its Attachment ID.
2455
2472
 
2456
- A successful HTTP response from the legacy form-upload route still
2457
- raises `ShotgunError`; no Attachment ID is established for that route.
2458
- The form request has already been sent when this error is raised.
2473
+ A server requiring legacy form uploads raises `ShotgunError` before any
2474
+ upload request is sent. Server capability reads may occur.
2459
2475
 
2460
2476
  Args:
2461
2477
  *args: Positional arguments from the documented upload signature;
@@ -2468,8 +2484,8 @@ class Shotgun:
2468
2484
  Raises:
2469
2485
  TypeError: If arguments do not match the documented signature.
2470
2486
  ShotgunError: If the file or an upload response is invalid, an RPC
2471
- fault or terminal transport failure occurs, or the legacy
2472
- form-upload route succeeds.
2487
+ fault or terminal transport failure occurs, or the server
2488
+ requires legacy form uploads.
2473
2489
  ProtocolError: If an upload endpoint returns an HTTP error.
2474
2490
  ValueError: If authenticated HTTP violates the configured origin
2475
2491
  policy.
@@ -2501,6 +2517,9 @@ class Shotgun:
2501
2517
  ) -> int:
2502
2518
  """Upload a filmstrip thumbnail through `upload`.
2503
2519
 
2520
+ Servers requiring legacy form uploads are rejected before any upload
2521
+ request is sent.
2522
+
2504
2523
  Args:
2505
2524
  entity_type: Entity type receiving the thumbnail.
2506
2525
  entity_id: Numeric identifier of the receiving entity.
@@ -2513,7 +2532,8 @@ class Shotgun:
2513
2532
  Raises:
2514
2533
  TypeError: If forwarded arguments do not match `upload`.
2515
2534
  ShotgunError: If an RPC fault or terminal transport failure occurs,
2516
- or if the file, upload response, or transport fails.
2535
+ or if the file, upload response, or transport fails, or the
2536
+ server requires legacy form uploads.
2517
2537
  ProtocolError: If an upload endpoint returns an HTTP error.
2518
2538
  ValueError: If authenticated HTTP violates the configured origin
2519
2539
  policy.
@@ -2535,6 +2555,9 @@ class Shotgun:
2535
2555
  ) -> int:
2536
2556
  """Upload a static thumbnail through `upload`.
2537
2557
 
2558
+ Servers requiring legacy form uploads are rejected before any upload
2559
+ request is sent.
2560
+
2538
2561
  Args:
2539
2562
  entity_type: Entity type receiving the thumbnail.
2540
2563
  entity_id: Numeric identifier of the receiving entity.
@@ -2547,7 +2570,8 @@ class Shotgun:
2547
2570
  Raises:
2548
2571
  TypeError: If forwarded arguments do not match `upload`.
2549
2572
  ShotgunError: If an RPC fault or terminal transport failure occurs,
2550
- or if the file, upload response, or transport fails.
2573
+ or if the file, upload response, or transport fails, or the
2574
+ server requires legacy form uploads.
2551
2575
  ProtocolError: If an upload endpoint returns an HTTP error.
2552
2576
  ValueError: If authenticated HTTP violates the configured origin
2553
2577
  policy.
@@ -2563,44 +2587,53 @@ class Shotgun:
2563
2587
  def user_subscriptions_create(self, users: list[dict[str, Any]]) -> bool:
2564
2588
  """Assign subscriptions to users.
2565
2589
 
2590
+ The server accepts this call from scripts and from human users with
2591
+ permission to manage subscriptions, such as members of the Admin
2592
+ permission group. It refuses other human users.
2593
+
2566
2594
  Args:
2567
- users: Subscription dictionaries that each contain a `user_id` key.
2595
+ users: Subscription dictionaries with a string or integer
2596
+ `humanUserId`; integers become decimal string keys and booleans
2597
+ are not accepted. Each user may appear only once.
2598
+ `subscription` is `None`, a string, or a list of strings.
2599
+ Lists are copied in order. Other entry keys are ignored.
2568
2600
 
2569
2601
  Returns:
2570
- Whether the server created the subscriptions.
2602
+ `True` when every per-user report is `None`, meaning the server
2603
+ reported no problem; `False` when any user has a non-`None` report.
2571
2604
 
2572
2605
  Raises:
2573
2606
  ShotgunError: If an RPC fault or terminal transport failure occurs,
2574
- or if `users` is not a list of dictionaries that each contain a
2575
- `user_id` key, or the server result is not a boolean.
2607
+ or if `users` contains invalid or duplicate entries, or the
2608
+ server result is not a mapping with string keys.
2576
2609
  ProtocolError: If a server request returns an HTTP error.
2577
2610
  ValueError: If authenticated HTTP violates the configured origin
2578
2611
  policy.
2579
2612
  """
2580
- return _crud.require_boolean_result(
2613
+ return _methods.require_user_subscriptions_create_result(
2581
2614
  self._call(
2582
2615
  'user_subscriptions_create',
2583
2616
  _methods.build_user_subscriptions_create_params(users),
2584
2617
  ),
2585
- 'user_subscriptions_create',
2586
2618
  )
2587
2619
 
2588
2620
  def user_subscriptions_read(self) -> list[dict[str, object]]:
2589
- """List the current user's subscriptions.
2621
+ """List user subscriptions in server order.
2590
2622
 
2591
2623
  Returns:
2592
- User subscription dictionaries returned by the server.
2624
+ Subscription entries in server order, each with an unchanged string
2625
+ `humanUserId` and a `subscription` of `None`, a string, or a copied
2626
+ list of strings.
2593
2627
 
2594
2628
  Raises:
2595
2629
  ShotgunError: If an RPC fault or terminal transport failure occurs,
2596
- or if the server result is not a list of dictionaries.
2630
+ or if the server result is invalid.
2597
2631
  ProtocolError: If a server request returns an HTTP error.
2598
2632
  ValueError: If authenticated HTTP violates the configured origin
2599
2633
  policy.
2600
2634
  """
2601
- return _methods.require_dictionary_list_result(
2635
+ return _methods.require_user_subscriptions_result(
2602
2636
  self._call('user_subscriptions_read', None),
2603
- 'user_subscriptions_read',
2604
2637
  )
2605
2638
 
2606
2639
  def work_schedule_read(
shotgun_api3/_config.py CHANGED
@@ -21,11 +21,7 @@ from ._sentinel import Unset as _Unset
21
21
  from ._transport import TimeoutConfig
22
22
  from ._version import PACKAGE_VERSION
23
23
 
24
- CompatibilityWarningCode: TypeAlias = Literal[
25
- 'deprecated-entity-types-alias',
26
- 'deprecated-schema-alias',
27
- 'legacy-infinite-timeout',
28
- ]
24
+ CompatibilityWarningCode: TypeAlias = Literal['legacy-infinite-timeout']
29
25
  EventRoute: TypeAlias = Literal[
30
26
  'allocation',
31
27
  'download',
@@ -85,8 +81,6 @@ _ORIGIN_FIELDS = frozenset({'api_path', 'scheme', 'server'})
85
81
  _ORIGIN_STRING_FIELDS = frozenset({'scheme', 'server'})
86
82
  _UNSET: Final = _Unset()
87
83
  _WARNING_MESSAGES: Mapping[CompatibilityWarningCode, str] = {
88
- 'deprecated-entity-types-alias': 'The entity_types alias is deprecated.',
89
- 'deprecated-schema-alias': 'The schema alias is deprecated.',
90
84
  'legacy-infinite-timeout': 'Explicit None timeout enables legacy infinite waits.',
91
85
  }
92
86
  _PUBLIC_FIELDS = frozenset(
@@ -1021,19 +1015,17 @@ def auth_spec(config: Config) -> _protocol.AuthSpec:
1021
1015
 
1022
1016
 
1023
1017
  def compatibility_warning_events(
1024
- warning_codes: tuple[CompatibilityWarningCode, ...],
1025
1018
  timeout: TimeoutConfig,
1026
1019
  ) -> tuple[CompatibilityWarningEvent, ...]:
1027
1020
  """Build fixed-message compatibility warnings for one operation.
1028
1021
 
1029
1022
  Args:
1030
- warning_codes: Sanitized compatibility notices from the client.
1031
1023
  timeout: Captured operation timeout semantics.
1032
1024
 
1033
1025
  Returns:
1034
1026
  Immutable compatibility-warning events in publication order.
1035
1027
  """
1036
- codes = list(warning_codes)
1028
+ codes: list[CompatibilityWarningCode] = []
1037
1029
  if timeout.connect is None and timeout.read is None:
1038
1030
  codes.append('legacy-infinite-timeout')
1039
1031
  return tuple(