sqlakit 0.19.0__tar.gz → 0.21.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.
- {sqlakit-0.19.0 → sqlakit-0.21.0}/PKG-INFO +21 -15
- {sqlakit-0.19.0 → sqlakit-0.21.0}/README.md +18 -10
- {sqlakit-0.19.0 → sqlakit-0.21.0}/pyproject.toml +10 -11
- {sqlakit-0.19.0 → sqlakit-0.21.0}/pyproject.toml.orig +37 -40
- {sqlakit-0.19.0 → sqlakit-0.21.0}/sqlakit/__init__.py +18 -4
- {sqlakit-0.19.0 → sqlakit-0.21.0}/sqlakit/_base.py +8 -9
- sqlakit-0.21.0/sqlakit/_cli.py +288 -0
- {sqlakit-0.19.0 → sqlakit-0.21.0}/sqlakit/_db.py +1 -3
- sqlakit-0.21.0/sqlakit/_project.py +422 -0
- sqlakit-0.21.0/sqlakit/_pycharm.py +156 -0
- {sqlakit-0.19.0 → sqlakit-0.21.0}/sqlakit/_query.py +14 -2
- sqlakit-0.21.0/sqlakit/_sql.py +3018 -0
- sqlakit-0.21.0/sqlakit/_sqruff.py +106 -0
- sqlakit-0.21.0/sqlakit/_static.py +544 -0
- {sqlakit-0.19.0 → sqlakit-0.21.0}/sqlakit/asyncio/_db.py +1 -3
- {sqlakit-0.19.0 → sqlakit-0.21.0}/sqlakit/asyncio/sql.py +22 -9
- {sqlakit-0.19.0 → sqlakit-0.21.0}/sqlakit/exceptions.py +171 -19
- {sqlakit-0.19.0 → sqlakit-0.21.0}/sqlakit/pytest_plugin.py +46 -13
- {sqlakit-0.19.0 → sqlakit-0.21.0}/sqlakit/sql.py +22 -7
- {sqlakit-0.19.0 → sqlakit-0.21.0}/sqlakit/types.py +0 -2
- sqlakit-0.19.0/sqlakit/_cli.py +0 -101
- sqlakit-0.19.0/sqlakit/_debugserver.py +0 -396
- sqlakit-0.19.0/sqlakit/_sql.py +0 -461
- sqlakit-0.19.0/sqlakit/debugserver.html +0 -103
- {sqlakit-0.19.0 → sqlakit-0.21.0}/LICENSE +0 -0
- {sqlakit-0.19.0 → sqlakit-0.21.0}/sqlakit/_discovery.py +0 -0
- {sqlakit-0.19.0 → sqlakit-0.21.0}/sqlakit/_model.py +0 -0
- {sqlakit-0.19.0 → sqlakit-0.21.0}/sqlakit/_recording.py +0 -0
- {sqlakit-0.19.0 → sqlakit-0.21.0}/sqlakit/_registry.py +0 -0
- {sqlakit-0.19.0 → sqlakit-0.21.0}/sqlakit/_routing.py +0 -0
- {sqlakit-0.19.0 → sqlakit-0.21.0}/sqlakit/asyncio/__init__.py +0 -0
- {sqlakit-0.19.0 → sqlakit-0.21.0}/sqlakit/asyncio/_registry.py +0 -0
- {sqlakit-0.19.0 → sqlakit-0.21.0}/sqlakit/asyncio/orm.py +0 -0
- {sqlakit-0.19.0 → sqlakit-0.21.0}/sqlakit/orm.py +0 -0
- {sqlakit-0.19.0 → sqlakit-0.21.0}/sqlakit/py.typed +0 -0
- {sqlakit-0.19.0 → sqlakit-0.21.0}/sqlakit/testing.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sqlakit
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.21.0
|
|
4
4
|
Summary: A toolkit for SQLAlchemy applications.
|
|
5
5
|
Keywords: sqlalchemy,database,orm,sql,asyncio
|
|
6
6
|
Author: Anton Ruhlov
|
|
@@ -22,13 +22,11 @@ Requires-Dist: sqlalchemy>=2.0.22
|
|
|
22
22
|
Requires-Dist: typing-extensions>=4.13
|
|
23
23
|
Requires-Dist: sqlalchemy[asyncio]>=2.0.22 ; extra == 'asyncio'
|
|
24
24
|
Requires-Dist: sqlparse>=0.6.0 ; extra == 'debug'
|
|
25
|
-
Requires-Dist: jinja2sql>=0.12.0 ; extra == 'sql'
|
|
26
25
|
Requires-Python: >=3.11
|
|
27
|
-
Project-URL: Repository, https://github.com/
|
|
28
|
-
Project-URL: Documentation, https://
|
|
26
|
+
Project-URL: Repository, https://github.com/sqlakit/sqlakit
|
|
27
|
+
Project-URL: Documentation, https://sqlakit.readthedocs.io/en/stable/
|
|
29
28
|
Provides-Extra: asyncio
|
|
30
29
|
Provides-Extra: debug
|
|
31
|
-
Provides-Extra: sql
|
|
32
30
|
Description-Content-Type: text/markdown
|
|
33
31
|
|
|
34
32
|
# SQLAKit
|
|
@@ -92,11 +90,11 @@ with db.autocommit(): # AUTOCOMMIT, no transaction held open
|
|
|
92
90
|
|
|
93
91
|
## SQL templates
|
|
94
92
|
|
|
95
|
-
Templates are `
|
|
96
|
-
a report with window functions or a recursive CTE
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
is
|
|
93
|
+
Templates are SQL files with `:name` parameters, so they hold anything from a
|
|
94
|
+
one-line query to a report with window functions or a recursive CTE, and every
|
|
95
|
+
SQL tool reads them as SQL. Values are bound, so they never reach the SQL
|
|
96
|
+
text. What changes per call, an optional
|
|
97
|
+
condition or a sort order, is a macro such as `tpl.if_set(:team, team = :team)`.
|
|
100
98
|
|
|
101
99
|
### From a file
|
|
102
100
|
|
|
@@ -104,7 +102,7 @@ is no way to inject anything. Requires the `sqlakit[sql]` extra.
|
|
|
104
102
|
-- reports/by_team.sql
|
|
105
103
|
SELECT team, count(*) AS members
|
|
106
104
|
FROM users
|
|
107
|
-
WHERE joined_at >
|
|
105
|
+
WHERE joined_at > :since
|
|
108
106
|
GROUP BY team
|
|
109
107
|
```
|
|
110
108
|
|
|
@@ -131,13 +129,17 @@ type each row is returned as.
|
|
|
131
129
|
`SQLAKit` adds the template name to the SQL as a comment, so a slow query log
|
|
132
130
|
shows the source file of each query right away.
|
|
133
131
|
|
|
132
|
+
`sqlakit check` reads every template the project uses and reports a macro call
|
|
133
|
+
that can't work, before the query runs. See [template
|
|
134
|
+
validation](docs/sql.md#template-validation).
|
|
135
|
+
|
|
134
136
|
### From a string
|
|
135
137
|
|
|
136
138
|
```python
|
|
137
139
|
db.sql.from_string("SELECT count(*) FROM users").scalars().one()
|
|
138
140
|
```
|
|
139
141
|
|
|
140
|
-
The same
|
|
142
|
+
The same syntax, with no directory to configure.
|
|
141
143
|
|
|
142
144
|
## Query builder
|
|
143
145
|
|
|
@@ -249,16 +251,20 @@ project has `rich`, the output is colored too.
|
|
|
249
251
|
|
|
250
252
|
### The debug server
|
|
251
253
|
|
|
252
|
-
`sqlakit
|
|
254
|
+
[`sqlakit-debugserver`](https://github.com/sqlakit/sqlakit-debugserver) serves a page that fills as the recordings
|
|
255
|
+
arrive. It is a package of its own:
|
|
253
256
|
|
|
254
257
|
```console
|
|
255
|
-
$ sqlakit
|
|
258
|
+
$ pip install sqlakit-debugserver
|
|
259
|
+
$ sqlakit-debugserver
|
|
256
260
|
|
|
257
261
|
SQLAKit debug server on http://localhost:5555
|
|
258
262
|
|
|
259
263
|
Send recordings to it:
|
|
260
264
|
|
|
261
|
-
│
|
|
265
|
+
│ from sqlakit_debugserver import DebugServer
|
|
266
|
+
│
|
|
267
|
+
│ with db.recording("GET /users", send_to=DebugServer("localhost", 5555)):
|
|
262
268
|
│ list_users()
|
|
263
269
|
```
|
|
264
270
|
|
|
@@ -59,11 +59,11 @@ with db.autocommit(): # AUTOCOMMIT, no transaction held open
|
|
|
59
59
|
|
|
60
60
|
## SQL templates
|
|
61
61
|
|
|
62
|
-
Templates are `
|
|
63
|
-
a report with window functions or a recursive CTE
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
is
|
|
62
|
+
Templates are SQL files with `:name` parameters, so they hold anything from a
|
|
63
|
+
one-line query to a report with window functions or a recursive CTE, and every
|
|
64
|
+
SQL tool reads them as SQL. Values are bound, so they never reach the SQL
|
|
65
|
+
text. What changes per call, an optional
|
|
66
|
+
condition or a sort order, is a macro such as `tpl.if_set(:team, team = :team)`.
|
|
67
67
|
|
|
68
68
|
### From a file
|
|
69
69
|
|
|
@@ -71,7 +71,7 @@ is no way to inject anything. Requires the `sqlakit[sql]` extra.
|
|
|
71
71
|
-- reports/by_team.sql
|
|
72
72
|
SELECT team, count(*) AS members
|
|
73
73
|
FROM users
|
|
74
|
-
WHERE joined_at >
|
|
74
|
+
WHERE joined_at > :since
|
|
75
75
|
GROUP BY team
|
|
76
76
|
```
|
|
77
77
|
|
|
@@ -98,13 +98,17 @@ type each row is returned as.
|
|
|
98
98
|
`SQLAKit` adds the template name to the SQL as a comment, so a slow query log
|
|
99
99
|
shows the source file of each query right away.
|
|
100
100
|
|
|
101
|
+
`sqlakit check` reads every template the project uses and reports a macro call
|
|
102
|
+
that can't work, before the query runs. See [template
|
|
103
|
+
validation](docs/sql.md#template-validation).
|
|
104
|
+
|
|
101
105
|
### From a string
|
|
102
106
|
|
|
103
107
|
```python
|
|
104
108
|
db.sql.from_string("SELECT count(*) FROM users").scalars().one()
|
|
105
109
|
```
|
|
106
110
|
|
|
107
|
-
The same
|
|
111
|
+
The same syntax, with no directory to configure.
|
|
108
112
|
|
|
109
113
|
## Query builder
|
|
110
114
|
|
|
@@ -216,16 +220,20 @@ project has `rich`, the output is colored too.
|
|
|
216
220
|
|
|
217
221
|
### The debug server
|
|
218
222
|
|
|
219
|
-
`sqlakit
|
|
223
|
+
[`sqlakit-debugserver`](https://github.com/sqlakit/sqlakit-debugserver) serves a page that fills as the recordings
|
|
224
|
+
arrive. It is a package of its own:
|
|
220
225
|
|
|
221
226
|
```console
|
|
222
|
-
$ sqlakit
|
|
227
|
+
$ pip install sqlakit-debugserver
|
|
228
|
+
$ sqlakit-debugserver
|
|
223
229
|
|
|
224
230
|
SQLAKit debug server on http://localhost:5555
|
|
225
231
|
|
|
226
232
|
Send recordings to it:
|
|
227
233
|
|
|
228
|
-
│
|
|
234
|
+
│ from sqlakit_debugserver import DebugServer
|
|
235
|
+
│
|
|
236
|
+
│ with db.recording("GET /users", send_to=DebugServer("localhost", 5555)):
|
|
229
237
|
│ list_users()
|
|
230
238
|
```
|
|
231
239
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "sqlakit"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.21.0"
|
|
4
4
|
description = "A toolkit for SQLAlchemy applications."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = "MIT"
|
|
@@ -42,18 +42,13 @@ sqlakit = "sqlakit._cli:main"
|
|
|
42
42
|
sqlakit = "sqlakit.pytest_plugin"
|
|
43
43
|
|
|
44
44
|
[project.urls]
|
|
45
|
-
Repository = "https://github.com/
|
|
46
|
-
Documentation = "https://
|
|
45
|
+
Repository = "https://github.com/sqlakit/sqlakit"
|
|
46
|
+
Documentation = "https://sqlakit.readthedocs.io/en/stable/"
|
|
47
47
|
|
|
48
48
|
[project.optional-dependencies]
|
|
49
49
|
asyncio = ["sqlalchemy[asyncio]>=2.0.22"]
|
|
50
|
-
sql = ["jinja2sql>=0.12.0"]
|
|
51
50
|
debug = ["sqlparse>=0.6.0"]
|
|
52
51
|
|
|
53
|
-
[build-system]
|
|
54
|
-
requires = ["uv_build>=0.11,<0.13"]
|
|
55
|
-
build-backend = "uv_build"
|
|
56
|
-
|
|
57
52
|
[tool.uv]
|
|
58
53
|
default-groups = [
|
|
59
54
|
"dev",
|
|
@@ -178,12 +173,12 @@ default_item_type = "cmd"
|
|
|
178
173
|
|
|
179
174
|
[tool.codespell]
|
|
180
175
|
ignore-words-list = "froms"
|
|
181
|
-
skip = "./.venv,./site,./uv.lock,./.git
|
|
176
|
+
skip = "./.venv,./site,./uv.lock,./.git"
|
|
182
177
|
|
|
183
178
|
[dependency-groups]
|
|
184
179
|
dev = [
|
|
185
180
|
"pytest>=9.1.1",
|
|
186
|
-
"ty>=0.0.
|
|
181
|
+
"ty>=0.0.84",
|
|
187
182
|
"ruff>=0.16.6",
|
|
188
183
|
"poethepoet>=0.48.0",
|
|
189
184
|
"anyio>=4.14.2",
|
|
@@ -194,7 +189,6 @@ dev = [
|
|
|
194
189
|
"httpx>=0.28.1",
|
|
195
190
|
"httpx2>=2.10.0",
|
|
196
191
|
"sqlmodel>=0.0.39",
|
|
197
|
-
"jinja2sql>=0.12.0",
|
|
198
192
|
"pydantic>=2.13.4",
|
|
199
193
|
"sqlparse>=0.6.0",
|
|
200
194
|
"rich>=15.0.0",
|
|
@@ -206,6 +200,7 @@ dev = [
|
|
|
206
200
|
"flask>=3.1.3",
|
|
207
201
|
"dirty-equals>=0.11",
|
|
208
202
|
"codespell>=2.4.3",
|
|
203
|
+
"sqruff>=0.40.0",
|
|
209
204
|
]
|
|
210
205
|
docs = [
|
|
211
206
|
"mkdocs>=1.6.1,<2",
|
|
@@ -213,3 +208,7 @@ docs = [
|
|
|
213
208
|
"mkdocstrings[python]>=1.0.6",
|
|
214
209
|
"ruff>=0.16.6",
|
|
215
210
|
]
|
|
211
|
+
|
|
212
|
+
[build-system]
|
|
213
|
+
requires = ["uv_build>=0.11,<0.13"]
|
|
214
|
+
build-backend = "uv_build"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "sqlakit"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.21.0"
|
|
4
4
|
description = "A toolkit for SQLAlchemy applications."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = "MIT"
|
|
@@ -33,24 +33,17 @@ sqlakit = "sqlakit._cli:main"
|
|
|
33
33
|
sqlakit = "sqlakit.pytest_plugin"
|
|
34
34
|
|
|
35
35
|
[project.urls]
|
|
36
|
-
Repository = "https://github.com/
|
|
37
|
-
Documentation = "https://
|
|
36
|
+
Repository = "https://github.com/sqlakit/sqlakit"
|
|
37
|
+
Documentation = "https://sqlakit.readthedocs.io/en/stable/"
|
|
38
38
|
|
|
39
39
|
[project.optional-dependencies]
|
|
40
40
|
asyncio = [
|
|
41
41
|
"sqlalchemy[asyncio]>=2.0.22",
|
|
42
42
|
]
|
|
43
|
-
sql = [
|
|
44
|
-
"jinja2sql>=0.12.0",
|
|
45
|
-
]
|
|
46
43
|
debug = [
|
|
47
44
|
"sqlparse>=0.6.0",
|
|
48
45
|
]
|
|
49
46
|
|
|
50
|
-
[build-system]
|
|
51
|
-
requires = ["uv_build>=0.11,<0.13"]
|
|
52
|
-
build-backend = "uv_build"
|
|
53
|
-
|
|
54
47
|
[tool.uv.build-backend]
|
|
55
48
|
module-name = "sqlakit"
|
|
56
49
|
module-root = ""
|
|
@@ -61,7 +54,7 @@ default-groups = ["dev", "docs"]
|
|
|
61
54
|
[dependency-groups]
|
|
62
55
|
dev = [
|
|
63
56
|
"pytest>=9.1.1",
|
|
64
|
-
"ty>=0.0.
|
|
57
|
+
"ty>=0.0.84",
|
|
65
58
|
"ruff>=0.16.6",
|
|
66
59
|
"poethepoet>=0.48.0",
|
|
67
60
|
"anyio>=4.14.2",
|
|
@@ -72,7 +65,6 @@ dev = [
|
|
|
72
65
|
"httpx>=0.28.1",
|
|
73
66
|
"httpx2>=2.10.0",
|
|
74
67
|
"sqlmodel>=0.0.39",
|
|
75
|
-
"jinja2sql>=0.12.0",
|
|
76
68
|
"pydantic>=2.13.4",
|
|
77
69
|
"sqlparse>=0.6.0",
|
|
78
70
|
"rich>=15.0.0",
|
|
@@ -84,6 +76,7 @@ dev = [
|
|
|
84
76
|
"flask>=3.1.3",
|
|
85
77
|
"dirty-equals>=0.11",
|
|
86
78
|
"codespell>=2.4.3",
|
|
79
|
+
"sqruff>=0.40.0",
|
|
87
80
|
]
|
|
88
81
|
docs = [
|
|
89
82
|
"mkdocs>=1.6.1,<2",
|
|
@@ -101,8 +94,8 @@ filterwarnings = [
|
|
|
101
94
|
addopts = "-p pytester"
|
|
102
95
|
# The example is a project of its own, run by tests/examples, not collected here.
|
|
103
96
|
norecursedirs = [
|
|
104
|
-
"examples/testing_plugin",
|
|
105
|
-
"tests/projects",
|
|
97
|
+
"examples/testing_plugin", # a project of its own, run by tests/examples
|
|
98
|
+
"tests/projects", # the projects the plugin tests copy
|
|
106
99
|
".*",
|
|
107
100
|
"build",
|
|
108
101
|
"dist",
|
|
@@ -137,39 +130,39 @@ ignore = [
|
|
|
137
130
|
|
|
138
131
|
[tool.ruff.lint.per-file-ignores]
|
|
139
132
|
"tests/**/*.py" = [
|
|
140
|
-
"S101",
|
|
141
|
-
"PLR2004",
|
|
142
|
-
"SLF001",
|
|
143
|
-
"PT012",
|
|
144
|
-
"B018",
|
|
145
|
-
"SIM117",
|
|
146
|
-
"PLC0415",
|
|
147
|
-
"DTZ001",
|
|
148
|
-
"ARG001",
|
|
149
|
-
"PT011",
|
|
150
|
-
"ANN401",
|
|
151
|
-
"D401",
|
|
152
|
-
"N818",
|
|
153
|
-
"FBT001",
|
|
154
|
-
"PLW0108",
|
|
155
|
-
"DTZ005",
|
|
156
|
-
"S106",
|
|
157
|
-
"S310",
|
|
133
|
+
"S101", # tests assert
|
|
134
|
+
"PLR2004", # and compare against the values they mean
|
|
135
|
+
"SLF001", # they read the internals they are testing
|
|
136
|
+
"PT012", # a `raises` block is allowed to set the scene
|
|
137
|
+
"B018", # touching an attribute is how a test triggers it
|
|
138
|
+
"SIM117", # nested `with` reads better than a long line of them
|
|
139
|
+
"PLC0415", # a fixture imports what it is about, where it is about it
|
|
140
|
+
"DTZ001", # naive datetimes are fine as fixtures
|
|
141
|
+
"ARG001", # a fixture may be requested for its side effect
|
|
142
|
+
"PT011", # `raises` without `match` is enough for our own errors
|
|
143
|
+
"ANN401", # test helpers take whatever the example hands them
|
|
144
|
+
"D401", # test docstrings read as descriptions
|
|
145
|
+
"N818", # a test's own exception needs no Error suffix
|
|
146
|
+
"FBT001", # parametrised booleans arrive positionally
|
|
147
|
+
"PLW0108", # a lambda in a parameter table is deliberate
|
|
148
|
+
"DTZ005", # naive datetimes are fine as fixtures
|
|
149
|
+
"S106", # the passwords here are fake, and the point of the test
|
|
150
|
+
"S310", # the URLs here are the tests' own server
|
|
158
151
|
]
|
|
159
152
|
"tools/**/*.py" = [
|
|
160
|
-
"INP001",
|
|
161
|
-
"T201",
|
|
153
|
+
"INP001", # a script, not a package
|
|
154
|
+
"T201", # a script that says what it found
|
|
162
155
|
]
|
|
163
156
|
"examples/**/*.py" = [
|
|
164
|
-
"INP001",
|
|
165
|
-
"ARG001",
|
|
166
|
-
"TC003",
|
|
157
|
+
"INP001", # examples are read one file at a time, not imported as a package
|
|
158
|
+
"ARG001", # FastAPI hands handlers a request they may not use
|
|
159
|
+
"TC003", # examples keep their imports where a reader expects them
|
|
167
160
|
]
|
|
168
161
|
"examples/testing_plugin/*.py" = [
|
|
169
|
-
"S101",
|
|
162
|
+
"S101", # the example is a test suite, and tests assert
|
|
170
163
|
]
|
|
171
164
|
"tests/projects/**/*.py" = [
|
|
172
|
-
"INP001",
|
|
165
|
+
"INP001", # a project the tests copy, not a package of this one
|
|
173
166
|
]
|
|
174
167
|
|
|
175
168
|
[tool.ty.src]
|
|
@@ -211,4 +204,8 @@ default_item_type = "cmd"
|
|
|
211
204
|
[tool.codespell]
|
|
212
205
|
# `froms` is SQLAlchemy's own word for what a SELECT selects from.
|
|
213
206
|
ignore-words-list = "froms"
|
|
214
|
-
skip = "./.venv,./site,./uv.lock,./.git
|
|
207
|
+
skip = "./.venv,./site,./uv.lock,./.git"
|
|
208
|
+
|
|
209
|
+
[build-system]
|
|
210
|
+
requires = ["uv_build>=0.11,<0.13"]
|
|
211
|
+
build-backend = "uv_build"
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
from ._base import DEFAULT_ENGINE_ARGS, DEFAULT_SESSION_ARGS
|
|
2
2
|
from ._db import Database, RetryingTransaction, Transaction
|
|
3
|
-
from ._debugserver import DebugServer
|
|
4
3
|
from ._discovery import import_models, import_string
|
|
5
4
|
from ._query import (
|
|
6
5
|
CASE_INSENSITIVE_COLLATIONS,
|
|
@@ -16,7 +15,6 @@ from ._routing import Router
|
|
|
16
15
|
from .exceptions import (
|
|
17
16
|
DEFAULT_ALIAS,
|
|
18
17
|
AliasInUseError,
|
|
19
|
-
AsyncFilterError,
|
|
20
18
|
BulkQueryError,
|
|
21
19
|
ConflictingDatabaseUrlError,
|
|
22
20
|
ConflictingJoinError,
|
|
@@ -25,12 +23,17 @@ from .exceptions import (
|
|
|
25
23
|
DefaultAliasError,
|
|
26
24
|
DetachedInstanceError,
|
|
27
25
|
DuplicateKeyError,
|
|
26
|
+
InlineValueError,
|
|
28
27
|
InstanceNotFoundError,
|
|
29
28
|
InvalidCursorError,
|
|
30
29
|
InvalidDatabaseConfigError,
|
|
31
30
|
InvalidNullsError,
|
|
32
31
|
InvalidOrderFieldError,
|
|
32
|
+
InvalidSortStringError,
|
|
33
33
|
KeyLookupError,
|
|
34
|
+
MacroArgumentError,
|
|
35
|
+
MacroDefinitionError,
|
|
36
|
+
MacroSyntaxError,
|
|
34
37
|
MissingConnectionError,
|
|
35
38
|
MissingDatabaseUrlError,
|
|
36
39
|
MissingDefaultDatabaseError,
|
|
@@ -40,6 +43,8 @@ from .exceptions import (
|
|
|
40
43
|
MultipleInstancesFoundError,
|
|
41
44
|
NullCursorValueError,
|
|
42
45
|
PageItemsMismatchError,
|
|
46
|
+
ParameterPathError,
|
|
47
|
+
ProjectConfigError,
|
|
43
48
|
RawStatementError,
|
|
44
49
|
RetryNotSupportedError,
|
|
45
50
|
SQLAKitError,
|
|
@@ -50,7 +55,9 @@ from .exceptions import (
|
|
|
50
55
|
UncomparableOrderingError,
|
|
51
56
|
UnknownDatabaseError,
|
|
52
57
|
UnknownFieldError,
|
|
58
|
+
UnknownIdentifierError,
|
|
53
59
|
UnknownImportPathError,
|
|
60
|
+
UnknownMacroError,
|
|
54
61
|
UnknownOrderFieldError,
|
|
55
62
|
UnorderedPageError,
|
|
56
63
|
UnregisteredDatabaseError,
|
|
@@ -71,7 +78,6 @@ __all__ = [
|
|
|
71
78
|
"DEFAULT_ENGINE_ARGS",
|
|
72
79
|
"DEFAULT_SESSION_ARGS",
|
|
73
80
|
"AliasInUseError",
|
|
74
|
-
"AsyncFilterError",
|
|
75
81
|
"BulkQueryError",
|
|
76
82
|
"ConflictingDatabaseUrlError",
|
|
77
83
|
"ConflictingJoinError",
|
|
@@ -81,17 +87,21 @@ __all__ = [
|
|
|
81
87
|
"DatabaseConfig",
|
|
82
88
|
"DatabaseNotConfiguredError",
|
|
83
89
|
"Databases",
|
|
84
|
-
"DebugServer",
|
|
85
90
|
"DefaultAliasError",
|
|
86
91
|
"DetachedInstanceError",
|
|
87
92
|
"DuplicateKeyError",
|
|
88
93
|
"EngineArgs",
|
|
94
|
+
"InlineValueError",
|
|
89
95
|
"InstanceNotFoundError",
|
|
90
96
|
"InvalidCursorError",
|
|
91
97
|
"InvalidDatabaseConfigError",
|
|
92
98
|
"InvalidNullsError",
|
|
93
99
|
"InvalidOrderFieldError",
|
|
100
|
+
"InvalidSortStringError",
|
|
94
101
|
"KeyLookupError",
|
|
102
|
+
"MacroArgumentError",
|
|
103
|
+
"MacroDefinitionError",
|
|
104
|
+
"MacroSyntaxError",
|
|
95
105
|
"MissingConnectionError",
|
|
96
106
|
"MissingDatabaseUrlError",
|
|
97
107
|
"MissingDefaultDatabaseError",
|
|
@@ -103,6 +113,8 @@ __all__ = [
|
|
|
103
113
|
"OrderBy",
|
|
104
114
|
"Page",
|
|
105
115
|
"PageItemsMismatchError",
|
|
116
|
+
"ParameterPathError",
|
|
117
|
+
"ProjectConfigError",
|
|
106
118
|
"QueryStats",
|
|
107
119
|
"RawStatementError",
|
|
108
120
|
"Recording",
|
|
@@ -122,7 +134,9 @@ __all__ = [
|
|
|
122
134
|
"UncountedPage",
|
|
123
135
|
"UnknownDatabaseError",
|
|
124
136
|
"UnknownFieldError",
|
|
137
|
+
"UnknownIdentifierError",
|
|
125
138
|
"UnknownImportPathError",
|
|
139
|
+
"UnknownMacroError",
|
|
126
140
|
"UnknownOrderFieldError",
|
|
127
141
|
"UnorderedPageError",
|
|
128
142
|
"UnregisteredDatabaseError",
|
|
@@ -24,7 +24,6 @@ import sqlalchemy as sa
|
|
|
24
24
|
import sqlalchemy.event
|
|
25
25
|
from typing_extensions import Unpack
|
|
26
26
|
|
|
27
|
-
from ._debugserver import DebugServer, send_recording
|
|
28
27
|
from ._discovery import import_string
|
|
29
28
|
from ._recording import (
|
|
30
29
|
KEEP,
|
|
@@ -358,7 +357,7 @@ class BaseDatabase(Generic[ConnectionT, SessionT]):
|
|
|
358
357
|
stacks: bool = False,
|
|
359
358
|
skip_queries_from: Sequence[str | PathLike[str]] = (),
|
|
360
359
|
into: Recording | None = None,
|
|
361
|
-
|
|
360
|
+
send_to: Callable[[Recording], object] | None = None,
|
|
362
361
|
) -> Iterator[Recording]:
|
|
363
362
|
"""Record the statements of this block, and what they add up to.
|
|
364
363
|
|
|
@@ -373,8 +372,8 @@ class BaseDatabase(Generic[ConnectionT, SessionT]):
|
|
|
373
372
|
|
|
374
373
|
``logger`` writes a summary when the block ends, at a level the numbers
|
|
375
374
|
choose. ``echo`` prints the statements instead, coloured where `rich` is
|
|
376
|
-
installed. ``
|
|
377
|
-
|
|
375
|
+
installed. ``send_to`` is called with the recording, to hand it on:
|
|
376
|
+
`sqlakit_debugserver.DebugServer` is one. ``stacks`` has every
|
|
378
377
|
statement remember the frames that led to it, at the cost of a stack walk
|
|
379
378
|
each time. ``skip_queries_from`` names the files whose statements are none of
|
|
380
379
|
your business: what those run is not recorded at all, which leaves a test's
|
|
@@ -403,8 +402,8 @@ class BaseDatabase(Generic[ConnectionT, SessionT]):
|
|
|
403
402
|
recording.log(logger)
|
|
404
403
|
if echo:
|
|
405
404
|
recording.echo()
|
|
406
|
-
if
|
|
407
|
-
|
|
405
|
+
if send_to is not None:
|
|
406
|
+
send_to(recording)
|
|
408
407
|
|
|
409
408
|
@contextmanager
|
|
410
409
|
def assert_queries(
|
|
@@ -886,7 +885,7 @@ class _DatabaseRegistryMixin(BaseDatabase[Any, Any], Generic[DatabaseT]):
|
|
|
886
885
|
stacks: bool = False,
|
|
887
886
|
skip_queries_from: Sequence[str | PathLike[str]] = (),
|
|
888
887
|
into: Recording | None = None,
|
|
889
|
-
|
|
888
|
+
send_to: Callable[[Recording], object] | None = None,
|
|
890
889
|
using: str | DatabaseT | Sequence[str | DatabaseT] | None = None,
|
|
891
890
|
) -> Iterator[Recording]:
|
|
892
891
|
"""Record every database this registry has, not the default one alone.
|
|
@@ -936,8 +935,8 @@ class _DatabaseRegistryMixin(BaseDatabase[Any, Any], Generic[DatabaseT]):
|
|
|
936
935
|
together.log(logger)
|
|
937
936
|
if echo:
|
|
938
937
|
together.echo()
|
|
939
|
-
if
|
|
940
|
-
|
|
938
|
+
if send_to is not None:
|
|
939
|
+
send_to(together)
|
|
941
940
|
|
|
942
941
|
@contextmanager
|
|
943
942
|
def unbound(self) -> Iterator[None]:
|