Skip to main content

Spring Boot-style CDI auto-configuration for pydbc (relational DB)

Project description

alt-python-boot-pydbc

Language Python License: MIT

Spring Boot-style CDI auto-configuration for relational databases via pydbc. Provides PydbcTemplate, NamedParameterPydbcTemplate, and a CDI auto-configuration factory that wires a DataSource, SQL template, and SchemaInitializer into a Boot application from a single config property.

Part of the alt-python/boot monorepo.

Quick Start

uv add alt-python-boot-pydbc pydbc-sqlite
# invoke.py
import os
os.chdir(os.path.dirname(os.path.abspath(__file__)))  # resolve config/ relative to this file

import pydbc_sqlite  # registers the SQLite driver
from boot import Boot
from cdi import Context, Singleton
from boot_pydbc import pydbc_auto_configuration

from services import NoteRepository, Application

Boot.boot({
    'contexts': [
        Context(pydbc_auto_configuration()),
        Context([Singleton(NoteRepository), Singleton(Application)]),
    ]
})
// config/application.json
{
  "boot": {
    "datasource": {
      "url": "pydbc:sqlite::memory:",
      "pool": { "enabled": true, "max": 1 }
    }
  }
}

Boot reads config/application.json, creates a PooledDataSource, runs config/schema.sql and config/data.sql, then starts the application.

What's Included

Class / Function Description
PydbcTemplate Execute SQL with positional ? parameters
NamedParameterPydbcTemplate Execute SQL with named :param parameters
ConfiguredDataSource CDI bean that reads boot.datasource.* config
SchemaInitializer CDI bean that runs config/schema.sql + config/data.sql at startup
DataSourceBuilder Fluent builder for secondary datasources
pydbc_auto_configuration() Returns 4 CDI Singletons ready for Context()
pydbc_template_starter() One-call Boot.boot() entry point
DEFAULT_PREFIX 'boot.datasource'

Configuration

All properties are under boot.datasource by default (override with a custom prefix).

Property Type Default Description
boot.datasource.url string pydbc JDBC-style URL, e.g. pydbc:sqlite::memory:
boot.datasource.username string Username (optional)
boot.datasource.password string Password (optional)
boot.datasource.pool.enabled bool false Use PooledDataSource instead of DataSource
boot.datasource.pool.min int Pool minimum size
boot.datasource.pool.max int Pool maximum size
boot.datasource.initialize bool true Set to false to skip schema initialisation
boot.datasource.schema string config/schema.sql Path to DDL file
boot.datasource.data string config/data.sql Path to seed data file

If boot.datasource.url is absent, the ConfiguredDataSource bean is still registered but its _delegate is None. Calling get_connection() will raise RuntimeError. This lets you deploy with an optional datasource without breaking the CDI wiring.

PydbcTemplate

PydbcTemplate wraps a DataSource and provides four methods:

from boot_pydbc import PydbcTemplate

template = PydbcTemplate(data_source)

# DDL
template.execute('CREATE TABLE notes (id INTEGER PRIMARY KEY, body TEXT)')

# DML — returns affected row count
count = template.update('INSERT INTO notes VALUES (?, ?)', (1, 'hello'))

# Query — returns list of row dicts
rows = template.query_for_list('SELECT * FROM notes')

# Query — returns exactly one row or raises RuntimeError
row = template.query_for_object('SELECT * FROM notes WHERE id = ?', (1,))

Column names are returned by the underlying driver. With pydbc-sqlite, column names are uppercase (ID, BODY). Normalise in a row mapper:

def row_mapper(row, _index):
    return {k.lower(): v for k, v in row.items()}

notes = template.query_for_list('SELECT * FROM notes', row_mapper=row_mapper)

NamedParameterPydbcTemplate

NamedParameterPydbcTemplate wraps PydbcTemplate and converts :param_name placeholders to positional ? via ParamstyleNormalizer:

from boot_pydbc import NamedParameterPydbcTemplate

template = NamedParameterPydbcTemplate(data_source)

template.update(
    'INSERT INTO notes VALUES (:id, :body)',
    {'id': 1, 'body': 'hello'},
)

rows = template.query_for_list(
    'SELECT * FROM notes WHERE id = :id',
    {'id': 1},
)

SchemaInitializer

When boot.datasource.url is configured, SchemaInitializer.init() runs config/schema.sql followed by config/data.sql before any application bean is used. Both files are optional — if either is absent, it is silently skipped.

