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.
Files changed (47) hide show
  1. sgalchemy-0.1.0/.gitignore +46 -0
  2. sgalchemy-0.1.0/LICENSE +21 -0
  3. sgalchemy-0.1.0/PKG-INFO +147 -0
  4. sgalchemy-0.1.0/README.md +120 -0
  5. sgalchemy-0.1.0/pyproject.toml +160 -0
  6. sgalchemy-0.1.0/src/sgalchemy/__init__.py +101 -0
  7. sgalchemy-0.1.0/src/sgalchemy/__main__.py +8 -0
  8. sgalchemy-0.1.0/src/sgalchemy/_cli.py +343 -0
  9. sgalchemy-0.1.0/src/sgalchemy/_core/__init__.py +1 -0
  10. sgalchemy-0.1.0/src/sgalchemy/_core/_async_backend.py +575 -0
  11. sgalchemy-0.1.0/src/sgalchemy/_core/_async_engine.py +595 -0
  12. sgalchemy-0.1.0/src/sgalchemy/_core/_compiler.py +622 -0
  13. sgalchemy-0.1.0/src/sgalchemy/_core/_construction.py +277 -0
  14. sgalchemy-0.1.0/src/sgalchemy/_core/_engine.py +618 -0
  15. sgalchemy-0.1.0/src/sgalchemy/_core/_engine_common.py +192 -0
  16. sgalchemy-0.1.0/src/sgalchemy/_core/_entity.py +55 -0
  17. sgalchemy-0.1.0/src/sgalchemy/_core/_expressions.py +756 -0
  18. sgalchemy-0.1.0/src/sgalchemy/_core/_options.py +240 -0
  19. sgalchemy-0.1.0/src/sgalchemy/_core/_registry.py +1168 -0
  20. sgalchemy-0.1.0/src/sgalchemy/_core/_result.py +41 -0
  21. sgalchemy-0.1.0/src/sgalchemy/_core/_schema_drift.py +27 -0
  22. sgalchemy-0.1.0/src/sgalchemy/_core/_statement.py +204 -0
  23. sgalchemy-0.1.0/src/sgalchemy/_downstream.py +44 -0
  24. sgalchemy-0.1.0/src/sgalchemy/_errors.py +95 -0
  25. sgalchemy-0.1.0/src/sgalchemy/_orm/__init__.py +34 -0
  26. sgalchemy-0.1.0/src/sgalchemy/_orm/_async_loaders.py +176 -0
  27. sgalchemy-0.1.0/src/sgalchemy/_orm/_async_scoping.py +376 -0
  28. sgalchemy-0.1.0/src/sgalchemy/_orm/_async_session.py +1334 -0
  29. sgalchemy-0.1.0/src/sgalchemy/_orm/_loaders.py +333 -0
  30. sgalchemy-0.1.0/src/sgalchemy/_orm/_scope_common.py +55 -0
  31. sgalchemy-0.1.0/src/sgalchemy/_orm/_scoping.py +506 -0
  32. sgalchemy-0.1.0/src/sgalchemy/_orm/_session.py +1028 -0
  33. sgalchemy-0.1.0/src/sgalchemy/_orm/_session_common.py +51 -0
  34. sgalchemy-0.1.0/src/sgalchemy/_orm/_state.py +718 -0
  35. sgalchemy-0.1.0/src/sgalchemy/_orm/_uow.py +2137 -0
  36. sgalchemy-0.1.0/src/sgalchemy/_shotgun.py +370 -0
  37. sgalchemy-0.1.0/src/sgalchemy/_stubgen.py +381 -0
  38. sgalchemy-0.1.0/src/sgalchemy/_stubgen_registry.py +162 -0
  39. sgalchemy-0.1.0/src/sgalchemy/entities/__init__.py +34 -0
  40. sgalchemy-0.1.0/src/sgalchemy/py.typed +0 -0
  41. sgalchemy-0.1.0/src/sgalchemy/schema/__init__.py +7 -0
  42. sgalchemy-0.1.0/src/sgalchemy/schema/_aliases.py +89 -0
  43. sgalchemy-0.1.0/src/sgalchemy/schema/_cache.py +190 -0
  44. sgalchemy-0.1.0/src/sgalchemy/schema/_cachedir.py +82 -0
  45. sgalchemy-0.1.0/src/sgalchemy/schema/_model.py +132 -0
  46. sgalchemy-0.1.0/src/sgalchemy/schema/_policy.py +34 -0
  47. 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
@@ -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.
@@ -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
+ ]
@@ -0,0 +1,8 @@
1
+ """Run the supported `python -m sgalchemy` command-line entry point."""
2
+
3
+ import sys
4
+
5
+ from sgalchemy._cli import main
6
+
7
+ if __name__ == '__main__':
8
+ sys.exit(main())