Skip to content

Commit 46d03fb

Browse files
author
Alc-Alc
committed
Add pyproject async template to Alembic
1 parent 4ff5cf7 commit 46d03fb

File tree

6 files changed

+239
-1
lines changed

6 files changed

+239
-1
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pyproject configuration, with an async dbapi.
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# A generic, single database configuration.
2+
3+
[alembic]
4+
5+
# database URL. This is consumed by the user-maintained env.py script only.
6+
# other means of configuring database URLs may be customized within the env.py
7+
# file.
8+
sqlalchemy.url = driver://user:pass@localhost/dbname
9+
10+
11+
# Logging configuration
12+
[loggers]
13+
keys = root,sqlalchemy,alembic
14+
15+
[handlers]
16+
keys = console
17+
18+
[formatters]
19+
keys = generic
20+
21+
[logger_root]
22+
level = WARNING
23+
handlers = console
24+
qualname =
25+
26+
[logger_sqlalchemy]
27+
level = WARNING
28+
handlers =
29+
qualname = sqlalchemy.engine
30+
31+
[logger_alembic]
32+
level = INFO
33+
handlers =
34+
qualname = alembic
35+
36+
[handler_console]
37+
class = StreamHandler
38+
args = (sys.stderr,)
39+
level = NOTSET
40+
formatter = generic
41+
42+
[formatter_generic]
43+
format = %(levelname)-5.5s [%(name)s] %(message)s
44+
datefmt = %H:%M:%S
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
import asyncio
2+
from logging.config import fileConfig
3+
4+
from sqlalchemy import pool
5+
from sqlalchemy.engine import Connection
6+
from sqlalchemy.ext.asyncio import async_engine_from_config
7+
8+
from alembic import context
9+
10+
# this is the Alembic Config object, which provides
11+
# access to the values within the .ini file in use.
12+
config = context.config
13+
14+
# Interpret the config file for Python logging.
15+
# This line sets up loggers basically.
16+
if config.config_file_name is not None:
17+
fileConfig(config.config_file_name)
18+
19+
# add your model's MetaData object here
20+
# for 'autogenerate' support
21+
# from myapp import mymodel
22+
# target_metadata = mymodel.Base.metadata
23+
target_metadata = None
24+
25+
# other values from the config, defined by the needs of env.py,
26+
# can be acquired:
27+
# my_important_option = config.get_main_option("my_important_option")
28+
# ... etc.
29+
30+
31+
def run_migrations_offline() -> None:
32+
"""Run migrations in 'offline' mode.
33+
34+
This configures the context with just a URL
35+
and not an Engine, though an Engine is acceptable
36+
here as well. By skipping the Engine creation
37+
we don't even need a DBAPI to be available.
38+
39+
Calls to context.execute() here emit the given string to the
40+
script output.
41+
42+
"""
43+
url = config.get_main_option("sqlalchemy.url")
44+
context.configure(
45+
url=url,
46+
target_metadata=target_metadata,
47+
literal_binds=True,
48+
dialect_opts={"paramstyle": "named"},
49+
)
50+
51+
with context.begin_transaction():
52+
context.run_migrations()
53+
54+
55+
def do_run_migrations(connection: Connection) -> None:
56+
context.configure(connection=connection, target_metadata=target_metadata)
57+
58+
with context.begin_transaction():
59+
context.run_migrations()
60+
61+
62+
async def run_async_migrations() -> None:
63+
"""In this scenario we need to create an Engine
64+
and associate a connection with the context.
65+
66+
"""
67+
68+
connectable = async_engine_from_config(
69+
config.get_section(config.config_ini_section, {}),
70+
prefix="sqlalchemy.",
71+
poolclass=pool.NullPool,
72+
)
73+
74+
async with connectable.connect() as connection:
75+
await connection.run_sync(do_run_migrations)
76+
77+
await connectable.dispose()
78+
79+
80+
def run_migrations_online() -> None:
81+
"""Run migrations in 'online' mode."""
82+
83+
asyncio.run(run_async_migrations())
84+
85+
86+
if context.is_offline_mode():
87+
run_migrations_offline()
88+
else:
89+
run_migrations_online()
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
[tool.alembic]
2+
3+
# path to migration scripts.
4+
# this is typically a path given in POSIX (e.g. forward slashes)
5+
# format, relative to the token %(here)s which refers to the location of this
6+
# ini file
7+
script_location = "${script_location}"
8+
9+
# template used to generate migration file names; The default value is %%(rev)s_%%(slug)s
10+
# Uncomment the line below if you want the files to be prepended with date and time
11+
# see https://alembic.sqlalchemy.org/en/latest/tutorial.html#editing-the-ini-file
12+
# for all available tokens
13+
# file_template = "%%(year)d_%%(month).2d_%%(day).2d_%%(hour).2d%%(minute).2d-%%(rev)s_%%(slug)s"
14+
15+
# additional paths to be prepended to sys.path. defaults to the current working directory.
16+
prepend_sys_path = [
17+
"."
18+
]
19+
20+
# timezone to use when rendering the date within the migration file
21+
# as well as the filename.
22+
# If specified, requires the python>=3.9 or backports.zoneinfo library and tzdata library.
23+
# Any required deps can installed by adding `alembic[tz]` to the pip requirements
24+
# string value is passed to ZoneInfo()
25+
# leave blank for localtime
26+
# timezone =
27+
28+
# max length of characters to apply to the "slug" field
29+
# truncate_slug_length = 40
30+
31+
# set to 'true' to run the environment during
32+
# the 'revision' command, regardless of autogenerate
33+
# revision_environment = false
34+
35+
# set to 'true' to allow .pyc and .pyo files without
36+
# a source .py file to be detected as revisions in the
37+
# versions/ directory
38+
# sourceless = false
39+
40+
# version location specification; This defaults
41+
# to <script_location>/versions. When using multiple version
42+
# directories, initial revisions must be specified with --version-path.
43+
# version_locations = [
44+
# "%(here)s/alembic/versions",
45+
# "%(here)s/foo/bar"
46+
# ]
47+
48+
49+
# set to 'true' to search source files recursively
50+
# in each "version_locations" directory
51+
# new in Alembic version 1.10
52+
# recursive_version_locations = false
53+
54+
# the output encoding used when revision files
55+
# are written from script.py.mako
56+
# output_encoding = "utf-8"
57+
58+
# This section defines scripts or Python functions that are run
59+
# on newly generated revision scripts. See the documentation for further
60+
# detail and examples
61+
# [[tool.alembic.post_write_hooks]]
62+
# format using "black" - use the console_scripts runner,
63+
# against the "black" entrypoint
64+
# name = "black"
65+
# type = "console_scripts"
66+
# entrypoint = "black"
67+
# options = "-l 79 REVISION_SCRIPT_FILENAME"
68+
#
69+
# [[tool.alembic.post_write_hooks]]
70+
# lint with attempts to fix using "ruff" - use the exec runner,
71+
# execute a binary
72+
# name = "ruff"
73+
# type = "exec"
74+
# executable = "%(here)s/.venv/bin/ruff"
75+
# options = "check --fix REVISION_SCRIPT_FILENAME"
76+
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
"""${message}
2+
3+
Revision ID: ${up_revision}
4+
Revises: ${down_revision | comma,n}
5+
Create Date: ${create_date}
6+
7+
"""
8+
from typing import Sequence, Union
9+
10+
from alembic import op
11+
import sqlalchemy as sa
12+
${imports if imports else ""}
13+
14+
# revision identifiers, used by Alembic.
15+
revision: str = ${repr(up_revision)}
16+
down_revision: Union[str, Sequence[str], None] = ${repr(down_revision)}
17+
branch_labels: Union[str, Sequence[str], None] = ${repr(branch_labels)}
18+
depends_on: Union[str, Sequence[str], None] = ${repr(depends_on)}
19+
20+
21+
def upgrade() -> None:
22+
"""Upgrade schema."""
23+
${upgrades if upgrades else "pass"}
24+
25+
26+
def downgrade() -> None:
27+
"""Downgrade schema."""
28+
${downgrades if downgrades else "pass"}

docs/build/cookbook.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1525,7 +1525,7 @@ use of most of its interface from async applications. Alembic currently does
15251525
not provide an async api directly, but it can use an use SQLAlchemy Async
15261526
engine to run the migrations and autogenerate.
15271527

1528-
New configurations can use the template "async" to bootstrap an environment which
1528+
New configurations can use the template "async" or "pyproject_async" to bootstrap an environment which
15291529
can be used with async DBAPI like asyncpg, running the command::
15301530

15311531
alembic init -t async <script_directory_here>

0 commit comments

Comments
 (0)