Skip to content

Commit d5d6951

Browse files
authored
Merge pull request olirice#105 from jdimmerman/feature/sqlalchemy-2-support
sqlalchemy 1.4 & 2.0 support (1.3 dropped)
2 parents aef523d + 3daa6e7 commit d5d6951

22 files changed

+224
-136
lines changed

README.md

+20
Original file line numberDiff line numberDiff line change
@@ -116,3 +116,23 @@ def downgrade():
116116
Visit the [quickstart guide](https://olirice.github.io/alembic_utils/quickstart/) for usage instructions.
117117

118118
<p align="center">&mdash;&mdash; &mdash;&mdash;</p>
119+
120+
### Contributing
121+
122+
To run the tests
123+
```
124+
# install pip dependencies
125+
pip install wheel && pip install -e ".[dev]"
126+
127+
# run the tests
128+
pytest src/test
129+
```
130+
131+
To invoke the linter automated formatting and generally make use of precommit checks:
132+
```
133+
pip install pre-commit
134+
pre-commit install
135+
136+
# manually run
137+
pre-commit run --all
138+
```

setup.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ def get_version(package):
3737
"alembic>=1.5.7",
3838
"flupy",
3939
"parse>=1.8.4",
40-
"sqlalchemy>=1.3.0,<2",
40+
"sqlalchemy>=1.4",
4141
"typing_extensions",
4242
],
4343
extras_require={

src/alembic_utils/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = "0.8.0"
1+
__version__ = "0.8.1"

src/alembic_utils/simulate.py

+6-7
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,8 @@ def simulate_entity(
3232

3333
deps: List["ReplaceableEntity"] = dependencies or []
3434

35+
outer_transaction = sess.begin_nested()
3536
try:
36-
sess.begin_nested()
37-
3837
dependency_managers = [simulate_entity(sess, x) for x in deps]
3938

4039
with ExitStack() as stack:
@@ -43,8 +42,8 @@ def simulate_entity(
4342
stack.enter_context(mgr)
4443

4544
did_drop = False
45+
inner_transaction = sess.begin_nested()
4646
try:
47-
sess.begin_nested()
4847
sess.execute(entity.to_sql_statement_drop(cascade=True))
4948
did_drop = True
5049
sess.execute(entity.to_sql_statement_create())
@@ -58,11 +57,11 @@ def simulate_entity(
5857

5958
# Try again without the drop in case the drop raised
6059
# a does not exist error
61-
sess.rollback()
62-
sess.begin_nested()
60+
inner_transaction.rollback()
61+
inner_transaction = sess.begin_nested()
6362
sess.execute(entity.to_sql_statement_create())
6463
yield sess
6564
finally:
66-
sess.rollback()
65+
inner_transaction.rollback()
6766
finally:
68-
sess.rollback()
67+
outer_transaction.rollback()

src/alembic_utils/testbase.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,10 @@ def build_alembic_config(engine: Engine) -> Config:
2727
path_to_alembic_ini = REPO_ROOT / "alembic.ini"
2828

2929
alembic_cfg = Config(path_to_alembic_ini)
30+
3031
# Make double sure alembic references the test database
31-
alembic_cfg.set_main_option("sqlalchemy.url", str(engine.url))
32+
alembic_cfg.set_main_option("sqlalchemy.url", engine.url.render_as_string(hide_password=False))
33+
3234
alembic_cfg.set_main_option("script_location", str((Path("src") / "test" / "alembic_config")))
3335
return alembic_cfg
3436

src/test/alembic_config/env.py

-1
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,6 @@ def run_migrations_online():
5757
config.get_section(config.config_ini_section),
5858
prefix="sqlalchemy.",
5959
poolclass=pool.NullPool,
60-
future=True,
6160
)
6261

6362
with connectable.connect() as connection:

src/test/conftest.py

+6-5
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
import pytest
1111
from parse import parse
12-
from sqlalchemy import create_engine
12+
from sqlalchemy import create_engine, text
1313
from sqlalchemy.engine import Engine
1414
from sqlalchemy.orm import Session, sessionmaker
1515

@@ -105,14 +105,15 @@ def raw_engine(maybe_start_pg: None) -> Generator[Engine, None, None]:
105105

106106

107107
@pytest.fixture(scope="function")
108-
def engine(raw_engine) -> Generator[Engine, None, None]:
108+
def engine(raw_engine: Engine) -> Generator[Engine, None, None]:
109109
"""Engine that has been reset between tests"""
110110

111111
def run_cleaners():
112112
registry.clear()
113-
raw_engine.execute("drop schema public cascade; create schema public;")
114-
raw_engine.execute('drop schema if exists "DEV" cascade; create schema "DEV";')
115-
raw_engine.execute('drop role if exists "anon_user"')
113+
with raw_engine.begin() as connection:
114+
connection.execute(text("drop schema public cascade; create schema public;"))
115+
connection.execute(text('drop schema if exists "DEV" cascade; create schema "DEV";'))
116+
connection.execute(text('drop role if exists "anon_user"'))
116117
# Remove any migrations that were left behind
117118
TEST_VERSIONS_ROOT.mkdir(exist_ok=True, parents=True)
118119
shutil.rmtree(TEST_VERSIONS_ROOT)

src/test/test_create_function.py

+8-5
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from sqlalchemy import text
2+
13
from alembic_utils.pg_function import PGFunction
24

35
to_upper = PGFunction(
@@ -18,8 +20,9 @@ def test_create_and_drop(engine) -> None:
1820
down_sql = to_upper.to_sql_statement_drop()
1921

2022
# Testing that the following two lines don't raise
21-
engine.execute(up_sql)
22-
result = engine.execute("select public.to_upper('hello');").fetchone()
23-
assert result[0] == "HELLO"
24-
engine.execute(down_sql)
25-
assert True
23+
with engine.begin() as connection:
24+
connection.execute(up_sql)
25+
result = connection.execute(text("select public.to_upper('hello');")).fetchone()
26+
assert result[0] == "HELLO"
27+
connection.execute(down_sql)
28+
assert True

src/test/test_include_filters.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,9 @@
7575

7676

7777
def test_create_revision_with_filters(engine) -> None:
78-
for entity in reflected_entities:
79-
engine.execute(entity.to_sql_statement_create())
78+
with engine.begin() as connection:
79+
for entity in reflected_entities:
80+
connection.execute(entity.to_sql_statement_create())
8081
register_entities(registered_entities)
8182

8283
run_alembic_command(

src/test/test_op_drop_cascade.py

+6-4
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,9 @@
2020

2121
def test_drop_fails_without_cascade(engine) -> None:
2222

23-
engine.execute(A.to_sql_statement_create())
24-
engine.execute(B_A.to_sql_statement_create())
23+
with engine.begin() as connection:
24+
connection.execute(A.to_sql_statement_create())
25+
connection.execute(B_A.to_sql_statement_create())
2526

2627
register_entities([B_A], schemas=["DEV"], entity_types=[PGView])
2728

@@ -48,8 +49,9 @@ def test_drop_fails_without_cascade(engine) -> None:
4849

4950
def test_drop_fails_with_cascade(engine, sess) -> None:
5051

51-
engine.execute(A.to_sql_statement_create())
52-
engine.execute(B_A.to_sql_statement_create())
52+
with engine.begin() as connection:
53+
connection.execute(A.to_sql_statement_create())
54+
connection.execute(B_A.to_sql_statement_create())
5355

5456
register_entities([B_A], schemas=["DEV"], entity_types=[PGView])
5557

src/test/test_pg_constraint_trigger.py

+24-16
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import pytest
2+
from sqlalchemy import text
23

34
from alembic_utils.exceptions import SQLParseFailure
45
from alembic_utils.pg_function import PGFunction
@@ -9,18 +10,21 @@
910

1011
@pytest.fixture(scope="function")
1112
def sql_setup(engine):
12-
conn = engine
13-
conn.execute(
13+
with engine.begin() as connection:
14+
connection.execute(
15+
text(
16+
"""
17+
create table public.account (
18+
id serial primary key,
19+
email text not null
20+
);
1421
"""
15-
create table public.account (
16-
id serial primary key,
17-
email text not null
18-
);
19-
"""
20-
)
22+
)
23+
)
2124

2225
yield
23-
conn.execute("drop table public.account cascade")
26+
with engine.begin() as connection:
27+
connection.execute(text("drop table public.account cascade"))
2428

2529

2630
FUNC = PGFunction.from_sql(
@@ -45,7 +49,8 @@ def sql_setup(engine):
4549

4650

4751
def test_create_revision(sql_setup, engine) -> None:
48-
engine.execute(FUNC.to_sql_statement_create())
52+
with engine.begin() as connection:
53+
connection.execute(FUNC.to_sql_statement_create())
4954

5055
register_entities([FUNC, TRIG], entity_types=[PGTrigger])
5156
run_alembic_command(
@@ -71,8 +76,9 @@ def test_create_revision(sql_setup, engine) -> None:
7176

7277

7378
def test_trig_update_revision(sql_setup, engine) -> None:
74-
engine.execute(FUNC.to_sql_statement_create())
75-
engine.execute(TRIG.to_sql_statement_create())
79+
with engine.begin() as connection:
80+
connection.execute(FUNC.to_sql_statement_create())
81+
connection.execute(TRIG.to_sql_statement_create())
7682

7783
UPDATED_TRIG = PGTrigger(
7884
schema="public",
@@ -113,8 +119,9 @@ def test_trig_update_revision(sql_setup, engine) -> None:
113119

114120

115121
def test_noop_revision(sql_setup, engine) -> None:
116-
engine.execute(FUNC.to_sql_statement_create())
117-
engine.execute(TRIG.to_sql_statement_create())
122+
with engine.begin() as connection:
123+
connection.execute(FUNC.to_sql_statement_create())
124+
connection.execute(TRIG.to_sql_statement_create())
118125

119126
register_entities([FUNC, TRIG], entity_types=[PGTrigger])
120127

@@ -141,8 +148,9 @@ def test_noop_revision(sql_setup, engine) -> None:
141148

142149
def test_drop(sql_setup, engine) -> None:
143150
# Manually create a SQL function
144-
engine.execute(FUNC.to_sql_statement_create())
145-
engine.execute(TRIG.to_sql_statement_create())
151+
with engine.begin() as connection:
152+
connection.execute(FUNC.to_sql_statement_create())
153+
connection.execute(TRIG.to_sql_statement_create())
146154

147155
# Register no functions locally
148156
register_entities([], schemas=["public"], entity_types=[PGTrigger])

src/test/test_pg_extension.py

+6-3
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,8 @@ def test_update_is_unreachable(engine) -> None:
4343
# scoping assumptions made for all other entities
4444

4545
# Create the view outside of a revision
46-
engine.execute(TEST_EXT.to_sql_statement_create())
46+
with engine.begin() as connection:
47+
connection.execute(TEST_EXT.to_sql_statement_create())
4748

4849
UPDATED_TEST_EXT = PGExtension("DEV", TEST_EXT.signature)
4950

@@ -68,7 +69,8 @@ def test_update_is_unreachable(engine) -> None:
6869

6970
def test_noop_revision(engine) -> None:
7071
# Create the view outside of a revision
71-
engine.execute(TEST_EXT.to_sql_statement_create())
72+
with engine.begin() as connection:
73+
connection.execute(TEST_EXT.to_sql_statement_create())
7274

7375
register_entities([TEST_EXT], entity_types=[PGExtension])
7476

@@ -102,7 +104,8 @@ def test_drop_revision(engine) -> None:
102104
register_entities([], entity_types=[PGExtension])
103105

104106
# Manually create a SQL function
105-
engine.execute(TEST_EXT.to_sql_statement_create())
107+
with engine.begin() as connection:
108+
connection.execute(TEST_EXT.to_sql_statement_create())
106109

107110
output = run_alembic_command(
108111
engine=engine,

src/test/test_pg_function.py

+12-5
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from sqlalchemy import text
2+
13
from alembic_utils.pg_function import PGFunction
24
from alembic_utils.replaceable_entity import register_entities
35
from alembic_utils.testbase import TEST_VERSIONS_ROOT, run_alembic_command
@@ -39,7 +41,8 @@ def test_create_revision(engine) -> None:
3941

4042

4143
def test_update_revision(engine) -> None:
42-
engine.execute(TO_UPPER.to_sql_statement_create())
44+
with engine.begin() as connection:
45+
connection.execute(TO_UPPER.to_sql_statement_create())
4346

4447
# Update definition of TO_UPPER
4548
UPDATED_TO_UPPER = PGFunction(
@@ -79,7 +82,8 @@ def test_update_revision(engine) -> None:
7982

8083

8184
def test_noop_revision(engine) -> None:
82-
engine.execute(TO_UPPER.to_sql_statement_create())
85+
with engine.begin() as connection:
86+
connection.execute(TO_UPPER.to_sql_statement_create())
8387

8488
register_entities([TO_UPPER], entity_types=[PGFunction])
8589

@@ -106,7 +110,8 @@ def test_noop_revision(engine) -> None:
106110

107111
def test_drop(engine) -> None:
108112
# Manually create a SQL function
109-
engine.execute(TO_UPPER.to_sql_statement_create())
113+
with engine.begin() as connection:
114+
connection.execute(TO_UPPER.to_sql_statement_create())
110115

111116
# Register no functions locally
112117
register_entities([], schemas=["public"], entity_types=[PGFunction])
@@ -174,7 +179,8 @@ def test_ignores_extension_functions(engine) -> None:
174179
# Unless they are excluded, every autogenerate migration will produce
175180
# drop statements for those functions
176181
try:
177-
engine.execute("create extension if not exists unaccent;")
182+
with engine.begin() as connection:
183+
connection.execute(text("create extension if not exists unaccent;"))
178184
register_entities([], schemas=["public"], entity_types=[PGFunction])
179185
run_alembic_command(
180186
engine=engine,
@@ -189,7 +195,8 @@ def test_ignores_extension_functions(engine) -> None:
189195

190196
assert "op.drop_entity" not in migration_contents
191197
finally:
192-
engine.execute("drop extension if exists unaccent;")
198+
with engine.begin() as connection:
199+
connection.execute(text("drop extension if exists unaccent;"))
193200

194201

195202
def test_plpgsql_colon_esacpe(engine) -> None:

src/test/test_pg_function_overloading.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,9 @@ def test_create_revision(engine) -> None:
4949

5050

5151
def test_update_revision(engine) -> None:
52-
engine.execute(TO_FLOAT_FROM_INT.to_sql_statement_create())
53-
engine.execute(TO_FLOAT_FROM_TEXT.to_sql_statement_create())
52+
with engine.begin() as connection:
53+
connection.execute(TO_FLOAT_FROM_INT.to_sql_statement_create())
54+
connection.execute(TO_FLOAT_FROM_TEXT.to_sql_statement_create())
5455

5556
UPDATE = PGFunction(
5657
schema="public",

0 commit comments

Comments
 (0)