sg-python-api 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (134) hide show
  1. sg_python_api-0.1.0/.gitignore +52 -0
  2. sg_python_api-0.1.0/COMPATIBILITY.md +290 -0
  3. sg_python_api-0.1.0/LICENSE +21 -0
  4. sg_python_api-0.1.0/Makefile +89 -0
  5. sg_python_api-0.1.0/PKG-INFO +177 -0
  6. sg_python_api-0.1.0/README.md +153 -0
  7. sg_python_api-0.1.0/docs/benchmarks.md +115 -0
  8. sg_python_api-0.1.0/docs/development.md +204 -0
  9. sg_python_api-0.1.0/docs/migration-guide.md +93 -0
  10. sg_python_api-0.1.0/docs/testing.md +119 -0
  11. sg_python_api-0.1.0/examples/async_client.py +116 -0
  12. sg_python_api-0.1.0/examples/sync_client.py +118 -0
  13. sg_python_api-0.1.0/pyproject.toml +166 -0
  14. sg_python_api-0.1.0/scripts/_artifact_evidence.py +204 -0
  15. sg_python_api-0.1.0/scripts/_conformance_runtime.py +520 -0
  16. sg_python_api-0.1.0/scripts/_conformance_setup.py +540 -0
  17. sg_python_api-0.1.0/scripts/_sdist_contract.py +372 -0
  18. sg_python_api-0.1.0/scripts/benchmark.py +3302 -0
  19. sg_python_api-0.1.0/scripts/bootstrap_test_site.py +128 -0
  20. sg_python_api-0.1.0/scripts/probe_wire.py +2167 -0
  21. sg_python_api-0.1.0/scripts/run_conformance.sh +377 -0
  22. sg_python_api-0.1.0/scripts/run_installed_tests.py +524 -0
  23. sg_python_api-0.1.0/scripts/validate_artifacts.sh +344 -0
  24. sg_python_api-0.1.0/src/shotgun_api3/__init__.py +68 -0
  25. sg_python_api-0.1.0/src/shotgun_api3/_async_client.py +2906 -0
  26. sg_python_api-0.1.0/src/shotgun_api3/_async_driver.py +945 -0
  27. sg_python_api-0.1.0/src/shotgun_api3/_async_httpx2.py +662 -0
  28. sg_python_api-0.1.0/src/shotgun_api3/_async_transport.py +162 -0
  29. sg_python_api-0.1.0/src/shotgun_api3/_capabilities.py +165 -0
  30. sg_python_api-0.1.0/src/shotgun_api3/_client.py +2682 -0
  31. sg_python_api-0.1.0/src/shotgun_api3/_config.py +1463 -0
  32. sg_python_api-0.1.0/src/shotgun_api3/_crud.py +916 -0
  33. sg_python_api-0.1.0/src/shotgun_api3/_driver.py +879 -0
  34. sg_python_api-0.1.0/src/shotgun_api3/_errors.py +49 -0
  35. sg_python_api-0.1.0/src/shotgun_api3/_execution.py +273 -0
  36. sg_python_api-0.1.0/src/shotgun_api3/_files.py +1094 -0
  37. sg_python_api-0.1.0/src/shotgun_api3/_methods.py +1205 -0
  38. sg_python_api-0.1.0/src/shotgun_api3/_protocol.py +1347 -0
  39. sg_python_api-0.1.0/src/shotgun_api3/_sentinel.py +5 -0
  40. sg_python_api-0.1.0/src/shotgun_api3/_sg_timezone.py +5 -0
  41. sg_python_api-0.1.0/src/shotgun_api3/_signatures.py +53 -0
  42. sg_python_api-0.1.0/src/shotgun_api3/_transport.py +901 -0
  43. sg_python_api-0.1.0/src/shotgun_api3/_version.py +3 -0
  44. sg_python_api-0.1.0/src/shotgun_api3/lib/__init__.py +1 -0
  45. sg_python_api-0.1.0/src/shotgun_api3/lib/mockgun/__init__.py +16 -0
  46. sg_python_api-0.1.0/src/shotgun_api3/lib/mockgun/_async.py +315 -0
  47. sg_python_api-0.1.0/src/shotgun_api3/lib/mockgun/_mockgun.py +1666 -0
  48. sg_python_api-0.1.0/src/shotgun_api3/lib/sgtimezone.py +209 -0
  49. sg_python_api-0.1.0/src/shotgun_api3/py.typed +0 -0
  50. sg_python_api-0.1.0/src/shotgun_api3/shotgun.py +58 -0
  51. sg_python_api-0.1.0/tests/__init__.py +0 -0
  52. sg_python_api-0.1.0/tests/_git.py +6 -0
  53. sg_python_api-0.1.0/tests/_loopback.py +25 -0
  54. sg_python_api-0.1.0/tests/_pyright.py +23 -0
  55. sg_python_api-0.1.0/tests/_required_mode.py +68 -0
  56. sg_python_api-0.1.0/tests/_transports.py +131 -0
  57. sg_python_api-0.1.0/tests/client/__init__.py +0 -0
  58. sg_python_api-0.1.0/tests/client/test_activity.py +509 -0
  59. sg_python_api-0.1.0/tests/client/test_async.py +2534 -0
  60. sg_python_api-0.1.0/tests/client/test_auth_methods.py +431 -0
  61. sg_python_api-0.1.0/tests/client/test_crud.py +1318 -0
  62. sg_python_api-0.1.0/tests/client/test_filters.py +262 -0
  63. sg_python_api-0.1.0/tests/client/test_misc_methods.py +677 -0
  64. sg_python_api-0.1.0/tests/client/test_nav.py +276 -0
  65. sg_python_api-0.1.0/tests/client/test_schema.py +634 -0
  66. sg_python_api-0.1.0/tests/client/test_summarize_search.py +380 -0
  67. sg_python_api-0.1.0/tests/client/test_sync.py +1787 -0
  68. sg_python_api-0.1.0/tests/conftest.py +124 -0
  69. sg_python_api-0.1.0/tests/files/__init__.py +0 -0
  70. sg_python_api-0.1.0/tests/files/test_async.py +2138 -0
  71. sg_python_api-0.1.0/tests/files/test_cookie_handler.py +221 -0
  72. sg_python_api-0.1.0/tests/files/test_downloads.py +993 -0
  73. sg_python_api-0.1.0/tests/files/test_multipart.py +146 -0
  74. sg_python_api-0.1.0/tests/files/test_upload_routing.py +66 -0
  75. sg_python_api-0.1.0/tests/files/test_uploads.py +1648 -0
  76. sg_python_api-0.1.0/tests/fixtures/activity_stream_read.json +40 -0
  77. sg_python_api-0.1.0/tests/fixtures/batch.json +52 -0
  78. sg_python_api-0.1.0/tests/fixtures/create.json +49 -0
  79. sg_python_api-0.1.0/tests/fixtures/fault_bad_auth.json +46 -0
  80. sg_python_api-0.1.0/tests/fixtures/following.json +38 -0
  81. sg_python_api-0.1.0/tests/fixtures/get_session_token.json +32 -0
  82. sg_python_api-0.1.0/tests/fixtures/info.json +27 -0
  83. sg_python_api-0.1.0/tests/fixtures/nav_expand.json +35 -0
  84. sg_python_api-0.1.0/tests/fixtures/preferences_read.json +35 -0
  85. sg_python_api-0.1.0/tests/fixtures/read.json +46 -0
  86. sg_python_api-0.1.0/tests/fixtures/schema_entity_read.json +32 -0
  87. sg_python_api-0.1.0/tests/fixtures/schema_read.json +32 -0
  88. sg_python_api-0.1.0/tests/fixtures/summarize.json +45 -0
  89. sg_python_api-0.1.0/tests/fixtures/text_search.json +43 -0
  90. sg_python_api-0.1.0/tests/fixtures/update_project_last_accessed.json +36 -0
  91. sg_python_api-0.1.0/tests/fixtures/work_schedule_read.json +36 -0
  92. sg_python_api-0.1.0/tests/lib/__init__.py +0 -0
  93. sg_python_api-0.1.0/tests/lib/conftest.py +151 -0
  94. sg_python_api-0.1.0/tests/lib/test_mockgun_async.py +252 -0
  95. sg_python_api-0.1.0/tests/lib/test_mockgun_crud.py +733 -0
  96. sg_python_api-0.1.0/tests/lib/test_mockgun_find.py +1357 -0
  97. sg_python_api-0.1.0/tests/lib/test_mockgun_schema.py +394 -0
  98. sg_python_api-0.1.0/tests/lib/test_sgtimezone.py +311 -0
  99. sg_python_api-0.1.0/tests/live/__init__.py +0 -0
  100. sg_python_api-0.1.0/tests/live/conftest.py +197 -0
  101. sg_python_api-0.1.0/tests/live/test_async.py +360 -0
  102. sg_python_api-0.1.0/tests/live/test_roundtrip.py +129 -0
  103. sg_python_api-0.1.0/tests/test_config.py +1714 -0
  104. sg_python_api-0.1.0/tests/test_datetimes.py +303 -0
  105. sg_python_api-0.1.0/tests/test_errors.py +19 -0
  106. sg_python_api-0.1.0/tests/test_examples.py +362 -0
  107. sg_python_api-0.1.0/tests/test_init.py +311 -0
  108. sg_python_api-0.1.0/tests/test_parity.py +1415 -0
  109. sg_python_api-0.1.0/tests/test_project_metadata.py +34 -0
  110. sg_python_api-0.1.0/tests/test_signature_parity.py +232 -0
  111. sg_python_api-0.1.0/tests/tooling/__init__.py +0 -0
  112. sg_python_api-0.1.0/tests/tooling/_wire_replay.py +161 -0
  113. sg_python_api-0.1.0/tests/tooling/test_artifact_evidence.py +382 -0
  114. sg_python_api-0.1.0/tests/tooling/test_benchmark.py +4999 -0
  115. sg_python_api-0.1.0/tests/tooling/test_conformance_runtime.py +563 -0
  116. sg_python_api-0.1.0/tests/tooling/test_conformance_setup.py +370 -0
  117. sg_python_api-0.1.0/tests/tooling/test_required_mode.py +698 -0
  118. sg_python_api-0.1.0/tests/tooling/test_run_conformance.py +715 -0
  119. sg_python_api-0.1.0/tests/tooling/test_run_installed_tests.py +615 -0
  120. sg_python_api-0.1.0/tests/tooling/test_validate_artifacts.py +1171 -0
  121. sg_python_api-0.1.0/tests/tooling/test_wire_fixtures.py +1539 -0
  122. sg_python_api-0.1.0/tests/transport/__init__.py +0 -0
  123. sg_python_api-0.1.0/tests/transport/test_abort.py +406 -0
  124. sg_python_api-0.1.0/tests/transport/test_async.py +278 -0
  125. sg_python_api-0.1.0/tests/transport/test_async_driver.py +2383 -0
  126. sg_python_api-0.1.0/tests/transport/test_async_httpx2.py +1483 -0
  127. sg_python_api-0.1.0/tests/transport/test_async_prefetch.py +428 -0
  128. sg_python_api-0.1.0/tests/transport/test_driver.py +1770 -0
  129. sg_python_api-0.1.0/tests/transport/test_prefetch.py +1214 -0
  130. sg_python_api-0.1.0/tests/transport/test_protocol.py +640 -0
  131. sg_python_api-0.1.0/tests/transport/test_retry_policy.py +775 -0
  132. sg_python_api-0.1.0/tests/transport/test_streaming.py +604 -0
  133. sg_python_api-0.1.0/tests/transport/test_sync.py +301 -0
  134. sg_python_api-0.1.0/tests/transport/test_urllib3.py +336 -0
