Apocryphes

Estoult includes a number of extensions in the apocryphes namespace.

Connection pooling

Lightweight connection pooling. This is heavily recommended for multi-threaded applications (e.g webservers).

In a multi-threaded application, up to max_connections will be opened. Each thread (or, if using gevent, greenlet) will have it’s own connection.

In a single-threaded application, only one connection will be created. It will be continually recycled until either it exceeds the stale timeout or is closed explicitly (using .manual_close()).

from apocryphes.pool import PooledPostgreSQLDatabase

db = PooledPostgreSQLDatabase(
    max_connections=32,
    stale_timeout=300,
    user=os.getenv("POSTGRES_USER", "postgres"),
    password=os.getenv("POSTGRES_PASSWORD", "postgres"),
    host=os.getenv("POSTGRES_HOST", "localhost"),
    database=os.getenv("POSTGRES_DB", "porg"),
)

Warning

autoconnect is disabled so your application needs to ensure that connections are opened and closed when you are finished with them, so they can be returned to the pool. With a Flask server, it could look like this:

@app.before_request
def open_connection():
    db.connect()

@app.teardown_request
def close_connection(exc):
    db.close()
class apocryphes.pool.PooledMySQLDatabase(max_connections=20, stale_timeout=None, timeout=None, *args, **kwargs)

Bases: apocryphes.pool.PooledDatabase, estoult.MySQLDatabase

class apocryphes.pool.PooledPostgreSQLDatabase(max_connections=20, stale_timeout=None, timeout=None, *args, **kwargs)

Bases: apocryphes.pool.PooledDatabase, estoult.PostgreSQLDatabase

class apocryphes.pool.PooledSQLiteDatabase(max_connections=20, stale_timeout=None, timeout=None, *args, **kwargs)

Bases: apocryphes.pool.PooledDatabase, estoult.SQLiteDatabase

Rider

rider is a simple CLI tool to help manage database migrations using the existing Estoult database object.

Start by creating a rider.py file to tell rider about your database object.

from src.schemas.base import db

# Export the db
# Not actually needed, but my linter screams at me otherwise
db = db

# Default config
config = {
    # Path where migrations are stored
    "source": "./migrations",
    # Table name for migrations
    "table_name": "_rider_migrations",
    # Table name of migration logs
    "log_name": "_rider_logs",
}

Create a new migration with a description.

rider create -m "add contacts table"

This will create the bellow scaffold file in which you can add your migration steps too.

'''
create table
'''

from apocryphes.rider import step

__depends__ = {"1602721237-add-pg-extensions"}

steps = [
    step("")
]

The step function takes 3 arguments:

  • migrate: a SQL query or function to apply the migration step.
  • rollback: (optional) a SQL query or function to rollback the migration step.
  • ignore_errors: (optional, one of “migrate”, “rollback” or “all”) causes rider to ignore database errors in either migrate, rollback, or both stages.
steps = [
    # Steps with sql queries
    step(
        migrate="create table contacts (id int not null);",
        rollback="drop table contacts;",
        ignore_errors="all",
    ),

    # Arguments don't need to be kwargs
    step("alter table contacts add column name varchar(256) not null")
]

You can supply a function to migrate or rolllback. Each function takes your db object.

def migrate_step(db):
    db.sql(...)

def rollback_step(db):
    db.sql(...)

steps = [
    step(migrate_step, rollback_step),
]

View all migrations with their index/message/applied at time:

rider migrations

Apply migrations:

rider migrate

Rollback database to a migration.

# Migration indicies can be found in the `migrations` list
rider migrations
# Rollback to 8th migration (index starts at 0)
rider rollback -i 7
class apocryphes.rider.Rider(db, config={})

Bases: object

applied_at(id)
config = {'log_name': '_rider_logs', 'source': './migrations', 'table_name': '_rider_migrations'}
create(args)
get_migrations()
init_tables(*args, **kwargs)
migrate(*args, **kwargs)
migrations(*args, **kwargs)
parse_args()
rollback(*args, **kwargs)
run(*args, **kwargs)
apocryphes.rider.step

alias of apocryphes.rider.Step