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.
Files changed (56) hide show
  1. {sg_python_api-0.1.1.dist-info → sg_python_api-0.2.0.dist-info}/METADATA +21 -24
  2. sg_python_api-0.2.0.dist-info/RECORD +58 -0
  3. shotgun_api3/__init__.py +4 -0
  4. shotgun_api3/_async_client.py +404 -316
  5. shotgun_api3/_async_driver.py +25 -55
  6. shotgun_api3/_async_httpx2.py +92 -59
  7. shotgun_api3/_async_transport.py +38 -3
  8. shotgun_api3/_capabilities.py +4 -3
  9. shotgun_api3/_client.py +354 -462
  10. shotgun_api3/_config.py +245 -120
  11. shotgun_api3/_connections.py +144 -0
  12. shotgun_api3/_crud.py +25 -250
  13. shotgun_api3/_driver.py +29 -54
  14. shotgun_api3/_execution.py +1 -3
  15. shotgun_api3/_files.py +215 -123
  16. shotgun_api3/_methods.py +153 -412
  17. shotgun_api3/_protocol.py +27 -146
  18. shotgun_api3/_transport.py +82 -407
  19. shotgun_api3/_version.py +7 -2
  20. shotgun_api3/lib/mockgun/__init__.py +20 -7
  21. shotgun_api3/lib/mockgun/_activity.py +510 -0
  22. shotgun_api3/lib/mockgun/_async_client.py +196 -0
  23. shotgun_api3/lib/mockgun/_dates.py +164 -0
  24. shotgun_api3/lib/mockgun/_errors.py +18 -0
  25. shotgun_api3/lib/mockgun/_events.py +1031 -0
  26. shotgun_api3/lib/mockgun/_follows.py +217 -0
  27. shotgun_api3/lib/mockgun/_handlers.py +558 -0
  28. shotgun_api3/lib/mockgun/_links.py +440 -0
  29. shotgun_api3/lib/mockgun/_new_client.py +92 -0
  30. shotgun_api3/lib/mockgun/_presets.py +492 -0
  31. shotgun_api3/lib/mockgun/_query.py +2163 -0
  32. shotgun_api3/lib/mockgun/_retirement.py +368 -0
  33. shotgun_api3/lib/mockgun/_schema.py +172 -0
  34. shotgun_api3/lib/mockgun/_schema_writes.py +425 -0
  35. shotgun_api3/lib/mockgun/_search.py +378 -0
  36. shotgun_api3/lib/mockgun/_server.py +169 -0
  37. shotgun_api3/lib/mockgun/_sessions.py +206 -0
  38. shotgun_api3/lib/mockgun/_site.py +150 -0
  39. shotgun_api3/lib/mockgun/_site_data.py +584 -0
  40. shotgun_api3/lib/mockgun/_storage.py +208 -0
  41. shotgun_api3/lib/mockgun/_store.py +1446 -0
  42. shotgun_api3/lib/mockgun/_summaries.py +964 -0
  43. shotgun_api3/lib/mockgun/_sync_client.py +190 -0
  44. shotgun_api3/lib/mockgun/_threads.py +226 -0
  45. shotgun_api3/lib/mockgun/_thumbnails.py +146 -0
  46. shotgun_api3/lib/mockgun/_transport.py +145 -0
  47. shotgun_api3/lib/mockgun/_uploads.py +417 -0
  48. shotgun_api3/lib/mockgun/_wire.py +120 -0
  49. shotgun_api3/lib/mockgun/mockgun.py +14 -0
  50. shotgun_api3/lib/sgtimezone.py +24 -1
  51. sg_python_api-0.1.1.dist-info/RECORD +0 -31
  52. shotgun_api3/_signatures.py +0 -53
  53. shotgun_api3/lib/mockgun/_async.py +0 -315
  54. shotgun_api3/lib/mockgun/_mockgun.py +0 -1666
  55. {sg_python_api-0.1.1.dist-info → sg_python_api-0.2.0.dist-info}/WHEEL +0 -0
  56. {sg_python_api-0.1.1.dist-info → sg_python_api-0.2.0.dist-info}/licenses/LICENSE +0 -0
shotgun_api3/_driver.py CHANGED
@@ -33,7 +33,6 @@ from ._execution import (
33
33
  build_rpc_request,
34
34
  )
