sqlspec 0.4.0__tar.gz → 0.6.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.

Potentially problematic release.


This version of sqlspec might be problematic. Click here for more details.

Files changed (83) hide show
  1. {sqlspec-0.4.0 → sqlspec-0.6.0}/.pre-commit-config.yaml +3 -3
  2. sqlspec-0.6.0/LICENSE +21 -0
  3. {sqlspec-0.4.0 → sqlspec-0.6.0}/Makefile +9 -18
  4. sqlspec-0.6.0/PKG-INFO +128 -0
  5. sqlspec-0.6.0/README.md +76 -0
  6. {sqlspec-0.4.0 → sqlspec-0.6.0}/pyproject.toml +4 -66
  7. {sqlspec-0.4.0 → sqlspec-0.6.0}/sqlspec/__metadata__.py +1 -1
  8. {sqlspec-0.4.0 → sqlspec-0.6.0}/sqlspec/_serialization.py +1 -1
  9. {sqlspec-0.4.0 → sqlspec-0.6.0}/sqlspec/_typing.py +31 -1
  10. {sqlspec-0.4.0 → sqlspec-0.6.0}/sqlspec/adapters/adbc/config.py +7 -7
  11. {sqlspec-0.4.0 → sqlspec-0.6.0}/sqlspec/adapters/aiosqlite/config.py +11 -18
  12. {sqlspec-0.4.0 → sqlspec-0.6.0}/sqlspec/adapters/asyncmy/config.py +9 -4
  13. {sqlspec-0.4.0 → sqlspec-0.6.0}/sqlspec/adapters/asyncpg/config.py +10 -10
  14. sqlspec-0.6.0/sqlspec/adapters/duckdb/__init__.py +3 -0
  15. {sqlspec-0.4.0 → sqlspec-0.6.0}/sqlspec/adapters/duckdb/config.py +15 -14
  16. {sqlspec-0.4.0 → sqlspec-0.6.0}/sqlspec/adapters/oracledb/__init__.py +1 -1
  17. {sqlspec-0.4.0 → sqlspec-0.6.0}/sqlspec/adapters/oracledb/config/_asyncio.py +17 -7
  18. {sqlspec-0.4.0 → sqlspec-0.6.0}/sqlspec/adapters/oracledb/config/_common.py +7 -25
  19. {sqlspec-0.4.0 → sqlspec-0.6.0}/sqlspec/adapters/oracledb/config/_sync.py +17 -7
  20. sqlspec-0.6.0/sqlspec/adapters/psycopg/config/__init__.py +9 -0
  21. {sqlspec-0.4.0 → sqlspec-0.6.0}/sqlspec/adapters/psycopg/config/_async.py +13 -8
  22. {sqlspec-0.4.0 → sqlspec-0.6.0}/sqlspec/adapters/psycopg/config/_common.py +3 -18
  23. {sqlspec-0.4.0 → sqlspec-0.6.0}/sqlspec/adapters/psycopg/config/_sync.py +12 -8
  24. {sqlspec-0.4.0 → sqlspec-0.6.0}/sqlspec/adapters/sqlite/config.py +3 -3
  25. sqlspec-0.6.0/sqlspec/base.py +221 -0
  26. sqlspec-0.6.0/sqlspec/extensions/litestar/plugin.py +42 -0
  27. {sqlspec-0.4.0 → sqlspec-0.6.0}/sqlspec/filters.py +2 -1
  28. {sqlspec-0.4.0 → sqlspec-0.6.0}/sqlspec/typing.py +119 -31
  29. sqlspec-0.6.0/sqlspec/utils/deprecation.py +111 -0
  30. sqlspec-0.6.0/sqlspec/utils/fixtures.py +66 -0
  31. sqlspec-0.6.0/sqlspec/utils/module_loader.py +94 -0
  32. sqlspec-0.6.0/sqlspec/utils/text.py +46 -0
  33. sqlspec-0.6.0/tests/unit/test_adapters/test_aiosqlite/test_config.py +108 -0
  34. sqlspec-0.6.0/tests/unit/test_adapters/test_asyncpg/__init__.py +1 -0
  35. sqlspec-0.6.0/tests/unit/test_adapters/test_asyncpg/test_config.py +159 -0
  36. sqlspec-0.6.0/tests/unit/test_adapters/test_duckdb/__init__.py +0 -0
  37. {sqlspec-0.4.0 → sqlspec-0.6.0}/tests/unit/test_adapters/test_duckdb/test_config.py +11 -9
  38. sqlspec-0.6.0/tests/unit/test_adapters/test_oracledb/__init__.py +1 -0
  39. sqlspec-0.6.0/tests/unit/test_adapters/test_oracledb/test_config.py +335 -0
  40. sqlspec-0.6.0/tests/unit/test_adapters/test_psycopg/test_async_config.py +167 -0
  41. sqlspec-0.6.0/tests/unit/test_adapters/test_psycopg/test_sync_config.py +152 -0
  42. sqlspec-0.6.0/tests/unit/test_adapters/test_sqlite/__init__.py +1 -0
  43. sqlspec-0.6.0/tests/unit/test_adapters/test_sqlite/test_config.py +87 -0
  44. sqlspec-0.6.0/tests/unit/test_base.py +229 -0
  45. {sqlspec-0.4.0 → sqlspec-0.6.0}/tests/unit/test_typing.py +12 -12
  46. sqlspec-0.6.0/tests/unit/test_utils/__init__.py +0 -0
  47. sqlspec-0.6.0/tests/unit/test_utils/test_module_loader.py +47 -0
  48. sqlspec-0.6.0/tests/unit/test_utils/test_text.py +16 -0
  49. sqlspec-0.6.0/tools/__init__.py +0 -0
  50. {sqlspec-0.4.0 → sqlspec-0.6.0}/uv.lock +912 -930
  51. sqlspec-0.4.0/PKG-INFO +0 -84
  52. sqlspec-0.4.0/README.md +0 -33
  53. sqlspec-0.4.0/sqlspec/adapters/psycopg/config/__init__.py +0 -9
  54. sqlspec-0.4.0/sqlspec/config.py +0 -16
  55. sqlspec-0.4.0/sqlspec/extensions/litestar/plugin.py +0 -34
  56. {sqlspec-0.4.0 → sqlspec-0.6.0}/.gitignore +0 -0
  57. {sqlspec-0.4.0 → sqlspec-0.6.0}/CONTRIBUTING.rst +0 -0
  58. {sqlspec-0.4.0 → sqlspec-0.6.0}/NOTICE +0 -0
  59. {sqlspec-0.4.0 → sqlspec-0.6.0}/sqlspec/__init__.py +0 -0
  60. {sqlspec-0.4.0 → sqlspec-0.6.0}/sqlspec/adapters/__init__.py +0 -0
  61. {sqlspec-0.4.0 → sqlspec-0.6.0}/sqlspec/adapters/adbc/__init__.py +0 -0
  62. {sqlspec-0.4.0 → sqlspec-0.6.0}/sqlspec/adapters/aiosqlite/__init__.py +0 -0
  63. {sqlspec-0.4.0 → sqlspec-0.6.0}/sqlspec/adapters/asyncmy/__init__.py +0 -0
  64. {sqlspec-0.4.0 → sqlspec-0.6.0}/sqlspec/adapters/asyncpg/__init__.py +0 -0
  65. {sqlspec-0.4.0 → sqlspec-0.6.0}/sqlspec/adapters/oracledb/config/__init__.py +0 -0
  66. {sqlspec-0.4.0/sqlspec/adapters/duckdb → sqlspec-0.6.0/sqlspec/adapters/psycopg}/__init__.py +0 -0
  67. {sqlspec-0.4.0/sqlspec/adapters/psycopg → sqlspec-0.6.0/sqlspec/adapters/sqlite}/__init__.py +0 -0
  68. {sqlspec-0.4.0 → sqlspec-0.6.0}/sqlspec/exceptions.py +0 -0
  69. {sqlspec-0.4.0/sqlspec/adapters/sqlite → sqlspec-0.6.0/sqlspec/extensions}/__init__.py +0 -0
  70. {sqlspec-0.4.0/sqlspec/extensions → sqlspec-0.6.0/sqlspec/extensions/litestar}/__init__.py +0 -0
  71. /sqlspec-0.4.0/sqlspec/extensions/litestar/__init__.py → /sqlspec-0.6.0/sqlspec/extensions/litestar/config.py +0 -0
  72. {sqlspec-0.4.0 → sqlspec-0.6.0}/sqlspec/py.typed +0 -0
  73. {sqlspec-0.4.0/tests → sqlspec-0.6.0/sqlspec/utils}/__init__.py +0 -0
  74. {sqlspec-0.4.0/tests/unit → sqlspec-0.6.0/tests}/__init__.py +0 -0
  75. {sqlspec-0.4.0 → sqlspec-0.6.0}/tests/conftest.py +0 -0
  76. {sqlspec-0.4.0/tests/unit/test_adapters → sqlspec-0.6.0/tests/integration}/__init__.py +0 -0
  77. {sqlspec-0.4.0/tests/unit/test_adapters/test_duckdb → sqlspec-0.6.0/tests/unit}/__init__.py +0 -0
  78. {sqlspec-0.4.0/tools → sqlspec-0.6.0/tests/unit/test_adapters}/__init__.py +0 -0
  79. {sqlspec-0.4.0 → sqlspec-0.6.0}/tools/build_docs.py +0 -0
  80. {sqlspec-0.4.0 → sqlspec-0.6.0}/tools/pypi_readme.py +0 -0
  81. {sqlspec-0.4.0 → sqlspec-0.6.0}/tools/sphinx_ext/__init__.py +0 -0
  82. {sqlspec-0.4.0 → sqlspec-0.6.0}/tools/sphinx_ext/changelog.py +0 -0
  83. {sqlspec-0.4.0 → sqlspec-0.6.0}/tools/sphinx_ext/missing_references.py +0 -0
