Skip to content

Commit 09f9e0b

Browse files
committed
refactoring migrations forward command
So it's easier to run programmatically
1 parent 8d3b28e commit 09f9e0b

File tree

3 files changed

+68
-38
lines changed

3 files changed

+68
-38
lines changed

piccolo/apps/migrations/commands/base.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from __future__ import annotations
2+
from dataclasses import dataclass
23
import importlib
34
import os
45
import sys
@@ -14,6 +15,12 @@
1415
from piccolo.apps.migrations.tables import Migration
1516

1617

18+
@dataclass
19+
class MigrationResult:
20+
success: bool
21+
message: t.Optional[str] = None
22+
23+
1724
class BaseMigrationManager(Finder):
1825
async def create_migration_table(self) -> bool:
1926
"""

piccolo/apps/migrations/commands/forwards.py

Lines changed: 53 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -3,26 +3,22 @@
33
import typing as t
44

55
from piccolo.apps.migrations.auto import MigrationManager
6-
from piccolo.apps.migrations.commands.base import BaseMigrationManager
6+
from piccolo.apps.migrations.commands.base import (
7+
BaseMigrationManager,
8+
MigrationResult,
9+
)
710
from piccolo.apps.migrations.tables import Migration
811
from piccolo.conf.apps import AppConfig, MigrationModule
912

1013

1114
class ForwardsMigrationManager(BaseMigrationManager):
12-
def __init__(
13-
self,
14-
app_name: str,
15-
migration_id: str,
16-
fake: bool = False,
17-
*args,
18-
**kwargs,
19-
):
15+
def __init__(self, app_name: str, migration_id: str, fake: bool = False):
2016
self.app_name = app_name
2117
self.migration_id = migration_id
2218
self.fake = fake
2319
super().__init__()
2420

25-
async def run_migrations(self, app_config: AppConfig) -> None:
21+
async def run_migrations(self, app_config: AppConfig) -> MigrationResult:
2622
already_ran = await Migration.get_migrations_which_ran(
2723
app_name=self.app_name
2824
)
@@ -38,10 +34,11 @@ async def run_migrations(self, app_config: AppConfig) -> None:
3834
print(f"Haven't run = {havent_run}")
3935

4036
if len(havent_run) == 0:
41-
# Make sure a status of 0 is returned, as we don't want this
37+
# Make sure this still appears successful, as we don't want this
4238
# to appear as an error in automated scripts.
43-
print("No migrations left to run!")
44-
sys.exit(0)
39+
message = "No migrations left to run!"
40+
print(message)
41+
return MigrationResult(success=True, message=message)
4542

4643
if self.migration_id == "all":
4744
subset = havent_run
@@ -51,7 +48,9 @@ async def run_migrations(self, app_config: AppConfig) -> None:
5148
try:
5249
index = havent_run.index(self.migration_id)
5350
except ValueError:
54-
sys.exit(f"{self.migration_id} is unrecognised")
51+
message = f"{self.migration_id} is unrecognised"
52+
print(message, file=sys.stderr)
53+
return MigrationResult(success=False, message=message)
5554
else:
5655
subset = havent_run[: index + 1]
5756

@@ -71,13 +70,45 @@ async def run_migrations(self, app_config: AppConfig) -> None:
7170
Migration(name=_id, app_name=self.app_name)
7271
).run()
7372

74-
async def run(self):
73+
return MigrationResult(success=True, message="Ran successfully")
74+
75+
async def run(self) -> MigrationResult:
7576
print("Running migrations ...")
7677
await self.create_migration_table()
7778

7879
app_config = self.get_app_config(app_name=self.app_name)
7980

80-
await self.run_migrations(app_config)
81+
return await self.run_migrations(app_config)
82+
83+
84+
async def run_forwards(
85+
app_name: str, migration_id: str = "all", fake: bool = False
86+
) -> MigrationResult:
87+
"""
88+
Run the migrations. This function can be used to programatically run
89+
migrations - for example, in a unit test.
90+
"""
91+
if app_name == "all":
92+
sorted_app_names = BaseMigrationManager().get_sorted_app_names()
93+
for _app_name in sorted_app_names:
94+
print(f"\nMigrating {_app_name}")
95+
print("------------------------------------------------")
96+
manager = ForwardsMigrationManager(
97+
app_name=_app_name, migration_id="all", fake=fake
98+
)
99+
response = await manager.run()
100+
if not response.success:
101+
return response
102+
103+
else:
104+
manager = ForwardsMigrationManager(
105+
app_name=app_name, migration_id=migration_id, fake=fake
106+
)
107+
response = await manager.run()
108+
if not response.success:
109+
return response
110+
111+
return MigrationResult(success=True)
81112

82113

83114
async def forwards(
@@ -97,17 +128,9 @@ async def forwards(
97128
If set, will record the migrations as being run without actually
98129
running them.
99130
"""
100-
if app_name == "all":
101-
sorted_app_names = BaseMigrationManager().get_sorted_app_names()
102-
for _app_name in sorted_app_names:
103-
print(f"\nMigrating {_app_name}")
104-
print("------------------------------------------------")
105-
manager = ForwardsMigrationManager(
106-
app_name=_app_name, migration_id="all", fake=fake
107-
)
108-
await manager.run()
109-
else:
110-
manager = ForwardsMigrationManager(
111-
app_name=app_name, migration_id=migration_id, fake=fake
112-
)
113-
await manager.run()
131+
response = await run_forwards(
132+
app_name=app_name, migration_id=migration_id, fake=fake
133+
)
134+
135+
if not response.success:
136+
sys.exit(1)

tests/apps/migrations/commands/test_forwards_backwards.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from __future__ import annotations
22

3+
import sys
34
import typing as t
45
from unittest import TestCase
56
from unittest.mock import patch, call, MagicMock
@@ -86,19 +87,21 @@ def test_forwards_backwards_single_migration(self):
8687
for table_class in table_classes:
8788
self.assertTrue(not table_class.table_exists().run_sync())
8889

89-
def test_forwards_unknown_migration(self):
90+
@patch("piccolo.apps.migrations.commands.forwards.print")
91+
def test_forwards_unknown_migration(self, print_):
9092
"""
9193
Test running an unknown migrations forwards.
9294
"""
93-
with self.assertRaises(SystemExit) as manager:
95+
with self.assertRaises(SystemExit):
9496
run_sync(
9597
forwards(
9698
app_name="example_app", migration_id="migration-12345"
9799
)
98100
)
99101

100-
self.assertEqual(
101-
manager.exception.__str__(), "migration-12345 is unrecognised"
102+
self.assertTrue(
103+
call("migration-12345 is unrecognised", file=sys.stderr)
104+
in print_.mock_calls
102105
)
103106

104107
def test_backwards_unknown_migration(self):
@@ -147,11 +150,8 @@ def test_forwards_no_migrations(self, print_: MagicMock):
147150
Test running the migrations if they've already run.
148151
"""
149152
run_sync(forwards(app_name="example_app", migration_id="all"))
153+
run_sync(forwards(app_name="example_app", migration_id="all"))
150154

151-
with self.assertRaises(SystemExit) as manager:
152-
run_sync(forwards(app_name="example_app", migration_id="all"))
153-
154-
self.assertEqual(manager.exception.code, 0)
155155
self.assertTrue(
156156
print_.mock_calls[-1] == call("No migrations left to run!")
157157
)

0 commit comments

Comments
 (0)