35
35
  from ._transport import (
36
- ReadTimeoutError,
37
36
  SendError,
38
37
  TransportError,
39
38
  TransportRequest,
@@ -46,23 +45,15 @@ _HTTP_REDIRECT = 302
46
45
 
47
46
  @dataclasses.dataclass(frozen=True)
48
47
  class AttemptControl:
49
- """Consume attempt bytes under deadline and progress control."""
48
+ """Consume attempt bytes with progress reporting."""
50
49
 
51
50
  _attempt: int
52
- _clock: Callable[[], float]
53
- _deadline: float | None
54
51
  _observer: ObserverCallback | None
55
52
  _operation: _protocol.OperationCategory
56
53
  _route: EventRoute
57
54
 
58
- def _check_deadline(self) -> None:
59
- """Raise when cumulative monotonic attempt time exceeds its limit."""
60
- if self._deadline is not None and self._clock() > self._deadline:
61
- raise ReadTimeoutError('total attempt timeout exceeded')
62
-
63
- def _consume_buffered(self, body: bytes, total_bytes: int | None = None) -> bytes:
55
+ def consume_buffered(self, body: bytes, total_bytes: int | None = None) -> bytes:
64
56
  """Observe a received buffered body without rebuilding it."""
65
- self._check_deadline()
66
57
  if body:
67
58
  notify_observer(
68
59
  self._observer,
@@ -76,22 +67,13 @@ class AttemptControl:
76
67
  )
77
68
  return body
78
69
 
79
- def _remaining_timeout(self) -> float | None:
80
- """Return the remaining cumulative attempt timeout."""
81
- if self._deadline is None:
82
- return None
83
- remaining = self._deadline - self._clock()
84
- if remaining < 0:
85
- raise ReadTimeoutError('total attempt timeout exceeded')
86
- return remaining
87
-
88
70
  def consume_into(
89
71
  self,
90
72
  chunks: Iterable[bytes],
91
73
  sink: Callable[[bytes], object],
92
74
  total_bytes: int | None = None,
93
75
  ) -> None:
94
- """Write chunks while enforcing deadline and progress controls.
76
+ """Write chunks while reporting progress.
95
77
 
96
78
  Args:
97
79
  chunks: Fresh streaming body chunks for this attempt.
@@ -99,14 +81,12 @@ class AttemptControl:
99
81
  total_bytes: Known complete body size, when available.
100
82
 
101
83
  Raises:
102
- ReadTimeoutError: If cumulative attempt time exceeds the total
103
- limit.
84
+ Exception: If reading a chunk or invoking the sink fails.
104
85
  """
105
86
  transferred = 0
106
87
  for chunk in chunks:
107
88
  sink(chunk)
108
89
  transferred += len(chunk)
109
- self._check_deadline()
110
90
  if chunk:
111
91
  notify_observer(
112
92
  self._observer,
@@ -120,7 +100,7 @@ class AttemptControl:
120
100
  )
121
101
 
122
102
  def consume(self, chunks: Iterable[bytes], total_bytes: int | None = None) -> bytes:
123
- """Collect chunks while enforcing the total attempt deadline.
103
+ """Collect chunks while reporting progress.
124
104
 
125
105
  Args:
126
106
  chunks: Fresh buffered or streaming body chunks for this attempt.
@@ -130,8 +110,7 @@ class AttemptControl:
130
110
  Concatenated response bytes.
131
111
 
132
112
  Raises:
133
- ReadTimeoutError: If cumulative attempt time exceeds the total
134
- limit.
113
+ Exception: If reading a chunk fails.
135
114
  """
136
115
  body = BytesIO()
137
116
  self.consume_into(chunks, body.write, total_bytes=total_bytes)
@@ -265,7 +244,13 @@ class _ObservedRequestBody:
265
244
  raise
266
245
 
267
246
  def close(self) -> None:
268
- """Close the source once even if its cleanup raises."""
247
+ """Close the source iterator at most once.
248
+
249
+ Later calls do not repeat cleanup even when the first close fails.
250
+
251
+ Raises:
252
+ Exception: If the source iterator's cleanup fails.
253
+ """
269
254
  if self._closed:
270
255
  return
271
256
  self._closed = True
@@ -351,11 +336,8 @@ class Driver:
351
336
  ) -> AttemptState[AttemptControl]:
352
337
  """Build immutable timing and observer state for one fresh attempt."""
353
338
  started = self._clock()