@@ -2,7 +2,7 @@ default_language_version:
2
2
  python: "3"
3
3
  repos:
4
4
  - repo: https://github.com/compilerla/conventional-pre-commit
5
- rev: v3.6.0
5
+ rev: v4.0.0
6
6
  hooks:
7
7
  - id: conventional-pre-commit
8
8
  stages: [commit-msg]
@@ -17,13 +17,13 @@ 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.8.3"
20
+ rev: "v0.9.4"
21
21
  hooks:
22
22
  - id: ruff
23
23
  args: ["--fix"]
24
24
  - id: ruff-format
25
25
  - repo: https://github.com/codespell-project/codespell
26
- rev: v2.3.0
26
+ rev: v2.4.1
27
27
  hooks:
28
28
  - id: codespell
29
29
  additional_dependencies:
sqlspec-0.6.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Litestar Organization
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.
@@ -121,45 +121,36 @@ test: ## Run the tests
121
121
  @uv run pytest tests
122
122
  @echo "${OK} Tests complete ✨"
123
123
 
124
- .PHONY: test-examples
125
- test-examples: ## Run the examples tests
126
- @echo "${INFO} Running example tests... 🧪"
127
- @uv run pytest docs/examples
128
- @echo "${OK} Example tests complete ✨"
129
-
130
124
  .PHONY: test-all
