sqlspec 0.7.1__tar.gz → 0.9.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 (167) hide show
  1. {sqlspec-0.7.1 → sqlspec-0.9.0}/.pre-commit-config.yaml +1 -1
  2. {sqlspec-0.7.1 → sqlspec-0.9.0}/Makefile +2 -2
  3. {sqlspec-0.7.1 → sqlspec-0.9.0}/PKG-INFO +4 -1
  4. {sqlspec-0.7.1 → sqlspec-0.9.0}/README.md +1 -0
  5. {sqlspec-0.7.1 → sqlspec-0.9.0}/pyproject.toml +56 -10
  6. sqlspec-0.9.0/sqlspec/__init__.py +15 -0
  7. {sqlspec-0.7.1 → sqlspec-0.9.0}/sqlspec/_serialization.py +16 -2
  8. {sqlspec-0.7.1 → sqlspec-0.9.0}/sqlspec/_typing.py +40 -7
  9. sqlspec-0.9.0/sqlspec/adapters/adbc/__init__.py +7 -0
  10. sqlspec-0.9.0/sqlspec/adapters/adbc/config.py +213 -0
  11. sqlspec-0.9.0/sqlspec/adapters/adbc/driver.py +392 -0
  12. sqlspec-0.9.0/sqlspec/adapters/aiosqlite/__init__.py +7 -0
  13. {sqlspec-0.7.1 → sqlspec-0.9.0}/sqlspec/adapters/aiosqlite/config.py +24 -6
  14. sqlspec-0.9.0/sqlspec/adapters/aiosqlite/driver.py +264 -0
  15. sqlspec-0.9.0/sqlspec/adapters/asyncmy/__init__.py +8 -0
  16. {sqlspec-0.7.1 → sqlspec-0.9.0}/sqlspec/adapters/asyncmy/config.py +71 -11
  17. sqlspec-0.9.0/sqlspec/adapters/asyncmy/driver.py +246 -0
  18. sqlspec-0.9.0/sqlspec/adapters/asyncpg/__init__.py +9 -0
  19. {sqlspec-0.7.1 → sqlspec-0.9.0}/sqlspec/adapters/asyncpg/config.py +102 -25
  20. sqlspec-0.9.0/sqlspec/adapters/asyncpg/driver.py +444 -0
  21. sqlspec-0.9.0/sqlspec/adapters/duckdb/__init__.py +7 -0
  22. sqlspec-0.9.0/sqlspec/adapters/duckdb/config.py +380 -0
  23. sqlspec-0.9.0/sqlspec/adapters/duckdb/driver.py +225 -0
  24. sqlspec-0.9.0/sqlspec/adapters/oracledb/__init__.py +16 -0
  25. sqlspec-0.9.0/sqlspec/adapters/oracledb/config/__init__.py +9 -0
  26. sqlspec-0.9.0/sqlspec/adapters/oracledb/config/_asyncio.py +187 -0
  27. {sqlspec-0.7.1 → sqlspec-0.9.0}/sqlspec/adapters/oracledb/config/_common.py +1 -1
  28. sqlspec-0.9.0/sqlspec/adapters/oracledb/config/_sync.py +187 -0
  29. sqlspec-0.9.0/sqlspec/adapters/oracledb/driver.py +571 -0
  30. sqlspec-0.9.0/sqlspec/adapters/psqlpy/config.py +258 -0
  31. sqlspec-0.9.0/sqlspec/adapters/psqlpy/driver.py +335 -0
  32. sqlspec-0.9.0/sqlspec/adapters/psycopg/__init__.py +16 -0
  33. sqlspec-0.9.0/sqlspec/adapters/psycopg/config/__init__.py +9 -0
  34. sqlspec-0.9.0/sqlspec/adapters/psycopg/config/_async.py +170 -0
  35. {sqlspec-0.7.1 → sqlspec-0.9.0}/sqlspec/adapters/psycopg/config/_common.py +2 -2
  36. sqlspec-0.9.0/sqlspec/adapters/psycopg/config/_sync.py +169 -0
  37. sqlspec-0.9.0/sqlspec/adapters/psycopg/driver.py +578 -0
  38. sqlspec-0.9.0/sqlspec/adapters/sqlite/__init__.py +7 -0
  39. {sqlspec-0.7.1 → sqlspec-0.9.0}/sqlspec/adapters/sqlite/config.py +24 -6
  40. sqlspec-0.9.0/sqlspec/adapters/sqlite/driver.py +305 -0
  41. sqlspec-0.9.0/sqlspec/base.py +729 -0
  42. {sqlspec-0.7.1 → sqlspec-0.9.0}/sqlspec/exceptions.py +30 -0
  43. sqlspec-0.9.0/sqlspec/extensions/litestar/__init__.py +19 -0
  44. sqlspec-0.9.0/sqlspec/extensions/litestar/_utils.py +56 -0
  45. sqlspec-0.9.0/sqlspec/extensions/litestar/config.py +87 -0
  46. sqlspec-0.9.0/sqlspec/extensions/litestar/handlers.py +213 -0
  47. sqlspec-0.9.0/sqlspec/extensions/litestar/plugin.py +140 -0
  48. sqlspec-0.9.0/sqlspec/statement.py +373 -0
  49. {sqlspec-0.7.1 → sqlspec-0.9.0}/sqlspec/typing.py +81 -17
  50. sqlspec-0.9.0/sqlspec/utils/__init__.py +3 -0
  51. {sqlspec-0.7.1 → sqlspec-0.9.0}/sqlspec/utils/fixtures.py +4 -5
  52. sqlspec-0.9.0/sqlspec/utils/sync_tools.py +335 -0
  53. {sqlspec-0.7.1 → sqlspec-0.9.0}/tests/conftest.py +8 -0
  54. sqlspec-0.9.0/tests/fixtures/__init__.py +1 -0
  55. sqlspec-0.9.0/tests/fixtures/example_usage.py +82 -0
  56. sqlspec-0.9.0/tests/fixtures/sql_utils.py +92 -0
  57. sqlspec-0.9.0/tests/integration/__init__.py +3 -0
  58. sqlspec-0.9.0/tests/integration/test_adapters/__init__.py +1 -0
  59. sqlspec-0.9.0/tests/integration/test_adapters/test_adbc/__init__.py +5 -0
  60. sqlspec-0.9.0/tests/integration/test_adapters/test_adbc/conftest.py +35 -0
  61. sqlspec-0.9.0/tests/integration/test_adapters/test_adbc/test_connection.py +31 -0
  62. sqlspec-0.9.0/tests/integration/test_adapters/test_adbc/test_driver_bigquery.py +227 -0
  63. sqlspec-0.9.0/tests/integration/test_adapters/test_adbc/test_driver_duckdb.py +444 -0
  64. sqlspec-0.9.0/tests/integration/test_adapters/test_adbc/test_driver_postgres.py +201 -0
  65. sqlspec-0.9.0/tests/integration/test_adapters/test_adbc/test_driver_sqlite.py +310 -0
  66. sqlspec-0.9.0/tests/integration/test_adapters/test_aiosqlite/__init__.py +5 -0
  67. sqlspec-0.9.0/tests/integration/test_adapters/test_aiosqlite/test_connection.py +28 -0
  68. sqlspec-0.9.0/tests/integration/test_adapters/test_aiosqlite/test_driver.py +168 -0
  69. sqlspec-0.9.0/tests/integration/test_adapters/test_asyncmy/__init__.py +3 -0
  70. sqlspec-0.9.0/tests/integration/test_adapters/test_asyncmy/test_connection.py +54 -0
  71. sqlspec-0.9.0/tests/integration/test_adapters/test_asyncmy/test_driver.py +218 -0
  72. sqlspec-0.9.0/tests/integration/test_adapters/test_asyncpg/test_connection.py +42 -0
  73. sqlspec-0.9.0/tests/integration/test_adapters/test_asyncpg/test_driver.py +275 -0
  74. sqlspec-0.9.0/tests/integration/test_adapters/test_duckdb/__init__.py +5 -0
  75. sqlspec-0.9.0/tests/integration/test_adapters/test_duckdb/test_connection.py +28 -0
  76. sqlspec-0.9.0/tests/integration/test_adapters/test_duckdb/test_driver.py +169 -0
  77. sqlspec-0.9.0/tests/integration/test_adapters/test_oracledb/__init__.py +5 -0
  78. sqlspec-0.9.0/tests/integration/test_adapters/test_oracledb/test_connection.py +106 -0
  79. sqlspec-0.9.0/tests/integration/test_adapters/test_oracledb/test_driver_async.py +201 -0
  80. sqlspec-0.9.0/tests/integration/test_adapters/test_oracledb/test_driver_sync.py +185 -0
  81. sqlspec-0.9.0/tests/integration/test_adapters/test_psqlpy/test_connection.py +66 -0
  82. sqlspec-0.9.0/tests/integration/test_adapters/test_psqlpy/test_driver.py +191 -0
  83. sqlspec-0.9.0/tests/integration/test_adapters/test_psycopg/__init__.py +5 -0
  84. sqlspec-0.9.0/tests/integration/test_adapters/test_psycopg/test_connection.py +81 -0
  85. sqlspec-0.9.0/tests/integration/test_adapters/test_psycopg/test_driver.py +408 -0
  86. sqlspec-0.9.0/tests/integration/test_adapters/test_sqlite/__init__.py +5 -0
  87. sqlspec-0.9.0/tests/integration/test_adapters/test_sqlite/test_connection.py +27 -0
  88. sqlspec-0.9.0/tests/integration/test_adapters/test_sqlite/test_driver.py +178 -0
  89. sqlspec-0.9.0/tests/unit/test_adapters/test_adbc/__init__.py +1 -0
  90. sqlspec-0.9.0/tests/unit/test_adapters/test_adbc/test_config.py +91 -0
  91. sqlspec-0.9.0/tests/unit/test_adapters/test_aiosqlite/test_config.py +110 -0
  92. sqlspec-0.9.0/tests/unit/test_adapters/test_asyncmy/__init__.py +1 -0
  93. sqlspec-0.9.0/tests/unit/test_adapters/test_asyncmy/test_config.py +152 -0
  94. sqlspec-0.9.0/tests/unit/test_adapters/test_asyncpg/test_config.py +153 -0
  95. sqlspec-0.9.0/tests/unit/test_adapters/test_duckdb/test_config.py +137 -0
  96. sqlspec-0.9.0/tests/unit/test_adapters/test_oracledb/__init__.py +1 -0
  97. sqlspec-0.9.0/tests/unit/test_adapters/test_oracledb/test_async_config.py +135 -0
  98. sqlspec-0.9.0/tests/unit/test_adapters/test_oracledb/test_sync_config.py +129 -0
  99. sqlspec-0.9.0/tests/unit/test_adapters/test_psycopg/__init__.py +1 -0
  100. sqlspec-0.9.0/tests/unit/test_adapters/test_psycopg/test_async_config.py +179 -0
  101. sqlspec-0.9.0/tests/unit/test_adapters/test_psycopg/test_sync_config.py +160 -0
  102. sqlspec-0.9.0/tests/unit/test_adapters/test_sqlite/test_config.py +89 -0
  103. sqlspec-0.9.0/tests/unit/test_base.py +300 -0
  104. sqlspec-0.9.0/tests/unit/test_typing.py +280 -0
  105. {sqlspec-0.7.1 → sqlspec-0.9.0}/tests/unit/test_utils/test_module_loader.py +2 -3
  106. sqlspec-0.9.0/tests/unit/test_utils/test_sync_tools.py +109 -0
  107. sqlspec-0.9.0/tools/sphinx_ext/__init__.py +16 -0
  108. {sqlspec-0.7.1 → sqlspec-0.9.0}/tools/sphinx_ext/missing_references.py +2 -2
  109. {sqlspec-0.7.1 → sqlspec-0.9.0}/uv.lock +1097 -834
  110. sqlspec-0.7.1/sqlspec/adapters/adbc/config.py +0 -47
  111. sqlspec-0.7.1/sqlspec/adapters/aiosqlite/__init__.py +0 -3
  112. sqlspec-0.7.1/sqlspec/adapters/asyncmy/__init__.py +0 -3
  113. sqlspec-0.7.1/sqlspec/adapters/duckdb/__init__.py +0 -3
  114. sqlspec-0.7.1/sqlspec/adapters/duckdb/config.py +0 -198
  115. sqlspec-0.7.1/sqlspec/adapters/oracledb/__init__.py +0 -13
  116. sqlspec-0.7.1/sqlspec/adapters/oracledb/config/__init__.py +0 -9
  117. sqlspec-0.7.1/sqlspec/adapters/oracledb/config/_asyncio.py +0 -103
  118. sqlspec-0.7.1/sqlspec/adapters/oracledb/config/_sync.py +0 -103
  119. sqlspec-0.7.1/sqlspec/adapters/psycopg/config/__init__.py +0 -9
  120. sqlspec-0.7.1/sqlspec/adapters/psycopg/config/_async.py +0 -78
  121. sqlspec-0.7.1/sqlspec/adapters/psycopg/config/_sync.py +0 -77
  122. sqlspec-0.7.1/sqlspec/base.py +0 -227
  123. sqlspec-0.7.1/sqlspec/extensions/litestar/config.py +0 -0
  124. sqlspec-0.7.1/sqlspec/extensions/litestar/plugin.py +0 -46
  125. sqlspec-0.7.1/tests/unit/__init__.py +0 -0
  126. sqlspec-0.7.1/tests/unit/test_adapters/__init__.py +0 -0
  127. sqlspec-0.7.1/tests/unit/test_adapters/test_aiosqlite/test_config.py +0 -108
  128. sqlspec-0.7.1/tests/unit/test_adapters/test_asyncpg/test_config.py +0 -159
  129. sqlspec-0.7.1/tests/unit/test_adapters/test_duckdb/__init__.py +0 -0
  130. sqlspec-0.7.1/tests/unit/test_adapters/test_duckdb/test_config.py +0 -255
  131. sqlspec-0.7.1/tests/unit/test_adapters/test_oracledb/test_config.py +0 -336
  132. sqlspec-0.7.1/tests/unit/test_adapters/test_psycopg/test_async_config.py +0 -167
  133. sqlspec-0.7.1/tests/unit/test_adapters/test_psycopg/test_sync_config.py +0 -152
  134. sqlspec-0.7.1/tests/unit/test_adapters/test_sqlite/test_config.py +0 -87
  135. sqlspec-0.7.1/tests/unit/test_base.py +0 -229
  136. sqlspec-0.7.1/tests/unit/test_typing.py +0 -276
  137. sqlspec-0.7.1/tests/unit/test_utils/__init__.py +0 -0
  138. sqlspec-0.7.1/tools/__init__.py +0 -0
  139. sqlspec-0.7.1/tools/sphinx_ext/__init__.py +0 -16
  140. {sqlspec-0.7.1 → sqlspec-0.9.0}/.gitignore +0 -0
  141. {sqlspec-0.7.1 → sqlspec-0.9.0}/CONTRIBUTING.rst +0 -0
  142. {sqlspec-0.7.1 → sqlspec-0.9.0}/LICENSE +0 -0
  143. {sqlspec-0.7.1 → sqlspec-0.9.0}/NOTICE +0 -0
  144. {sqlspec-0.7.1 → sqlspec-0.9.0}/sqlspec/__metadata__.py +0 -0
  145. {sqlspec-0.7.1/sqlspec → sqlspec-0.9.0/sqlspec/adapters}/__init__.py +0 -0
  146. {sqlspec-0.7.1/sqlspec/adapters → sqlspec-0.9.0/sqlspec/adapters/psqlpy}/__init__.py +0 -0
  147. {sqlspec-0.7.1/sqlspec/adapters/adbc → sqlspec-0.9.0/sqlspec/extensions}/__init__.py +0 -0
  148. {sqlspec-0.7.1 → sqlspec-0.9.0}/sqlspec/filters.py +0 -0
  149. {sqlspec-0.7.1 → sqlspec-0.9.0}/sqlspec/py.typed +0 -0
  150. {sqlspec-0.7.1 → sqlspec-0.9.0}/sqlspec/utils/deprecation.py +0 -0
  151. {sqlspec-0.7.1 → sqlspec-0.9.0}/sqlspec/utils/module_loader.py +0 -0
  152. {sqlspec-0.7.1 → sqlspec-0.9.0}/sqlspec/utils/text.py +0 -0
  153. {sqlspec-0.7.1/sqlspec/adapters/asyncpg → sqlspec-0.9.0/tests}/__init__.py +0 -0
  154. {sqlspec-0.7.1/sqlspec/adapters/psycopg → sqlspec-0.9.0/tests/integration/test_adapters/test_asyncpg}/__init__.py +0 -0
  155. {sqlspec-0.7.1/sqlspec/adapters/sqlite → sqlspec-0.9.0/tests/integration/test_adapters/test_psqlpy}/__init__.py +0 -0
  156. {sqlspec-0.7.1/sqlspec/extensions → sqlspec-0.9.0/tests/unit}/__init__.py +0 -0
  157. {sqlspec-0.7.1/sqlspec/extensions/litestar → sqlspec-0.9.0/tests/unit/test_adapters}/__init__.py +0 -0
  158. {sqlspec-0.7.1/tests/unit/test_adapters/test_oracledb → sqlspec-0.9.0/tests/unit/test_adapters/test_aiosqlite}/__init__.py +0 -0
  159. {sqlspec-0.7.1 → sqlspec-0.9.0}/tests/unit/test_adapters/test_asyncpg/__init__.py +0 -0
  160. {sqlspec-0.7.1/sqlspec/utils → sqlspec-0.9.0/tests/unit/test_adapters/test_duckdb}/__init__.py +0 -0
  161. {sqlspec-0.7.1 → sqlspec-0.9.0}/tests/unit/test_adapters/test_sqlite/__init__.py +0 -0
  162. {sqlspec-0.7.1/tests → sqlspec-0.9.0/tests/unit/test_utils}/__init__.py +0 -0
  163. {sqlspec-0.7.1 → sqlspec-0.9.0}/tests/unit/test_utils/test_text.py +0 -0
  164. {sqlspec-0.7.1/tests/integration → sqlspec-0.9.0/tools}/__init__.py +0 -0
  165. {sqlspec-0.7.1 → sqlspec-0.9.0}/tools/build_docs.py +0 -0
  166. {sqlspec-0.7.1 → sqlspec-0.9.0}/tools/pypi_readme.py +0 -0
  167. {sqlspec-0.7.1 → sqlspec-0.9.0}/tools/sphinx_ext/changelog.py +0 -0