@@ -0,0 +1,52 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ build/
12
+ dist/
13
+ .eggs/
14
+ *.egg-info/
15
+ *.egg
16
+
17
+ # Virtual environments
18
+ .venv/
19
+ venv/
20
+ ENV/
21
+
22
+ # PyInstaller
23
+ *.manifest
24
+ *.spec
25
+
26
+ # Unit test / coverage reports
27
+ htmlcov/
28
+ .tox/
29
+ .nox/
30
+ .coverage*
31
+ .pytest_cache/
32
+ .cache/
33
+
34
+ # Type checker / linter caches
35
+ .pyright/
36
+ .mypy_cache/
37
+ .ruff_cache/
38
+ pyrightconfig.json
39
+
40
+ # Root scratch files
41
+ /*.png
42
+ /*.txt
43
+ !/requirements-ci.txt
44
+ !/requirements-test.txt
45
+
46
+ # Editors
47
+ .vscode/
48
+ .idea/
49
+
50
+ # OS
51
+ .DS_Store
52
+ Thumbs.db
@@ -0,0 +1,290 @@
1
+ # Compatibility
2
+
3
+ Use this reference to decide whether sg-python-api fits an existing integration and to configure
4
+ the behavior that differs from Autodesk's client. For the installation and validation sequence,
5
+ start with the [migration guide](docs/migration-guide.md); for a first request, see the
6
+ [README quick start](README.md#quick-start).
7
+
8
+ | If you want to... | Start here |
9
+ | -------------------------------------------------- | --------------------------------------------------------------------------------------------------- |
10
+ | Check whether your application is in scope | [Supported scope](#supported-scope) |
11
+ | Review the differences that affect applications | [What changes for your application](#what-changes-for-your-application) |
12
+ | Tune pooling or page prefetch, or observe requests | [Configuration and observer extensions](#configuration-and-observer-extensions) |
13
+ | Use the async client or async `mockgun` | [Async extension surfaces](#async-extension-surfaces) |
14
+ | Check which configuration names are supported | [Supported configuration and capability surfaces](#supported-configuration-and-capability-surfaces) |
15
+ | Check when plain HTTP is allowed | [Authenticated transport security](#authenticated-transport-security) |
16
+ | Point an existing client at a different site | [Server floor and readiness](#server-floor-and-readiness) |
17
+
18
+ Two terms recur on this page. A site's *origin* is its scheme, normalized hostname, and effective
19
+ port, where the effective port is the URL's explicit port or else 443 for HTTPS and 80 for HTTP.
20
+ The URL path is not part of the origin. Before the first operation against an origin, the client
21
+ reads that server's information and checks its version and page limit; this page calls that step
22
+ the *readiness check*.
23
+
24
+ ## Supported scope
25
+
26
+ The package provides the `shotgun_api3` import name and targets the documented synchronous API of
27
+ Autodesk's Python client `3.10.2`, including its method signatures, return shapes, errors, filters,
28
+ paging, schema, file, activity, navigation, and scheduling operations. It also provides the
29
+ `shotgun_api3.lib.mockgun` and `shotgun_api3.lib.sgtimezone` modules and native async extensions.
30
+
31
+ The target is the documented API, so test the workflows your application uses before adopting the
32
+ package in production. Exception-message text is not guaranteed to match Autodesk's client; handle
33
+ documented exception types instead of parsing messages.
34
+
35
+ You need Python 3.11 or newer and a server version of at least 7.10.0. This package and the
36
+ official distribution both install `shotgun_api3`, so they cannot coexist in one Python
37
+ environment.
38
+
39
+ ## What changes for your application
40
+
41
+ The most consequential differences concern timeouts, retries, trust, and shared-client use. Other
42
+ differences are grouped below so you can check only the areas your application uses.
43
+
44
+ ### Timeouts and retries
45
+
46
+ Requests have a 15-second connect timeout and a 300-second read timeout by default. Finite waits
47
+ let an application recover when a dependency stops responding. To change both limits, assign
48
+ `sg.config.timeout_secs` a finite positive number of seconds no larger than the platform's
49
+ `threading.TIMEOUT_MAX`. Assigning a Boolean, zero, a negative or non-finite number, or a value
50
+ above that limit raises an error immediately. Set it to `None` only when the application
51
+ deliberately accepts an unlimited wait.
52
+
53
+ Retries use one policy for every operation, with backoff and jitter. Reads can retry. A write that
54
+ is unsafe to replay retries only when a failure occurs before the request is sent. Once sent, its
55
+ result may be ambiguous, so the client does not repeat it automatically. HTTP status retries,
56
+ including those directed by `Retry-After`, require an operation that is safe to replay. There is no
57
+ exact legacy retry mode.
58
+
59
+ Tune the number of attempts with `sg.config.max_rpc_attempts` (default `3`). Tune the base delay
60
+ with `sg.config.rpc_attempt_interval`, in milliseconds; its default is `3000`, or the value of the
61
+ `SHOTGUN_API_RETRY_INTERVAL` environment variable when the client is created.
62
+
63
+ When a retryable response carries a valid `Retry-After` header of 0 to 30 seconds, the client waits
64
+ that long before the next attempt. A larger valid value stops retrying and raises the current
65
+ response's error, so the client neither waits without a bound nor retries earlier than the server
66
+ asked. This 30-second bound is not configurable. A malformed `Retry-After` value is ignored, and
67
+ the client uses its computed backoff.
68
+
69
+ ### Error handling
70
+
71
+ - RPC faults, including authentication faults, and terminal transport failures raise exceptions in
72
+ the `ShotgunError` family.
73
+ - An HTTP error response raises `shotgun_api3.ProtocolError`. This is the standard library's
74
+ `xmlrpc.client.ProtocolError`, which is outside the `ShotgunError` family.
75
+ - An invalid configuration value raises `ValueError`, or `TypeError` when the value has the wrong
76
+ type. An authenticated operation against a disallowed HTTP origin also raises `ValueError`.
77
+
78
+ Handle these families separately. The transport's concrete exception classes are implementation
79
+ details, so do not catch them directly.
80
+
81
+ ### Trust and authentication
82
+
83
+ The client uses the system trust store by default. Passing `ca_certs=` or setting
84
+ `SHOTGUN_API_CACERTS` replaces that trust store with the selected bundle, so a custom bundle must
85
+ include every CA needed for the site and for any upload or download hosts. Remote authenticated
86
+ operations require HTTPS.
87
+
88
+ Attachment downloads send the site session cookie only to the client's configured origin.
89
+ Retargeting a client to a different origin clears any authentication and session values you do not
90
+ supply again. These rules prevent credentials from following an unrelated download or site change.
91
+ See [authenticated transport security](#authenticated-transport-security) and
92
+ [server floor and readiness](#server-floor-and-readiness) for the exact boundaries.
93
+
94
+ ### Concurrency, pooling, and paging
95
+
96
+ A synchronous `Shotgun` can be shared across threads. An `AsyncShotgun` can be shared across tasks
97
+ on its owning event loop. Existing one-client-per-thread code remains valid.
98
+
99
+ Page fetching is serial by default, which preserves request order. A positive
100
+ `sg.config.page_prefetch` opts into bounded lookahead while still returning results in order.
101
+ [Configuration extensions](#configuration-and-observer-extensions) explains pooling and prefetch.
102
+ Measure your own workload before tuning them.
103
+
104
+ The client takes its page size from the server's `api_max_entities_per_page` value and leaves that
105
+ raw metadata available. When the value is missing, `None`, `0`, or empty, the page size is 500. Any
106
+ other malformed or non-positive value fails the readiness check with `ShotgunError`, and the next
107
+ operation repeats the check; the client never pages with an invalid size.
108
+
109
+ ### Files, values, and diagnostics
110
+
111
+ | Behavior | Application impact |
112
+ | ------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
113
+ | Download setup errors | Setup RPC faults keep their specific types. Setup or transfer HTTP failures and invalid download URLs raise `ShotgunFileDownloadError` with a generic message and no underlying exception details. Configuration and security failures keep their existing types. |
114
+ | Multi-target thumbnail sharing | The client waits for source readiness before sharing and does not upload the source again. |
115
+ | Legacy form uploads | A successful response from the legacy form-upload route is unsupported. Hosted direct and multipart routes are supported. |
116
+ | Datetime-shaped strings | Invalid values remain unchanged strings. On platforms without pre-1970 time conversion, valid pre-epoch values use the process standard offset without historical daylight-saving accuracy. |
117
+ | Request diagnostics | Structured observers omit credentials and bodies. Application `httpx2` INFO logging can include signed URLs; set that logger to WARNING. |
118
+ | User agent | The default includes an `sg-python-api` marker. `add_user_agent()` preserves it, `reset_user_agent()` restores the default, and assigning `sg.config.user_agents` replaces every entry. |
119
+ | `shotgun_api3.__version__` | Reports the installed sg-python-api version, rather than Autodesk's compatibility-target version. |
120
+
121
+ ### Unsupported legacy surfaces
122
+
123
+ The package omits `shotgun_api3.lib.httplib2` and the official client's `shotgun.Http`,
124
+ `shotgun.ProxyInfo`, and `shotgun.socks` internals. Applications that import them must use a
125
+ supported public API or manage their own transport integration.
126
+
127
+ Python versions below 3.11 cannot install the package, so setting `SHOTGUN_ALLOW_OLD_PYTHON` has no
128
+ effect. Servers below 7.10.0 are unsupported, and the official client's thumbnail URL synthesis for
129
+ servers older than 3.3.0 is not provided.
130
+
131
+ ## Configuration and observer extensions
132
+
133
+ These settings have no counterpart in Autodesk's client. Set them on `sg.config` like any other
134
+ configuration attribute.
135
+
136
+ `page_prefetch` controls bounded lookahead for paged `find()` requests. Its default, `0`, keeps
137
+ page fetching serial. A positive value N permits N concurrent page requests, with at most N-1 later
138
+ pages fetched ahead while results remain ordered; a value of 1 therefore fetches nothing ahead.
139
+
140
+ `pool_maxsize` defaults to `10`. For the synchronous client, it sets how many connections the pool
141
+ keeps open per origin; concurrent demand can still open additional connections. For the
142
+ asynchronous client, it caps both simultaneous and retained keep-alive connections.
143
+
144
+ ### Observers
145
+
146
+ The five observer fields accept a synchronous callback or `None`. Each callback receives one event
147
+ record:
148
+
149
+ | Configuration field | Event record |
150
+ | -------------------------- | ------------------------------------------------------------------------------------ |
151
+ | `on_compatibility_warning` | `CompatibilityWarningEvent(code, message)` |
152
+ | `on_progress` | `ProgressEvent(operation, route, attempt, bytes_transferred, total_bytes)` |
153
+ | `on_request` | `RequestEvent(operation, route, http_method, attempt)` |
154
+ | `on_response` | `ResponseEvent(operation, route, attempt, latency, status, outcome, correlation_id)` |
155
+ | `on_retry` | `RetryEvent(operation, route, attempt, delay, status)` |
156
+
157
+ These frozen records contain only allowlisted metadata. They exclude credentials, cookies, request
158
+ and response bodies, signed URLs, arbitrary headers, and backend exception text. The
159
+ `correlation_id` field contains only the `x-sg-correlation-id` response header, when the server
160
+ sends it.
161
+
162
+ Application logging of `httpx2` is separate from these observers. Its INFO request logs can include
163
+ signed upload or download URLs. Set the logger to WARNING or higher when handling signed URLs:
164
+
165
+ ```python
166
+ import logging
167
+
168
+ logging.getLogger('httpx2').setLevel(logging.WARNING)
169
+ ```
170
+
171
+ Compatibility warnings use stable codes and fixed messages:
172
+
173
+ | Code | Emitted when |
174
+ | ------------------------------- | --------------------------------------------------------------------------------------- |
175
+ | `deprecated-entity-types-alias` | The application calls `entity_types()`, the deprecated alias of `schema_entity_read()`. |
176
+ | `deprecated-schema-alias` | The application calls `schema()`, the deprecated alias of `schema_field_read()`. |
177
+ | `legacy-infinite-timeout` | An operation runs with `sg.config.timeout_secs` set to `None`. |
178
+
179
+ ### Callback threads and failures
180
+
181
+ Callbacks run synchronously and are never awaited, so keep them short. Serial synchronous
182
+ operations invoke callbacks on the caller's thread. With `page_prefetch` above 0 and a multi-page
183
+ `find()`, callbacks for every page request, including the first, run on the worker thread executing
184
+ that request. `AsyncShotgun` invokes callbacks on its owning event loop.
185
+
186
+ Each progress record is cumulative and ordered within one attempt: `bytes_transferred` is the total
187
+ reported so far, and `total_bytes` is `None` when the complete size is unknown. Chunking determines
188
+ when progress is reported, so there is no fixed byte interval between records.
189
+
190
+ If a callback raises an ordinary `Exception`, the client suppresses it and logs a sanitized warning
191
+ that names the event type without formatting the exception value. This boundary does not suppress
192
+ `BaseException` subclasses. A callback must not start an operation that would wait on the same
193
+ in-progress readiness check; the client raises `RuntimeError` instead of waiting on itself.
194
+
195
+ ## Async extension surfaces
196
+
197
+ ### `shotgun_api3.AsyncShotgun`
198
+
199
+ `AsyncShotgun` provides native asynchronous counterparts to the synchronous client operations.
200
+ Install the `async` extra to use it. Await I/O methods; configuration and properties remain
201
+ synchronous.
202
+
203
+ Import it explicitly with `from shotgun_api3 import AsyncShotgun`. Star imports exclude optional
204
+ dependency names, including `AsyncShotgun`.
205
+
206
+ An instance belongs to one event loop and may be shared by tasks on that loop. Prefer `async with`;
207
+ otherwise, call `await sg.close()`. Closing drains in-flight work and releases the current
208
+ transport, including page requests already reserved by an in-flight `find()`. Once closing begins,
209
+ the closing transport rejects unrelated new work, and a later operation reconnects lazily.
210
+
211
+ The async client has no `abort()` method; cancel an operation by cancelling its task. Cancellation
212
+ is not retried. A cancelled write may already have reached the server, so check its outcome before
213
+ repeating it.
214
+
215
+ `AsyncShotgun` validates constructor arguments synchronously but defers network work. The readiness
216
+ check and any one-use `auth_token` exchange run at the first of these points: entering an
217
+ `async with` block (only with the default `connect=True`), calling `await sg.connect()`, or
218
+ starting an operation that needs readiness. Until then, `server_caps` and `server_info` raise
219
+ `ShotgunError` and direct the caller to await `connect()` or an RPC.
220
+
221
+ ### `shotgun_api3.lib.mockgun.AsyncShotgun`
222
+
223
+ `mockgun.AsyncShotgun` delegates awaitable methods to the in-memory `mockgun.Shotgun`
224
+ implementation and works without the `async` extra. Use it to exercise async application code
225
+ without making network requests. Configure schema files before construction with
226
+ `await mockgun.AsyncShotgun.set_schema_paths(field_path, entity_path)`; retrieving them with
227
+ `get_schema_paths()` also requires `await`.
228
+
229
+ Both async extension surfaces inherit the semantics of their synchronous counterparts.
230
+ Transport-specific differences apply to `AsyncShotgun`; `mockgun.AsyncShotgun` works in memory and
231
+ has no HTTP transport behavior.
232
+
233
+ ## Supported configuration and capability surfaces
234
+
235
+ Applications may use these configuration and capability surfaces from Autodesk's client:
236
+
237
+ - Official `sg.config` attributes, with `records_per_page` read-only.
238
+ - `sg.config.set_server_params()`.
239
+ - `server_caps` and `ServerCapabilities`: `host`, `server_info`, `version`, `is_dev`, and the
240
+ `ensure_*` methods.
241
+ - `client_caps` and `ClientCapabilities`: `platform`, `local_path_field`, `py_version`, and
242
+ `ssl_version`.
243
+ - Module-level `LOG`, `SG_TIMEZONE`, `SgTimezone`, and `SHOTGUN_API_DISABLE_ENTITY_OPTIMIZATION`.
244
+
245
+ The following opt-in extensions have no official counterpart:
246
+
247
+ - `sg.config.page_prefetch`.
248
+ - `sg.config.pool_maxsize`.
249
+ - `sg.config.on_compatibility_warning`.
250
+ - `sg.config.on_progress`.
251
+ - `sg.config.on_request`.
252
+ - `sg.config.on_response`.
253
+ - `sg.config.on_retry`.
254
+ - `sg.config.update_fields()`.
255
+ - `shotgun_api3.AsyncShotgun`.
256
+ - `shotgun_api3.lib.mockgun.AsyncShotgun`.
257
+
258
+ ## Authenticated transport security
259
+
260
+ Authenticated RPCs, uploads, thumbnail sharing, and same-origin attachment downloads require HTTPS,
261
+ because they carry credentials or the site session cookie. HTTP is allowed only for direct
262
+ connections to the exact hostname `localhost` (case-insensitive) or a literal loopback IP address:
263
+ IPv4 `127.0.0.0/8` or IPv6 `::1`. A configured proxy disallows this exception even when the proxy
264
+ itself is on loopback, because it can forward traffic elsewhere. Hostnames that resolve to
265
+ loopback, `localhost.`, and subdomains of `localhost` are not exceptions. There is no opt-out for
266
+ insecure remote origins.
267
+
268
+ The client checks this rule when each authenticated operation starts, not when you construct the
269
+ client. A client for a remote or proxied HTTP origin can therefore be created, but its first
270
+ authenticated operation raises `ValueError` before sending a request. Cached session tokens do not
271
+ bypass this check.
272
+
273
+ Credential-free downloads from external attachment URLs may still use HTTP. A URL with a different
274
+ port from the configured origin counts as external and receives no site session cookie.
275
+
276
+ ## Server floor and readiness
277
+
278
+ The supported server floor is version 7.10.0. The readiness check belongs to one exact origin, not
279
+ to the client as a whole: the client rejects a server below the floor before the first operation
280
+ other than `info()` for each origin. A successful `info()` call completes the readiness check for
281
+ its origin without a duplicate request. After you point a client at a different origin, the next
282
+ operation runs a new readiness check for that origin.
283
+
284
+ Calling `sg.config.set_server_params()` with a new origin clears every authentication and session
285
+ value that belongs to the old origin. A change that affects only the API path keeps them. To switch
286
+ origins and credentials together, supply the new origin and its replacement credentials in one
287
+ atomic `sg.config.update_fields()` call.
288
+
289
+ For `AsyncShotgun`, the readiness check runs at the points described under
290
+ [async extension surfaces](#async-extension-surfaces).
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 the sg-python-api authors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,89 @@
1
+ .DEFAULT_GOAL := all
2
+
3
+ PYTHON ?= python
4
+ VENV_BIN := .venv/bin
5
+ ifeq ($(OS),Windows_NT)
6
+ VENV_BIN := .venv/Scripts
7
+ endif
8
+ DEV_PYTHON := $(VENV_BIN)/python
9
+ export PATH := $(abspath $(VENV_BIN)):$(PATH)
10
+ export UV_SYSTEM_PYTHON := 0
11
+ UV_VERSION := 0.12.23
12
+ CI_PYTHON := 3.11
13
+ CI_EXTRAS := --extra async
14
+ CI_BUILD_REQUIREMENTS = $(DEV_PYTHON) -c 'import tomllib; print("\n".join(tomllib.load(open("pyproject.toml", "rb"))["build-system"]["requires"]))'
15
+ CI_COMPILE = uv pip compile pyproject.toml - $(CI_EXTRAS) --universal --python-version $(CI_PYTHON) --no-python-downloads --no-sources --no-build --generate-hashes --no-annotate --custom-compile-command 'make lock-update'
16
+
17
+ MARKDOWN_FILES := CHANGELOG.md COMPATIBILITY.md README.md docs/development.md docs/benchmarks.md docs/migration-guide.md docs/testing.md
18
+ PUBLIC_MARKDOWN_FILES := $(wildcard *.md docs/*.md)
19
+ SHELL_FILES := scripts/run_conformance.sh scripts/validate_artifacts.sh
20
+
21
+ .PHONY: all check env-check format format-check lock-check lock-update quality setup shell-check test type-check uv-check venv-check whitespace-check
22
+
23
+ uv-check:
24
+ @test "$$(uv --version | cut -d ' ' -f 2)" = "$(UV_VERSION)" || \
25
+ { echo 'Use uv $(UV_VERSION), matching CI.' >&2; exit 1; }
26
+
27
+ setup: uv-check
28
+ @test -d .venv || uv venv --python "$(PYTHON)" --no-python-downloads .venv
29
+ rm -f .venv/.requirements-ci.txt .venv/.pyproject.toml
30
+ uv pip sync --python "$(DEV_PYTHON)" --no-python-downloads --require-hashes --build-constraints requirements-ci.txt requirements-ci.txt
31
+ uv pip install --python "$(DEV_PYTHON)" --no-python-downloads --no-deps --no-build-isolation -e .
32
+ cp requirements-ci.txt .venv/.requirements-ci.txt
33
+ cp pyproject.toml .venv/.pyproject.toml
34
+
35
+ venv-check:
36
+ @test -x "$(DEV_PYTHON)" || { echo 'Run make setup before project commands.' >&2; exit 1; }
37
+
38
+ env-check: venv-check
39
+ @cmp -s requirements-ci.txt .venv/.requirements-ci.txt && \
40
+ cmp -s pyproject.toml .venv/.pyproject.toml || \
41
+ { echo 'Project setup is incomplete or outdated; run make setup.' >&2; exit 1; }
42
+
43
+ format: env-check
44
+ $(DEV_PYTHON) -m ruff format .
45
+ $(DEV_PYTHON) -m ruff check --fix .
46
+ biome format --write .
47
+ $(DEV_PYTHON) -m mdformat $(sort $(MARKDOWN_FILES) $(PUBLIC_MARKDOWN_FILES))
48
+ $(VENV_BIN)/pyproject-fmt pyproject.toml
49
+ shfmt -w $(SHELL_FILES)
50
+
51
+ format-check: env-check
52
+ $(DEV_PYTHON) -m ruff format --check .
53
+ $(DEV_PYTHON) -m ruff check .
54
+ biome format .
55
+ $(DEV_PYTHON) -m mdformat --check $(sort $(MARKDOWN_FILES) $(PUBLIC_MARKDOWN_FILES))
56
+ $(VENV_BIN)/pyproject-fmt --check pyproject.toml
57
+ shfmt -d $(SHELL_FILES)
58
+
59
+ type-check: env-check
60
+ $(DEV_PYTHON) -m pyright --project pyproject.toml --pythonpath "$(DEV_PYTHON)" --pythonplatform Linux
61
+ $(DEV_PYTHON) -m pyright --project pyproject.toml --pythonpath "$(DEV_PYTHON)" --pythonplatform Darwin
62
+ $(DEV_PYTHON) -m pyright --project pyproject.toml --pythonpath "$(DEV_PYTHON)" --pythonplatform Windows
63
+
64
+ lock-check: env-check uv-check
65
+ @tmp=$$(mktemp -d); trap 'rm -rf "$$tmp"' EXIT; \
66
+ cp requirements-ci.txt "$$tmp/requirements-ci.txt" && \
67
+ $(CI_BUILD_REQUIREMENTS) | $(CI_COMPILE) --group dev --quiet --output-file "$$tmp/requirements-ci.txt" && \
68
+ diff -u requirements-ci.txt "$$tmp/requirements-ci.txt" && \
69
+ $(CI_BUILD_REQUIREMENTS) | $(CI_COMPILE) --group test --constraint "$$tmp/requirements-ci.txt" --quiet --output-file "$$tmp/requirements-test.txt" && \
70
+ diff -u requirements-test.txt "$$tmp/requirements-test.txt"
71
+
72
+ lock-update: venv-check uv-check
73
+ $(CI_BUILD_REQUIREMENTS) | $(CI_COMPILE) --group dev --output-file requirements-ci.txt $(LOCK_FLAGS)
74
+ $(CI_BUILD_REQUIREMENTS) | $(CI_COMPILE) --group test --constraint requirements-ci.txt --output-file requirements-test.txt
75
+
76
+ test: env-check
77
+ $(DEV_PYTHON) -m pytest -q $(TEST_FLAGS)
78
+
79
+ shell-check:
80
+ bash -n $(SHELL_FILES)
81
+
82
+ whitespace-check:
83
+ git diff --check HEAD
84
+
85
+ quality: format-check lock-check type-check shell-check whitespace-check
86
+
87
+ check: quality test
88
+
89
+ all: check
@@ -0,0 +1,177 @@
1
+ Metadata-Version: 2.5
2
+ Name: sg-python-api
3
+ Version: 0.1.0
4
+ Summary: Python client for Autodesk Flow Production Tracking
5
+ Project-URL: Documentation, https://github.com/dactylo/sg-python-api/tree/main/docs
6
+ Project-URL: Homepage, https://github.com/dactylo/sg-python-api
7
+ Project-URL: Issues, https://github.com/dactylo/sg-python-api/issues
8
+ Project-URL: Repository, https://github.com/dactylo/sg-python-api
9
+ License-Expression: MIT
10
+ License-File: LICENSE
11
+ Keywords: api,flow-production-tracking,shotgrid,shotgun,shotgun-api3
12
+ Classifier: Programming Language :: Python :: 3 :: Only
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Programming Language :: Python :: 3.13
16
+ Classifier: Programming Language :: Python :: 3.14
17
+ Classifier: Programming Language :: Python :: 3.15
18
+ Classifier: Typing :: Typed
19
+ Requires-Python: >=3.11
20
+ Requires-Dist: urllib3<3,>=2
21
+ Provides-Extra: async
22
+ Requires-Dist: httpx2<3,>=2; extra == 'async'
23
+ Description-Content-Type: text/markdown
24
+
25
+ # sg-python-api
26
+
27
+ sg-python-api is a Python client for Autodesk Flow Production Tracking, written for pipeline tools
28
+ and scripts built on Autodesk's `shotgun_api3` client. It keeps the same import name and documented
29
+ API, so an existing integration can switch without rewriting its calls and gain shared connections,
30
+ bounded network waits, and retries that do not repeat an ambiguous write. It also adds a native
31
+ asynchronous client.
32
+
33
+ This documentation uses SG for Flow Production Tracking, formerly named Shotgun and ShotGrid.
34
+
35
+ ## Why use it?
36
+
37
+ - **Reuse connections:** share one synchronous client across threads, or one async client across
38
+ tasks on the same event loop.
39
+ - **Bound network waits:** default connect and read timeouts help applications recover from an
40
+ unavailable service.
41
+ - **Handle writes carefully:** retries account for whether an operation is safe to repeat. An
42
+ ambiguous write is left for the application to reconcile.
43
+ - **Test without a site:** synchronous and asynchronous `mockgun` clients provide an in-memory API
44
+ for application tests.
45
+
46
+ sg-python-api is an independent implementation of the documented API of Autodesk's Python client
47
+ `3.10.2`. 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.1.0/docs/migration-guide.md) walks
49
+ through the switch, and the [compatibility reference](https://github.com/dactylo/sg-python-api/blob/v0.1.0/COMPATIBILITY.md) lists the differences.
50
+
51
+ ## Installation
52
+
53
+ You need Python 3.11 or newer and an SG site running server version 7.10.0 or newer. Authenticated
54
+ requests to a remote site require HTTPS.
55
+
56
+ `sg-python-api` and Autodesk's `shotgun-api3` both install the `shotgun_api3` package, so install
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.1.0/docs/migration-guide.md#install-the-replacement) to replace it.
59
+
60
+ When a release is available on your package index, install into the Python environment that runs
61
+ your application:
62
+
63
+ ```sh
64
+ python -m pip install sg-python-api
65
+ ```
66
+
67
+ To install from a checkout instead, run:
68
+
69
+ ```sh
70
+ python -m pip install .
71
+ ```
72
+
73
+ The base installation provides the synchronous `Shotgun` client and both `mockgun` test clients. To
74
+ use the native asynchronous client, `AsyncShotgun`, install the `async` extra, which adds the
75
+ `httpx2` HTTP library:
76
+
77
+ ```sh
78
+ python -m pip install 'sg-python-api[async]'
79
+ ```
80
+
81
+ From a checkout, use:
82
+
83
+ ```sh
84
+ python -m pip install '.[async]'
85
+ ```
86
+
87
+ ## Quick start
88
+
89
+ These examples read one project from your site. They take the site URL and script credentials from
90
+ environment variables so the API key stays out of source code; the client does not read these
91
+ variables itself. Set `SHOTGUN_URL` to your site's HTTPS URL, and set `SHOTGUN_SCRIPT_NAME` and
92
+ `SHOTGUN_API_KEY` to a script account that can read projects.
93
+
94
+ ### Synchronous client
95
+
96
+ ```python
97
+ import os
98
+
99
+ from shotgun_api3 import Shotgun
100
+
101
+ sg = Shotgun(
102
+ os.environ['SHOTGUN_URL'],
103
+ script_name=os.environ['SHOTGUN_SCRIPT_NAME'],
104
+ api_key=os.environ['SHOTGUN_API_KEY'],
105
+ )
106
+ try:
107
+ project = sg.find_one('Project', [], ['name'])
108
+ print(project)
109
+ finally:
110
+ sg.close()
111
+ ```
112
+
113
+ The script prints a project dictionary containing `type`, `id`, and `name`, or `None` when no
114
+ project is visible to that account. You can share one `Shotgun` client across threads; `close()`
115
+ releases its pooled connections when you no longer need it.
116
+
117
+ ### Asynchronous client
118
+
119
+ With the `async` extra installed and the same environment variables set, run:
120
+
121
+ ```python
122
+ import asyncio
123
+ import os
124
+
125
+ from shotgun_api3 import AsyncShotgun
126
+
127
+
128
+ async def main() -> None:
129
+ async with AsyncShotgun(
130
+ os.environ['SHOTGUN_URL'],
131
+ script_name=os.environ['SHOTGUN_SCRIPT_NAME'],
132
+ api_key=os.environ['SHOTGUN_API_KEY'],
133
+ ) as sg:
134
+ project = await sg.find_one('Project', [], ['name'])
135
+ print(project)
136
+
137
+
138
+ asyncio.run(main())
139
+ ```
140
+
141
+ The output matches the synchronous example. The `async with` block closes the client even when an
142
+ operation fails. Without the `async` extra, importing `AsyncShotgun` raises `ImportError`.
143
+
144
+ Share an `AsyncShotgun` only among tasks on one event loop, and cancel its work by cancelling the
145
+ task. A cancelled write may already have reached the server, so check its outcome before repeating
146
+ it. The [async reference](https://github.com/dactylo/sg-python-api/blob/v0.1.0/COMPATIBILITY.md#async-extension-surfaces) covers shutdown and
147
+ cancellation in detail.
148
+
149
+ ## Documentation
150
+
151
+ | If you want to... | Read |
152
+ | ----------------------------------------------------- | ---------------------------------------------------------------------------------- |
153
+ | Replace Autodesk's client in an existing application | [Migration guide](https://github.com/dactylo/sg-python-api/blob/v0.1.0/docs/migration-guide.md) |
154
+ | Check what behaves differently from Autodesk's client | [Compatibility](https://github.com/dactylo/sg-python-api/blob/v0.1.0/COMPATIBILITY.md) |
155
+ | Tune pooling and page prefetch, or observe requests | [Configuration extensions](https://github.com/dactylo/sg-python-api/blob/v0.1.0/COMPATIBILITY.md#configuration-and-observer-extensions) |
156
+ | Confirm which client loads and run a smoke test | [Testing an installation](https://github.com/dactylo/sg-python-api/blob/v0.1.0/docs/testing.md) |
157
+ | Compare measured latency and transfer memory | [Benchmarks](https://github.com/dactylo/sg-python-api/blob/v0.1.0/docs/benchmarks.md) |
158
+ | See changes by version | [Changelog](https://github.com/dactylo/sg-python-api/blob/v0.1.0/CHANGELOG.md) |
159
+
160
+ ## Development
161
+
162
+ See the [development guide](https://github.com/dactylo/sg-python-api/blob/v0.1.0/docs/development.md) for the required tools, CI, and dependency locks.
163
+ With those tools installed, run:
164
+
165
+ ```sh
166
+ make setup
167
+ make check
168
+ ```
169
+
170
+ `make check` also runs the live-site tests, which create and delete records, whenever the
171
+ `SG_TEST_*` variables described in the development guide are set.
172
+
173
+ ## License
174
+
175
+ sg-python-api is available under the [MIT License](https://github.com/dactylo/sg-python-api/blob/v0.1.0/LICENSE). It is an independent project and is
176
+ not affiliated with or endorsed by Autodesk. Flow Production Tracking is a product of Autodesk,
177
+ Inc.