131
- test-all: test test-examples ## Run all tests
125
+ test-all: tests ## Run all tests
132
126
  @echo "${INFO} All tests executed successfully ✨"
133
127
 
134
128
  .PHONY: coverage
135
- coverage: ## Run tests with coverage report
129
+ coverage: ## Run tests with coverage report
136
130
  @echo "${INFO} Running tests with coverage... 📊"
137
- @uv run pytest tests --cov -n auto --quiet
131
+ @uv run pytest --cov -n auto --quiet
138
132
  @uv run coverage html >/dev/null 2>&1
139
133
  @uv run coverage xml >/dev/null 2>&1
140
134
  @echo "${OK} Coverage report generated ✨"
141
135
 
136
+ # -----------------------------------------------------------------------------
137
+ # Type Checking
138
+ # -----------------------------------------------------------------------------
139
+
142
140
  .PHONY: mypy
143
141
  mypy: ## Run mypy
144
142
  @echo "${INFO} Running mypy... 🔍"
145
143
  @uv run dmypy run
146
- @echo "${OK} mypy complete ✨"
147
-
148
- .PHONY: mypy-nocache
149
- mypy-nocache: ## Run Mypy without cache
150
- @echo "${INFO} Running mypy without cache... 🔍"
151
- @uv run mypy
152
- @echo "${OK} mypy complete ✨"
144
+ @echo "${OK} Mypy checks passed ✨"
153
145
 
154
146
  .PHONY: pyright
155
147
  pyright: ## Run pyright
156
148
  @echo "${INFO} Running pyright... 🔍"
157
149
  @uv run pyright
158
- @echo "${OK} pyright complete ✨"
150
+ @echo "${OK} Pyright checks passed ✨"
159
151
 
160
152
  .PHONY: type-check
161
153
  type-check: mypy pyright ## Run all type checking
162
- @echo "${OK} All type checks passed ✨"
163
154
 
164
155
  # -----------------------------------------------------------------------------
165
156
  # Linting and Formatting