354
- deadline = None if context.timeout.total is None else started + context.timeout.total
355
339
  control = AttemptControl(
356
340
  _attempt=attempt,
357
- _clock=self._clock,
358
- _deadline=deadline,
359
341
  _observer=context.on_progress,
360
342
  _operation=operation.category,
361
343
  _route=route,
@@ -393,7 +375,6 @@ class Driver:
393
375
  response_status = response.status
394
376
  response_headers = response.headers
395
377
  correlation_id = response_correlation_id(response_headers)
396
- state.control._check_deadline() # noqa: SLF001 # pyright: ignore[reportPrivateUsage]
397
378
  failure = _protocol.classify_http_failure(
398
379
  response_status,
399
380
  response_headers,
@@ -475,7 +456,7 @@ class Driver:
475
456
  buffered.headers,
476
457
  buffered.content,
477
458
  )
478
- body = control._consume_buffered( # noqa: SLF001 # pyright: ignore[reportPrivateUsage]
459
+ body = control.consume_buffered(
479
460
  buffered.content,
480
461
  total_bytes=len(buffered.content),
481
462
  )
@@ -569,7 +550,6 @@ class Driver:
569
550
  request: TransportRequest,
570
551
  state: AttemptState[AttemptControl],
571
552
  *,
572
- redirected: bool,
573
553
  streaming: bool,
574
554
  ) -> tuple[LiveResponse, '_TerminalResponseBoundary'] | AttemptRetry:
575
555
  """Send once and keep a received response through body cleanup."""
@@ -579,8 +559,6 @@ class Driver:
579
559
  try:
580
560
  try:
581
561
  with exception_capture:
582
- if redirected:
583
- request = _request_with_remaining_timeout(request, state.control)
584
562
  if streaming:
585
563
  response = self._transport.send_streaming(request)
586
564
  else:
@@ -635,6 +613,10 @@ class Driver:
635
613
  Raises:
636
614
  TypeError: If the RPC transport response lacks the buffered
637
615
  response contract (`reason` and `content`).
616
+ TransportError: If a terminal or exhausted transport failure
617
+ occurs.
618
+ Exception: If request construction, status validation, response
619
+ consumption, or cleanup fails.
638
620
  """
639
621
  normalized_params = copy.deepcopy(params)
640
622
  options = ExecutionOptions(logical_method_name=_logical_method_name)
@@ -667,6 +649,11 @@ class Driver:
667
649
 
668
650
  Yields:
669
651
  Decoded RPC results in page order.
652
+
653
+ Raises:
654
+ TransportError: If a page exhausts its transport attempts.
655
+ Exception: If building, validating, decoding, or cleaning up a page
656
+ operation fails.
670
657
  """
671
658
  if context.page_prefetch <= 0:
672
659
  page = first_page
@@ -718,9 +705,8 @@ class Driver:
718
705
  context: Immutable operation configuration.
719
706
  request_factory: Builder called once for every fresh attempt.
720
707
  consumer: Buffered or streaming response consumer using the
721
- supplied deadline/progress control.
722
- options: Explicit redirect, streaming, and sanitized
723
- compatibility-warning controls.
708
+ supplied progress control.
709
+ options: Redirect, streaming, and observer controls.
724
710
 
725
711
  Returns:
726
712
  The successful consumer result.
@@ -728,7 +714,8 @@ class Driver:
728
714
  Raises:
729
715
  TransportError: If a terminal or exhausted transport failure
730
716
  occurs.
731
- Exception: If status validation or response consumption fails.
717
+ Exception: If request or redirect construction, status validation,
718
+ response consumption, or cleanup fails.
732
719
  """
733
720
  execution_options = options or ExecutionOptions()
734
721
  self._emit_warnings(context)
@@ -742,7 +729,6 @@ class Driver:
742
729
  sent = self._send_request(
743
730
  request,
744
731
  state,
745
- redirected=redirected,
746
732
  streaming=execution_options.streaming,
747
733
  )
748
734
  if isinstance(sent, AttemptRetry):
@@ -786,6 +772,7 @@ class Driver:
786
772
  Raises:
787
773
  ValueError: If `attempts` is not positive or `interval` is
788
774
  negative.
775
+ Exception: If the predicate or configured sleep fails.
789
776
  """
790
777
  if attempts <= 0:
791
778
  raise ValueError('poll attempts must be positive')
@@ -817,13 +804,12 @@ def _observe_request_body(
817
804
  total_bytes: int | None,
818
805
  state: AttemptState[AttemptControl],
819
806
  ) -> Generator[bytes, None, None]:
820
- """Yield request chunks with attempt deadline and progress reporting."""
807
+ """Yield request chunks with attempt progress reporting."""
821
808
  transferred = 0
822
809
  for chunk in chunks:
823
810
  yield chunk
824
811
  transferred += len(chunk)
825
812
  if chunk:
826
- state.control._check_deadline() # noqa: SLF001 # pyright: ignore[reportPrivateUsage]
827
813
  notify_observer(
828
814
  state.context.on_progress,
829
815
  ProgressEvent(
@@ -852,14 +838,3 @@ def _request_with_progress(
852
838
  state,
853
839
  ),
854
840
  )
855
-
856
-
857
- def _request_with_remaining_timeout(
858
- request: TransportRequest,
859
- control: AttemptControl,
860
- ) -> TransportRequest:
861
- """Restrict a later hop to its remaining cumulative attempt timeout."""
862
- return _transport.narrow_total_timeout(
863
- request,
864
- control._remaining_timeout(), # noqa: SLF001 # pyright: ignore[reportPrivateUsage]
865
- )
@@ -1,7 +1,6 @@
1
1
  """Share pure execution, attempt, and lease logic between the sync and async twins."""
2
2
 
3
3
  import dataclasses
4
- import uuid
5
4
  from collections.abc import Callable, Mapping
6
5
  from typing import Generic, Literal, TypeVar
7
6
 
@@ -35,7 +34,7 @@ class AttemptState(Generic[_ControlT_co]):
35
34
 
36
35
  Attributes:
37
36
  context: Configuration captured for the logical operation.
38
- control: Attempt-local deadline and progress control.
37
+ control: Attempt-local consumption and progress control.
39
38
  number: One-based attempt number.
40
39
  operation: Logical operation and replay classification.
41
40
  route: Observer route for the attempt.
@@ -235,7 +234,6 @@ def build_rpc_request(
235
234
  headers=headers,
236
235
  body=_protocol.encode_payload(payload),
237
236
  timeout=context.timeout,
238
- token=uuid.uuid4().hex,
239
237
  )
240
238
 
241
239