-- config/schema.sql
CREATE TABLE IF NOT EXISTS notes (
    id    INTEGER PRIMARY KEY,
    title TEXT    NOT NULL,
    body  TEXT,
    done  INTEGER NOT NULL DEFAULT 0
);

-- config/data.sql
INSERT INTO notes (id, title, body) VALUES (1, 'First note', '');
INSERT INTO notes (id, title, body) VALUES (2, 'Second note', '');

Set boot.datasource.initialize: false in config to skip initialisation entirely (useful for production environments where schema is managed separately).

Secondary Datasources

DataSourceBuilder creates a second set of CDI beans with a custom config prefix and custom bean names:

from boot_pydbc import DataSourceBuilder
from cdi import Context

reporting_beans = (
    DataSourceBuilder.create()
    .prefix('myapp.reporting')
    .bean_names({
        'data_source': 'reporting_ds',
        'pydbc_template': 'reporting_template',
        'named_parameter_pydbc_template': 'reporting_named_template',
        'schema_initializer': 'reporting_schema_initializer',
    })
    .build()
)

# Or skip the schema initializer entirely:
read_only_beans = (
    DataSourceBuilder.create()
    .prefix('myapp.readonly')
    .without_schema_initializer()
    .build()
)

ctx = Context([*pydbc_auto_configuration(), *reporting_beans, ...])

Config for the secondary datasource lives under myapp.reporting.*:

{
  "myapp": {
    "reporting": {
      "url": "pydbc:sqlite:reporting.db"
    }
  }
}

Using with Boot

pydbc_auto_configuration()

Returns a flat list of 4 Singleton beans. Concatenate with your application beans and pass to Context():

from boot import Boot
from cdi import Context, Singleton
from boot_pydbc import pydbc_auto_configuration

Boot.boot({
    'contexts': [
        Context(pydbc_auto_configuration()),
        Context([Singleton(MyService), Singleton(MyApp)]),
    ]
})

pydbc_template_starter()

One-call entry point for applications that need only a single datasource:

from boot_pydbc import pydbc_template_starter
from cdi import Context, Singleton

pydbc_template_starter({
    'contexts': [Context([Singleton(MyService), Singleton(MyApp)])],
})

SQLite In-Memory Databases

When using an in-memory SQLite URL (pydbc:sqlite::memory:), you must use a connection pool to prevent PydbcTemplate from destroying the database on conn.close():

{
  "boot": {
    "datasource": {
      "url": "pydbc:sqlite::memory:",
      "pool": { "enabled": true, "max": 1 }
    }
  }
}

Without the pool, the finally: conn.close() block in PydbcTemplate closes the DBAPI connection, which destroys the in-memory database. With PooledDataSource(max=1), conn.close() returns the connection to the pool instead.

Running Tests

uv run pytest packages/boot-pydbc -v

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

alt_python_boot_pydbc-1.1.1.tar.gz (8.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

alt_python_boot_pydbc-1.1.1-py3-none-any.whl (8.1 kB view details)

Uploaded Python 3

File details

Details for the file alt_python_boot_pydbc-1.1.1.tar.gz.

File metadata

  • Download URL: alt_python_boot_pydbc-1.1.1.tar.gz
  • Upload date:
  • Size: 8.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.2 {"installer":{"name":"uv","version":"0.11.2","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for alt_python_boot_pydbc-1.1.1.tar.gz
Algorithm Hash digest
SHA256 2cff3e57d811fe4870a2b50b48c38bdcffba04ed1709370b44be614fb3552a2d
MD5 7c5f7ddeabf7498fa7c4a3eedcacbb02
BLAKE2b-256 38b30855bae9f3c3c97d9461429475b648b6b13cc9eb1db4b3c3056669c4ba46

See more details on using hashes here.

File details

Details for the file alt_python_boot_pydbc-1.1.1-py3-none-any.whl.

File metadata

  • Download URL: alt_python_boot_pydbc-1.1.1-py3-none-any.whl
  • Upload date:
  • Size: 8.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.2 {"installer":{"name":"uv","version":"0.11.2","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for alt_python_boot_pydbc-1.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 9567a070ddcaf0c460594b7e7e16ee9760fc4aa99b4013a527ad5268dbd21972
MD5 204ea3b86db93bc09faf5a4c08af045e
BLAKE2b-256 da710c9b238587331507a314bf34bf75154afd68dff79412c81cfa5775bc7b34

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page