@@ -17,7 +17,7 @@ repos:
17
17
  - id: mixed-line-ending
18
18
  - id: trailing-whitespace
19
19
  - repo: https://github.com/charliermarsh/ruff-pre-commit
20
- rev: "v0.9.10"
20
+ rev: "v0.11.6"
21
21
  hooks:
22
22
  - id: ruff
23
23
  args: ["--fix"]
@@ -118,7 +118,7 @@ clean: ## Cleanup temporary build a
118
118
  .PHONY: test
119
119
  test: ## Run the tests
120
120
  @echo "${INFO} Running test cases... 🧪"
121
- @uv run pytest tests
121
+ @uv run pytest -n 2 --dist=loadgroup tests
122
122
  @echo "${OK} Tests complete ✨"
123
123
 
124
124
  .PHONY: test-all
@@ -128,7 +128,7 @@ test-all: tests ## Run all tests
128
128
  .PHONY: coverage
129
129
  coverage: ## Run tests with coverage report
130
130
  @echo "${INFO} Running tests with coverage... 📊"
131
- @uv run pytest --cov -n auto --quiet
131
+ @uv run pytest --cov -n 2 --dist=loadgroup --quiet
132
132
  @uv run coverage html >/dev/null 2>&1
133
133
  @uv run coverage xml >/dev/null 2>&1
134
134
  @echo "${OK} Coverage report generated ✨"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sqlspec
3
- Version: 0.7.1
3
+ Version: 0.9.0
4
4
  Summary: SQL Experiments in Python
5
5
  Author-email: Cody Fincher <cody@litestar.dev>
6
6
  Maintainer-email: Litestar Developers <hello@litestar.dev>
@@ -42,6 +42,8 @@ Provides-Extra: orjson
42
42
  Requires-Dist: orjson; extra == 'orjson'
43
43
  Provides-Extra: performance
44
44
  Requires-Dist: sqlglot[rs]; extra == 'performance'
45
+ Provides-Extra: psqlpy
46
+ Requires-Dist: psqlpy; extra == 'psqlpy'
45
47
  Provides-Extra: psycopg
46
48
  Requires-Dist: psycopg[binary,pool]; extra == 'psycopg'
47
49
  Provides-Extra: pydantic
@@ -102,6 +104,7 @@ This list is not final. If you have a driver you'd like to see added, please ope
102
104
  | [`asyncpg`](https://magicstack.github.io/asyncpg/current/) | PostgreSQL | Async | ✅ |
103
105
  | [`psycopg`](https://www.psycopg.org/) | PostgreSQL | Sync | ✅ |
104
106
  | [`psycopg`](https://www.psycopg.org/) | PostgreSQL | Async | ✅ |
107
+ | [`psqlpy`](https://psqlpy-python.github.io/) | PostgreSQL | Async | ✅ |
105
108
  | [`aiosqlite`](https://github.com/omnilib/aiosqlite) | SQLite | Async | ✅ |
106
109
  | `sqlite3` | SQLite | Sync | ✅ |
107
110
  | [`oracledb`](https://oracle.github.io/python-oracledb/) | Oracle | Async | ✅ |
@@ -43,6 +43,7 @@ This list is not final. If you have a driver you'd like to see added, please ope
43
43
  | [`asyncpg`](https://magicstack.github.io/asyncpg/current/) | PostgreSQL | Async | ✅ |
44
44
  | [`psycopg`](https://www.psycopg.org/) | PostgreSQL | Sync | ✅ |
45
45
  | [`psycopg`](https://www.psycopg.org/) | PostgreSQL | Async | ✅ |
46
+ | [`psqlpy`](https://psqlpy-python.github.io/) | PostgreSQL | Async | ✅ |
46
47
  | [`aiosqlite`](https://github.com/omnilib/aiosqlite) | SQLite | Async | ✅ |
47
48
  | `sqlite3` | SQLite | Sync | ✅ |
48
49
  | [`oracledb`](https://oracle.github.io/python-oracledb/) | Oracle | Async | ✅ |
@@ -7,10 +7,10 @@ maintainers = [{ name = "Litestar Developers", email = "hello@litestar.dev" }]
7
7
  name = "sqlspec"
8
8
  readme = "README.md"
9
9
  requires-python = ">=3.9, <4.0"
10
- version = "0.7.1"
10
+ version = "0.9.0"
11
11
 
12
12
  [project.optional-dependencies]
13
- adbc = ["adbc-driver-manager", "pyarrow"]
13
+ adbc = ["adbc_driver_manager", "pyarrow"]
14
14
  aioodbc = ["aioodbc"]
15
15
  aiosqlite = ["aiosqlite"]
16
16
  asyncmy = ["asyncmy"]
@@ -25,6 +25,7 @@ nanoid = ["fastnanoid>=0.4.1"]
25
25
  oracledb = ["oracledb"]
26
26
  orjson = ["orjson"]
27
27
  performance = ["sqlglot[rs]"]
28
+ psqlpy = ["psqlpy"]
28
29
  psycopg = ["psycopg[binary,pool]"]
29
30
  pydantic = ["pydantic", "pydantic-extra-types"]
30
31
  pymssql = ["pymssql"]
@@ -35,9 +36,7 @@ uuid = ["uuid-utils>=0.6.1"]
35
36
  [dependency-groups]
36
37
  build = ["bump-my-version"]
37
38
  dev = [
38
- "adbc-driver-sqlite",
39
- "adbc-driver-postgresql",
40
- "adbc-driver-flightsql",
39
+ { include-group = "extras" },
41
40
  { include-group = "lint" },
42
41
  { include-group = "doc" },
43
42
  { include-group = "test" },
@@ -59,6 +58,15 @@ doc = [
59
58
  "myst-parser",
60
59
  "sphinx-autodoc-typehints",
61
60
  ]
61
+ extras = [
62
+ "adbc_driver_manager",
63
+ "pyarrow",
64
+ "polars",
65
+ "adbc_driver_sqlite",
66
+ "adbc_driver_postgresql",
67
+ "adbc_driver_flightsql",
68
+ "adbc_driver_bigquery",
69
+ ]
62
70
  lint = [
63
71
  "mypy>=1.13.0",
64
72
  "pre-commit>=3.5.0",
@@ -75,7 +83,7 @@ test = [
75
83
  "pytest>=8.0.0",
76
84
  "pytest-asyncio>=0.23.8",
77
85
  "pytest-cov>=5.0.0",
78
- "pytest-databases>=0.10.0",
86
+ "pytest-databases[postgres,oracle,mysql,bigquery,spanner]>=0.12.2",
79
87
  "pytest-mock>=3.14.0",
80
88
  "pytest-sugar>=1.0.0",
81
89
  "pytest-xdist>=3.6.1",
@@ -101,7 +109,7 @@ packages = ["sqlspec"]
101
109
  allow_dirty = true
102
110
  commit = false
103
111
  commit_args = "--no-verify"
104
- current_version = "0.7.1"
112
+ current_version = "0.9.0"
105
113
  ignore_missing_files = false
106
114
  ignore_missing_version = false
107
115
  message = "chore(release): bump to v{new_version}"
@@ -168,7 +176,7 @@ exclude_lines = [
168
176
  ]
169
177
 
170
178
  [tool.pytest.ini_options]
171
- addopts = "-ra -q --doctest-glob='*.md' --strict-markers --strict-config"
179
+ addopts = ["-q", "-ra"]
172
180
  asyncio_default_fixture_loop_scope = "function"
173
181
  asyncio_mode = "auto"
174
182
  filterwarnings = [
@@ -182,8 +190,32 @@ filterwarnings = [
182
190
  "ignore::DeprecationWarning:websockets.connection",
183
191
  "ignore::DeprecationWarning:websockets.legacy",
184
192
  ]
193
+ markers = [
194
+ "integration: marks tests that require an external database",
195
+ "postgres: marks tests specific to PostgreSQL",
196
+ "duckdb: marks tests specific to DuckDB",
197
+ "sqlite: marks tests specific to SQLite",
198
+ "bigquery: marks tests specific to Google BigQuery",
199
+ "mysql: marks tests specific to MySQL",
200
+ "oracle: marks tests specific to Oracle",
201
+ "spanner: marks tests specific to Google Cloud Spanner",
202
+ "mssql: marks tests specific to Microsoft SQL Server",
203
+ # Driver markers
204
+ "adbc: marks tests using ADBC drivers",
205
+ "aioodbc: marks tests using aioodbc",
206
+ "aiosqlite: marks tests using aiosqlite",
207
+ "asyncmy: marks tests using asyncmy",
208
+ "asyncpg: marks tests using asyncpg",
209
+ "duckdb_driver: marks tests using the duckdb driver",
210
+ "google_bigquery: marks tests using google-cloud-bigquery",
211
+ "google_spanner: marks tests using google-cloud-spanner",
212
+ "oracledb: marks tests using oracledb",
213
+ "psycopg: marks tests using psycopg",
214
+ "pymssql: marks tests using pymssql",
215
+ "pymysql: marks tests using pymysql",
216
+ "psqlpy: marks tests using psqlpy",
217
+ ]
185
218
  testpaths = ["tests"]
186
- xfail_strict = true
187
219
 
188
220
  [tool.mypy]
189
221
  packages = ["sqlspec", "tests"]
@@ -213,14 +245,23 @@ module = [
213
245
  "uvloop.*",
214
246
  "asyncmy",
215
247
  "asyncmy.*",
248
+ "pyarrow",
249
+ "pyarrow.*",
216
250
  ]
217
251
 
218
252
  [tool.pyright]
219
253
  disableBytesTypePromotions = true
220
- exclude = ["tools", "docs"]
254
+ exclude = ["**/node_modules", "**/__pycache__", ".venv", "tools", "docs"]
221
255
  include = ["sqlspec", "tests"]
222
256
  pythonVersion = "3.9"
257
+ reportMissingTypeStubs = false
258
+ reportPrivateImportUsage = false
259
+ reportPrivateUsage = false
260
+ reportUnknownArgumentType = false
261
+ reportUnknownMemberType = false
262
+ reportUnknownVariableType = false
223
263
  reportUnnecessaryTypeIgnoreComments = true
264
+ root = "."
224
265
 
225
266
 
226
267
  [tool.slotscheck]
@@ -267,6 +308,9 @@ ignore = [
267
308
  "ARG002", # Unused method argument
268
309
  "ARG001", # Unused function argument
269
310
  "CPY001", # pycodestyle - Missing Copywrite notice at the top of the file
311
+ "RUF029", # Ruff - function is declared as async but has no awaitable calls
312
+ "COM812", # flake8-comma - Missing trailing comma
313
+ "PGH003", # Use Specific ignore for pyright
270
314
  ]
271
315
  select = ["ALL"]
272
316
 
@@ -310,6 +354,8 @@ known-first-party = ["sqlspec", "tests"]
310
354
  "TRY",
311
355
  "PT012",
312
356
  "INP001",
357
+ "DOC",
358
+ "PLC",
313
359
  ]
314
360
  "tools/**/*.*" = ["D", "ARG", "EM", "TRY", "G", "FBT", "S603", "F811", "PLW0127", "PLR0911"]
315
361
  "tools/prepare_release.py" = ["S603", "S607"]
@@ -0,0 +1,15 @@
1
+ from sqlspec import adapters, base, exceptions, extensions, filters, typing, utils
2
+ from sqlspec.__metadata__ import __version__
3
+ from sqlspec.base import SQLSpec
4
+
5
+ __all__ = (
6
+ "SQLSpec",
7
+ "__version__",
8
+ "adapters",
9
+ "base",
10
+ "exceptions",
11
+ "extensions",
12
+ "filters",
13
+ "typing",
14
+ "utils",
15
+ )
@@ -60,12 +60,26 @@ __all__ = (
60
60
 
61
61
 
62
62
  def convert_datetime_to_gmt_iso(dt: datetime.datetime) -> str: # pragma: no cover
63
- """Handle datetime serialization for nested timestamps."""
63
+ """Handle datetime serialization for nested timestamps.
64
+
65
+ Args:
66
+ dt: The datetime to convert.
67
+
68
+ Returns:
69
+ The ISO formatted datetime string.
70
+ """
64
71
  if not dt.tzinfo:
65
72
  dt = dt.replace(tzinfo=datetime.timezone.utc)
66
73
  return dt.isoformat().replace("+00:00", "Z")
67
74
 
68
75
 
69
76
  def convert_date_to_iso(dt: datetime.date) -> str: # pragma: no cover
70
- """Handle datetime serialization for nested timestamps."""
77
+ """Handle datetime serialization for nested timestamps.
78
+
79
+ Args:
80
+ dt: The date to convert.
81
+
82
+ Returns:
83
+ The ISO formatted date string.
84
+ """
71
85
  return dt.isoformat()
@@ -1,8 +1,10 @@
1
+ # ruff: noqa: RUF100, PLR0913, A002, DOC201, PLR6301
1
2
  """This is a simple wrapper around a few important classes in each library.
2
3
 
3
4
  This is used to ensure compatibility when one or more of the libraries are installed.
4
5
  """
5
6
 
7
+ from collections.abc import Iterable, Mapping
6
8
  from enum import Enum
7
9
  from typing import (
8
10
  Any,
@@ -29,7 +31,7 @@ T_co = TypeVar("T_co", covariant=True)
29
31
 
30
32
  try:
31
33
  from pydantic import (
32
- BaseModel,
34
+ BaseModel, # pyright: ignore[reportAssignmentType]
33
35
  FailFast, # pyright: ignore[reportGeneralTypeIssues,reportAssignmentType]
34
36
  TypeAdapter,
35
37
  )
@@ -96,7 +98,7 @@ except ImportError:
96
98
 
97
99
  def validate_python(
98
100
  self,
99
- object: Any, # noqa: A002
101
+ object: Any,
100
102
  /,
101
103
  *,
102
104
  strict: "Optional[bool]" = None,
@@ -127,10 +129,7 @@ try:
127
129
  except ImportError:
128
130
  import enum
129
131
  from collections.abc import Iterable
130
- from typing import TYPE_CHECKING, Callable, Optional, Union
131
-
132
- if TYPE_CHECKING:
133
- from collections.abc import Iterable
132
+ from typing import Callable, Optional, Union
134
133
 
135
134
  @dataclass_transform()
136
135
  @runtime_checkable
@@ -174,7 +173,6 @@ except ImportError:
174
173
  """Placeholder init"""
175
174
 
176
175
  def create_instance(self, **kwargs: Any) -> "T":
177
- """Placeholder implementation"""
178
176
  return cast("T", kwargs)
179
177
 
180
178
  def update_instance(self, instance: "T", **kwargs: Any) -> "T":
@@ -198,11 +196,46 @@ EmptyType = Union[Literal[EmptyEnum.EMPTY], UnsetType]
198
196
  Empty: Final = EmptyEnum.EMPTY
199
197
 
200
198
 
199
+ try:
200
+ from pyarrow import Table as ArrowTable
201
+
202
+ PYARROW_INSTALLED = True
203
+ except ImportError:
204
+
205
+ @runtime_checkable
206
+ class ArrowTable(Protocol): # type: ignore[no-redef]
207
+ """Placeholder Implementation"""
208
+
209
+ def to_batches(self, batch_size: int) -> Any: ...
210
+ def num_rows(self) -> int: ...
211
+ def num_columns(self) -> int: ...
212
+ def to_pydict(self) -> dict[str, Any]: ...
213
+ def to_string(self) -> str: ...
214
+ def from_arrays(
215
+ self,
216
+ arrays: list[Any],
217
+ names: "Optional[list[str]]" = None,
218
+ schema: "Optional[Any]" = None,
219
+ metadata: "Optional[Mapping[str, Any]]" = None,
220
+ ) -> Any: ...
221
+ def from_pydict(
222
+ self,
223
+ mapping: dict[str, Any],
224
+ schema: "Optional[Any]" = None,
225
+ metadata: "Optional[Mapping[str, Any]]" = None,
226
+ ) -> Any: ...
227
+ def from_batches(self, batches: Iterable[Any], schema: Optional[Any] = None) -> Any: ...
228
+
229
+ PYARROW_INSTALLED = False # pyright: ignore[reportConstantRedefinition]
230
+
231
+
201
232
  __all__ = (
202
233
  "LITESTAR_INSTALLED",
203
234
  "MSGSPEC_INSTALLED",
235
+ "PYARROW_INSTALLED",
204
236
  "PYDANTIC_INSTALLED",
205
237
  "UNSET",
238
+ "ArrowTable",
206
239
  "BaseModel",
207
240
  "DTOData",
208
241
  "DataclassProtocol",
@@ -0,0 +1,7 @@
1
+ from sqlspec.adapters.adbc.config import AdbcConfig
2
+ from sqlspec.adapters.adbc.driver import AdbcDriver
3
+
4
+ __all__ = (
5
+ "AdbcConfig",
6
+ "AdbcDriver",
7
+ )
@@ -0,0 +1,213 @@
1
+ from contextlib import contextmanager
2
+ from dataclasses import dataclass, field
3
+ from typing import TYPE_CHECKING, Any, Callable, Optional, Union, cast
4
+
5
+ from adbc_driver_manager.dbapi import Connection
6
+
7
+ from sqlspec.adapters.adbc.driver import AdbcDriver
8
+ from sqlspec.base import NoPoolSyncConfig
9
+ from sqlspec.exceptions import ImproperConfigurationError
10
+ from sqlspec.typing import Empty, EmptyType
11
+ from sqlspec.utils.module_loader import import_string
12
+
13
+ if TYPE_CHECKING:
14
+ from collections.abc import Generator
15
+
16
+
17
+ __all__ = ("AdbcConfig",)
18
+
19
+
20
+ @dataclass
21
+ class AdbcConfig(NoPoolSyncConfig["Connection", "AdbcDriver"]):
22
+ """Configuration for ADBC connections.
23
+
24
+ This class provides configuration options for ADBC database connections using the
25
+ ADBC Driver Manager.([1](https://arrow.apache.org/adbc/current/python/api/adbc_driver_manager.html))
26
+ """
27
+
28
+ uri: "Union[str, EmptyType]" = Empty
29
+ """Database URI"""
30
+ driver_name: "Union[str, EmptyType]" = Empty
31
+ """Full dotted path to the ADBC driver's connect function (e.g., 'adbc_driver_sqlite.dbapi.connect')"""
32
+ db_kwargs: "Optional[dict[str, Any]]" = None
33
+ """Additional database-specific connection parameters"""
34
+ conn_kwargs: "Optional[dict[str, Any]]" = None
35
+ """Additional database-specific connection parameters"""
36
+ connection_type: "type[Connection]" = field(init=False, default_factory=lambda: Connection)
37
+ """Type of the connection object"""
38
+ driver_type: "type[AdbcDriver]" = field(init=False, default_factory=lambda: AdbcDriver) # type: ignore[type-abstract,unused-ignore]
39
+ """Type of the driver object"""
40
+ pool_instance: None = field(init=False, default=None)
41
+ """No connection pool is used for ADBC connections"""
42
+ _is_in_memory: bool = field(init=False, default=False)
43
+ """Flag indicating if the connection is for an in-memory database"""
44
+
45
+ def _set_adbc(self) -> str: # noqa: PLR0912
46
+ """Identify the driver type based on the URI (if provided) or preset driver name.
47
+
48
+ Also sets the `_is_in_memory` flag for specific in-memory URIs.
49
+
50
+ Raises:
51
+ ImproperConfigurationError: If the driver name is not recognized or supported.
52
+
53
+ Returns:
54
+ str: The driver name to be used for the connection.
55
+ """
56
+
57
+ if isinstance(self.driver_name, str):
58
+ if self.driver_name != "adbc_driver_sqlite.dbapi.connect" and self.driver_name in {
59
+ "sqlite",
60
+ "sqlite3",
61
+ "adbc_driver_sqlite",
62
+ }:
63
+ self.driver_name = "adbc_driver_sqlite.dbapi.connect"
64
+ elif self.driver_name != "adbc_driver_duckdb.dbapi.connect" and self.driver_name in {
65
+ "duckdb",
66
+ "adbc_driver_duckdb",
67
+ }:
68
+ self.driver_name = "adbc_driver_duckdb.dbapi.connect"
69
+ elif self.driver_name != "adbc_driver_postgresql.dbapi.connect" and self.driver_name in {
70
+ "postgres",
71
+ "adbc_driver_postgresql",
72
+ "postgresql",
73
+ "pg",
74
+ }:
75
+ self.driver_name = "adbc_driver_postgresql.dbapi.connect"
76
+ elif self.driver_name != "adbc_driver_snowflake.dbapi.connect" and self.driver_name in {
77
+ "snowflake",
78
+ "adbc_driver_snowflake",
79
+ "sf",
80
+ }:
81
+ self.driver_name = "adbc_driver_snowflake.dbapi.connect"
82
+ elif self.driver_name != "adbc_driver_bigquery.dbapi.connect" and self.driver_name in {
83
+ "bigquery",
84
+ "adbc_driver_bigquery",
85
+ "bq",
86
+ }:
87
+ self.driver_name = "adbc_driver_bigquery.dbapi.connect"
88
+ elif self.driver_name != "adbc_driver_flightsql.dbapi.connect" and self.driver_name in {
89
+ "flightsql",
90
+ "adbc_driver_flightsql",
91
+ "grpc",
92
+ }:
93
+ self.driver_name = "adbc_driver_flightsql.dbapi.connect"
94
+ return self.driver_name
95
+
96
+ # If driver_name wasn't explicit, try to determine from URI
97
+ if isinstance(self.uri, str) and self.uri.startswith("postgresql://"):
98
+ self.driver_name = "adbc_driver_postgresql.dbapi.connect"
99
+ elif isinstance(self.uri, str) and self.uri.startswith("sqlite://"):
100
+ self.driver_name = "adbc_driver_sqlite.dbapi.connect"
101
+ elif isinstance(self.uri, str) and self.uri.startswith("grpc://"):
102
+ self.driver_name = "adbc_driver_flightsql.dbapi.connect"
103
+ elif isinstance(self.uri, str) and self.uri.startswith("snowflake://"):
104
+ self.driver_name = "adbc_driver_snowflake.dbapi.connect"
105
+ elif isinstance(self.uri, str) and self.uri.startswith("bigquery://"):
106
+ self.driver_name = "adbc_driver_bigquery.dbapi.connect"
107
+ elif isinstance(self.uri, str) and self.uri.startswith("duckdb://"):
108
+ self.driver_name = "adbc_driver_duckdb.dbapi.connect"
109
+
110
+ # Check if we successfully determined a driver name
111
+ if self.driver_name is Empty or not isinstance(self.driver_name, str):
112
+ msg = (
113
+ "Could not determine ADBC driver connect path. Please specify 'driver_name' "
114
+ "(e.g., 'adbc_driver_sqlite.dbapi.connect') or provide a supported 'uri'. "
115
+ f"URI: {self.uri}, Driver Name: {self.driver_name}"
116
+ )
117
+ raise ImproperConfigurationError(msg)
118
+ return self.driver_name
119
+
120
+ @property
121
+ def connection_config_dict(self) -> "dict[str, Any]":
122
+ """Return the connection configuration as a dict.
123
+
124
+ Omits the 'uri' key for known in-memory database types.
125
+
126
+ Returns:
127
+ A string keyed dict of config kwargs for the adbc_driver_manager.dbapi.connect function.
128
+ """
129
+ config = {}
130
+ db_kwargs = self.db_kwargs or {}
131
+ conn_kwargs = self.conn_kwargs or {}
132
+ if isinstance(self.uri, str) and self.uri.startswith("sqlite://"):
133
+ db_kwargs["uri"] = self.uri.replace("sqlite://", "")
134
+ elif isinstance(self.uri, str) and self.uri.startswith("duckdb://"):
135
+ db_kwargs["path"] = self.uri.replace("duckdb://", "")
136
+ elif isinstance(self.uri, str):
137
+ db_kwargs["uri"] = self.uri
138
+ if isinstance(self.driver_name, str) and self.driver_name.startswith("adbc_driver_bigquery"):
139
+ config["db_kwargs"] = db_kwargs
140
+ else:
141
+ config = db_kwargs
142
+ if conn_kwargs:
143
+ config["conn_kwargs"] = conn_kwargs
144
+ return config
145
+
146
+ def _get_connect_func(self) -> "Callable[..., Connection]":
147
+ self._set_adbc()
148
+ driver_path = cast("str", self.driver_name)
149
+ try:
150
+ connect_func = import_string(driver_path)
151
+ except ImportError as e:
152
+ # Check if the error is likely due to missing suffix and try again
153
+ if ".dbapi.connect" not in driver_path:
154
+ try:
155
+ driver_path += ".dbapi.connect"
156
+ connect_func = import_string(driver_path)
157
+ except ImportError as e2:
158
+ msg = f"Failed to import ADBC connect function from '{self.driver_name}' or '{driver_path}'. Is the driver installed and the path correct? Original error: {e} / {e2}"
159
+ raise ImproperConfigurationError(msg) from e2
160
+ else:
161
+ # Original import failed, and suffix was already present or added
162
+ msg = f"Failed to import ADBC connect function from '{driver_path}'. Is the driver installed and the path correct? Original error: {e}"
163
+ raise ImproperConfigurationError(msg) from e
164
+ if not callable(connect_func):
165
+ msg = f"The path '{driver_path}' did not resolve to a callable function."
166
+ raise ImproperConfigurationError(msg)
167
+ return connect_func # type: ignore[no-any-return]
168
+
169
+ def create_connection(self) -> "Connection":
170
+ """Create and return a new database connection using the specific driver.
171
+
172
+ Returns:
173
+ A new ADBC connection instance.
174
+
175
+ Raises:
176
+ ImproperConfigurationError: If the connection could not be established.
177
+ """
178
+ try:
179
+ connect_func = self._get_connect_func()
180
+ return connect_func(**self.connection_config_dict)
181
+ except Exception as e:
182
+ # Include driver name in error message for better context
183
+ driver_name = self.driver_name if isinstance(self.driver_name, str) else "Unknown/Missing"
184
+ # Use the potentially modified driver_path from _get_connect_func if available,
185
+ # otherwise fallback to self.driver_name for the error message.
186
+ # This requires _get_connect_func to potentially return the used path or store it.
187
+ # For simplicity now, we stick to self.driver_name in the message.
188
+ msg = f"Could not configure the ADBC connection using driver path '{driver_name}'. Error: {e!s}"
189
+ raise ImproperConfigurationError(msg) from e
190
+
191
+ @contextmanager
192
+ def provide_connection(self, *args: "Any", **kwargs: "Any") -> "Generator[Connection, None, None]":
193
+ """Create and provide a database connection using the specific driver.
194
+
195
+ Yields:
196
+ Connection: A database connection instance.
197
+ """
198
+
199
+ connection = self.create_connection()
200
+ try:
201
+ yield connection
202
+ finally:
203
+ connection.close()
204
+
205
+ @contextmanager
206
+ def provide_session(self, *args: Any, **kwargs: Any) -> "Generator[AdbcDriver, None, None]":
207
+ """Create and provide a database session.
208
+
209
+ Yields:
210
+ An ADBC driver instance with an active connection.
211
+ """
212
+ with self.provide_connection(*args, **kwargs) as connection:
213
+ yield self.driver_type(connection)