Skip to content

Commit dbc1d14

Browse files
committed
Expand use of config
1 parent 8a7a790 commit dbc1d14

11 files changed

+95
-65
lines changed

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Flux Migrations
22

33
[![codecov](https://codecov.io/gh/k2bd/flux-migrations/graph/badge.svg?token=PJF3cYLtZh)](https://codecov.io/gh/k2bd/flux-migrations)
4-
![Discord](https://img.shields.io/discord/1238849973123154001)
4+
![Discord](https://img.shields.io/discord/1238849973123154001?logo=discord&label=discord)
55

66
A modern database migration tool written in Python.
77

src/flux/backend/base.py

+9-5
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@
22
from contextlib import asynccontextmanager
33

44
from flux.backend.applied_migration import AppliedMigration
5+
from flux.config import FluxConfig
6+
from flux.migration.migration import Migration
57

68

79
class MigrationBackend(ABC):
810

911
@classmethod
10-
def from_config(cls, config: dict) -> "MigrationBackend":
12+
def from_config(cls, config: FluxConfig) -> "MigrationBackend":
1113
"""
1214
Create a MigrationBackend from a configuration
1315
@@ -52,21 +54,23 @@ async def migration_lock(self):
5254
yield
5355

5456
@abstractmethod
55-
async def register_migration(self, migration: str):
57+
async def register_migration(self, migration: Migration) -> AppliedMigration:
5658
"""
5759
Register a migration as applied (when up-migrated)
5860
"""
5961

6062
@abstractmethod
61-
async def unregister_migration(self, migration: str):
63+
async def unregister_migration(self, migration: Migration):
6264
"""
6365
Unregister a migration (when down-migrated)
6466
"""
6567

6668
@abstractmethod
67-
async def apply_migration(self, migration: str):
69+
async def apply_migration(self, content: str):
6870
"""
69-
Apply a migration to the database.
71+
Apply the content of a migration to the database. This is used for both
72+
up and down migrations so should not register or unregister the
73+
migration hash.
7074
"""
7175

7276
@abstractmethod

src/flux/backend/builtin/sqlite.py

Whitespace-only changes.

src/flux/migration/migration.py

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
@dataclass
66
class Migration:
7+
id: str
78
up: str
89
down: str | None
910

src/flux/migration/read_migration.py

+13-5
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,25 @@
1+
import os
2+
3+
from flux.config import FluxConfig
14
from flux.exceptions import InvalidMigrationModuleError
25
from flux.migration.migration import Migration
36
from flux.migration.temporary_module import temporary_module
47

58

6-
def read_sql_migration(up_file: str, down_file: str | None) -> Migration:
9+
def read_sql_migration(*, config: FluxConfig, migration_id: str) -> Migration:
710
"""
811
Read a pair of SQL migration files and return a Migration object
912
"""
13+
up_file = os.path.join(config.migration_directory, f"{migration_id}_up.sql")
14+
down_file = os.path.join(config.migration_directory, f"{migration_id}_down.sql")
15+
1016
try:
1117
with open(up_file) as f:
1218
up = f.read()
1319
except Exception as e:
1420
raise InvalidMigrationModuleError("Error reading up migration") from e
1521

16-
if down_file is None:
22+
if not os.path.exists(down_file):
1723
down = None
1824
else:
1925
try:
@@ -22,13 +28,15 @@ def read_sql_migration(up_file: str, down_file: str | None) -> Migration:
2228
except Exception as e:
2329
raise InvalidMigrationModuleError("Error reading down migration") from e
2430

25-
return Migration(up=up, down=down)
31+
return Migration(id=migration_id, up=up, down=down)
2632

2733

28-
def read_python_migration(migration_file: str) -> Migration:
34+
def read_python_migration(*, config: FluxConfig, migration_id: str) -> Migration:
2935
"""
3036
Read a Python migration file and return a Migration object
3137
"""
38+
migration_file = os.path.join(config.migration_directory, f"{migration_id}.py")
39+
3240
with temporary_module(migration_file) as module:
3341
try:
3442
up_migration = module.up()
@@ -48,4 +56,4 @@ def read_python_migration(migration_file: str) -> Migration:
4856
else:
4957
down_migration = None
5058

51-
return Migration(up=up_migration, down=down_migration)
59+
return Migration(id=migration_id, up=up_migration, down=down_migration)

tests/helpers.py

+15-5
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
import asyncio
2+
import datetime as dt
23
from contextlib import asynccontextmanager
34
from dataclasses import dataclass, field
45

56
from flux.backend.applied_migration import AppliedMigration
67
from flux.backend.base import MigrationBackend
8+
from flux.migration.migration import Migration
79

810

911
@dataclass
@@ -72,19 +74,27 @@ async def migration_lock(self):
7274
finally:
7375
self.migration_lock_active = False
7476

75-
async def register_migration(self, migration: str):
77+
async def register_migration(self, migration: Migration) -> AppliedMigration:
7678
"""
7779
Register a migration as applied (when up-migrated)
7880
"""
79-
80-
async def unregister_migration(self, migration: str):
81+
applied_migration = AppliedMigration(
82+
id=migration.id,
83+
hash=migration.up_hash,
84+
applied_at=dt.datetime.now(),
85+
)
86+
self.staged_migrations.add(applied_migration)
87+
return applied_migration
88+
89+
async def unregister_migration(self, migration: Migration):
8190
"""
8291
Unregister a migration (when down-migrated)
8392
"""
8493

85-
async def apply_migration(self, migration: str):
94+
async def apply_migration(self, content: str):
8695
"""
87-
Apply a migration to the database.
96+
Apply a migration to the database. This is used for both up and down
97+
migrations so should not register or unregister the migration hash.
8898
"""
8999

90100
async def get_applied_migrations(self) -> set[AppliedMigration]:

tests/unit/conftest.py

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import pytest
2+
3+
from flux.config import FluxConfig
4+
from tests.unit.constants import MIGRATIONS_DIR
5+
6+
7+
@pytest.fixture
8+
def in_memory_config():
9+
return FluxConfig(
10+
backend="in_memory",
11+
migration_directory=MIGRATIONS_DIR,
12+
log_level="DEBUG",
13+
backend_config={},
14+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
create table example_table ( id serial primary key, name text );

tests/unit/test_read_migration.py

+41-49
Original file line numberDiff line numberDiff line change
@@ -1,91 +1,83 @@
1-
import os
2-
31
import pytest
42

3+
from flux.config import FluxConfig
54
from flux.exceptions import InvalidMigrationModuleError
65
from flux.migration.read_migration import read_python_migration, read_sql_migration
7-
from tests.unit.constants import MIGRATIONS_DIR
86

9-
EXAMPLE_SQL_UP = os.path.join(MIGRATIONS_DIR, "example_up.sql")
10-
EXAMPLE_SQL_DOWN = os.path.join(MIGRATIONS_DIR, "example_down.sql")
11-
EXAMPLE_PYTHON_DOWN_STR = os.path.join(
12-
MIGRATIONS_DIR, "example_python_migration_down_str.py"
13-
)
14-
EXAMPLE_PYTHON_DOWN_NONE = os.path.join(
15-
MIGRATIONS_DIR, "example_python_migration_down_none.py"
16-
)
17-
EXAMPLE_PYTHON_DOWN_MISSING = os.path.join(
18-
MIGRATIONS_DIR, "example_python_migration_down_missing.py"
19-
)
7+
EXAMPLE_SQL_WITH_DOWN = "sql_example"
8+
EXAMPLE_SQL_WITHOUT_DOWN = "sql_example_2"
9+
EXAMPLE_PYTHON_DOWN_STR = "example_python_migration_down_str"
10+
EXAMPLE_PYTHON_DOWN_NONE = "example_python_migration_down_none"
11+
EXAMPLE_PYTHON_DOWN_MISSING = "example_python_migration_down_missing"
2012

2113

22-
INVALID_PYTHON_DOWN_INT = os.path.join(
23-
MIGRATIONS_DIR, "invalid_python_migration_down_int.py"
24-
)
25-
INVALID_PYTHON_DOWN_RAISES = os.path.join(
26-
MIGRATIONS_DIR, "invalid_python_migration_down_raises.py"
27-
)
28-
INVALID_PYTHON_UP_INT = os.path.join(
29-
MIGRATIONS_DIR, "invalid_python_migration_up_int.py"
30-
)
31-
INVALID_PYTHON_UP_MISSING = os.path.join(
32-
MIGRATIONS_DIR, "invalid_python_migration_up_missing.py"
33-
)
34-
INVALID_PYTHON_UP_RAISES = os.path.join(
35-
MIGRATIONS_DIR, "invalid_python_migration_up_raises.py"
36-
)
14+
INVALID_PYTHON_DOWN_INT = "invalid_python_migration_down_int"
15+
INVALID_PYTHON_DOWN_RAISES = "invalid_python_migration_down_raises"
16+
INVALID_PYTHON_UP_INT = "invalid_python_migration_up_int"
17+
INVALID_PYTHON_UP_MISSING = "invalid_python_migration_up_missing"
18+
INVALID_PYTHON_UP_RAISES = "invalid_python_migration_up_raises"
3719

3820
EXAMPLE_UP_TEXT = "create table example_table ( id serial primary key, name text );"
3921
EXAMPLE_DOWN_TEXT = "drop table example_table;"
4022

4123

42-
def test_read_sql_migration_with_down():
43-
migration = read_sql_migration(EXAMPLE_SQL_UP, EXAMPLE_SQL_DOWN)
24+
def test_read_sql_migration_with_down(in_memory_config: FluxConfig):
25+
migration = read_sql_migration(
26+
config=in_memory_config,
27+
migration_id=EXAMPLE_SQL_WITH_DOWN,
28+
)
4429
assert migration.up == f"{EXAMPLE_UP_TEXT}\n"
4530
assert migration.down == f"{EXAMPLE_DOWN_TEXT}\n"
4631
assert migration.up_hash == "a53b94e10e61374e5a20f9e361d638e2"
4732

4833

49-
def test_read_sql_migration_without_down():
50-
migration = read_sql_migration(EXAMPLE_SQL_UP, None)
34+
def test_read_sql_migration_without_down(in_memory_config: FluxConfig):
35+
migration = read_sql_migration(
36+
config=in_memory_config,
37+
migration_id=EXAMPLE_SQL_WITHOUT_DOWN,
38+
)
5139
assert migration.up == f"{EXAMPLE_UP_TEXT}\n"
5240
assert migration.down is None
5341
assert migration.up_hash == "a53b94e10e61374e5a20f9e361d638e2"
5442

5543

56-
def test_read_sql_migration_with_up_not_exist():
57-
with pytest.raises(InvalidMigrationModuleError):
58-
read_sql_migration("fake.sql", EXAMPLE_SQL_DOWN)
59-
60-
61-
def test_read_sql_migration_with_down_not_exist():
44+
def test_read_sql_migration_not_exist(in_memory_config: FluxConfig):
6245
with pytest.raises(InvalidMigrationModuleError):
63-
read_sql_migration(EXAMPLE_SQL_UP, "fake.sql")
46+
read_sql_migration(config=in_memory_config, migration_id="fake")
6447

6548

66-
def test_read_python_migration_down_str():
67-
migration = read_python_migration(EXAMPLE_PYTHON_DOWN_STR)
49+
def test_read_python_migration_down_str(in_memory_config: FluxConfig):
50+
migration = read_python_migration(
51+
config=in_memory_config,
52+
migration_id=EXAMPLE_PYTHON_DOWN_STR,
53+
)
6854
assert migration.up == f"{EXAMPLE_UP_TEXT}"
6955
assert migration.down == f"{EXAMPLE_DOWN_TEXT}"
7056
assert migration.up_hash == "2ea715441b43f1bdc8428238385bb592"
7157

7258

73-
def test_read_python_migration_down_none():
74-
migration = read_python_migration(EXAMPLE_PYTHON_DOWN_NONE)
59+
def test_read_python_migration_down_none(in_memory_config: FluxConfig):
60+
migration = read_python_migration(
61+
config=in_memory_config,
62+
migration_id=EXAMPLE_PYTHON_DOWN_NONE,
63+
)
7564
assert migration.up == f"{EXAMPLE_UP_TEXT}"
7665
assert migration.down is None
7766
assert migration.up_hash == "2ea715441b43f1bdc8428238385bb592"
7867

7968

80-
def test_read_python_migration_down_missing():
81-
migration = read_python_migration(EXAMPLE_PYTHON_DOWN_MISSING)
69+
def test_read_python_migration_down_missing(in_memory_config: FluxConfig):
70+
migration = read_python_migration(
71+
config=in_memory_config,
72+
migration_id=EXAMPLE_PYTHON_DOWN_MISSING,
73+
)
8274
assert migration.up == f"{EXAMPLE_UP_TEXT}"
8375
assert migration.down is None
8476
assert migration.up_hash == "2ea715441b43f1bdc8428238385bb592"
8577

8678

8779
@pytest.mark.parametrize(
88-
"migration_file",
80+
"migration_id",
8981
[
9082
INVALID_PYTHON_DOWN_INT,
9183
INVALID_PYTHON_DOWN_RAISES,
@@ -94,6 +86,6 @@ def test_read_python_migration_down_missing():
9486
INVALID_PYTHON_UP_RAISES,
9587
],
9688
)
97-
def test_read_python_migration_invalid(migration_file):
89+
def test_read_python_migration_invalid(in_memory_config: FluxConfig, migration_id: str):
9890
with pytest.raises(InvalidMigrationModuleError):
99-
read_python_migration(migration_file)
91+
read_python_migration(config=in_memory_config, migration_id=migration_id)

0 commit comments

Comments
 (0)