Skip to content

Commit 3c726b2

Browse files
committed
Repair as_revision_number to return a tuple for "heads"
Fixed bug where the :meth:`.Script.as_revision_number` method did not accommodate for the 'heads' identifier, which in turn caused the :meth:`.EnvironmentContext.get_head_revisions` and :meth:`.EnvironmentContext.get_revision_argument` methods to be not usable when multiple heads were present. The :meth:.`EnvironmentContext.get_head_revisions` method returns a tuple in all cases as documented. Change-Id: I085d9b6c3f4ceafd6828d24983768a3d3916ce00 Fixes: #482
1 parent 1a6d6cd commit 3c726b2

File tree

4 files changed

+124
-3
lines changed

4 files changed

+124
-3
lines changed

alembic/script/base.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,8 @@ def as_revision_number(self, id_):
237237
if not rev:
238238
# convert () to None
239239
return None
240+
elif id_ == "heads":
241+
return rev
240242
else:
241243
return rev[0]
242244

alembic/testing/env.py

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,74 @@ def downgrade():
319319
return a, b, c
320320

321321

322+
def multi_heads_fixture(cfg, a, b, c):
323+
"""Create a multiple head fixture from the three-revs fixture"""
324+
325+
d = util.rev_id()
326+
e = util.rev_id()
327+
f = util.rev_id()
328+
329+
script = ScriptDirectory.from_config(cfg)
330+
script.generate_revision(
331+
d, "revision d from b", head=b, splice=True, refresh=True)
332+
write_script(script, d, """\
333+
"Rev D"
334+
revision = '%s'
335+
down_revision = '%s'
336+
337+
from alembic import op
338+
339+
340+
def upgrade():
341+
op.execute("CREATE STEP 4")
342+
343+
344+
def downgrade():
345+
op.execute("DROP STEP 4")
346+
347+
""" % (d, b))
348+
349+
script.generate_revision(
350+
e, "revision e from d", head=d, splice=True, refresh=True)
351+
write_script(script, e, """\
352+
"Rev E"
353+
revision = '%s'
354+
down_revision = '%s'
355+
356+
from alembic import op
357+
358+
359+
def upgrade():
360+
op.execute("CREATE STEP 5")
361+
362+
363+
def downgrade():
364+
op.execute("DROP STEP 5")
365+
366+
""" % (e, d))
367+
368+
script.generate_revision(
369+
f, "revision f from b", head=b, splice=True, refresh=True)
370+
write_script(script, f, """\
371+
"Rev F"
372+
revision = '%s'
373+
down_revision = '%s'
374+
375+
from alembic import op
376+
377+
378+
def upgrade():
379+
op.execute("CREATE STEP 6")
380+
381+
382+
def downgrade():
383+
op.execute("DROP STEP 6")
384+
385+
""" % (f, b))
386+
387+
return d, e, f
388+
389+
322390
def _multidb_testing_config(engines):
323391
"""alembic.ini fixture to work exactly with the 'multidb' template"""
324392

docs/build/unreleased/482.rst

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
.. change::
2+
:tags: bug, runtime
3+
:tickets: 482
4+
5+
Fixed bug where the :meth:`.Script.as_revision_number` method
6+
did not accommodate for the 'heads' identifier, which in turn
7+
caused the :meth:`.EnvironmentContext.get_head_revisions`
8+
and :meth:`.EnvironmentContext.get_revision_argument` methods
9+
to be not usable when multiple heads were present.
10+
The :meth:.`EnvironmentContext.get_head_revisions` method returns
11+
a tuple in all cases as documented.
12+
13+

tests/test_offline_environment.py

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44

55
from alembic.testing import assert_raises_message
66
from alembic.testing.env import staging_env, _no_sql_testing_config, \
7-
three_rev_fixture, clear_staging_env, env_file_fixture
7+
three_rev_fixture, clear_staging_env, env_file_fixture, \
8+
multi_heads_fixture
89
import re
910

1011
a = b = c = None
@@ -80,6 +81,12 @@ def test_destination_rev_pre_context(self):
8081
command.stamp(self.cfg, b, sql=True)
8182
command.downgrade(self.cfg, "%s:%s" % (c, b), sql=True)
8283

84+
def test_destination_rev_pre_context_multihead(self):
85+
d, e, f = multi_heads_fixture(self.cfg, a, b, c)
86+
env_file_fixture("""
87+
assert set(context.get_revision_argument()) == set(('%s', '%s', '%s', ))
88+
""" % (f, e, c))
89+
command.upgrade(self.cfg, 'heads', sql=True)
8390

8491
def test_destination_rev_post_context(self):
8592
env_file_fixture("""
@@ -90,25 +97,56 @@ def test_destination_rev_post_context(self):
9097
command.downgrade(self.cfg, "%s:%s" % (c, b), sql=True)
9198
command.stamp(self.cfg, b, sql=True)
9299

100+
def test_destination_rev_post_context_multihead(self):
101+
d, e, f = multi_heads_fixture(self.cfg, a, b, c)
102+
env_file_fixture("""
103+
context.configure(dialect_name='sqlite')
104+
assert set(context.get_revision_argument()) == set(('%s', '%s', '%s', ))
105+
""" % (f, e, c))
106+
command.upgrade(self.cfg, 'heads', sql=True)
107+
93108
def test_head_rev_pre_context(self):
94109
env_file_fixture("""
95110
assert context.get_head_revision() == '%s'
96-
""" % c)
111+
assert context.get_head_revisions() == ('%s', )
112+
""" % (c, c))
97113
command.upgrade(self.cfg, b, sql=True)
98114
command.downgrade(self.cfg, "%s:%s" % (b, a), sql=True)
99115
command.stamp(self.cfg, b, sql=True)
100116
command.current(self.cfg)
101117

118+
def test_head_rev_pre_context_multihead(self):
119+
d, e, f = multi_heads_fixture(self.cfg, a, b, c)
120+
env_file_fixture("""
121+
assert set(context.get_head_revisions()) == set(('%s', '%s', '%s', ))
122+
""" % (e, f, c))
123+
command.upgrade(self.cfg, e, sql=True)
124+
command.downgrade(self.cfg, "%s:%s" % (e, b), sql=True)
125+
command.stamp(self.cfg, c, sql=True)
126+
command.current(self.cfg)
127+
102128
def test_head_rev_post_context(self):
103129
env_file_fixture("""
104130
context.configure(dialect_name='sqlite')
105131
assert context.get_head_revision() == '%s'
106-
""" % c)
132+
assert context.get_head_revisions() == ('%s', )
133+
""" % (c, c))
107134
command.upgrade(self.cfg, b, sql=True)
108135
command.downgrade(self.cfg, "%s:%s" % (b, a), sql=True)
109136
command.stamp(self.cfg, b, sql=True)
110137
command.current(self.cfg)
111138

139+
def test_head_rev_post_context_multihead(self):
140+
d, e, f = multi_heads_fixture(self.cfg, a, b, c)
141+
env_file_fixture("""
142+
context.configure(dialect_name='sqlite')
143+
assert set(context.get_head_revisions()) == set(('%s', '%s', '%s', ))
144+
""" % (e, f, c))
145+
command.upgrade(self.cfg, e, sql=True)
146+
command.downgrade(self.cfg, "%s:%s" % (e, b), sql=True)
147+
command.stamp(self.cfg, c, sql=True)
148+
command.current(self.cfg)
149+
112150
def test_tag_pre_context(self):
113151
env_file_fixture("""
114152
assert context.get_tag_argument() == 'hi'

0 commit comments

Comments
 (0)