sgalchemy 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.
- sgalchemy-0.1.0/.gitignore +46 -0
- sgalchemy-0.1.0/LICENSE +21 -0
- sgalchemy-0.1.0/PKG-INFO +147 -0
- sgalchemy-0.1.0/README.md +120 -0
- sgalchemy-0.1.0/pyproject.toml +160 -0
- sgalchemy-0.1.0/src/sgalchemy/__init__.py +101 -0
- sgalchemy-0.1.0/src/sgalchemy/__main__.py +8 -0
- sgalchemy-0.1.0/src/sgalchemy/_cli.py +343 -0
- sgalchemy-0.1.0/src/sgalchemy/_core/__init__.py +1 -0
- sgalchemy-0.1.0/src/sgalchemy/_core/_async_backend.py +575 -0
- sgalchemy-0.1.0/src/sgalchemy/_core/_async_engine.py +595 -0
- sgalchemy-0.1.0/src/sgalchemy/_core/_compiler.py +622 -0
- sgalchemy-0.1.0/src/sgalchemy/_core/_construction.py +277 -0
- sgalchemy-0.1.0/src/sgalchemy/_core/_engine.py +618 -0
- sgalchemy-0.1.0/src/sgalchemy/_core/_engine_common.py +192 -0
- sgalchemy-0.1.0/src/sgalchemy/_core/_entity.py +55 -0
- sgalchemy-0.1.0/src/sgalchemy/_core/_expressions.py +756 -0
- sgalchemy-0.1.0/src/sgalchemy/_core/_options.py +240 -0
- sgalchemy-0.1.0/src/sgalchemy/_core/_registry.py +1168 -0
- sgalchemy-0.1.0/src/sgalchemy/_core/_result.py +41 -0
- sgalchemy-0.1.0/src/sgalchemy/_core/_schema_drift.py +27 -0
- sgalchemy-0.1.0/src/sgalchemy/_core/_statement.py +204 -0
- sgalchemy-0.1.0/src/sgalchemy/_downstream.py +44 -0
- sgalchemy-0.1.0/src/sgalchemy/_errors.py +95 -0
- sgalchemy-0.1.0/src/sgalchemy/_orm/__init__.py +34 -0
- sgalchemy-0.1.0/src/sgalchemy/_orm/_async_loaders.py +176 -0
- sgalchemy-0.1.0/src/sgalchemy/_orm/_async_scoping.py +376 -0
- sgalchemy-0.1.0/src/sgalchemy/_orm/_async_session.py +1334 -0
- sgalchemy-0.1.0/src/sgalchemy/_orm/_loaders.py +333 -0
- sgalchemy-0.1.0/src/sgalchemy/_orm/_scope_common.py +55 -0
- sgalchemy-0.1.0/src/sgalchemy/_orm/_scoping.py +506 -0
- sgalchemy-0.1.0/src/sgalchemy/_orm/_session.py +1028 -0
- sgalchemy-0.1.0/src/sgalchemy/_orm/_session_common.py +51 -0
- sgalchemy-0.1.0/src/sgalchemy/_orm/_state.py +718 -0
- sgalchemy-0.1.0/src/sgalchemy/_orm/_uow.py +2137 -0
- sgalchemy-0.1.0/src/sgalchemy/_shotgun.py +370 -0
- sgalchemy-0.1.0/src/sgalchemy/_stubgen.py +381 -0
- sgalchemy-0.1.0/src/sgalchemy/_stubgen_registry.py +162 -0
- sgalchemy-0.1.0/src/sgalchemy/entities/__init__.py +34 -0
- sgalchemy-0.1.0/src/sgalchemy/py.typed +0 -0
- sgalchemy-0.1.0/src/sgalchemy/schema/__init__.py +7 -0
- sgalchemy-0.1.0/src/sgalchemy/schema/_aliases.py +89 -0
- sgalchemy-0.1.0/src/sgalchemy/schema/_cache.py +190 -0
- sgalchemy-0.1.0/src/sgalchemy/schema/_cachedir.py +82 -0
- sgalchemy-0.1.0/src/sgalchemy/schema/_model.py +132 -0
- sgalchemy-0.1.0/src/sgalchemy/schema/_policy.py +34 -0
- sgalchemy-0.1.0/src/sgalchemy/testing/__init__.py +227 -0
|
@@ -0,0 +1,46 @@
|
|
|
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
|
+
# Editors
|
|
41
|
+
.vscode/
|
|
42
|
+
.idea/
|
|
43
|
+
|
|
44
|
+
# OS
|
|
45
|
+
.DS_Store
|
|
46
|
+
Thumbs.db
|
sgalchemy-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 the sgalchemy 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.
|
sgalchemy-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: sgalchemy
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: SQLAlchemy-style ORM for Autodesk Flow Production Tracking
|
|
5
|
+
Project-URL: Documentation, https://github.com/dactylo/sgalchemy/tree/main/docs
|
|
6
|
+
Project-URL: Homepage, https://github.com/dactylo/sgalchemy
|
|
7
|
+
Project-URL: Issues, https://github.com/dactylo/sgalchemy/issues
|
|
8
|
+
Project-URL: Repository, https://github.com/dactylo/sgalchemy
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: flow-production-tracking,orm,shotgrid,shotgun,sqlalchemy
|
|
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
|
+
Provides-Extra: api
|
|
21
|
+
Requires-Dist: sg-python-api<0.2,>=0.1; extra == 'api'
|
|
22
|
+
Provides-Extra: async
|
|
23
|
+
Requires-Dist: sg-python-api[async]<0.2,>=0.1; extra == 'async'
|
|
24
|
+
Provides-Extra: official
|
|
25
|
+
Requires-Dist: shotgun-api3>=3.10.2; extra == 'official'
|
|
26
|
+
Description-Content-Type: text/markdown
|
|
27
|
+
|
|
28
|
+
# sgalchemy
|
|
29
|
+
|
|
30
|
+
sgalchemy is a SQLAlchemy-style ORM for Autodesk Flow Production Tracking. It gives pipeline code
|
|
31
|
+
typed entity classes reflected from your site's schema, `select()` queries with relationship
|
|
32
|
+
loaders, and a `Session` that keeps one object per record and sends your changes only when you call
|
|
33
|
+
`commit()`. You decide when the network is used: reading an attribute never makes a hidden provider
|
|
34
|
+
call.
|
|
35
|
+
|
|
36
|
+
This documentation uses SG for Flow Production Tracking, formerly named Shotgun and ShotGrid.
|
|
37
|
+
|
|
38
|
+
## Why use it?
|
|
39
|
+
|
|
40
|
+
Calling the provider API directly means working with dictionaries: you choose field lists for each
|
|
41
|
+
`find()`, track which records changed, and assemble `batch()` requests yourself. sgalchemy handles
|
|
42
|
+
that bookkeeping while keeping every provider call explicit.
|
|
43
|
+
|
|
44
|
+
The SQLAlchemy concepts are familiar, but SG is not a relational database, and sgalchemy follows
|
|
45
|
+
the provider where the two differ:
|
|
46
|
+
|
|
47
|
+
- Schema loading is explicit. Call `load_schema()` before you use entity classes; sgalchemy caches
|
|
48
|
+
the schema on disk so later runs can skip the fetch.
|
|
49
|
+
- Queries never send pending changes. `commit()` is the only write boundary.
|
|
50
|
+
- Reading a field or relationship that a query did not load raises an error instead of calling the
|
|
51
|
+
network. Synchronous code can opt into `lazyload()`.
|
|
52
|
+
- A commit is atomic only when it compiles to one transactional `batch()` call. Revives, thumbnail
|
|
53
|
+
uploads, and creates that link to other new records need extra calls, so a failure can leave part
|
|
54
|
+
of a commit applied. [Sessions and writes](https://github.com/dactylo/sgalchemy/blob/v0.1.0/docs/sessions.md) explains how to recover.
|
|
55
|
+
|
|
56
|
+
## Installation
|
|
57
|
+
|
|
58
|
+
You need Python 3.11 or newer and one compatible SG API provider. From a checkout, install
|
|
59
|
+
sgalchemy with the extra for your provider. For Autodesk's provider, run:
|
|
60
|
+
|
|
61
|
+
```sh
|
|
62
|
+
python -m pip install '.[official]'
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Choose exactly one extra, and replace `official` in the command to pick another:
|
|
66
|
+
|
|
67
|
+
- `official` installs Autodesk's `shotgun-api3` for synchronous and threaded async engines.
|
|
68
|
+
- `api` installs `sg-python-api` for synchronous and threaded async engines.
|
|
69
|
+
- `async` installs `sg-python-api` with its native async client, which also supports native async
|
|
70
|
+
engines.
|
|
71
|
+
|
|
72
|
+
If your application already supplies a compatible API client, install the base package with
|
|
73
|
+
`python -m pip install .` and install the client separately.
|
|
74
|
+
|
|
75
|
+
## Quick start
|
|
76
|
+
|
|
77
|
+
This example queries in-progress shots and creates one shot. It authenticates with a session token
|
|
78
|
+
that your application supplies in `SHOTGUN_SESSION_TOKEN`; see
|
|
79
|
+
[Connections and authentication](https://github.com/dactylo/sgalchemy/blob/v0.1.0/docs/authentication.md) for other credentials. Replace the site
|
|
80
|
+
URL and the status and code values with your studio's, and add any other fields your site requires
|
|
81
|
+
for a new shot. Run it against a test site: the commit creates a real record.
|
|
82
|
+
|
|
83
|
+
```python
|
|
84
|
+
import os
|
|
85
|
+
|
|
86
|
+
from sgalchemy import Session, create_engine, select
|
|
87
|
+
|
|
88
|
+
engine = create_engine(
|
|
89
|
+
'https://example.shotgunstudio.com',
|
|
90
|
+
session_token=os.environ['SHOTGUN_SESSION_TOKEN'],
|
|
91
|
+
)
|
|
92
|
+
try:
|
|
93
|
+
engine.load_schema()
|
|
94
|
+
from sgalchemy.entities import Shot
|
|
95
|
+
|
|
96
|
+
with Session(engine) as session:
|
|
97
|
+
shots = session.scalars(select(Shot).where(Shot.sg_status_list == 'ip'))
|
|
98
|
+
print(f'{len(shots)} shots in progress')
|
|
99
|
+
|
|
100
|
+
new_shot = Shot.new(code='sq010_sh020')
|
|
101
|
+
session.add(new_shot)
|
|
102
|
+
session.commit()
|
|
103
|
+
print(new_shot)
|
|
104
|
+
finally:
|
|
105
|
+
engine.dispose()
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
The script prints the number of matching shots, then the new shot with its server ID, such as
|
|
109
|
+
`Shot(id=1234, code='sq010_sh020')`.
|
|
110
|
+
|
|
111
|
+
`load_schema()` fetches the site schema, or reads a fresh cached copy, before you import entity
|
|
112
|
+
classes. The query does not send the pending create; `session.commit()` does. Leaving the `with`
|
|
113
|
+
block closes the session and discards any work you did not commit. If a commit fails after a
|
|
114
|
+
provider call, [check what was applied](https://github.com/dactylo/sgalchemy/blob/v0.1.0/docs/sessions.md#recover-from-a-failed-commit) before you
|
|
115
|
+
retry.
|
|
116
|
+
|
|
117
|
+
To give your type checker the reflected fields of `Shot`, generate site-specific stubs; see
|
|
118
|
+
[typed stubs](https://github.com/dactylo/sgalchemy/blob/v0.1.0/docs/tooling.md#generate-typed-stubs).
|
|
119
|
+
|
|
120
|
+
## Documentation
|
|
121
|
+
|
|
122
|
+
| If you want to... | Read |
|
|
123
|
+
| ----------------------------------------------------------- | --------------------------------------------------------------- |
|
|
124
|
+
| Choose credentials and recover when SG rejects them | [Connections and authentication](https://github.com/dactylo/sgalchemy/blob/v0.1.0/docs/authentication.md) |
|
|
125
|
+
| Write queries and load related entities | [Queries and relationships](https://github.com/dactylo/sgalchemy/blob/v0.1.0/docs/relationships.md) |
|
|
126
|
+
| Commit changes, upload thumbnails, or use session factories | [Sessions and writes](https://github.com/dactylo/sgalchemy/blob/v0.1.0/docs/sessions.md) |
|
|
127
|
+
| Recover from a failed or partial commit | [Failed commits](https://github.com/dactylo/sgalchemy/blob/v0.1.0/docs/sessions.md#recover-from-a-failed-commit) |
|
|
128
|
+
| Use sgalchemy from asyncio code | [Async usage](https://github.com/dactylo/sgalchemy/blob/v0.1.0/docs/async.md) |
|
|
129
|
+
| Type-check with stubs, manage the schema cache, or test | [Typing, schema cache, and testing](https://github.com/dactylo/sgalchemy/blob/v0.1.0/docs/tooling.md) |
|
|
130
|
+
| See changes by version | [Changelog](https://github.com/dactylo/sgalchemy/blob/v0.1.0/CHANGELOG.md) |
|
|
131
|
+
|
|
132
|
+
## Development
|
|
133
|
+
|
|
134
|
+
See the [development guide](https://github.com/dactylo/sgalchemy/blob/v0.1.0/docs/development.md) for the required tools, CI, and dependency locks.
|
|
135
|
+
With those tools installed, run:
|
|
136
|
+
|
|
137
|
+
```sh
|
|
138
|
+
make setup
|
|
139
|
+
make check
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
The checks never contact a live SG site unless you configure the opt-in live test.
|
|
143
|
+
|
|
144
|
+
## License
|
|
145
|
+
|
|
146
|
+
sgalchemy is available under the [MIT License](https://github.com/dactylo/sgalchemy/blob/v0.1.0/LICENSE). It is an independent project and is not
|
|
147
|
+
affiliated with or endorsed by Autodesk. Flow Production Tracking is a product of Autodesk, Inc.
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# sgalchemy
|
|
2
|
+
|
|
3
|
+
sgalchemy is a SQLAlchemy-style ORM for Autodesk Flow Production Tracking. It gives pipeline code
|
|
4
|
+
typed entity classes reflected from your site's schema, `select()` queries with relationship
|
|
5
|
+
loaders, and a `Session` that keeps one object per record and sends your changes only when you call
|
|
6
|
+
`commit()`. You decide when the network is used: reading an attribute never makes a hidden provider
|
|
7
|
+
call.
|
|
8
|
+
|
|
9
|
+
This documentation uses SG for Flow Production Tracking, formerly named Shotgun and ShotGrid.
|
|
10
|
+
|
|
11
|
+
## Why use it?
|
|
12
|
+
|
|
13
|
+
Calling the provider API directly means working with dictionaries: you choose field lists for each
|
|
14
|
+
`find()`, track which records changed, and assemble `batch()` requests yourself. sgalchemy handles
|
|
15
|
+
that bookkeeping while keeping every provider call explicit.
|
|
16
|
+
|
|
17
|
+
The SQLAlchemy concepts are familiar, but SG is not a relational database, and sgalchemy follows
|
|
18
|
+
the provider where the two differ:
|
|
19
|
+
|
|
20
|
+
- Schema loading is explicit. Call `load_schema()` before you use entity classes; sgalchemy caches
|
|
21
|
+
the schema on disk so later runs can skip the fetch.
|
|
22
|
+
- Queries never send pending changes. `commit()` is the only write boundary.
|
|
23
|
+
- Reading a field or relationship that a query did not load raises an error instead of calling the
|
|
24
|
+
network. Synchronous code can opt into `lazyload()`.
|
|
25
|
+
- A commit is atomic only when it compiles to one transactional `batch()` call. Revives, thumbnail
|
|
26
|
+
uploads, and creates that link to other new records need extra calls, so a failure can leave part
|
|
27
|
+
of a commit applied. [Sessions and writes](docs/sessions.md) explains how to recover.
|
|
28
|
+
|
|
29
|
+
## Installation
|
|
30
|
+
|
|
31
|
+
You need Python 3.11 or newer and one compatible SG API provider. From a checkout, install
|
|
32
|
+
sgalchemy with the extra for your provider. For Autodesk's provider, run:
|
|
33
|
+
|
|
34
|
+
```sh
|
|
35
|
+
python -m pip install '.[official]'
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Choose exactly one extra, and replace `official` in the command to pick another:
|
|
39
|
+
|
|
40
|
+
- `official` installs Autodesk's `shotgun-api3` for synchronous and threaded async engines.
|
|
41
|
+
- `api` installs `sg-python-api` for synchronous and threaded async engines.
|
|
42
|
+
- `async` installs `sg-python-api` with its native async client, which also supports native async
|
|
43
|
+
engines.
|
|
44
|
+
|
|
45
|
+
If your application already supplies a compatible API client, install the base package with
|
|
46
|
+
`python -m pip install .` and install the client separately.
|
|
47
|
+
|
|
48
|
+
## Quick start
|
|
49
|
+
|
|
50
|
+
This example queries in-progress shots and creates one shot. It authenticates with a session token
|
|
51
|
+
that your application supplies in `SHOTGUN_SESSION_TOKEN`; see
|
|
52
|
+
[Connections and authentication](docs/authentication.md) for other credentials. Replace the site
|
|
53
|
+
URL and the status and code values with your studio's, and add any other fields your site requires
|
|
54
|
+
for a new shot. Run it against a test site: the commit creates a real record.
|
|
55
|
+
|
|
56
|
+
```python
|
|
57
|
+
import os
|
|
58
|
+
|
|
59
|
+
from sgalchemy import Session, create_engine, select
|
|
60
|
+
|
|
61
|
+
engine = create_engine(
|
|
62
|
+
'https://example.shotgunstudio.com',
|
|
63
|
+
session_token=os.environ['SHOTGUN_SESSION_TOKEN'],
|
|
64
|
+
)
|
|
65
|
+
try:
|
|
66
|
+
engine.load_schema()
|
|
67
|
+
from sgalchemy.entities import Shot
|
|
68
|
+
|
|
69
|
+
with Session(engine) as session:
|
|
70
|
+
shots = session.scalars(select(Shot).where(Shot.sg_status_list == 'ip'))
|
|
71
|
+
print(f'{len(shots)} shots in progress')
|
|
72
|
+
|
|
73
|
+
new_shot = Shot.new(code='sq010_sh020')
|
|
74
|
+
session.add(new_shot)
|
|
75
|
+
session.commit()
|
|
76
|
+
print(new_shot)
|
|
77
|
+
finally:
|
|
78
|
+
engine.dispose()
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
The script prints the number of matching shots, then the new shot with its server ID, such as
|
|
82
|
+
`Shot(id=1234, code='sq010_sh020')`.
|
|
83
|
+
|
|
84
|
+
`load_schema()` fetches the site schema, or reads a fresh cached copy, before you import entity
|
|
85
|
+
classes. The query does not send the pending create; `session.commit()` does. Leaving the `with`
|
|
86
|
+
block closes the session and discards any work you did not commit. If a commit fails after a
|
|
87
|
+
provider call, [check what was applied](docs/sessions.md#recover-from-a-failed-commit) before you
|
|
88
|
+
retry.
|
|
89
|
+
|
|
90
|
+
To give your type checker the reflected fields of `Shot`, generate site-specific stubs; see
|
|
91
|
+
[typed stubs](docs/tooling.md#generate-typed-stubs).
|
|
92
|
+
|
|
93
|
+
## Documentation
|
|
94
|
+
|
|
95
|
+
| If you want to... | Read |
|
|
96
|
+
| ----------------------------------------------------------- | --------------------------------------------------------------- |
|
|
97
|
+
| Choose credentials and recover when SG rejects them | [Connections and authentication](docs/authentication.md) |
|
|
98
|
+
| Write queries and load related entities | [Queries and relationships](docs/relationships.md) |
|
|
99
|
+
| Commit changes, upload thumbnails, or use session factories | [Sessions and writes](docs/sessions.md) |
|
|
100
|
+
| Recover from a failed or partial commit | [Failed commits](docs/sessions.md#recover-from-a-failed-commit) |
|
|
101
|
+
| Use sgalchemy from asyncio code | [Async usage](docs/async.md) |
|
|
102
|
+
| Type-check with stubs, manage the schema cache, or test | [Typing, schema cache, and testing](docs/tooling.md) |
|
|
103
|
+
| See changes by version | [Changelog](CHANGELOG.md) |
|
|
104
|
+
|
|
105
|
+
## Development
|
|
106
|
+
|
|
107
|
+
See the [development guide](docs/development.md) for the required tools, CI, and dependency locks.
|
|
108
|
+
With those tools installed, run:
|
|
109
|
+
|
|
110
|
+
```sh
|
|
111
|
+
make setup
|
|
112
|
+
make check
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
The checks never contact a live SG site unless you configure the opt-in live test.
|
|
116
|
+
|
|
117
|
+
## License
|
|
118
|
+
|
|
119
|
+
sgalchemy is available under the [MIT License](LICENSE). It is an independent project and is not
|
|
120
|
+
affiliated with or endorsed by Autodesk. Flow Production Tracking is a product of Autodesk, Inc.
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
# ==========================================================
|
|
2
|
+
# Build System
|
|
3
|
+
# ==========================================================
|
|
4
|
+
[build-system]
|
|
5
|
+
build-backend = "hatchling.build"
|
|
6
|
+
requires = [ "editables~=0.3", "hatch-fancy-pypi-readme>=25.1", "hatchling>=1.27" ]
|
|
7
|
+
|
|
8
|
+
# ==========================================================
|
|
9
|
+
# Project Metadata
|
|
10
|
+
# ==========================================================
|
|
11
|
+
[project]
|
|
12
|
+
name = "sgalchemy"
|
|
13
|
+
version = "0.1.0"
|
|
14
|
+
description = "SQLAlchemy-style ORM for Autodesk Flow Production Tracking"
|
|
15
|
+
keywords = [ "flow-production-tracking", "orm", "shotgrid", "shotgun", "sqlalchemy" ]
|
|
16
|
+
license = "MIT"
|
|
17
|
+
license-files = [ "LICENSE" ]
|
|
18
|
+
requires-python = ">=3.11"
|
|
19
|
+
classifiers = [
|
|
20
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
21
|
+
"Programming Language :: Python :: 3.11",
|
|
22
|
+
"Programming Language :: Python :: 3.12",
|
|
23
|
+
"Programming Language :: Python :: 3.13",
|
|
24
|
+
"Programming Language :: Python :: 3.14",
|
|
25
|
+
"Programming Language :: Python :: 3.15",
|
|
26
|
+
"Typing :: Typed",
|
|
27
|
+
]
|
|
28
|
+
dynamic = [ "readme" ]
|
|
29
|
+
dependencies = []
|
|
30
|
+
optional-dependencies.api = [
|
|
31
|
+
"sg-python-api>=0.1,<0.2",
|
|
32
|
+
]
|
|
33
|
+
optional-dependencies.async = [
|
|
34
|
+
"sg-python-api[async]>=0.1,<0.2",
|
|
35
|
+
]
|
|
36
|
+
optional-dependencies.official = [
|
|
37
|
+
"shotgun-api3>=3.10.2",
|
|
38
|
+
]
|
|
39
|
+
urls.Documentation = "https://github.com/dactylo/sgalchemy/tree/main/docs"
|
|
40
|
+
urls.Homepage = "https://github.com/dactylo/sgalchemy"
|
|
41
|
+
urls.Issues = "https://github.com/dactylo/sgalchemy/issues"
|
|
42
|
+
urls.Repository = "https://github.com/dactylo/sgalchemy"
|
|
43
|
+
scripts.sgalchemy = "sgalchemy._cli:main"
|
|
44
|
+
|
|
45
|
+
[dependency-groups]
|
|
46
|
+
dev = [
|
|
47
|
+
"mdformat",
|
|
48
|
+
"mdformat-config",
|
|
49
|
+
"mdformat-frontmatter",
|
|
50
|
+
"mdformat-gfm",
|
|
51
|
+
"mdformat-pyproject",
|
|
52
|
+
"mdformat-ruff",
|
|
53
|
+
"mdformat-shfmt",
|
|
54
|
+
"pyproject-fmt",
|
|
55
|
+
"ruff",
|
|
56
|
+
{ include-group = "test" },
|
|
57
|
+
]
|
|
58
|
+
test = [
|
|
59
|
+
"pyright",
|
|
60
|
+
"pytest",
|
|
61
|
+
"pytest-xdist>=3.2",
|
|
62
|
+
]
|
|
63
|
+
|
|
64
|
+
[tool.hatch]
|
|
65
|
+
metadata.hooks.fancy-pypi-readme.content-type = "text/markdown"
|
|
66
|
+
metadata.hooks.fancy-pypi-readme.fragments = [ { path = "README.md" } ]
|
|
67
|
+
build.targets.wheel.packages = [ "src/sgalchemy" ]
|
|
68
|
+
build.targets.sdist.include = [ "/README.md", "/src/sgalchemy" ]
|
|
69
|
+
|
|
70
|
+
# Keep checkout links relative; package links target this release.
|
|
71
|
+
[[tool.hatch.metadata.hooks.fancy-pypi-readme.substitutions]]
|
|
72
|
+
pattern = '(!\[[^\]\n]*\]\()(?![A-Za-z][A-Za-z0-9+.-]*:|[/#])(?:\./)?([^\s)]+)(\))'
|
|
73
|
+
replacement = '\1https://raw.githubusercontent.com/dactylo/sgalchemy/v$HFPR_VERSION/\2\3'
|
|
74
|
+
|
|
75
|
+
[[tool.hatch.metadata.hooks.fancy-pypi-readme.substitutions]]
|
|
76
|
+
pattern = '(?<!!)(\[[^\]\n]+\]\()(?![A-Za-z][A-Za-z0-9+.-]*:|[/#])(?:\./)?([^\s)]+)(\))'
|
|
77
|
+
replacement = '\1https://github.com/dactylo/sgalchemy/blob/v$HFPR_VERSION/\2\3'
|
|
78
|
+
|
|
79
|
+
[tool.uv]
|
|
80
|
+
# CI supplies the native Taplo CLI; its Python package lacks Linux/Windows ARM wheels.
|
|
81
|
+
exclude-dependencies = [
|
|
82
|
+
{ package = { name = "mdformat-config", version = "0.2.1" }, dependencies = [ "taplo" ] },
|
|
83
|
+
]
|
|
84
|
+
|
|
85
|
+
# ==========================================================
|
|
86
|
+
# Ruff Configuration
|
|
87
|
+
# ==========================================================
|
|
88
|
+
[tool.ruff]
|
|
89
|
+
target-version = "py311"
|
|
90
|
+
line-length = 99
|
|
91
|
+
format.quote-style = "single"
|
|
92
|
+
format.exclude = [ "*.md" ]
|
|
93
|
+
lint.select = [ "ALL" ]
|
|
94
|
+
lint.ignore = [
|
|
95
|
+
"COM812", # Conflicts with the Ruff formatter
|
|
96
|
+
"CPY001", # Copyright notices are not required
|
|
97
|
+
"EM101", # Exception must not use string literal
|
|
98
|
+
"EM102", # Exception must not use f-string literal
|
|
99
|
+
"FBT001", # Boolean positional arg in function definition
|
|
100
|
+
"FBT002", # Boolean default value in function definition
|
|
101
|
+
"Q000", # Conflicts with the Ruff formatter
|
|
102
|
+
"Q003", # Conflicts with the Ruff formatter
|
|
103
|
+
"T201", # print() found
|
|
104
|
+
"TRY003", # Avoid specifying long messages outside exception class
|
|
105
|
+
]
|
|
106
|
+
lint.per-file-ignores."src/sgalchemy/_core/_engine.py" = [ "SLF001" ]
|
|
107
|
+
lint.per-file-ignores."src/sgalchemy/_orm/_async_session.py" = [ "SLF001" ]
|
|
108
|
+
lint.per-file-ignores."src/sgalchemy/_orm/_uow.py" = [ "SLF001" ]
|
|
109
|
+
lint.per-file-ignores."tests/**/*.py" = [
|
|
110
|
+
"D", # No docstrings required in tests
|
|
111
|
+
"INP001", # pytest rootdir-style tests directory has no __init__.py by design
|
|
112
|
+
"PLR2004", # Allow magic values in tests
|
|
113
|
+
"S101", # Allow asserts in tests
|
|
114
|
+
"S603", # Allow subprocess calls in tests
|
|
115
|
+
"S607", # Allow partial executable paths in tests
|
|
116
|
+
"SLF001", # Tests intentionally inspect and patch private state
|
|
117
|
+
]
|
|
118
|
+
lint.isort.known-first-party = [ "sgalchemy" ]
|
|
119
|
+
lint.pydocstyle.convention = "google"
|
|
120
|
+
|
|
121
|
+
[tool.pyproject-fmt]
|
|
122
|
+
max_supported_python = "3.15"
|
|
123
|
+
|
|
124
|
+
# ==========================================================
|
|
125
|
+
# Pyright Configuration
|
|
126
|
+
# ==========================================================
|
|
127
|
+
[tool.pyright]
|
|
128
|
+
pythonVersion = "3.11" # Keep in sync with tool.ruff.target-version
|
|
129
|
+
typeCheckingMode = "strict"
|
|
130
|
+
extraPaths = [ ".", "src" ]
|
|
131
|
+
executionEnvironments = [
|
|
132
|
+
{ root = "tests", reportPrivateUsage = "none" },
|
|
133
|
+
{ root = "." },
|
|
134
|
+
]
|
|
135
|
+
|
|
136
|
+
# ==========================================================
|
|
137
|
+
# Pytest Configuration
|
|
138
|
+
# ==========================================================
|
|
139
|
+
[tool.pytest]
|
|
140
|
+
ini_options.testpaths = [ "tests" ]
|
|
141
|
+
ini_options.pythonpath = [ "src" ]
|
|
142
|
+
ini_options.addopts = [ "-q", "--strict-markers", "--tb=short" ]
|
|
143
|
+
ini_options.markers = [
|
|
144
|
+
"live: requires explicit Flow Production Tracking test-site configuration",
|
|
145
|
+
"mockgun_gap: exercises behavior Mockgun cannot emulate; skipped against the Mockgun backend",
|
|
146
|
+
"native_async: exercises native async provider behavior",
|
|
147
|
+
"provider_contract: exercises the selected real provider without credentials",
|
|
148
|
+
"slow: marks tests as slow (deselect with '-m \"not slow\"')",
|
|
149
|
+
]
|
|
150
|
+
ini_options.faulthandler_timeout = 300
|
|
151
|
+
|
|
152
|
+
[tool.defsort]
|
|
153
|
+
method-bodies = "across-categories"
|
|
154
|
+
|
|
155
|
+
# ==========================================================
|
|
156
|
+
# Mdformat Configuration (Requires mdformat-pyproject)
|
|
157
|
+
# ==========================================================
|
|
158
|
+
[tool.mdformat]
|
|
159
|
+
wrap = 99 # Keep in sync with tool.ruff.line-length
|
|
160
|
+
number = true
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"""Build reflected queries and manage explicit entity reads and writes.
|
|
2
|
+
|
|
3
|
+
Create an engine, call its `load_schema()` method, and obtain reflected classes
|
|
4
|
+
from `engine.entities`. Async engines require awaited schema preparation.
|
|
5
|
+
Queries never autoflush staged changes; call `Session.commit()` or await
|
|
6
|
+
`AsyncSession.commit()` explicitly.
|
|
7
|
+
|
|
8
|
+
Factories use the process default Registry unless an explicit Registry or
|
|
9
|
+
`bind_default=False` selects isolation. Importing this package installs the
|
|
10
|
+
synchronous `Entity.find()` callback; `connect()` configures its default
|
|
11
|
+
session. Importing the package performs no provider or schema-cache I/O.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from sgalchemy._core._async_engine import AsyncEngine, create_async_engine
|
|
15
|
+
from sgalchemy._core._engine import Engine, create_engine
|
|
16
|
+
from sgalchemy._core._expressions import FieldExpr, and_, func, or_, raw
|
|
17
|
+
from sgalchemy._core._options import defer, joinedload, lazyload, load_only, selectinload
|
|
18
|
+
from sgalchemy._core._registry import (
|
|
19
|
+
Entity,
|
|
20
|
+
InspectionInfo,
|
|
21
|
+
Registry,
|
|
22
|
+
default_registry,
|
|
23
|
+
inspect,
|
|
24
|
+
set_default_registry,
|
|
25
|
+
)
|
|
26
|
+
from sgalchemy._core._result import Result, Row
|
|
27
|
+
from sgalchemy._core._statement import Select, select
|
|
28
|
+
from sgalchemy._errors import (
|
|
29
|
+
CommitError,
|
|
30
|
+
CompilationError,
|
|
31
|
+
RefreshRequiredError,
|
|
32
|
+
RelationshipTargetUnavailableError,
|
|
33
|
+
RetiredEntityWriteError,
|
|
34
|
+
SchemaUnavailableError,
|
|
35
|
+
SgalchemyError,
|
|
36
|
+
StaleObjectError,
|
|
37
|
+
UnfetchedFieldError,
|
|
38
|
+
)
|
|
39
|
+
from sgalchemy._orm import (
|
|
40
|
+
AsyncScopedSession,
|
|
41
|
+
AsyncSession,
|
|
42
|
+
AsyncSessionFactory,
|
|
43
|
+
ScopedSession,
|
|
44
|
+
Session,
|
|
45
|
+
SessionFactory,
|
|
46
|
+
async_scoped_session,
|
|
47
|
+
async_sessionmaker,
|
|
48
|
+
connect,
|
|
49
|
+
scoped_session,
|
|
50
|
+
sessionmaker,
|
|
51
|
+
)
|
|
52
|
+
from sgalchemy._shotgun import AsyncShotgunLike, ShotgunLike
|
|
53
|
+
|
|
54
|
+
__all__ = [
|
|
55
|
+
'AsyncEngine',
|
|
56
|
+
'AsyncScopedSession',
|
|
57
|
+
'AsyncSession',
|
|
58
|
+
'AsyncSessionFactory',
|
|
59
|
+
'AsyncShotgunLike',
|
|
60
|
+
'CommitError',
|
|
61
|
+
'CompilationError',
|
|
62
|
+
'Engine',
|
|
63
|
+
'Entity',
|
|
64
|
+
'FieldExpr',
|
|
65
|
+
'InspectionInfo',
|
|
66
|
+
'RefreshRequiredError',
|
|
67
|
+
'Registry',
|
|
68
|
+
'RelationshipTargetUnavailableError',
|
|
69
|
+
'Result',
|
|
70
|
+
'RetiredEntityWriteError',
|
|
71
|
+
'Row',
|
|
72
|
+
'SchemaUnavailableError',
|
|
73
|
+
'ScopedSession',
|
|
74
|
+
'Select',
|
|
75
|
+
'Session',
|
|
76
|
+
'SessionFactory',
|
|
77
|
+
'SgalchemyError',
|
|
78
|
+
'ShotgunLike',
|
|
79
|
+
'StaleObjectError',
|
|
80
|
+
'UnfetchedFieldError',
|
|
81
|
+
'and_',
|
|
82
|
+
'async_scoped_session',
|
|
83
|
+
'async_sessionmaker',
|
|
84
|
+
'connect',
|
|
85
|
+
'create_async_engine',
|
|
86
|
+
'create_engine',
|
|
87
|
+
'default_registry',
|
|
88
|
+
'defer',
|
|
89
|
+
'func',
|
|
90
|
+
'inspect',
|
|
91
|
+
'joinedload',
|
|
92
|
+
'lazyload',
|
|
93
|
+
'load_only',
|
|
94
|
+
'or_',
|
|
95
|
+
'raw',
|
|
96
|
+
'scoped_session',
|
|
97
|
+
'select',
|
|
98
|
+
'selectinload',
|
|
99
|
+
'sessionmaker',
|
|
100
|
+
'set_default_registry',
|
|
101
|
+
]
|