sg-python-api 0.2.0__py3-none-any.whl → 0.2.1__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: sg-python-api
3
- Version: 0.2.0
3
+ Version: 0.2.1
4
4
  Summary: Python client for Autodesk Flow Production Tracking
5
5
  Project-URL: Documentation, https://github.com/dactylo/sg-python-api/tree/main/docs
6
6
  Project-URL: Homepage, https://github.com/dactylo/sg-python-api
@@ -45,8 +45,8 @@ This documentation uses SG for Flow Production Tracking, formerly named Shotgun
45
45
 
46
46
  sg-python-api is an independent implementation of the documented API of Autodesk's Python client
47
47
  `3.10.5`. Some behavior differs on purpose, so validate your application's workflows against a test
48
- site before replacing its production client. The [migration guide](https://github.com/dactylo/sg-python-api/blob/v0.2.0/docs/migration-guide.md) walks
49
- through the switch, and the [compatibility reference](https://github.com/dactylo/sg-python-api/blob/v0.2.0/COMPATIBILITY.md) lists the differences.
48
+ site before replacing its production client. The [migration guide](https://github.com/dactylo/sg-python-api/blob/v0.2.1/docs/migration-guide.md) walks
49
+ through the switch, and the [compatibility reference](https://github.com/dactylo/sg-python-api/blob/v0.2.1/COMPATIBILITY.md) lists the differences.
50
50
 
51
51
  ## Installation
52
52
 
@@ -55,7 +55,7 @@ requests to a remote site require HTTPS.
55
55
 
56
56
  `sg-python-api` and Autodesk's `shotgun-api3` both install the `shotgun_api3` package, so install
57
57
  exactly one of them in a Python environment. If the official client is already installed, follow
58
- the [migration steps](https://github.com/dactylo/sg-python-api/blob/v0.2.0/docs/migration-guide.md#install-the-replacement) to replace it.
58
+ the [migration steps](https://github.com/dactylo/sg-python-api/blob/v0.2.1/docs/migration-guide.md#install-the-replacement) to replace it.
59
59
 
60
60
  From a checkout, install into the Python environment that runs your application:
61
61
 
@@ -127,23 +127,23 @@ asyncio.run(main())
127
127
 
128
128
  The output matches the synchronous example. The `async with` block closes the client even when an
129
129
  operation fails. Share an `AsyncShotgun` only among tasks on one event loop. See the
130
- [async reference](https://github.com/dactylo/sg-python-api/blob/v0.2.0/COMPATIBILITY.md#async-extension-surfaces) for lifecycle and cancellation
130
+ [async reference](https://github.com/dactylo/sg-python-api/blob/v0.2.1/COMPATIBILITY.md#async-extension-surfaces) for lifecycle and cancellation
131
131
  details.
132
132
 
133
133
  ## Documentation
134
134
 
135
135
  | If you want to... | Read |
136
136
  | ------------------------------------------------------------------ | ---------------------------------------------------------------------------------- |
137
- | Replace Autodesk's client in an existing application | [Migration guide](https://github.com/dactylo/sg-python-api/blob/v0.2.0/docs/migration-guide.md) |
138
- | Check what behaves differently from Autodesk's client | [Compatibility](https://github.com/dactylo/sg-python-api/blob/v0.2.0/COMPATIBILITY.md) |
139
- | Tune pooling, TCP keepalive, or page prefetch, or observe requests | [Configuration extensions](https://github.com/dactylo/sg-python-api/blob/v0.2.0/COMPATIBILITY.md#configuration-and-observer-extensions) |
140
- | Confirm which client loads and run a smoke test | [Testing an installation](https://github.com/dactylo/sg-python-api/blob/v0.2.0/docs/testing.md) |
141
- | Compare measured latency and transfer memory | [Benchmarks](https://github.com/dactylo/sg-python-api/blob/v0.2.0/docs/benchmarks.md) |
142
- | See changes by version | [Changelog](https://github.com/dactylo/sg-python-api/blob/v0.2.0/CHANGELOG.md) |
137
+ | Replace Autodesk's client in an existing application | [Migration guide](https://github.com/dactylo/sg-python-api/blob/v0.2.1/docs/migration-guide.md) |
138
+ | Check what behaves differently from Autodesk's client | [Compatibility](https://github.com/dactylo/sg-python-api/blob/v0.2.1/COMPATIBILITY.md) |
139
+ | Tune pooling, TCP keepalive, or page prefetch, or observe requests | [Configuration extensions](https://github.com/dactylo/sg-python-api/blob/v0.2.1/COMPATIBILITY.md#configuration-and-observer-extensions) |
140
+ | Confirm which client loads and run a smoke test | [Testing an installation](https://github.com/dactylo/sg-python-api/blob/v0.2.1/docs/testing.md) |
141
+ | Compare measured latency and transfer memory | [Benchmarks](https://github.com/dactylo/sg-python-api/blob/v0.2.1/docs/benchmarks.md) |
142
+ | See changes by version | [Changelog](https://github.com/dactylo/sg-python-api/blob/v0.2.1/CHANGELOG.md) |
143
143
 
144
144
  ## Development
145
145
 
146
- See the [development guide](https://github.com/dactylo/sg-python-api/blob/v0.2.0/docs/development.md) for the required tools, CI, and dependency locks.
146
+ See the [development guide](https://github.com/dactylo/sg-python-api/blob/v0.2.1/docs/development.md) for the required tools, CI, and dependency locks.
147
147
  With those tools installed, run:
148
148
 
149
149
  ```sh
@@ -151,11 +151,11 @@ make setup
151
151
  make check TEST_FLAGS="-m 'not live'"
152
152
  ```
153
153
 
154
- See [live-site tests](https://github.com/dactylo/sg-python-api/blob/v0.2.0/docs/development.md#live-site-tests) to configure tests that create and
154
+ See [live-site tests](https://github.com/dactylo/sg-python-api/blob/v0.2.1/docs/development.md#live-site-tests) to configure tests that create and
155
155
  retire records on a disposable site.
156
156
 
157
157
  ## License
158
158
 
159
- sg-python-api is available under the [MIT License](https://github.com/dactylo/sg-python-api/blob/v0.2.0/LICENSE). It is an independent project and is
159
+ sg-python-api is available under the [MIT License](https://github.com/dactylo/sg-python-api/blob/v0.2.1/LICENSE). It is an independent project and is
160
160
  not affiliated with or endorsed by Autodesk. Flow Production Tracking is a product of Autodesk,
161
161
  Inc.
@@ -27,14 +27,14 @@ shotgun_api3/lib/mockgun/_activity.py,sha256=MK09A1FERRN9Tg2sl2tCtu7n7Gebi-qJKAl
27
27
  shotgun_api3/lib/mockgun/_async_client.py,sha256=UqsoACBDzjkCY191E09grBWAGBm-E8nUhytSJRBATOo,7411
28
28
  shotgun_api3/lib/mockgun/_dates.py,sha256=d0lAIM-SaKjj-vtwe7qg5OXHwgGk42N8OQtxYQ8GWnE,6682
29
29
  shotgun_api3/lib/mockgun/_errors.py,sha256=LL8WI7Sv-p51pEe92k1UsNGygTHXmVki1N0ntJMVnj0,620
30
- shotgun_api3/lib/mockgun/_events.py,sha256=DPi7RrGqg8pz5fbf9fh8goGu5ss0bG7QXW8zJXSGbBI,36800
30
+ shotgun_api3/lib/mockgun/_events.py,sha256=qHmUoa4p3dpibPpmsCQef6KECqWQXp3cYQVt_t1J5zY,37812
31
31
  shotgun_api3/lib/mockgun/_follows.py,sha256=TeSRhlWWYKukz7ZOFa5KDfwMl7zGdQwqHzR7i24ej2E,8401
32
- shotgun_api3/lib/mockgun/_handlers.py,sha256=f4UorkMrqhERWp0r7A8d7CUemyYOlmScroYisnKsF_Y,23642
33
- shotgun_api3/lib/mockgun/_links.py,sha256=eSEYlZ20ZQ-FkX7jbV20acQPni1ODGUoqAhEFK2j0jQ,17158
32
+ shotgun_api3/lib/mockgun/_handlers.py,sha256=YOb7YXZz0SUoNpTMRy-E8yxzzlsS1SULT0K1kElX-Zo,23428
33
+ shotgun_api3/lib/mockgun/_links.py,sha256=TVThdzw0EqPspnUWpHv9iPyMPkvBhbpxASUQH-sSJb4,24305
34
34
  shotgun_api3/lib/mockgun/_new_client.py,sha256=M9qzuT910xQDW1GwIxhZ8cCfdeMucT0FpAe4CVPmZ-U,2981
35
35
  shotgun_api3/lib/mockgun/_presets.py,sha256=R8CAWg-sPGj6OX2yWfivJ1g5YNKIuU7D9LyAXxR2hFo,20339
36
- shotgun_api3/lib/mockgun/_query.py,sha256=Z2c1cforUL0ULFKHdDuG8gJDj4SSW5ewwJzeJPik2lQ,90432
37
- shotgun_api3/lib/mockgun/_retirement.py,sha256=TuiElPOyktDBP-eaKBoJ2pVLDNsxMbkx1bBEx9je3eU,14049
36
+ shotgun_api3/lib/mockgun/_query.py,sha256=0i1JH4lwpEJogvur1vsPCl7cbN5MiJHaDUvKHo7Irlk,92186
37
+ shotgun_api3/lib/mockgun/_retirement.py,sha256=kTwUWbsUEZp00F8957ByBsEsp6GenSx35vlxv5WqGGg,18051
38
38
  shotgun_api3/lib/mockgun/_schema.py,sha256=MJokdscaJP1zVlNtZR679Z3eW3r3PuMvAtYmiLtg9So,7069
39
39
  shotgun_api3/lib/mockgun/_schema_writes.py,sha256=lxxrtCDK6FG2_RpikYYerdMlD9YcH16fHGqUcv4CDoo,16781
40
40
  shotgun_api3/lib/mockgun/_search.py,sha256=xhTD_5nILWHs7jfXnJKAJZVQS4CQAYN0DwTBf4izoj8,14507
@@ -43,16 +43,16 @@ shotgun_api3/lib/mockgun/_sessions.py,sha256=3p4PL-5eNypS8WwdhKX5RNLWKM_alhqN2pD
43
43
  shotgun_api3/lib/mockgun/_site.py,sha256=cj3HeblefYV0jX-4P8ginh5EvVG1thUEaxSV4dN_Q_I,6107
44
44
  shotgun_api3/lib/mockgun/_site_data.py,sha256=Dkivld3zuCqcpC-R4QhMAxUrhsT3Sqkm7UP6fP6v4lA,23470
45
45
  shotgun_api3/lib/mockgun/_storage.py,sha256=OLE09uaO0GoYj_SsUoVCu2MeTl0qW_cDIwlYMXh7EIk,7780
46
- shotgun_api3/lib/mockgun/_store.py,sha256=JrNRaa0eHM3ILTSEg8C7V6ut-_JaJpoSoCfnDuhaenk,60853
47
- shotgun_api3/lib/mockgun/_summaries.py,sha256=HE7ljsMijG3BTWQhhoU3sFpB2hz3skNgnUk_9jQt8SE,40565
46
+ shotgun_api3/lib/mockgun/_store.py,sha256=ufc79zzoLy8A15fOKqxo_BD9ygxsphB8UP0MI77aBFc,65988
47
+ shotgun_api3/lib/mockgun/_summaries.py,sha256=9GwoGr4gm_UKzTrYGkpbO3mDKglqaNywq0qdoYnGEaY,40497
48
48
  shotgun_api3/lib/mockgun/_sync_client.py,sha256=Vo-3rm9dxp4_4ulnFN2tf3xCDUCmw_v1OcE8-8UKtRI,7276
49
- shotgun_api3/lib/mockgun/_threads.py,sha256=Q0vu0J6_bGYXV_R3bOryIveIGRyDjAQnerYhTz0MBdw,9718
49
+ shotgun_api3/lib/mockgun/_threads.py,sha256=0g1RHgySKzrYheQzfnG1fFAJ7cUhFWuZYH_OuNdmEU4,9632
50
50
  shotgun_api3/lib/mockgun/_thumbnails.py,sha256=VG1d4OJCBowtJIhmps8d-DlOCLFNcEyl6fdEG8ylOEU,6169
51
51
  shotgun_api3/lib/mockgun/_transport.py,sha256=o4pOjwek9BVVLZYCVRy0ega1qYOnsFipaxGV7sPwKoM,5174
52
52
  shotgun_api3/lib/mockgun/_uploads.py,sha256=aUzptrzj4fiMgVQCu_vaiXAsfsmds_YuVeRSz1-5ZH4,16711
53
53
  shotgun_api3/lib/mockgun/_wire.py,sha256=OC2B1AziX1y0ugQuso4fQXiK4sAdy_lAF_E5ktvjxFc,4123
54
54
  shotgun_api3/lib/mockgun/mockgun.py,sha256=OQaZbVxuAtYdBkO0ItV_KpJ6XsnbbYWA1Oti8Wm51Io,522
55
- sg_python_api-0.2.0.dist-info/METADATA,sha256=PdPF2YfKAvN9yyM6sus9sJOuXSUsHH2R6TO2d4s5JK4,7530
56
- sg_python_api-0.2.0.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
57
- sg_python_api-0.2.0.dist-info/licenses/LICENSE,sha256=V3moMsQxKDchTd3mtXl5J-ODaNBDvFrapOf1DK7Nx_U,1082
58
- sg_python_api-0.2.0.dist-info/RECORD,,
55
+ sg_python_api-0.2.1.dist-info/METADATA,sha256=7l66bxk1MsZPrlvfGKMDvmsS64-HxwzqEXI2Dt5VlWI,7530
56
+ sg_python_api-0.2.1.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
57
+ sg_python_api-0.2.1.dist-info/licenses/LICENSE,sha256=V3moMsQxKDchTd3mtXl5J-ODaNBDvFrapOf1DK7Nx_U,1082
58
+ sg_python_api-0.2.1.dist-info/RECORD,,
@@ -111,6 +111,9 @@ _LIFECYCLE_TYPES = frozenset(
111
111
  'Asset',
112
112
  'PublishedFile',
113
113
  'CutItem',
114
+ # 2026-11-10: a lone Cut or Attachment logs only its own lifecycle rows.
115
+ 'Cut',
116
+ 'Attachment',
114
117
  }
115
118
  )
116
119
  _LINK_TARGETS = {
@@ -145,6 +148,10 @@ _LINK_UPDATE_FORMS = {
145
148
  _SCALAR_ENCODINGS = frozenset(
146
149
  {'number', 'duration', 'checkbox', 'text', 'list', 'status_list', 'date', 'date_time', 'float'}
147
150
  )
151
+ # 2026-11-10 trial capture: no create logs a default it fills into a field of these data types,
152
+ # across 459 Task, 240 Note, 83 Reply and 36 Playlist creates; status list and list defaults are
153
+ # always logged.
154
+ _UNLOGGED_DEFAULT_TYPES = frozenset({'duration', 'number', 'percent', 'text'})
148
155
  _WRITE_TYPES = frozenset(
149
156
  {
150
157
  'Shot',
@@ -216,6 +223,10 @@ def _create_fields(store: Store, row: Row, supplied: Sequence[str]) -> list[str]
216
223
  if field not in _CREATE_IGNORED
217
224
  and row.get(field) is not None
218
225
  and row.get(field) != []
226
+ and not (
227
+ field not in supplied
228
+ and store.schema.field_type(kind, field) in _UNLOGGED_DEFAULT_TYPES
229
+ )
219
230
  and not (
220
231
  field not in supplied
221
232
  and row.get(field) is False
@@ -718,7 +729,13 @@ def _observed_create_fields(
718
729
  populated = {
719
730
  field
720
731
  for field in store.schema.fields[kind]
721
- if field not in _CREATE_IGNORED and row.get(field) is not None and row.get(field) != []
732
+ if field not in _CREATE_IGNORED
733
+ and row.get(field) is not None
734
+ and row.get(field) != []
735
+ and not (
736
+ field not in supplied
737
+ and store.schema.field_type(kind, field) in _UNLOGGED_DEFAULT_TYPES
738
+ )
722
739
  }
723
740
  for form in _CREATE_FORMS:
724
741
  if (kind, session) != (form.kind, form.session):
@@ -862,10 +879,14 @@ def record_create(
862
879
  plans = _plan_create(store, row, supplied, context=context)
863
880
  if store.schema.declares('EventLogEntry') and store.events_unknown is None:
864
881
  if plans is None:
865
- _unsettled(
866
- store,
867
- f'{row["type"]} {row["id"]} create has unsettled event rows',
882
+ # 2026-11-10: a new Project also logs hundreds of rows for the pages,
883
+ # page settings and field preferences the site copies from a template.
884
+ detail = (
885
+ ' for the pages and preferences the site creates with it'
886
+ if row['type'] == 'Project'
887
+ else ''
868
888
  )
889
+ _unsettled(store, f'{row["type"]} {row["id"]} create has unsettled event rows{detail}')
869
890
  else:
870
891
  _write_plans(store, plans, context)
871
892
  return plans is not None
@@ -27,7 +27,6 @@ from ._wire import RpcCall
27
27
 
28
28
  if TYPE_CHECKING:
29
29
  from ._server import FakeServer
30
- from ._store import FieldValue
31
30
 
32
31
  _Handler: TypeAlias = Callable[['FakeServer', RpcCall, RequestContext], object]
33
32
  HANDLERS: dict[str, _Handler] = {}
@@ -385,6 +384,7 @@ def _update(server: 'FakeServer', call: RpcCall, context: RequestContext) -> obj
385
384
  entity_type = cast('str', params['type'])
386
385
  entity_id = cast('int', params['id'])
387
386
  _check_event_write(server, entity_type)
387
+ server.store.check_link_targets(entity_type, fields, 'update')
388
388
  kept = kept_members(server.store, entity_type, entity_id, fields)
389
389
  effective = server.store.kept_fields(
390
390
  server.store.require(entity_type, entity_id), fields, modes, kept
@@ -401,11 +401,6 @@ def _update(server: 'FakeServer', call: RpcCall, context: RequestContext) -> obj
401
401
  row = server.store.update(
402
402
  entity_type, entity_id, effective, effective_modes, context=context
403
403
  )
404
- for field in kept:
405
- if len(cast('list[FieldValue]', row[field])) > 1:
406
- server.store.mark_unknown(
407
- row, field, 'retained hidden members have unsettled order', order_only=True
408
- )
409
404
  settled = record_update(
410
405
  server.store,
411
406
  before,