sqlspec-0.6.0/PKG-INFO ADDED
@@ -0,0 +1,128 @@
1
+ Metadata-Version: 2.4
2
+ Name: sqlspec
3
+ Version: 0.6.0
4
+ Summary: SQL Experiments in Python
5
+ Author-email: Cody Fincher <cody@litestar.dev>
6
+ Maintainer-email: Litestar Developers <hello@litestar.dev>
7
+ License-Expression: MIT
8
+ License-File: LICENSE
9
+ License-File: NOTICE
10
+ Requires-Python: <4.0,>=3.9
11
+ Requires-Dist: eval-type-backport; python_version < '3.10'
12
+ Requires-Dist: sqlglot
13
+ Requires-Dist: typing-extensions
14
+ Provides-Extra: adbc
15
+ Requires-Dist: adbc-driver-manager; extra == 'adbc'
16
+ Requires-Dist: pyarrow; extra == 'adbc'
17
+ Provides-Extra: aioodbc
18
+ Requires-Dist: aioodbc; extra == 'aioodbc'
19
+ Provides-Extra: aiosqlite
20
+ Requires-Dist: aiosqlite; extra == 'aiosqlite'
21
+ Provides-Extra: asyncmy
22
+ Requires-Dist: asyncmy; extra == 'asyncmy'
23
+ Provides-Extra: asyncpg
24
+ Requires-Dist: asyncpg; extra == 'asyncpg'
25
+ Provides-Extra: bigquery
26
+ Requires-Dist: google-cloud-bigquery; extra == 'bigquery'
27
+ Provides-Extra: duckdb
28
+ Requires-Dist: duckdb; extra == 'duckdb'
29
+ Provides-Extra: fastapi
30
+ Requires-Dist: fastapi; extra == 'fastapi'
31
+ Provides-Extra: flask
32
+ Requires-Dist: flask; extra == 'flask'
33
+ Provides-Extra: litestar
34
+ Requires-Dist: litestar; extra == 'litestar'
35
+ Provides-Extra: msgspec
36
+ Requires-Dist: msgspec; extra == 'msgspec'
37
+ Provides-Extra: oracledb
38
+ Requires-Dist: oracledb; extra == 'oracledb'
39
+ Provides-Extra: performance
40
+ Requires-Dist: sqlglot[rs]; extra == 'performance'
41
+ Provides-Extra: psycopg
42
+ Requires-Dist: psycopg[binary,pool]; extra == 'psycopg'
43
+ Provides-Extra: pydantic
44
+ Requires-Dist: pydantic; extra == 'pydantic'
45
+ Provides-Extra: pymssql
46
+ Requires-Dist: pymssql; extra == 'pymssql'
47
+ Provides-Extra: pymysql
48
+ Requires-Dist: pymysql; extra == 'pymysql'
49
+ Provides-Extra: spanner
50
+ Requires-Dist: google-cloud-spanner; extra == 'spanner'
51
+ Description-Content-Type: text/markdown
52
+
53
+ # SQLSpec
54
+
55
+ ## A Query Mapper for Python
56
+
57
+ SQLSpec is an experimental Python library designed to streamline and modernize your SQL interactions across a variety of database systems. While still in its early stages, SQLSpec aims to provide a flexible, typed, and extensible interface for working with SQL in Python.
58
+
59
+ **Note**: SQLSpec is currently under active development and the API is subject to change. It is not yet ready for production use. Contributions are welcome!
60
+
61
+ ## Core Features (Planned but subject to change, removal or redesign)
62
+
63
+ - **Consistent Database Session Interface**: Provides a consistent connectivity interface for interacting with one or more database systems, including SQLite, Postgres, DuckDB, MySQL, Oracle, SQL Server, Spanner, BigQuery, and more.
64
+ - **Emphasis on RAW SQL and Minimal Abstractions and Performance**: SQLSpec is a library for working with SQL in Python. It's goals are to offer minimal abstractions between the user and the database. It does not aim to be an ORM library.
65
+ - **Type-Safe Queries**: Quickly map SQL queries to typed objects using libraries such as Pydantic, Msgspec, Attrs, etc.
66
+ - **Extensible Design**: Easily add support for new database dialects or extend existing functionality to meet your specific needs. Easily add support for async and sync database drivers.
67
+ - **Minimal Dependencies**: SQLSpec is designed to be lightweight and can run on it's own or with other libraries such as `litestar`, `fastapi`, `flask` and more. (Contributions welcome!)
68
+ - **Dynamic Query Manipulation**: Easily apply filters to pre-defined queries with a fluent, Pythonic API. Safely manipulate queries without the risk of SQL injection.
69
+ - **Dialect Validation and Conversion**: Use `sqlglot` to validate your SQL against specific dialects and seamlessly convert between them.
70
+ - **Support for Async and Sync Database Drivers**: SQLSpec supports both async and sync database drivers, allowing you to choose the style that best fits your application.
71
+ - **Basic Migration Management**: A mechanism to generate empty migration files where you can add your own SQL and intelligently track which migrations have been applied.
72
+
73
+ ## What SQLSpec Is Not (Yet)
74
+
75
+ SQLSpec is a work in progress. While it offers a solid foundation for modern SQL interactions, it does not yet include every feature you might find in a mature ORM or database toolkit. The focus is on building a robust, flexible core that can be extended over time.
76
+
77
+ ## Inspiration and Future Direction
78
+
79
+ SQLSpec originally drew inspiration from features found in the `aiosql` library. This is a great library for working with and executed SQL stored in files. It's unclear how much of an overlap there will be between the two libraries, but it's possible that some features will be contributed back to `aiosql` where appropriate.
80
+
81
+ ## Current Focus: Universal Connectivity
82
+
83
+ The primary goal at this stage is to establish a **native connectivity interface** that works seamlessly across all supported database environments. This means you can connect to any of the supported databases using a consistent API, regardless of the underlying driver or dialect.
84
+
85
+ ## Adapters: Completed, In Progress, and Planned
86
+
87
+ This list is not final. If you have a driver you'd like to see added, please open an issue or submit a PR!
88
+
89
+ | Driver | Database | Mode | Status |
90
+ | :----------------------------------------------------------------------------------------------------------- | :--------- | :------ | :--------- |
91
+ | [`adbc`](https://arrow.apache.org/adbc/) | Postgres | Sync | ✅ |
92
+ | [`adbc`](https://arrow.apache.org/adbc/) | SQLite | Sync | ✅ |
93
+ | [`adbc`](https://arrow.apache.org/adbc/) | Snowflake | Sync | ✅ |
94
+ | [`adbc`](https://arrow.apache.org/adbc/) | DuckDB | Sync | ✅ |
95
+ | [`asyncpg`](https://magicstack.github.io/asyncpg/current/) | PostgreSQL | Async | ✅ |
96
+ | [`psycopg`](https://www.psycopg.org/) | PostgreSQL | Sync | ✅ |
97
+ | [`psycopg`](https://www.psycopg.org/) | PostgreSQL | Async | ✅ |
98
+ | [`aiosqlite`](https://github.com/omnilib/aiosqlite) | SQLite | Async | ✅ |
99
+ | `sqlite3` | SQLite | Sync | ✅ |
100
+ | [`oracledb`](https://oracle.github.io/python-oracledb/) | Oracle | Async | ✅ |
101
+ | [`oracledb`](https://oracle.github.io/python-oracledb/) | Oracle | Sync | ✅ |
102
+ | [`duckdb`](https://duckdb.org/) | DuckDB | Sync | ✅ |
103
+ | [`bigquery`](https://googleapis.dev/python/bigquery/latest/index.html) | BigQuery | Sync | 🗓️ |
104
+ | [`spanner`](https://googleapis.dev/python/spanner/latest/index.html) | Spanner | Sync | 🗓️ |
105
+ | [`sqlserver`](https://docs.microsoft.com/en-us/sql/connect/python/pyodbc/python-sql-driver-for-pyodbc?view=sql-server-ver16) | SQL Server | Sync | 🗓️ |
106
+ | [`mysql`](https://dev.mysql.com/doc/connector-python/en/connector-python-api-mysql-connector-python.html) | MySQL | Sync | 🗓️ |
107
+ | [`snowflake`](https://docs.snowflake.com) | Snowflake | Sync | 🗓️ |
108
+
109
+ ## Proposed Project Structure
110
+
111
+ - `sqlspec/`:
112
+ - `adapters/`: Contains all database drivers and associated configuration.
113
+ - `extensions/`:
114
+ - `litestar/`: Future home of `litestar` integration.
115
+ - `fastapi/`: Future home of `fastapi` integration.
116
+ - `flask/`: Future home of `flask` integration.
117
+ - `*/`: Future home of your favorite framework integration 🔌 ✨
118
+ - `base.py`: Contains base protocols for database configurations.
119
+ - `filters.py`: Contains the `Filter` class which is used to apply filters to pre-defined SQL queries.
120
+ - `utils/`: Contains utility functions used throughout the project.
121
+ - `exceptions.py`: Contains custom exceptions for SQLSpec.
122
+ - `typing.py`: Contains type hints, type guards and several facades for optional libraries that are not required for the core functionality of SQLSpec.
123
+
124
+ ## Get Involved
125
+
126
+ SQLSpec is an open-source project, and contributions are welcome! Whether you're interested in adding support for new databases, improving the query interface, or simply providing feedback, your input is valuable.
127
+
128
+ **Disclaimer**: SQLSpec is under active development. Expect changes and improvements as the project evolves.
@@ -0,0 +1,76 @@
1
+ # SQLSpec
2
+
3
+ ## A Query Mapper for Python
4
+
5
+ SQLSpec is an experimental Python library designed to streamline and modernize your SQL interactions across a variety of database systems. While still in its early stages, SQLSpec aims to provide a flexible, typed, and extensible interface for working with SQL in Python.
6
+
7
+ **Note**: SQLSpec is currently under active development and the API is subject to change. It is not yet ready for production use. Contributions are welcome!
8
+
9
+ ## Core Features (Planned but subject to change, removal or redesign)
10
+
11
+ - **Consistent Database Session Interface**: Provides a consistent connectivity interface for interacting with one or more database systems, including SQLite, Postgres, DuckDB, MySQL, Oracle, SQL Server, Spanner, BigQuery, and more.
12
+ - **Emphasis on RAW SQL and Minimal Abstractions and Performance**: SQLSpec is a library for working with SQL in Python. It's goals are to offer minimal abstractions between the user and the database. It does not aim to be an ORM library.
13
+ - **Type-Safe Queries**: Quickly map SQL queries to typed objects using libraries such as Pydantic, Msgspec, Attrs, etc.
14
+ - **Extensible Design**: Easily add support for new database dialects or extend existing functionality to meet your specific needs. Easily add support for async and sync database drivers.
15
+ - **Minimal Dependencies**: SQLSpec is designed to be lightweight and can run on it's own or with other libraries such as `litestar`, `fastapi`, `flask` and more. (Contributions welcome!)
16
+ - **Dynamic Query Manipulation**: Easily apply filters to pre-defined queries with a fluent, Pythonic API. Safely manipulate queries without the risk of SQL injection.
17
+ - **Dialect Validation and Conversion**: Use `sqlglot` to validate your SQL against specific dialects and seamlessly convert between them.
18
+ - **Support for Async and Sync Database Drivers**: SQLSpec supports both async and sync database drivers, allowing you to choose the style that best fits your application.
19
+ - **Basic Migration Management**: A mechanism to generate empty migration files where you can add your own SQL and intelligently track which migrations have been applied.
20
+
21
+ ## What SQLSpec Is Not (Yet)
22
+
23
+ SQLSpec is a work in progress. While it offers a solid foundation for modern SQL interactions, it does not yet include every feature you might find in a mature ORM or database toolkit. The focus is on building a robust, flexible core that can be extended over time.
24
+
25
+ ## Inspiration and Future Direction
26
+
27
+ SQLSpec originally drew inspiration from features found in the `aiosql` library. This is a great library for working with and executed SQL stored in files. It's unclear how much of an overlap there will be between the two libraries, but it's possible that some features will be contributed back to `aiosql` where appropriate.
28
+
29
+ ## Current Focus: Universal Connectivity
30
+
31
+ The primary goal at this stage is to establish a **native connectivity interface** that works seamlessly across all supported database environments. This means you can connect to any of the supported databases using a consistent API, regardless of the underlying driver or dialect.
32
+
33
+ ## Adapters: Completed, In Progress, and Planned
34
+
35
+ This list is not final. If you have a driver you'd like to see added, please open an issue or submit a PR!
36
+
37
+ | Driver | Database | Mode | Status |
38
+ | :----------------------------------------------------------------------------------------------------------- | :--------- | :------ | :--------- |
39
+ | [`adbc`](https://arrow.apache.org/adbc/) | Postgres | Sync | ✅ |
40
+ | [`adbc`](https://arrow.apache.org/adbc/) | SQLite | Sync | ✅ |
41
+ | [`adbc`](https://arrow.apache.org/adbc/) | Snowflake | Sync | ✅ |
42
+ | [`adbc`](https://arrow.apache.org/adbc/) | DuckDB | Sync | ✅ |
43
+ | [`asyncpg`](https://magicstack.github.io/asyncpg/current/) | PostgreSQL | Async | ✅ |
44
+ | [`psycopg`](https://www.psycopg.org/) | PostgreSQL | Sync | ✅ |
45
+ | [`psycopg`](https://www.psycopg.org/) | PostgreSQL | Async | ✅ |
46
+ | [`aiosqlite`](https://github.com/omnilib/aiosqlite) | SQLite | Async | ✅ |
47
+ | `sqlite3` | SQLite | Sync | ✅ |
48
+ | [`oracledb`](https://oracle.github.io/python-oracledb/) | Oracle | Async | ✅ |
49
+ | [`oracledb`](https://oracle.github.io/python-oracledb/) | Oracle | Sync | ✅ |
50
+ | [`duckdb`](https://duckdb.org/) | DuckDB | Sync | ✅ |
51
+ | [`bigquery`](https://googleapis.dev/python/bigquery/latest/index.html) | BigQuery | Sync | 🗓️ |
52
+ | [`spanner`](https://googleapis.dev/python/spanner/latest/index.html) | Spanner | Sync | 🗓️ |
53
+ | [`sqlserver`](https://docs.microsoft.com/en-us/sql/connect/python/pyodbc/python-sql-driver-for-pyodbc?view=sql-server-ver16) | SQL Server | Sync | 🗓️ |
54
+ | [`mysql`](https://dev.mysql.com/doc/connector-python/en/connector-python-api-mysql-connector-python.html) | MySQL | Sync | 🗓️ |
55
+ | [`snowflake`](https://docs.snowflake.com) | Snowflake | Sync | 🗓️ |
56
+
57
+ ## Proposed Project Structure
58
+
59
+ - `sqlspec/`:
60
+ - `adapters/`: Contains all database drivers and associated configuration.
61
+ - `extensions/`:
62
+ - `litestar/`: Future home of `litestar` integration.
63
+ - `fastapi/`: Future home of `fastapi` integration.
64
+ - `flask/`: Future home of `flask` integration.
65
+ - `*/`: Future home of your favorite framework integration 🔌 ✨
66
+ - `base.py`: Contains base protocols for database configurations.
67
+ - `filters.py`: Contains the `Filter` class which is used to apply filters to pre-defined SQL queries.
68
+ - `utils/`: Contains utility functions used throughout the project.
69
+ - `exceptions.py`: Contains custom exceptions for SQLSpec.
70
+ - `typing.py`: Contains type hints, type guards and several facades for optional libraries that are not required for the core functionality of SQLSpec.
71
+
72
+ ## Get Involved
73
+
74
+ SQLSpec is an open-source project, and contributions are welcome! Whether you're interested in adding support for new databases, improving the query interface, or simply providing feedback, your input is valuable.
75
+
76
+ **Disclaimer**: SQLSpec is under active development. Expect changes and improvements as the project evolves.
@@ -1,12 +1,13 @@
1
1
  [project]
2
2
  authors = [{ name = "Cody Fincher", email = "cody@litestar.dev" }]
3
- dependencies = ["typing-extensions>=4.0.0", "eval_type_backport; python_version <= \"3.9\"", "sqlglot"]
3
+ dependencies = ["typing-extensions", "sqlglot", "eval_type_backport; python_version < \"3.10\""]
4
4
  description = "SQL Experiments in Python"
5
+ license = "MIT"
5
6
  maintainers = [{ name = "Litestar Developers", email = "hello@litestar.dev" }]
6
7
  name = "sqlspec"
7
8
  readme = "README.md"
8
9
  requires-python = ">=3.9, <4.0"
9
- version = "0.4.0"
10
+ version = "0.6.0"
10
11
 
11
12
  [project.optional-dependencies]
12
13
  adbc = ["adbc-driver-manager", "pyarrow"]
@@ -21,7 +22,7 @@ flask = ["flask"]
21
22
  litestar = ["litestar"]
22
23
  msgspec = ["msgspec"]
23
24
  oracledb = ["oracledb"]
24
- performance = ["sqlglot[rs]", "google-re2; sys_platform == \"linux\""]
25
+ performance = ["sqlglot[rs]"]
25
26
  psycopg = ["psycopg[binary,pool]"]
26
27
  pydantic = ["pydantic"]
27
28
  pymssql = ["pymssql"]
@@ -280,66 +281,3 @@ known-first-party = ["sqlspec", "tests"]
280
281
  [tool.ruff.format]
281
282
  docstring-code-format = true
282
283
  docstring-code-line-length = 88
283
-
284
- [tool.git-cliff.changelog]
285
- body = """
286
- {% if version %}\
287
- `Release [v{{ version | trim_start_matches(pat="v") }}] - {{ timestamp | date(format="%Y-%m-%d") }} <https://github.com/litestar-org/sqlspec/releases/tag/v{{ version | trim_start_matches(pat="v") }}>`_
288
- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
289
- * `See All commits in v{{ version | trim_start_matches(pat="v") }} <https://github.com/litestar-org/sqlspec/commits/v{{ version | trim_start_matches(pat="v") }}>`_
290
- {% else %}\
291
- [unreleased]
292
- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
293
- {% endif %}\
294
- {% if previous %}\
295
- {% if previous.commit_id %}
296
- `{{ previous.commit_id | truncate(length=7, end="") }} <https://github.com/litestar-org/sqlspec/commit/{{ previous.commit_id }}>`_ ... \
297
- `{{ commit_id | truncate(length=7, end="") }} <https://github.com/litestar-org/sqlspec/commit/{{ commit_id }}>`_ \
298
- | `See diff for {{ version | trim_start_matches(pat="v") }} <https://github.com/litestar-org/sqlspec/compare/{{ previous.commit_id }}...{{ commit_id }}>`_
299
- {% endif %}\
300
- {% endif %}\
301
- {% for group, commits in commits | group_by(attribute="group") %}
302
- {{ group | upper_first }}
303
- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
304
- {% for commit in commits %}
305
- * (`{{ commit.id | truncate(length=7, end="") }} <https://github.com/litestar-org/sqlspec/commit/{{ commit.id }}>`_) {% if commit.breaking %}[**breaking**] {% endif %} - {{ commit.message | upper_first }} ({{ commit.author.name }})\
306
- {% for footer in commit.footers -%}
307
- , {{ footer.token }}{{ footer.separator }}{{ footer.value }}\
308
- {% endfor %}\
309
- {% endfor %}
310
- {% endfor %}\n
311
- """
312
- footer = """
313
- Type Lens Changelog
314
- """
315
- header = """
316
- =========
317
- Changelog
318
- =========\n
319
- All commits to this project will be documented in this file.\n
320
- """
321
- trim = true
322
-
323
- [tool.git-cliff.git]
324
- commit_parsers = [
325
- { message = "^feat", group = "Features" },
326
- { message = "^fix", group = "Bug Fixes" },
327
- { message = "^doc", group = "Documentation" },
328
- { message = "^perf", group = "Performance" },
329
- { message = "^refactor", group = "Refactor" },
330
- { message = "^style", group = "Styling" },
331
- { message = "^test", group = "Testing" },
332
- { message = "^chore\\(release\\): prepare for", skip = true },
333
- { message = "^chore", group = "Miscellaneous Tasks" },
334
- { body = ".*security", group = "Security" },
335
- ]
336
- conventional_commits = true
337
- filter_commits = false
338
- filter_unconventional = true
339
- ignore_tags = ""
340
- protect_breaking_commits = false
341
- skip_tags = "v0.1.0-beta.1"
342
- sort_commits = "oldest"
343
- split_commits = false
344
- tag_pattern = "v[0-9]*"
345
- topo_order = false
@@ -4,7 +4,7 @@ from __future__ import annotations
4
4
 
5
5
  from importlib.metadata import PackageNotFoundError, metadata, version
6
6
 
7
- __all__ = ["__project__", "__version__"]
7
+ __all__ = ("__project__", "__version__")
8
8
 
9
9
  try:
10
10
  __version__ = version("sqlspec")
@@ -13,7 +13,7 @@ try:
13
13
 
14
14
  except ImportError:
15
15
  try:
16
- from orjson import dumps as _encode_json # pyright: ignore[reportMissingImports]
16
+ from orjson import dumps as _encode_json # pyright: ignore[reportMissingImports,reportUnknownVariableType]
17
17
  from orjson import loads as decode_json # type: ignore[no-redef]
18
18
 
19
19
  def encode_json(data: Any) -> str:
@@ -29,7 +29,6 @@ class DataclassProtocol(Protocol):
29
29
  T = TypeVar("T")
30
30
  T_co = TypeVar("T_co", covariant=True)
31
31
 
32
-
33
32
  try:
34
33
  from pydantic import BaseModel, FailFast, TypeAdapter
35
34
 
@@ -110,6 +109,35 @@ except ImportError:
110
109
  UNSET = UnsetType.UNSET # pyright: ignore[reportConstantRedefinition]
111
110
  MSGSPEC_INSTALLED = False # pyright: ignore[reportConstantRedefinition]
112
111
 
112
+ try:
113
+ from litestar.dto.data_structures import DTOData # pyright: ignore[reportUnknownVariableType]
114
+
115
+ LITESTAR_INSTALLED = True
116
+ except ImportError:
117
+
118
+ @runtime_checkable
119
+ class DTOData(Protocol[T]): # type: ignore[no-redef]
120
+ """Placeholder implementation"""
121
+
122
+ __slots__ = ("_backend", "_data_as_builtins")
123
+
124
+ def __init__(self, backend: Any, data_as_builtins: Any) -> None:
125
+ """Placeholder init"""
126
+
127
+ def create_instance(self, **kwargs: Any) -> T:
128
+ """Placeholder implementation"""
129
+ return cast("T", kwargs)
130
+
131
+ def update_instance(self, instance: T, **kwargs: Any) -> T:
132
+ """Placeholder implementation"""
133
+ return cast("T", kwargs)
134
+
135
+ def as_builtins(self) -> Any:
136
+ """Placeholder implementation"""
137
+ return {}
138
+
139
+ LITESTAR_INSTALLED = False # pyright: ignore[reportConstantRedefinition]
140
+
113
141
 
114
142
  class EmptyEnum(Enum):
115
143
  """A sentinel enum used as placeholder."""
@@ -122,10 +150,12 @@ Empty: Final = EmptyEnum.EMPTY
122
150
 
123
151
 
124
152
  __all__ = (
153
+ "LITESTAR_INSTALLED",
125
154
  "MSGSPEC_INSTALLED",
126
155
  "PYDANTIC_INSTALLED",
127
156
  "UNSET",
128
157
  "BaseModel",
158
+ "DTOData",
129
159
  "DataclassProtocol",
130
160
  "Empty",
131
161
  "EmptyEnum",
@@ -2,31 +2,31 @@ from __future__ import annotations
2
2
 
3
3
  from contextlib import contextmanager
4
4
  from dataclasses import dataclass
5
- from typing import TYPE_CHECKING, TypeVar
5
+ from typing import TYPE_CHECKING
6
6
 
7
- from sqlspec.config import GenericDatabaseConfig
7
+ from sqlspec.base import NoPoolSyncConfig
8
8
  from sqlspec.typing import Empty, EmptyType
9
9
 
10
10
  if TYPE_CHECKING:
11
11
  from collections.abc import Generator
12
12
  from typing import Any
13
13
 
14
- from adbc_driver_manager.dbapi import Connection, Cursor
14
+ from adbc_driver_manager.dbapi import Connection
15
15
 
16
16
  __all__ = ("AdbcDatabaseConfig",)
17
17
 
18
- ConnectionT = TypeVar("ConnectionT", bound="Connection")
19
- CursorT = TypeVar("CursorT", bound="Cursor")
20
-
21
18
 
22
19
  @dataclass
23
- class AdbcDatabaseConfig(GenericDatabaseConfig):
20
+ class AdbcDatabaseConfig(NoPoolSyncConfig["Connection"]):
24
21
  """Configuration for ADBC connections.
25
22
 
26
23
  This class provides configuration options for ADBC database connections using the
27
24
  ADBC Driver Manager.([1](https://arrow.apache.org/adbc/current/python/api/adbc_driver_manager.html))
28
25
  """
29
26
 
27
+ __supports_connection_pooling = False
28
+ __is_async = False
29
+
30
30
  uri: str | EmptyType = Empty
31
31
  """Database URI"""
32
32
  driver_name: str | EmptyType = Empty
@@ -1,10 +1,10 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  from contextlib import asynccontextmanager
4
- from dataclasses import dataclass
4
+ from dataclasses import dataclass, field
5
5
  from typing import TYPE_CHECKING, Any
6
6
 
7
- from sqlspec.config import GenericDatabaseConfig
7
+ from sqlspec.base import NoPoolSyncConfig
8
8
  from sqlspec.exceptions import ImproperConfigurationError
9
9
  from sqlspec.typing import Empty, EmptyType, dataclass_to_dict
10
10
 
@@ -19,7 +19,7 @@ __all__ = ("AiosqliteConfig",)
19
19
 
20
20
 
21
21
  @dataclass
22
- class AiosqliteConfig(GenericDatabaseConfig):
22
+ class AiosqliteConfig(NoPoolSyncConfig["Connection"]):
23
23
  """Configuration for Aiosqlite database connections.
24
24
 
25
25
  This class provides configuration options for Aiosqlite database connections, wrapping all parameters
@@ -28,28 +28,21 @@ class AiosqliteConfig(GenericDatabaseConfig):
28
28
  For details see: https://github.com/omnilib/aiosqlite/blob/main/aiosqlite/__init__.pyi
29
29
  """
30
30
 
31
- database: str
31
+ database: str = field(default=":memory:")
32
32
  """The path to the database file to be opened. Pass ":memory:" to open a connection to a database that resides in RAM instead of on disk."""
33
-
34
- timeout: float | EmptyType = Empty
33
+ timeout: float | EmptyType = field(default=Empty)
35
34
  """How many seconds the connection should wait before raising an OperationalError when a table is locked. If another thread or process has acquired a shared lock, a wait for the specified timeout occurs."""
36
-
37
- detect_types: int | EmptyType = Empty
35
+ detect_types: int | EmptyType = field(default=Empty)
38
36
  """Control whether and how data types are detected. It can be 0 (default) or a combination of PARSE_DECLTYPES and PARSE_COLNAMES."""
39
-
40
- isolation_level: Literal["DEFERRED", "IMMEDIATE", "EXCLUSIVE"] | None | EmptyType = Empty
37
+ isolation_level: Literal["DEFERRED", "IMMEDIATE", "EXCLUSIVE"] | None | EmptyType = field(default=Empty)
41
38
  """The isolation_level of the connection. This can be None for autocommit mode or one of "DEFERRED", "IMMEDIATE" or "EXCLUSIVE"."""
42
-
43
- check_same_thread: bool | EmptyType = Empty
39
+ check_same_thread: bool | EmptyType = field(default=Empty)
44
40
  """If True (default), ProgrammingError is raised if the database connection is used by a thread other than the one that created it. If False, the connection may be shared across multiple threads."""
45
-
46
- factory: type[SQLite3Connection] | EmptyType = Empty
41
+ factory: type[SQLite3Connection] | EmptyType = field(default=Empty)
47
42
  """A custom Connection class factory. If given, must be a callable that returns a Connection instance."""
48
-
49
- cached_statements: int | EmptyType = Empty
43
+ cached_statements: int | EmptyType = field(default=Empty)
50
44
  """The number of statements that SQLite will cache for this connection. The default is 128."""
51
-
52
- uri: bool | EmptyType = Empty
45
+ uri: bool | EmptyType = field(default=Empty)
53
46
  """If set to True, database is interpreted as a URI with supported options."""
54
47
 
55
48
  @property
@@ -4,6 +4,10 @@ from contextlib import asynccontextmanager
4
4
  from dataclasses import dataclass
5
5
  from typing import TYPE_CHECKING, TypeVar
6
6
 
7
+ from asyncmy.connection import Connection
8
+ from asyncmy.pool import Pool
9
+
10
+ from sqlspec.base import AsyncDatabaseConfig, GenericPoolConfig
7
11
  from sqlspec.exceptions import ImproperConfigurationError
8
12
  from sqlspec.typing import Empty, EmptyType, dataclass_to_dict
9
13
 
@@ -11,9 +15,7 @@ if TYPE_CHECKING:
11
15
  from collections.abc import AsyncGenerator
12
16
  from typing import Any
13
17
 
14
- from asyncmy.connection import Connection
15
18
  from asyncmy.cursors import Cursor, DictCursor
16
- from asyncmy.pool import Pool
17
19
 
18
20
  __all__ = (
19
21
  "AsyncMyConfig",
@@ -25,7 +27,7 @@ T = TypeVar("T")
25
27
 
26
28
 
27
29
  @dataclass
28
- class AsyncmyPoolConfig:
30
+ class AsyncmyPoolConfig(GenericPoolConfig):
29
31
  """Configuration for Asyncmy's connection pool.
30
32
 
31
33
  This class provides configuration options for Asyncmy database connection pools.
@@ -104,9 +106,12 @@ class AsyncmyPoolConfig:
104
106
 
105
107
 
106
108
  @dataclass
107
- class AsyncMyConfig:
109
+ class AsyncMyConfig(AsyncDatabaseConfig[Connection, Pool]):
108
110
  """Asyncmy Configuration."""
109
111
 
112
+ __is_async__ = True
113
+ __supports_connection_pooling__ = True
114
+
110
115
  pool_config: AsyncmyPoolConfig | None = None
111
116
  """Asyncmy Pool configuration"""
112
117