Skip to content

SQLite🔗

Supported drivers:

dbapi default driver connection class
sqlite3 👍 sqlite+sqlite3 sqlite3.Connection

sqlite3🔗

sqlite3 is the default dbapi driver for SQLite in pydapper.

Installation🔗

sqlite3 is part of the stdlib and thus does not require installing an extra.

pip install pydapper
poetry add pydapper

DSN format🔗

dsn = f"sqlite+sqlite3:///{relative_or_absolute_path_to_db}"
dsn = "sqlite+sqlite3://my.db"
dsn = "sqlite://my.db"

SQLite slash counts distinguish relative and absolute paths. The connection target below is the value available as database / dbname and passed to sqlite3.connect(); the parse result's path remains the decoded URL path before this convenience normalization.

DSN SQLite connection target
sqlite://relative.db relative.db
sqlite+sqlite3://relative.db relative.db
sqlite:///relative/path.db relative/path.db
sqlite:////absolute/path.db /absolute/path.db
sqlite:///:memory: :memory:
sqlite:// empty string

Paths are percent-decoded without treating plus signs as spaces. For example, sqlite:///data/my%20database%23one.db connects to data/my database#one.db.

Example - connect🔗

See Transactions below for what the connection's context manager does on exit.

import pydapper

with pydapper.connect("sqlite://pydapper.db") as commands:
    print(type(commands))
    # <class 'pydapper.sqlite.sqlite3.Sqlite3Commands'>

    print(type(commands.connection))
    # <class 'sqlite3.Connection'>

    # sqlite3 cursors are not context managers; close explicitly if you use one directly
    # (cursors owned by pydapper command methods are cleaned up for you)
    raw_cursor = commands.cursor()
    print(type(raw_cursor))
    # <class 'sqlite3.Cursor'>
    raw_cursor.close()

Example - using🔗

Use pydapper with a custom connection pool.

import sqlite3
from collections import deque

import pydapper


class SimplePool:
    """sqlite3 does not provide a pool interface, this is a simple example that should never be used in production"""

    def __init__(self, database: str):
        self._database = database
        self._connections = deque()

    def getconn(self):
        if len(self._connections) == 0:
            return sqlite3.connect(self._database)
        return self._connections.pop()

    def putconn(self, conn):
        self._connections.append(conn)

    def __del__(self):
        for conn in self._connections:
            conn.close()


my_pool = SimplePool("pydapper.db")

commands = pydapper.using(my_pool.getconn())
print(type(commands))
# <class 'pydapper.sqlite.sqlite3.Sqlite3Commands'>

print(type(commands.connection))
# <class 'sqlite3.Connection'>

my_pool.putconn(commands.connection)

Transactions🔗

sqlite3 connects in its legacy isolation_level mode by default: an implicit transaction opens before the first DML statement, and DDL issued while no transaction is open runs in autocommit — so a rolled-back transaction() block can leave a CREATE TABLE behind while its inserts are rolled back. (DDL issued after DML has already opened the implicit transaction participates in it and rolls back with it.) All of pydapper's transaction APIs (commit(), rollback(), transaction()) are supported.

Exiting with pydapper.connect(...) delegates to sqlite3.Connection's own context manager: it commits on clean exit, rolls back on error, and never closes the connection — close it explicitly when you are done:

with pydapper.connect("sqlite://my.db") as commands:
    commands.execute(insert_sql, params=task)
# the exit committed the insert, but the connection is still open
commands.connection.close()

See Transactions and Context manager semantics for the cross-driver picture.