Skip to content

Examples

counters.py

A command line tool for manipulating and querying bunch of counters stored in an SQLite database. This demonstrates basic use of dbkit.

"""
A counter management tool.
"""

from contextlib import closing
from os import path
import sqlite3
import sys

from dbkit import connect, execute, query, query_column, query_value, transactional


# --8<-- [start:get_counter]
def get_counter(counter):
    """
    Get the value of a counter.
    """
    print(
        query_value(
            "SELECT value FROM counters WHERE counter = ?",
            (counter,),
            default=0,
        )
    )


# --8<-- [end:get_counter]


@transactional
def set_counter(counter, value):
    """
    Set a counter.
    """
    execute("REPLACE INTO counters (counter, value) VALUES (?, ?)", (counter, value))


@transactional
def increment_counter(counter, by):
    """
    Modify the value of a counter by a certain amount.
    """
    execute("UPDATE counters SET value = value + ? WHERE counter = ?", (by, counter))


# --8<-- [start:delete_counter]
@transactional
def delete_counter(counter):
    """
    Delete a counter.
    """
    execute("DELETE FROM counters WHERE counter = ?", (counter,))


# --8<-- [end:delete_counter]


# --8<-- [start:list_counters]
def list_counters():
    """
    List the names of all the stored counters.
    """
    print("\n".join(query_column("SELECT counter FROM counters")))


# --8<-- [end:list_counters]


# --8<-- [start:dump_counters]
def dump_counters():
    """
    Query the database for all counters and their values.
    """
    return query("SELECT counter, value FROM counters")


# --8<-- [end:dump_counters]


# --8<-- [start:print_counters_and_values]
def print_counters_and_values():
    """
    List all the counters and their values.
    """
    for counter, value in dump_counters():
        print(f"{counter}: {value}")


# --8<-- [end:print_counters_and_values]


# --8<-- [start:print_help]
def print_help(filename, table, dest=sys.stdout):
    """
    Print help to the given destination file object.
    """
    cmds = "|".join(sorted(table.keys()))
    print(f"Syntax: {path.basename(filename)} {cmds} [args]", file=dest)


# --8<-- [end:print_help]


# --8<-- [start:dispatch]
def dispatch(table, args):
    """
    Dispatches to a function based on the contents of `args`.
    """
    # No arguments: print help.
    if len(args) == 1:
        print_help(args[0], table)
        sys.exit(0)

    # Bad command or incorrect number of arguments: print help to stderr.
    if args[1] not in table or len(args) != len(table[args[1]]) + 1:
        print_help(args[0], table, dest=sys.stderr)
        sys.exit(1)

    # Cast all the arguments to fit their function's signature to ensure
    # they're correct and to make them safe for consumption.
    sig = table[args[1]]
    try:
        fixed_args = [type_(arg) for arg, type_ in zip(args[2:], sig[1:])]
    except TypeError:
        # If any are wrong, complain to stderr.
        print_help(args[0], table, dest=sys.stderr)
        sys.exit(1)

    # Dispatch the call to the correct function.
    sig[0](*fixed_args)


# --8<-- [end:dispatch]


# --8<-- [start:main]
def main():
    # This table tells us the subcommands, the functions to dispatch to,
    # and their signatures.
    command_table = {
        "set": (set_counter, str, int),
        "del": (delete_counter, str),
        "get": (get_counter, str),
        "list": (list_counters,),
        "incr": (increment_counter, str, int),
        "dump": (print_counters_and_values,),
    }
    with connect(sqlite3, "counters.sqlite") as ctx:
        with closing(ctx):
            dispatch(command_table, sys.argv)


# --8<-- [end:main]


if __name__ == "__main__":
    main()

pools.py

A small web application, built using Bottle and psycopg2, to say that prints “Hello, name” based on the URL fetched, and which records how many times it’s said hello to a particular name.

This demonstrates use of connection pools.

from bottle import route, run, template
import psycopg2

from dbkit import DictFactory, execute, Pool, query, query_value, transactional

TEMPLATE = """<!DOCTYPE html>
<html>
    <head>
        <title>Hello!</title>
    </head>
    <body>
        <p>Hello, {{name}}!</p>
        <p>Previously, I've said hello to:</p>
        <ul>
        % for item in hellos
            <li>{{item.name}}, {{item.n}} times</li>
        % end
        </ul>
    </body>
</html>"""


pool = Pool(psycopg2, 2, "dbname=namecounter user=keith")


@transactional
def save_name(name):
    if query_value("SELECT n FROM greeted WHERE name = %s", (name,), 0) == 0:
        execute("INSERT INTO greeted (name, n) VALUES (%s, 1)", (name,))
    else:
        execute("UPDATE greeted SET n = n + 1 WHERE name = %s", (name,))


def get_names():
    return query("SELECT name, n FROM greeted ORDER BY n", factory=DictFactory)


@route("/<name>")
def index(name):
    ctx = pool.connect()
    if not name:
        name = "World"
    with ctx:
        hellos = list(get_names())
        save_name(name)
    return template(TEMPLATE, name=name, hellos=hellos)


if __name__ == "__main__":
    try:
        run(host="localhost", port=8080)
    finally:
        pool.finalise()