Python - Packaging Alembic Migrations with Setuptools

alembic, python, setuptools

Solution

I am not sure this is the right way but I did it this way:

First, you can add sort of custom options to alembic using the -x option and you can find details explained in this great answer. This allows you to specify the `db_url` at runtime and make it override the value in the `config.ini`.

Then I managed to package alembic and my migrations by moving the `alembic.ini` file and the `alembic` directory from my project root to my top-level python package:

<project root>
├── src
│   └── <top-level package dir>
│       ├── alembic
│       │   ├── env.py
│       │   ├── README
│       │   ├── script.py.mako
│       │   └── versions
│       │       ├── 58c8dcd5fbdc_revision_1.py
│       │       └── ec385b47da23_revision_2.py
│       ├── alembic.ini
│       ├── __init__.py
│       └── <other files and dirs>
└── <other files and dirs>

This allows to use the setuptools `package_data` directive inside my `setup.py`:

setup(
    name=<package_name>,
    package_dir={'': 'src'},
    packages=find_packages(where='src'),
    package_data={
        '<top-level package dir>': ['alembic.ini', 'alembic/*', 'alembic/**/*'],
    },
    [...]
)  

A this point, the alembic config and revisions are correctly packaged but the `alembic.ini` settings have to be tweaked to reflect the new directory tree. It can be done using the `%(here)s` param which contains the absolute path of the directory containing the `alembic.ini` file:

# A generic, single database configuration.

[alembic]
# path to migration scripts
script_location = %(here)s/alembic

[...]

# version location specification; this defaults
# to alembic/versions.  When using multiple version
# directories, initial revisions must be specified with --version-path
# version_locations = %(here)s/bar %(here)s/bat alembic/versions
version_locations = %(here)s/alembic/versions

[...]

Finally, you have to call `alembic` with the `-c` option which allows to provide the path of the config file:

alembic -c <path to alembic.ini> ...

Problem

What is the right way to package Alembic migration files in a Setuptools `setup.py` file? Everything is in my repo root as `alembic/`. This is a Python application, not a library. My desired installation flow is that someone can `pip install` the wheel that is my application. They would then be able to initialize the application database by running something like `<app> alembic upgrade --sqlalchemy.url=<db_url>`. Upgrades would then require a `pip install -U`, after which they can run the Alembic command again. Is this unorthodox? If not, how would I accomplish this? Certainly a `console_scripts` `entry_points`. But beyond that?

Original source

Related problems