SQL Server stored procedure introspection and Pydantic model generator
Project description
SQL Server → Pydantic v2. Automatically.
Singularity bridges the gap between SQL Server and Python. Connect to your database, point at a stored procedure, and get a Pydantic v2 model — as a runtime class or a .py file you can commit.
singularity --config config.toml
from singularity import SQLServerIntrospector, generate_model
introspector = SQLServerIntrospector("DRIVER={ODBC Driver 17};SERVER=...;DATABASE=...")
meta = introspector.introspect("usp_GetOrders")
model = generate_model(meta, mode="dynamic")
# <class 'pydantic.main.UspGetOrders'>
Why Singularity?
The problem: You have dozens of complex stored procedures in SQL Server. Calling them from Python means manually writing Pydantic models for every parameter and result set. One typo, and you get a runtime error.
The solution: Singularity reads SQL Server's system catalog (sys.parameters, sp_describe_first_result_set) and generates the models for you. Zero manual mapping.
| Approach | Lines of code | Maintainable | Type-safe |
|---|---|---|---|
| Manual Pydantic models | 100s–1000s | ❌ | ⚠️ (manual) |
| Raw dicts / tuples | Fewer | ❌ | ❌ |
| Singularity | Zero | ✅ | ✅ |
Features
- 🔌 Auto-connect — pyodbc connection with
@@VERSIONdetection - 🧠 Version-aware — Modern (2016+), Legacy (2008–2014), and Azure SQL strategies
- 📦 Two output modes:
"dynamic"—create_model()at runtime, usable immediately"source"—.pyfiles you can commit and review
- 🏷️ Full type mapping —
INT→int,VARCHAR→str,DATETIME→datetime,BIT→bool, etc. - 🎨 Configurable naming — snake_case, camelCase, PascalCase for field names
- 🗂️ File naming templates —
{schema}_{sp_name}.py,{database}_{sp_name}.py, etc. - 🛡️ Nullable awareness —
Optional[T]for nullable columns - ⚡ UV-first — fast dependency management
Installation
uv add singularity
# or
pip install singularity
Prerequisite: ODBC Driver for SQL Server (17 or 18).
Quick Start
1. Create a config file
# config.toml
[connection]
server = "localhost"
database = "AdventureWorks"
driver = "ODBC Driver 18 for SQL Server"
trusted_connection = true
[sp_selection]
pattern = "usp_%"
[output]
directory = "generated_models"
mode = "source"
file_naming = "{schema}_{sp_name}.py"
naming_convention = "snake_case"
2. Generate models
singularity --config config.toml
Connected. Detected version: modern
Introspecting usp_GetOrders... → generated_models/dbo_usp_GetOrders.py
Introspecting usp_GetCustomers... → generated_models/dbo_usp_GetCustomers.py
Done. 2 succeeded, 0 failed.
3. Use the generated models
from generated_models.dbo_usp_GetOrders import UspGetOrders
order = UspGetOrders(id=1, customer_name="Acme Corp", total=99.99)
Library Usage
from singularity import SQLServerIntrospector, generate_model
# Connect and introspect
introspector = SQLServerIntrospector(conn_str)
introspector.connect()
version = introspector.detect_version() # ServerVersion.MODERN
metadata = introspector.introspect("usp_GetOrders")
# Runtime model
DynamicModel = generate_model(metadata, mode="dynamic")
instance = DynamicModel(id=1, customer_name="Acme")
# Source code string
source_code = generate_model(metadata, mode="source")
with open("models/usp_GetOrders.py", "w") as f:
f.write(source_code)
Configuration Reference
[connection]
| Field | Required | Default | Description |
|---|---|---|---|
server |
✅ | — | Server hostname or IP |
database |
✅ | — | Database name |
driver |
❌ | ODBC Driver 18 for SQL Server |
ODBC driver name |
trusted_connection |
❌ | true |
Use Windows auth |
username |
❌ | — | SQL auth username |
password |
❌ | — | SQL auth password |
[sp_selection]
| Field | Required | Description |
|---|---|---|
procedures |
❌ | Explicit list of SP names |
pattern |
❌ | Wildcard pattern (e.g. usp_%) |
At least one of procedures or pattern must be specified.
[output]
| Field | Required | Default | Description |
|---|---|---|---|
directory |
❌ | . |
Output directory |
mode |
❌ | source |
source or dynamic |
file_naming |
❌ | {sp_name}.py |
Template with {schema}, {database}, {sp_name} |
naming_convention |
❌ | snake_case |
snake_case, camelCase, or PascalCase |
Supported SQL Server Versions
| Version | Strategy | Parameter introspection | Result set metadata |
|---|---|---|---|
| 2016+ | Modern | sys.parameters |
sp_describe_first_result_set |
| 2008–2014 | Legacy | sys.parameters |
sp_describe_first_result_set + sys.columns fallback |
| Azure SQL | Azure | sys.parameters |
sys.dm_exec_describe_first_result_set |
Type Mapping
| SQL Server | Python | Pydantic |
|---|---|---|
INT, BIGINT, SMALLINT, TINYINT |
int |
int |
VARCHAR, NVARCHAR, CHAR, NCHAR, TEXT |
str |
str |
DATETIME, DATETIME2, DATE, SMALLDATETIME |
datetime |
datetime |
BIT |
bool |
bool |
DECIMAL, NUMERIC, FLOAT, REAL, MONEY |
float |
float |
UNIQUEIDENTIFIER |
str |
str |
| Unknown types | str + warning |
str |
Development
# Clone and install
git clone https://github.com/Samuel-Urrego/Singularity
cd singularity
uv sync
# Run tests
uv run pytest
# Lint and type-check
uv run ruff check .
uv run mypy singularity/
# Install pre-commit hooks
uv run pre-commit install
License
MIT — see LICENSE for details.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file singularitysql-0.1.0.tar.gz.
File metadata
- Download URL: singularitysql-0.1.0.tar.gz
- Upload date:
- Size: 31.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fd39ca3d451834383dd0ab830e9dc70b126d80bcdaf4bb2f522da3f825307717
|
|
| MD5 |
b33af32114bfacf052381c7945498529
|
|
| BLAKE2b-256 |
0d67084d4fe0c6ae397e494e4882ad0652c652414fe5f95d9d9f1c6ab5f25952
|
Provenance
The following attestation bundles were made for singularitysql-0.1.0.tar.gz:
Publisher:
publish.yml on Samuel-Urrego/Singularity
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
singularitysql-0.1.0.tar.gz -
Subject digest:
fd39ca3d451834383dd0ab830e9dc70b126d80bcdaf4bb2f522da3f825307717 - Sigstore transparency entry: 1633948926
- Sigstore integration time:
-
Permalink:
Samuel-Urrego/Singularity@47d832dd69139cf50d2c766711cb29d5b8492f30 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/Samuel-Urrego
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@47d832dd69139cf50d2c766711cb29d5b8492f30 -
Trigger Event:
push
-
Statement type:
File details
Details for the file singularitysql-0.1.0-py3-none-any.whl.
File metadata
- Download URL: singularitysql-0.1.0-py3-none-any.whl
- Upload date:
- Size: 28.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f73b083ed82ca4fc5399434a4e4adc57ca5726cee4ba0b4623b5dce5faaf4c38
|
|
| MD5 |
15ea0a9be43cdab90a96489693638fa5
|
|
| BLAKE2b-256 |
c72828d4bec1c65ce8748044012cff8c27ef379e4b3e2ba6e366799ccc219595
|
Provenance
The following attestation bundles were made for singularitysql-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on Samuel-Urrego/Singularity
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
singularitysql-0.1.0-py3-none-any.whl -
Subject digest:
f73b083ed82ca4fc5399434a4e4adc57ca5726cee4ba0b4623b5dce5faaf4c38 - Sigstore transparency entry: 1633948955
- Sigstore integration time:
-
Permalink:
Samuel-Urrego/Singularity@47d832dd69139cf50d2c766711cb29d5b8492f30 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/Samuel-Urrego
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@47d832dd69139cf50d2c766711cb29d5b8492f30 -
Trigger Event:
push
-
Statement type: