Port to Python3
Brief summary of the modifications: * Use six for compatibility with both Python 2 and 3; * Replace UserDict.DictMixin with collections.MutableMapping; * Fix relative imports; * Use test-requirements.txt for requirements that are common to both Python 2 and 3, and test-requirements-py{2,3}.txt for version-specific requirements; * Miscellaneous fixes. * Use a specific test_db_py3.cfg file for Python 3, that only runs tests on sqlite. Thanks to Victor Stinner who co-wrote this patch. Change-Id: Ia6dc536c39d274924c21fd5bb619e8e5721e04c4 Co-Authored-By: Victor Stinner <victor.stinner@enovance.com>
This commit is contained in:
parent
07909159ae
commit
a03b141a95
@ -4,7 +4,6 @@
|
|||||||
At the moment, this isn't so much based off of ANSI as much as
|
At the moment, this isn't so much based off of ANSI as much as
|
||||||
things that just happen to work with multiple databases.
|
things that just happen to work with multiple databases.
|
||||||
"""
|
"""
|
||||||
import StringIO
|
|
||||||
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
from sqlalchemy.schema import SchemaVisitor
|
from sqlalchemy.schema import SchemaVisitor
|
||||||
@ -20,6 +19,7 @@ from migrate import exceptions
|
|||||||
import sqlalchemy.sql.compiler
|
import sqlalchemy.sql.compiler
|
||||||
from migrate.changeset import constraint
|
from migrate.changeset import constraint
|
||||||
from migrate.changeset import util
|
from migrate.changeset import util
|
||||||
|
from six.moves import StringIO
|
||||||
|
|
||||||
from sqlalchemy.schema import AddConstraint, DropConstraint
|
from sqlalchemy.schema import AddConstraint, DropConstraint
|
||||||
from sqlalchemy.sql.compiler import DDLCompiler
|
from sqlalchemy.sql.compiler import DDLCompiler
|
||||||
@ -43,11 +43,12 @@ class AlterTableVisitor(SchemaVisitor):
|
|||||||
try:
|
try:
|
||||||
return self.connection.execute(self.buffer.getvalue())
|
return self.connection.execute(self.buffer.getvalue())
|
||||||
finally:
|
finally:
|
||||||
self.buffer.truncate(0)
|
self.buffer.seek(0)
|
||||||
|
self.buffer.truncate()
|
||||||
|
|
||||||
def __init__(self, dialect, connection, **kw):
|
def __init__(self, dialect, connection, **kw):
|
||||||
self.connection = connection
|
self.connection = connection
|
||||||
self.buffer = StringIO.StringIO()
|
self.buffer = StringIO()
|
||||||
self.preparer = dialect.identifier_preparer
|
self.preparer = dialect.identifier_preparer
|
||||||
self.dialect = dialect
|
self.dialect = dialect
|
||||||
|
|
||||||
|
@ -3,7 +3,10 @@
|
|||||||
|
|
||||||
.. _`SQLite`: http://www.sqlite.org/
|
.. _`SQLite`: http://www.sqlite.org/
|
||||||
"""
|
"""
|
||||||
from UserDict import DictMixin
|
try: # Python 3
|
||||||
|
from collections import MutableMapping as DictMixin
|
||||||
|
except ImportError: # Python 2
|
||||||
|
from UserDict import DictMixin
|
||||||
from copy import copy
|
from copy import copy
|
||||||
|
|
||||||
from sqlalchemy.databases import sqlite as sa_base
|
from sqlalchemy.databases import sqlite as sa_base
|
||||||
|
@ -1,10 +1,14 @@
|
|||||||
"""
|
"""
|
||||||
Schema module providing common schema operations.
|
Schema module providing common schema operations.
|
||||||
"""
|
"""
|
||||||
|
import abc
|
||||||
|
try: # Python 3
|
||||||
|
from collections import MutableMapping as DictMixin
|
||||||
|
except ImportError: # Python 2
|
||||||
|
from UserDict import DictMixin
|
||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
from UserDict import DictMixin
|
import six
|
||||||
|
|
||||||
import sqlalchemy
|
import sqlalchemy
|
||||||
|
|
||||||
from sqlalchemy.schema import ForeignKeyConstraint
|
from sqlalchemy.schema import ForeignKeyConstraint
|
||||||
@ -163,7 +167,39 @@ def _to_index(index, table=None, engine=None):
|
|||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
|
||||||
class ColumnDelta(DictMixin, sqlalchemy.schema.SchemaItem):
|
|
||||||
|
# Python3: if we just use:
|
||||||
|
#
|
||||||
|
# class ColumnDelta(DictMixin, sqlalchemy.schema.SchemaItem):
|
||||||
|
# ...
|
||||||
|
#
|
||||||
|
# We get the following error:
|
||||||
|
# TypeError: metaclass conflict: the metaclass of a derived class must be a
|
||||||
|
# (non-strict) subclass of the metaclasses of all its bases.
|
||||||
|
#
|
||||||
|
# The complete inheritance/metaclass relationship list of ColumnDelta can be
|
||||||
|
# summarized by this following dot file:
|
||||||
|
#
|
||||||
|
# digraph test123 {
|
||||||
|
# ColumnDelta -> MutableMapping;
|
||||||
|
# MutableMapping -> Mapping;
|
||||||
|
# Mapping -> {Sized Iterable Container};
|
||||||
|
# {Sized Iterable Container} -> ABCMeta[style=dashed];
|
||||||
|
#
|
||||||
|
# ColumnDelta -> SchemaItem;
|
||||||
|
# SchemaItem -> {SchemaEventTarget Visitable};
|
||||||
|
# SchemaEventTarget -> object;
|
||||||
|
# Visitable -> {VisitableType object} [style=dashed];
|
||||||
|
# VisitableType -> type;
|
||||||
|
# }
|
||||||
|
#
|
||||||
|
# We need to use a metaclass that inherits from all the metaclasses of
|
||||||
|
# DictMixin and sqlalchemy.schema.SchemaItem. Let's call it "MyMeta".
|
||||||
|
class MyMeta(sqlalchemy.sql.visitors.VisitableType, abc.ABCMeta, object):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class ColumnDelta(six.with_metaclass(MyMeta, DictMixin, sqlalchemy.schema.SchemaItem)):
|
||||||
"""Extracts the differences between two columns/column-parameters
|
"""Extracts the differences between two columns/column-parameters
|
||||||
|
|
||||||
May receive parameters arranged in several different ways:
|
May receive parameters arranged in several different ways:
|
||||||
@ -229,7 +265,7 @@ class ColumnDelta(DictMixin, sqlalchemy.schema.SchemaItem):
|
|||||||
diffs = self.compare_1_column(*p, **kw)
|
diffs = self.compare_1_column(*p, **kw)
|
||||||
else:
|
else:
|
||||||
# Zero columns specified
|
# Zero columns specified
|
||||||
if not len(p) or not isinstance(p[0], basestring):
|
if not len(p) or not isinstance(p[0], six.string_types):
|
||||||
raise ValueError("First argument must be column name")
|
raise ValueError("First argument must be column name")
|
||||||
diffs = self.compare_parameters(*p, **kw)
|
diffs = self.compare_parameters(*p, **kw)
|
||||||
|
|
||||||
@ -254,6 +290,12 @@ class ColumnDelta(DictMixin, sqlalchemy.schema.SchemaItem):
|
|||||||
def __delitem__(self, key):
|
def __delitem__(self, key):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
def keys(self):
|
def keys(self):
|
||||||
return self.diffs.keys()
|
return self.diffs.keys()
|
||||||
|
|
||||||
@ -332,7 +374,7 @@ class ColumnDelta(DictMixin, sqlalchemy.schema.SchemaItem):
|
|||||||
"""Extracts data from p and modifies diffs"""
|
"""Extracts data from p and modifies diffs"""
|
||||||
p = list(p)
|
p = list(p)
|
||||||
while len(p):
|
while len(p):
|
||||||
if isinstance(p[0], basestring):
|
if isinstance(p[0], six.string_types):
|
||||||
k.setdefault('name', p.pop(0))
|
k.setdefault('name', p.pop(0))
|
||||||
elif isinstance(p[0], sqlalchemy.types.TypeEngine):
|
elif isinstance(p[0], sqlalchemy.types.TypeEngine):
|
||||||
k.setdefault('type', p.pop(0))
|
k.setdefault('type', p.pop(0))
|
||||||
@ -370,7 +412,7 @@ class ColumnDelta(DictMixin, sqlalchemy.schema.SchemaItem):
|
|||||||
return getattr(self, '_table', None)
|
return getattr(self, '_table', None)
|
||||||
|
|
||||||
def _set_table(self, table):
|
def _set_table(self, table):
|
||||||
if isinstance(table, basestring):
|
if isinstance(table, six.string_types):
|
||||||
if self.alter_metadata:
|
if self.alter_metadata:
|
||||||
if not self.meta:
|
if not self.meta:
|
||||||
raise ValueError("metadata must be specified for table"
|
raise ValueError("metadata must be specified for table"
|
||||||
@ -587,7 +629,7 @@ populated with defaults
|
|||||||
if isinstance(cons,(ForeignKeyConstraint,
|
if isinstance(cons,(ForeignKeyConstraint,
|
||||||
UniqueConstraint)):
|
UniqueConstraint)):
|
||||||
for col_name in cons.columns:
|
for col_name in cons.columns:
|
||||||
if not isinstance(col_name,basestring):
|
if not isinstance(col_name,six.string_types):
|
||||||
col_name = col_name.name
|
col_name = col_name.name
|
||||||
if self.name==col_name:
|
if self.name==col_name:
|
||||||
to_drop.add(cons)
|
to_drop.add(cons)
|
||||||
@ -622,7 +664,7 @@ populated with defaults
|
|||||||
if (getattr(self, name[:-5]) and not obj):
|
if (getattr(self, name[:-5]) and not obj):
|
||||||
raise InvalidConstraintError("Column.create() accepts index_name,"
|
raise InvalidConstraintError("Column.create() accepts index_name,"
|
||||||
" primary_key_name and unique_name to generate constraints")
|
" primary_key_name and unique_name to generate constraints")
|
||||||
if not isinstance(obj, basestring) and obj is not None:
|
if not isinstance(obj, six.string_types) and obj is not None:
|
||||||
raise InvalidConstraintError(
|
raise InvalidConstraintError(
|
||||||
"%s argument for column must be constraint name" % name)
|
"%s argument for column must be constraint name" % name)
|
||||||
|
|
||||||
|
@ -6,10 +6,11 @@ sys.path.append(os.path.dirname(os.path.abspath(__file__)))
|
|||||||
|
|
||||||
from unittest import TestCase
|
from unittest import TestCase
|
||||||
import migrate
|
import migrate
|
||||||
|
import six
|
||||||
|
|
||||||
|
|
||||||
class TestVersionDefined(TestCase):
|
class TestVersionDefined(TestCase):
|
||||||
def test_version(self):
|
def test_version(self):
|
||||||
"""Test for migrate.__version__"""
|
"""Test for migrate.__version__"""
|
||||||
self.assertTrue(isinstance(migrate.__version__, basestring))
|
self.assertTrue(isinstance(migrate.__version__, six.string_types))
|
||||||
self.assertTrue(len(migrate.__version__) > 0)
|
self.assertTrue(len(migrate.__version__) > 0)
|
||||||
|
@ -11,6 +11,7 @@ from migrate.changeset import constraint
|
|||||||
from migrate.changeset.schema import ColumnDelta
|
from migrate.changeset.schema import ColumnDelta
|
||||||
from migrate.tests import fixture
|
from migrate.tests import fixture
|
||||||
from migrate.tests.fixture.warnings import catch_warnings
|
from migrate.tests.fixture.warnings import catch_warnings
|
||||||
|
import six
|
||||||
|
|
||||||
class TestAddDropColumn(fixture.DB):
|
class TestAddDropColumn(fixture.DB):
|
||||||
"""Test add/drop column through all possible interfaces
|
"""Test add/drop column through all possible interfaces
|
||||||
@ -400,7 +401,7 @@ class TestAddDropColumn(fixture.DB):
|
|||||||
if isinstance(cons,ForeignKeyConstraint):
|
if isinstance(cons,ForeignKeyConstraint):
|
||||||
col_names = []
|
col_names = []
|
||||||
for col_name in cons.columns:
|
for col_name in cons.columns:
|
||||||
if not isinstance(col_name,basestring):
|
if not isinstance(col_name,six.string_types):
|
||||||
col_name = col_name.name
|
col_name = col_name.name
|
||||||
col_names.append(col_name)
|
col_names.append(col_name)
|
||||||
result.append(col_names)
|
result.append(col_names)
|
||||||
@ -612,7 +613,7 @@ class TestColumnChange(fixture.DB):
|
|||||||
self.table.drop()
|
self.table.drop()
|
||||||
try:
|
try:
|
||||||
self.table.create()
|
self.table.create()
|
||||||
except sqlalchemy.exc.SQLError, e:
|
except sqlalchemy.exc.SQLError:
|
||||||
# SQLite: database schema has changed
|
# SQLite: database schema has changed
|
||||||
if not self.url.startswith('sqlite://'):
|
if not self.url.startswith('sqlite://'):
|
||||||
raise
|
raise
|
||||||
@ -621,7 +622,7 @@ class TestColumnChange(fixture.DB):
|
|||||||
if self.table.exists():
|
if self.table.exists():
|
||||||
try:
|
try:
|
||||||
self.table.drop(self.engine)
|
self.table.drop(self.engine)
|
||||||
except sqlalchemy.exc.SQLError,e:
|
except sqlalchemy.exc.SQLError:
|
||||||
# SQLite: database schema has changed
|
# SQLite: database schema has changed
|
||||||
if not self.url.startswith('sqlite://'):
|
if not self.url.startswith('sqlite://'):
|
||||||
raise
|
raise
|
||||||
@ -843,7 +844,7 @@ class TestColumnDelta(fixture.DB):
|
|||||||
|
|
||||||
def verify(self, expected, original, *p, **k):
|
def verify(self, expected, original, *p, **k):
|
||||||
self.delta = ColumnDelta(original, *p, **k)
|
self.delta = ColumnDelta(original, *p, **k)
|
||||||
result = self.delta.keys()
|
result = list(self.delta.keys())
|
||||||
result.sort()
|
result.sort()
|
||||||
self.assertEqual(expected, result)
|
self.assertEqual(expected, result)
|
||||||
return self.delta
|
return self.delta
|
||||||
|
@ -12,7 +12,7 @@ def main(imports=None):
|
|||||||
defaultTest=None
|
defaultTest=None
|
||||||
return testtools.TestProgram(defaultTest=defaultTest)
|
return testtools.TestProgram(defaultTest=defaultTest)
|
||||||
|
|
||||||
from base import Base
|
from .base import Base
|
||||||
from migrate.tests.fixture.pathed import Pathed
|
from .pathed import Pathed
|
||||||
from shell import Shell
|
from .shell import Shell
|
||||||
from database import DB,usedb
|
from .database import DB,usedb
|
||||||
|
@ -3,6 +3,9 @@
|
|||||||
|
|
||||||
import os
|
import os
|
||||||
import logging
|
import logging
|
||||||
|
import sys
|
||||||
|
|
||||||
|
import six
|
||||||
from decorator import decorator
|
from decorator import decorator
|
||||||
|
|
||||||
from sqlalchemy import create_engine, Table, MetaData
|
from sqlalchemy import create_engine, Table, MetaData
|
||||||
@ -23,7 +26,7 @@ log = logging.getLogger(__name__)
|
|||||||
def readurls():
|
def readurls():
|
||||||
"""read URLs from config file return a list"""
|
"""read URLs from config file return a list"""
|
||||||
# TODO: remove tmpfile since sqlite can store db in memory
|
# TODO: remove tmpfile since sqlite can store db in memory
|
||||||
filename = 'test_db.cfg'
|
filename = 'test_db.cfg' if six.PY2 else "test_db_py3.cfg"
|
||||||
ret = list()
|
ret = list()
|
||||||
tmpfile = Pathed.tmp()
|
tmpfile = Pathed.tmp()
|
||||||
fullpath = os.path.join(os.curdir, filename)
|
fullpath = os.path.join(os.curdir, filename)
|
||||||
@ -46,12 +49,12 @@ def is_supported(url, supported, not_supported):
|
|||||||
db = url.split(':', 1)[0]
|
db = url.split(':', 1)[0]
|
||||||
|
|
||||||
if supported is not None:
|
if supported is not None:
|
||||||
if isinstance(supported, basestring):
|
if isinstance(supported, six.string_types):
|
||||||
return supported == db
|
return supported == db
|
||||||
else:
|
else:
|
||||||
return db in supported
|
return db in supported
|
||||||
elif not_supported is not None:
|
elif not_supported is not None:
|
||||||
if isinstance(not_supported, basestring):
|
if isinstance(not_supported, six.string_types):
|
||||||
return not_supported != db
|
return not_supported != db
|
||||||
else:
|
else:
|
||||||
return not (db in not_supported)
|
return not (db in not_supported)
|
||||||
@ -96,7 +99,7 @@ def usedb(supported=None, not_supported=None):
|
|||||||
finally:
|
finally:
|
||||||
try:
|
try:
|
||||||
self._teardown()
|
self._teardown()
|
||||||
except Exception,e:
|
except Exception as e:
|
||||||
teardown_exception=e
|
teardown_exception=e
|
||||||
else:
|
else:
|
||||||
teardown_exception=None
|
teardown_exception=None
|
||||||
@ -106,14 +109,14 @@ def usedb(supported=None, not_supported=None):
|
|||||||
'setup: %r\n'
|
'setup: %r\n'
|
||||||
'teardown: %r\n'
|
'teardown: %r\n'
|
||||||
)%(setup_exception,teardown_exception))
|
)%(setup_exception,teardown_exception))
|
||||||
except Exception,e:
|
except Exception:
|
||||||
failed_for.append(url)
|
failed_for.append(url)
|
||||||
fail = True
|
fail = sys.exc_info()
|
||||||
for url in failed_for:
|
for url in failed_for:
|
||||||
log.error('Failed for %s', url)
|
log.error('Failed for %s', url)
|
||||||
if fail:
|
if fail:
|
||||||
# cause the failure :-)
|
# cause the failure :-)
|
||||||
raise
|
six.reraise(*fail)
|
||||||
return dec
|
return dec
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
#!/usr/bin/python
|
#!/usr/bin/python
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import six
|
||||||
|
|
||||||
from migrate.exceptions import *
|
from migrate.exceptions import *
|
||||||
from migrate.versioning import api
|
from migrate.versioning import api
|
||||||
|
|
||||||
@ -12,7 +14,7 @@ from migrate.tests import fixture
|
|||||||
class TestAPI(Pathed):
|
class TestAPI(Pathed):
|
||||||
|
|
||||||
def test_help(self):
|
def test_help(self):
|
||||||
self.assertTrue(isinstance(api.help('help'), basestring))
|
self.assertTrue(isinstance(api.help('help'), six.string_types))
|
||||||
self.assertRaises(UsageError, api.help)
|
self.assertRaises(UsageError, api.help)
|
||||||
self.assertRaises(UsageError, api.help, 'foobar')
|
self.assertRaises(UsageError, api.help, 'foobar')
|
||||||
self.assertTrue(isinstance(api.help('create'), str))
|
self.assertTrue(isinstance(api.help('create'), str))
|
||||||
@ -48,7 +50,7 @@ class TestAPI(Pathed):
|
|||||||
repo = self.tmp_repos()
|
repo = self.tmp_repos()
|
||||||
api.create(repo, 'temp')
|
api.create(repo, 'temp')
|
||||||
api.version_control('sqlite:///', repo)
|
api.version_control('sqlite:///', repo)
|
||||||
api.version_control('sqlite:///', unicode(repo))
|
api.version_control('sqlite:///', six.text_type(repo))
|
||||||
|
|
||||||
def test_source(self):
|
def test_source(self):
|
||||||
repo = self.tmp_repos()
|
repo = self.tmp_repos()
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
import six
|
||||||
import sqlalchemy
|
import sqlalchemy
|
||||||
from sqlalchemy import *
|
from sqlalchemy import *
|
||||||
|
|
||||||
@ -43,13 +44,12 @@ class TestSchemaDiff(fixture.DB):
|
|||||||
# so the schema diffs on the columns don't work with this test.
|
# so the schema diffs on the columns don't work with this test.
|
||||||
@fixture.usedb(not_supported='ibm_db_sa')
|
@fixture.usedb(not_supported='ibm_db_sa')
|
||||||
def test_functional(self):
|
def test_functional(self):
|
||||||
|
|
||||||
def assertDiff(isDiff, tablesMissingInDatabase, tablesMissingInModel, tablesWithDiff):
|
def assertDiff(isDiff, tablesMissingInDatabase, tablesMissingInModel, tablesWithDiff):
|
||||||
diff = schemadiff.getDiffOfModelAgainstDatabase(self.meta, self.engine, excludeTables=['migrate_version'])
|
diff = schemadiff.getDiffOfModelAgainstDatabase(self.meta, self.engine, excludeTables=['migrate_version'])
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
(diff.tables_missing_from_B,
|
(diff.tables_missing_from_B,
|
||||||
diff.tables_missing_from_A,
|
diff.tables_missing_from_A,
|
||||||
diff.tables_different.keys(),
|
list(diff.tables_different.keys()),
|
||||||
bool(diff)),
|
bool(diff)),
|
||||||
(tablesMissingInDatabase,
|
(tablesMissingInDatabase,
|
||||||
tablesMissingInModel,
|
tablesMissingInModel,
|
||||||
@ -97,10 +97,11 @@ class TestSchemaDiff(fixture.DB):
|
|||||||
diff = schemadiff.getDiffOfModelAgainstDatabase(MetaData(), self.engine, excludeTables=['migrate_version'])
|
diff = schemadiff.getDiffOfModelAgainstDatabase(MetaData(), self.engine, excludeTables=['migrate_version'])
|
||||||
src = genmodel.ModelGenerator(diff,self.engine).genBDefinition()
|
src = genmodel.ModelGenerator(diff,self.engine).genBDefinition()
|
||||||
|
|
||||||
exec src in locals()
|
namespace = {}
|
||||||
|
six.exec_(src, namespace)
|
||||||
|
|
||||||
c1 = Table('tmp_schemadiff', self.meta, autoload=True).c
|
c1 = Table('tmp_schemadiff', self.meta, autoload=True).c
|
||||||
c2 = tmp_schemadiff.c
|
c2 = namespace['tmp_schemadiff'].c
|
||||||
self.compare_columns_equal(c1, c2, ['type'])
|
self.compare_columns_equal(c1, c2, ['type'])
|
||||||
# TODO: get rid of ignoring type
|
# TODO: get rid of ignoring type
|
||||||
|
|
||||||
@ -139,19 +140,19 @@ class TestSchemaDiff(fixture.DB):
|
|||||||
decls, upgradeCommands, downgradeCommands = genmodel.ModelGenerator(diff,self.engine).genB2AMigration(indent='')
|
decls, upgradeCommands, downgradeCommands = genmodel.ModelGenerator(diff,self.engine).genB2AMigration(indent='')
|
||||||
|
|
||||||
# decls have changed since genBDefinition
|
# decls have changed since genBDefinition
|
||||||
exec decls in locals()
|
six.exec_(decls, namespace)
|
||||||
# migration commands expect a namespace containing migrate_engine
|
# migration commands expect a namespace containing migrate_engine
|
||||||
migrate_engine = self.engine
|
namespace['migrate_engine'] = self.engine
|
||||||
# run the migration up and down
|
# run the migration up and down
|
||||||
exec upgradeCommands in locals()
|
six.exec_(upgradeCommands, namespace)
|
||||||
assertDiff(False, [], [], [])
|
assertDiff(False, [], [], [])
|
||||||
|
|
||||||
exec decls in locals()
|
six.exec_(decls, namespace)
|
||||||
exec downgradeCommands in locals()
|
six.exec_(downgradeCommands, namespace)
|
||||||
assertDiff(True, [], [], [self.table_name])
|
assertDiff(True, [], [], [self.table_name])
|
||||||
|
|
||||||
exec decls in locals()
|
six.exec_(decls, namespace)
|
||||||
exec upgradeCommands in locals()
|
six.exec_(upgradeCommands, namespace)
|
||||||
assertDiff(False, [], [], [])
|
assertDiff(False, [], [], [])
|
||||||
|
|
||||||
if not self.engine.name == 'oracle':
|
if not self.engine.name == 'oracle':
|
||||||
|
@ -111,7 +111,6 @@ class TestVersionedRepository(fixture.Pathed):
|
|||||||
# Create a script and test again
|
# Create a script and test again
|
||||||
now = int(datetime.utcnow().strftime('%Y%m%d%H%M%S'))
|
now = int(datetime.utcnow().strftime('%Y%m%d%H%M%S'))
|
||||||
repos.create_script('')
|
repos.create_script('')
|
||||||
print repos.latest
|
|
||||||
self.assertEqual(repos.latest, now)
|
self.assertEqual(repos.latest, now)
|
||||||
|
|
||||||
def test_source(self):
|
def test_source(self):
|
||||||
|
@ -4,6 +4,8 @@
|
|||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
|
|
||||||
|
import six
|
||||||
|
|
||||||
from migrate import exceptions
|
from migrate import exceptions
|
||||||
from migrate.versioning.schema import *
|
from migrate.versioning.schema import *
|
||||||
from migrate.versioning import script, schemadiff
|
from migrate.versioning import script, schemadiff
|
||||||
@ -163,10 +165,10 @@ class TestControlledSchema(fixture.Pathed, fixture.DB):
|
|||||||
def test_create_model(self):
|
def test_create_model(self):
|
||||||
"""Test workflow to generate create_model"""
|
"""Test workflow to generate create_model"""
|
||||||
model = ControlledSchema.create_model(self.engine, self.repos, declarative=False)
|
model = ControlledSchema.create_model(self.engine, self.repos, declarative=False)
|
||||||
self.assertTrue(isinstance(model, basestring))
|
self.assertTrue(isinstance(model, six.string_types))
|
||||||
|
|
||||||
model = ControlledSchema.create_model(self.engine, self.repos.path, declarative=True)
|
model = ControlledSchema.create_model(self.engine, self.repos.path, declarative=True)
|
||||||
self.assertTrue(isinstance(model, basestring))
|
self.assertTrue(isinstance(model, six.string_types))
|
||||||
|
|
||||||
@fixture.usedb()
|
@fixture.usedb()
|
||||||
def test_compare_model_to_db(self):
|
def test_compare_model_to_db(self):
|
||||||
|
@ -27,9 +27,9 @@ class SchemaDiffBase(fixture.DB):
|
|||||||
# print diff
|
# print diff
|
||||||
self.assertTrue(diff)
|
self.assertTrue(diff)
|
||||||
self.assertEqual(1,len(diff.tables_different))
|
self.assertEqual(1,len(diff.tables_different))
|
||||||
td = diff.tables_different.values()[0]
|
td = list(diff.tables_different.values())[0]
|
||||||
self.assertEqual(1,len(td.columns_different))
|
self.assertEqual(1,len(td.columns_different))
|
||||||
cd = td.columns_different.values()[0]
|
cd = list(td.columns_different.values())[0]
|
||||||
label_width = max(len(self.name1), len(self.name2))
|
label_width = max(len(self.name1), len(self.name2))
|
||||||
self.assertEqual(('Schema diffs:\n'
|
self.assertEqual(('Schema diffs:\n'
|
||||||
' table with differences: xtable\n'
|
' table with differences: xtable\n'
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import imp
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import shutil
|
import shutil
|
||||||
|
|
||||||
|
import six
|
||||||
from migrate import exceptions
|
from migrate import exceptions
|
||||||
from migrate.versioning import version, repository
|
from migrate.versioning import version, repository
|
||||||
from migrate.versioning.script import *
|
from migrate.versioning.script import *
|
||||||
@ -51,6 +53,9 @@ class TestPyScript(fixture.Pathed, fixture.DB):
|
|||||||
self.assertRaises(exceptions.ScriptError, pyscript._func, 'foobar')
|
self.assertRaises(exceptions.ScriptError, pyscript._func, 'foobar')
|
||||||
|
|
||||||
# clean pyc file
|
# clean pyc file
|
||||||
|
if six.PY3:
|
||||||
|
os.remove(imp.cache_from_source(script_path))
|
||||||
|
else:
|
||||||
os.remove(script_path + 'c')
|
os.remove(script_path + 'c')
|
||||||
|
|
||||||
# test deprecated upgrade/downgrade with no arguments
|
# test deprecated upgrade/downgrade with no arguments
|
||||||
@ -94,7 +99,7 @@ class TestPyScript(fixture.Pathed, fixture.DB):
|
|||||||
path = self.tmp_py()
|
path = self.tmp_py()
|
||||||
# Create empty file
|
# Create empty file
|
||||||
f = open(path, 'w')
|
f = open(path, 'w')
|
||||||
f.write("def zergling():\n\tprint 'rush'")
|
f.write("def zergling():\n\tprint('rush')")
|
||||||
f.close()
|
f.close()
|
||||||
self.assertRaises(exceptions.InvalidScriptError, self.cls.verify_module, path)
|
self.assertRaises(exceptions.InvalidScriptError, self.cls.verify_module, path)
|
||||||
# script isn't verified on creation, but on module reference
|
# script isn't verified on creation, but on module reference
|
||||||
|
@ -5,7 +5,8 @@ import os
|
|||||||
import sys
|
import sys
|
||||||
import tempfile
|
import tempfile
|
||||||
|
|
||||||
from cStringIO import StringIO
|
import six
|
||||||
|
from six.moves import cStringIO
|
||||||
from sqlalchemy import MetaData, Table
|
from sqlalchemy import MetaData, Table
|
||||||
|
|
||||||
from migrate.exceptions import *
|
from migrate.exceptions import *
|
||||||
@ -29,7 +30,7 @@ class TestShellCommands(Shell):
|
|||||||
# we can only test that we get some output
|
# we can only test that we get some output
|
||||||
for cmd in api.__all__:
|
for cmd in api.__all__:
|
||||||
result = self.env.run('migrate help %s' % cmd)
|
result = self.env.run('migrate help %s' % cmd)
|
||||||
self.assertTrue(isinstance(result.stdout, basestring))
|
self.assertTrue(isinstance(result.stdout, six.string_types))
|
||||||
self.assertTrue(result.stdout)
|
self.assertTrue(result.stdout)
|
||||||
self.assertFalse(result.stderr)
|
self.assertFalse(result.stderr)
|
||||||
|
|
||||||
@ -61,11 +62,11 @@ class TestShellCommands(Shell):
|
|||||||
def _check_error(self,args,code,expected,**kw):
|
def _check_error(self,args,code,expected,**kw):
|
||||||
original = sys.stderr
|
original = sys.stderr
|
||||||
try:
|
try:
|
||||||
actual = StringIO()
|
actual = cStringIO()
|
||||||
sys.stderr = actual
|
sys.stderr = actual
|
||||||
try:
|
try:
|
||||||
shell.main(args,**kw)
|
shell.main(args,**kw)
|
||||||
except SystemExit, e:
|
except SystemExit as e:
|
||||||
self.assertEqual(code,e.args[0])
|
self.assertEqual(code,e.args[0])
|
||||||
else:
|
else:
|
||||||
self.fail('No exception raised')
|
self.fail('No exception raised')
|
||||||
@ -502,7 +503,7 @@ class TestShellDatabase(Shell, DB):
|
|||||||
|
|
||||||
result = self.env.run('migrate create_model %s %s' % (self.url, repos_path))
|
result = self.env.run('migrate create_model %s %s' % (self.url, repos_path))
|
||||||
temp_dict = dict()
|
temp_dict = dict()
|
||||||
exec result.stdout in temp_dict
|
six.exec_(result.stdout, temp_dict)
|
||||||
|
|
||||||
# TODO: breaks on SA06 and SA05 - in need of total refactor - use different approach
|
# TODO: breaks on SA06 and SA05 - in need of total refactor - use different approach
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
Configuration parser module.
|
Configuration parser module.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from ConfigParser import ConfigParser
|
from six.moves.configparser import ConfigParser
|
||||||
|
|
||||||
from migrate.versioning.config import *
|
from migrate.versioning.config import *
|
||||||
from migrate.versioning import pathed
|
from migrate.versioning import pathed
|
||||||
|
@ -9,6 +9,7 @@ http://code.google.com/p/sqlautocode/
|
|||||||
import sys
|
import sys
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
import six
|
||||||
import sqlalchemy
|
import sqlalchemy
|
||||||
|
|
||||||
import migrate
|
import migrate
|
||||||
@ -68,6 +69,9 @@ class ModelGenerator(object):
|
|||||||
|
|
||||||
# crs: not sure if this is good idea, but it gets rid of extra
|
# crs: not sure if this is good idea, but it gets rid of extra
|
||||||
# u''
|
# u''
|
||||||
|
if six.PY3:
|
||||||
|
name = col.name
|
||||||
|
else:
|
||||||
name = col.name.encode('utf8')
|
name = col.name.encode('utf8')
|
||||||
|
|
||||||
type_ = col.type
|
type_ = col.type
|
||||||
@ -192,7 +196,7 @@ class ModelGenerator(object):
|
|||||||
downgradeCommands.append(
|
downgradeCommands.append(
|
||||||
"post_meta.tables[%(table)r].drop()" % {'table': tn})
|
"post_meta.tables[%(table)r].drop()" % {'table': tn})
|
||||||
|
|
||||||
for (tn, td) in self.diff.tables_different.iteritems():
|
for (tn, td) in six.iteritems(self.diff.tables_different):
|
||||||
if td.columns_missing_from_A or td.columns_different:
|
if td.columns_missing_from_A or td.columns_different:
|
||||||
pre_table = self.diff.metadataB.tables[tn]
|
pre_table = self.diff.metadataB.tables[tn]
|
||||||
decls.extend(self._getTableDefn(
|
decls.extend(self._getTableDefn(
|
||||||
|
@ -43,7 +43,7 @@ class Changeset(dict):
|
|||||||
"""
|
"""
|
||||||
In a series of upgrades x -> y, keys are version x. Sorted.
|
In a series of upgrades x -> y, keys are version x. Sorted.
|
||||||
"""
|
"""
|
||||||
ret = super(Changeset, self).keys()
|
ret = list(super(Changeset, self).keys())
|
||||||
# Reverse order if downgrading
|
# Reverse order if downgrading
|
||||||
ret.sort(reverse=(self.step < 1))
|
ret.sort(reverse=(self.step < 1))
|
||||||
return ret
|
return ret
|
||||||
@ -94,7 +94,7 @@ class Repository(pathed.Pathed):
|
|||||||
cls.require_found(path)
|
cls.require_found(path)
|
||||||
cls.require_found(os.path.join(path, cls._config))
|
cls.require_found(os.path.join(path, cls._config))
|
||||||
cls.require_found(os.path.join(path, cls._versions))
|
cls.require_found(os.path.join(path, cls._versions))
|
||||||
except exceptions.PathNotFoundError, e:
|
except exceptions.PathNotFoundError:
|
||||||
raise exceptions.InvalidRepositoryError(path)
|
raise exceptions.InvalidRepositoryError(path)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -221,7 +221,7 @@ class Repository(pathed.Pathed):
|
|||||||
range_mod = 0
|
range_mod = 0
|
||||||
op = 'downgrade'
|
op = 'downgrade'
|
||||||
|
|
||||||
versions = range(start + range_mod, end + range_mod, step)
|
versions = range(int(start) + range_mod, int(end) + range_mod, step)
|
||||||
changes = [self.version(v).script(database, op) for v in versions]
|
changes = [self.version(v).script(database, op) for v in versions]
|
||||||
ret = Changeset(start, step=step, *changes)
|
ret = Changeset(start, step=step, *changes)
|
||||||
return ret
|
return ret
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
import sys
|
import sys
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
import six
|
||||||
from sqlalchemy import (Table, Column, MetaData, String, Text, Integer,
|
from sqlalchemy import (Table, Column, MetaData, String, Text, Integer,
|
||||||
create_engine)
|
create_engine)
|
||||||
from sqlalchemy.sql import and_
|
from sqlalchemy.sql import and_
|
||||||
@ -24,7 +25,7 @@ class ControlledSchema(object):
|
|||||||
"""A database under version control"""
|
"""A database under version control"""
|
||||||
|
|
||||||
def __init__(self, engine, repository):
|
def __init__(self, engine, repository):
|
||||||
if isinstance(repository, basestring):
|
if isinstance(repository, six.string_types):
|
||||||
repository = Repository(repository)
|
repository = Repository(repository)
|
||||||
self.engine = engine
|
self.engine = engine
|
||||||
self.repository = repository
|
self.repository = repository
|
||||||
@ -49,7 +50,8 @@ class ControlledSchema(object):
|
|||||||
data = list(result)[0]
|
data = list(result)[0]
|
||||||
except:
|
except:
|
||||||
cls, exc, tb = sys.exc_info()
|
cls, exc, tb = sys.exc_info()
|
||||||
raise exceptions.DatabaseNotControlledError, exc.__str__(), tb
|
six.reraise(exceptions.DatabaseNotControlledError,
|
||||||
|
exceptions.DatabaseNotControlledError(str(exc)), tb)
|
||||||
|
|
||||||
self.version = data['version']
|
self.version = data['version']
|
||||||
return data
|
return data
|
||||||
@ -133,7 +135,7 @@ class ControlledSchema(object):
|
|||||||
"""
|
"""
|
||||||
# Confirm that the version # is valid: positive, integer,
|
# Confirm that the version # is valid: positive, integer,
|
||||||
# exists in repos
|
# exists in repos
|
||||||
if isinstance(repository, basestring):
|
if isinstance(repository, six.string_types):
|
||||||
repository = Repository(repository)
|
repository = Repository(repository)
|
||||||
version = cls._validate_version(repository, version)
|
version = cls._validate_version(repository, version)
|
||||||
table = cls._create_table_version(engine, repository, version)
|
table = cls._create_table_version(engine, repository, version)
|
||||||
@ -198,7 +200,7 @@ class ControlledSchema(object):
|
|||||||
"""
|
"""
|
||||||
Compare the current model against the current database.
|
Compare the current model against the current database.
|
||||||
"""
|
"""
|
||||||
if isinstance(repository, basestring):
|
if isinstance(repository, six.string_types):
|
||||||
repository = Repository(repository)
|
repository = Repository(repository)
|
||||||
model = load_model(model)
|
model = load_model(model)
|
||||||
|
|
||||||
@ -211,7 +213,7 @@ class ControlledSchema(object):
|
|||||||
"""
|
"""
|
||||||
Dump the current database as a Python model.
|
Dump the current database as a Python model.
|
||||||
"""
|
"""
|
||||||
if isinstance(repository, basestring):
|
if isinstance(repository, six.string_types):
|
||||||
repository = Repository(repository)
|
repository = Repository(repository)
|
||||||
|
|
||||||
diff = schemadiff.getDiffOfModelAgainstDatabase(
|
diff = schemadiff.getDiffOfModelAgainstDatabase(
|
||||||
|
@ -99,6 +99,9 @@ class ColDiff(object):
|
|||||||
def __nonzero__(self):
|
def __nonzero__(self):
|
||||||
return self.diff
|
return self.diff
|
||||||
|
|
||||||
|
__bool__ = __nonzero__
|
||||||
|
|
||||||
|
|
||||||
class TableDiff(object):
|
class TableDiff(object):
|
||||||
"""
|
"""
|
||||||
Container for differences in one :class:`~sqlalchemy.schema.Table`
|
Container for differences in one :class:`~sqlalchemy.schema.Table`
|
||||||
@ -135,6 +138,8 @@ class TableDiff(object):
|
|||||||
self.columns_different
|
self.columns_different
|
||||||
)
|
)
|
||||||
|
|
||||||
|
__bool__ = __nonzero__
|
||||||
|
|
||||||
class SchemaDiff(object):
|
class SchemaDiff(object):
|
||||||
"""
|
"""
|
||||||
Compute the difference between two :class:`~sqlalchemy.schema.MetaData`
|
Compute the difference between two :class:`~sqlalchemy.schema.MetaData`
|
||||||
|
@ -5,7 +5,6 @@ import shutil
|
|||||||
import warnings
|
import warnings
|
||||||
import logging
|
import logging
|
||||||
import inspect
|
import inspect
|
||||||
from StringIO import StringIO
|
|
||||||
|
|
||||||
import migrate
|
import migrate
|
||||||
from migrate.versioning import genmodel, schemadiff
|
from migrate.versioning import genmodel, schemadiff
|
||||||
@ -14,6 +13,8 @@ from migrate.versioning.template import Template
|
|||||||
from migrate.versioning.script import base
|
from migrate.versioning.script import base
|
||||||
from migrate.versioning.util import import_path, load_model, with_engine
|
from migrate.versioning.util import import_path, load_model, with_engine
|
||||||
from migrate.exceptions import MigrateDeprecationWarning, InvalidScriptError, ScriptError
|
from migrate.exceptions import MigrateDeprecationWarning, InvalidScriptError, ScriptError
|
||||||
|
import six
|
||||||
|
from six.moves import StringIO
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
__all__ = ['PythonScript']
|
__all__ = ['PythonScript']
|
||||||
@ -51,7 +52,7 @@ class PythonScript(base.BaseScript):
|
|||||||
:rtype: string
|
:rtype: string
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if isinstance(repository, basestring):
|
if isinstance(repository, six.string_types):
|
||||||
# oh dear, an import cycle!
|
# oh dear, an import cycle!
|
||||||
from migrate.versioning.repository import Repository
|
from migrate.versioning.repository import Repository
|
||||||
repository = Repository(repository)
|
repository = Repository(repository)
|
||||||
@ -96,7 +97,7 @@ class PythonScript(base.BaseScript):
|
|||||||
module = import_path(path)
|
module = import_path(path)
|
||||||
try:
|
try:
|
||||||
assert callable(module.upgrade)
|
assert callable(module.upgrade)
|
||||||
except Exception, e:
|
except Exception as e:
|
||||||
raise InvalidScriptError(path + ': %s' % str(e))
|
raise InvalidScriptError(path + ': %s' % str(e))
|
||||||
return module
|
return module
|
||||||
|
|
||||||
@ -127,7 +128,9 @@ class PythonScript(base.BaseScript):
|
|||||||
:type engine: string
|
:type engine: string
|
||||||
:type step: int
|
:type step: int
|
||||||
"""
|
"""
|
||||||
if step > 0:
|
if step in ('downgrade', 'upgrade'):
|
||||||
|
op = step
|
||||||
|
elif step > 0:
|
||||||
op = 'upgrade'
|
op = 'upgrade'
|
||||||
elif step < 0:
|
elif step < 0:
|
||||||
op = 'downgrade'
|
op = 'downgrade'
|
||||||
|
@ -12,6 +12,7 @@ from migrate import exceptions
|
|||||||
from migrate.versioning import api
|
from migrate.versioning import api
|
||||||
from migrate.versioning.config import *
|
from migrate.versioning.config import *
|
||||||
from migrate.versioning.util import asbool
|
from migrate.versioning.util import asbool
|
||||||
|
import six
|
||||||
|
|
||||||
|
|
||||||
alias = dict(
|
alias = dict(
|
||||||
@ -23,7 +24,7 @@ alias = dict(
|
|||||||
|
|
||||||
def alias_setup():
|
def alias_setup():
|
||||||
global alias
|
global alias
|
||||||
for key, val in alias.iteritems():
|
for key, val in six.iteritems(alias):
|
||||||
setattr(api, key, val)
|
setattr(api, key, val)
|
||||||
alias_setup()
|
alias_setup()
|
||||||
|
|
||||||
@ -135,7 +136,7 @@ def main(argv=None, **kwargs):
|
|||||||
override_kwargs[opt] = value
|
override_kwargs[opt] = value
|
||||||
|
|
||||||
# override kwargs with options if user is overwriting
|
# override kwargs with options if user is overwriting
|
||||||
for key, value in options.__dict__.iteritems():
|
for key, value in six.iteritems(options.__dict__):
|
||||||
if value is not None:
|
if value is not None:
|
||||||
override_kwargs[key] = value
|
override_kwargs[key] = value
|
||||||
|
|
||||||
@ -143,7 +144,7 @@ def main(argv=None, **kwargs):
|
|||||||
f_required = list(f_args)
|
f_required = list(f_args)
|
||||||
candidates = dict(kwargs)
|
candidates = dict(kwargs)
|
||||||
candidates.update(override_kwargs)
|
candidates.update(override_kwargs)
|
||||||
for key, value in candidates.iteritems():
|
for key, value in six.iteritems(candidates):
|
||||||
if key in f_args:
|
if key in f_args:
|
||||||
f_required.remove(key)
|
f_required.remove(key)
|
||||||
|
|
||||||
@ -160,7 +161,7 @@ def main(argv=None, **kwargs):
|
|||||||
kwargs.update(override_kwargs)
|
kwargs.update(override_kwargs)
|
||||||
|
|
||||||
# configure options
|
# configure options
|
||||||
for key, value in options.__dict__.iteritems():
|
for key, value in six.iteritems(options.__dict__):
|
||||||
kwargs.setdefault(key, value)
|
kwargs.setdefault(key, value)
|
||||||
|
|
||||||
# configure logging
|
# configure logging
|
||||||
@ -198,6 +199,7 @@ def main(argv=None, **kwargs):
|
|||||||
num_defaults = 0
|
num_defaults = 0
|
||||||
f_args_default = f_args[len(f_args) - num_defaults:]
|
f_args_default = f_args[len(f_args) - num_defaults:]
|
||||||
required = list(set(f_required) - set(f_args_default))
|
required = list(set(f_required) - set(f_args_default))
|
||||||
|
required.sort()
|
||||||
if required:
|
if required:
|
||||||
parser.error("Not enough arguments for command %s: %s not specified" \
|
parser.error("Not enough arguments for command %s: %s not specified" \
|
||||||
% (command, ', '.join(required)))
|
% (command, ', '.join(required)))
|
||||||
@ -207,7 +209,7 @@ def main(argv=None, **kwargs):
|
|||||||
ret = command_func(**kwargs)
|
ret = command_func(**kwargs)
|
||||||
if ret is not None:
|
if ret is not None:
|
||||||
log.info(ret)
|
log.info(ret)
|
||||||
except (exceptions.UsageError, exceptions.KnownError), e:
|
except (exceptions.UsageError, exceptions.KnownError) as e:
|
||||||
parser.error(e.args[0])
|
parser.error(e.args[0])
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
@ -2,10 +2,11 @@
|
|||||||
from migrate.versioning.shell import main
|
from migrate.versioning.shell import main
|
||||||
|
|
||||||
{{py:
|
{{py:
|
||||||
|
import six
|
||||||
_vars = locals().copy()
|
_vars = locals().copy()
|
||||||
del _vars['__template_name__']
|
del _vars['__template_name__']
|
||||||
_vars.pop('repository_name', None)
|
_vars.pop('repository_name', None)
|
||||||
defaults = ", ".join(["%s='%s'" % var for var in _vars.iteritems()])
|
defaults = ", ".join(["%s='%s'" % var for var in six.iteritems(_vars)])
|
||||||
}}
|
}}
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
@ -17,9 +17,10 @@ else:
|
|||||||
conf_path = 'development.ini'
|
conf_path = 'development.ini'
|
||||||
|
|
||||||
{{py:
|
{{py:
|
||||||
|
import six
|
||||||
_vars = locals().copy()
|
_vars = locals().copy()
|
||||||
del _vars['__template_name__']
|
del _vars['__template_name__']
|
||||||
defaults = ", ".join(["%s='%s'" % var for var in _vars.iteritems()])
|
defaults = ", ".join(["%s='%s'" % var for var in six.iteritems(_vars)])
|
||||||
}}
|
}}
|
||||||
|
|
||||||
conf_dict = ConfigLoader(conf_path).parser._sections['app:main']
|
conf_dict = ConfigLoader(conf_path).parser._sections['app:main']
|
||||||
|
@ -7,6 +7,7 @@ import logging
|
|||||||
from decorator import decorator
|
from decorator import decorator
|
||||||
from pkg_resources import EntryPoint
|
from pkg_resources import EntryPoint
|
||||||
|
|
||||||
|
import six
|
||||||
from sqlalchemy import create_engine
|
from sqlalchemy import create_engine
|
||||||
from sqlalchemy.engine import Engine
|
from sqlalchemy.engine import Engine
|
||||||
from sqlalchemy.pool import StaticPool
|
from sqlalchemy.pool import StaticPool
|
||||||
@ -26,7 +27,7 @@ def load_model(dotted_name):
|
|||||||
.. versionchanged:: 0.5.4
|
.. versionchanged:: 0.5.4
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if isinstance(dotted_name, basestring):
|
if isinstance(dotted_name, six.string_types):
|
||||||
if ':' not in dotted_name:
|
if ':' not in dotted_name:
|
||||||
# backwards compatibility
|
# backwards compatibility
|
||||||
warnings.warn('model should be in form of module.model:User '
|
warnings.warn('model should be in form of module.model:User '
|
||||||
@ -39,7 +40,7 @@ def load_model(dotted_name):
|
|||||||
|
|
||||||
def asbool(obj):
|
def asbool(obj):
|
||||||
"""Do everything to use object as bool"""
|
"""Do everything to use object as bool"""
|
||||||
if isinstance(obj, basestring):
|
if isinstance(obj, six.string_types):
|
||||||
obj = obj.strip().lower()
|
obj = obj.strip().lower()
|
||||||
if obj in ['true', 'yes', 'on', 'y', 't', '1']:
|
if obj in ['true', 'yes', 'on', 'y', 't', '1']:
|
||||||
return True
|
return True
|
||||||
@ -87,7 +88,7 @@ def catch_known_errors(f, *a, **kw):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
return f(*a, **kw)
|
return f(*a, **kw)
|
||||||
except exceptions.PathFoundError, e:
|
except exceptions.PathFoundError as e:
|
||||||
raise exceptions.KnownError("The path %s already exists" % e.args[0])
|
raise exceptions.KnownError("The path %s already exists" % e.args[0])
|
||||||
|
|
||||||
def construct_engine(engine, **opts):
|
def construct_engine(engine, **opts):
|
||||||
@ -112,7 +113,7 @@ def construct_engine(engine, **opts):
|
|||||||
"""
|
"""
|
||||||
if isinstance(engine, Engine):
|
if isinstance(engine, Engine):
|
||||||
return engine
|
return engine
|
||||||
elif not isinstance(engine, basestring):
|
elif not isinstance(engine, six.string_types):
|
||||||
raise ValueError("you need to pass either an existing engine or a database uri")
|
raise ValueError("you need to pass either an existing engine or a database uri")
|
||||||
|
|
||||||
# get options for create_engine
|
# get options for create_engine
|
||||||
@ -130,7 +131,7 @@ def construct_engine(engine, **opts):
|
|||||||
kwargs['echo'] = echo
|
kwargs['echo'] = echo
|
||||||
|
|
||||||
# parse keyword arguments
|
# parse keyword arguments
|
||||||
for key, value in opts.iteritems():
|
for key, value in six.iteritems(opts):
|
||||||
if key.startswith('engine_arg_'):
|
if key.startswith('engine_arg_'):
|
||||||
kwargs[key[11:]] = guess_obj_type(value)
|
kwargs[key[11:]] = guess_obj_type(value)
|
||||||
|
|
||||||
@ -174,6 +175,6 @@ class Memoize:
|
|||||||
self.memo = {}
|
self.memo = {}
|
||||||
|
|
||||||
def __call__(self, *args):
|
def __call__(self, *args):
|
||||||
if not self.memo.has_key(args):
|
if args not in self.memo:
|
||||||
self.memo[args] = self.fn(*args)
|
self.memo[args] = self.fn(*args)
|
||||||
return self.memo[args]
|
return self.memo[args]
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
from six.moves import reload_module as reload
|
||||||
|
|
||||||
def import_path(fullpath):
|
def import_path(fullpath):
|
||||||
""" Import a file with full path specification. Allows one to
|
""" Import a file with full path specification. Allows one to
|
||||||
import from anywhere, something __import__ does not do.
|
import from anywhere, something __import__ does not do.
|
||||||
|
@ -9,6 +9,7 @@ import logging
|
|||||||
from migrate import exceptions
|
from migrate import exceptions
|
||||||
from migrate.versioning import pathed, script
|
from migrate.versioning import pathed, script
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
import six
|
||||||
|
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
@ -64,6 +65,10 @@ class VerNum(object):
|
|||||||
def __int__(self):
|
def __int__(self):
|
||||||
return int(self.value)
|
return int(self.value)
|
||||||
|
|
||||||
|
if six.PY3:
|
||||||
|
def __hash__(self):
|
||||||
|
return hash(self.value)
|
||||||
|
|
||||||
|
|
||||||
class Collection(pathed.Pathed):
|
class Collection(pathed.Pathed):
|
||||||
"""A collection of versioning scripts in a repository"""
|
"""A collection of versioning scripts in a repository"""
|
||||||
@ -102,7 +107,7 @@ class Collection(pathed.Pathed):
|
|||||||
@property
|
@property
|
||||||
def latest(self):
|
def latest(self):
|
||||||
""":returns: Latest version in Collection"""
|
""":returns: Latest version in Collection"""
|
||||||
return max([VerNum(0)] + self.versions.keys())
|
return max([VerNum(0)] + list(self.versions.keys()))
|
||||||
|
|
||||||
def _next_ver_num(self, use_timestamp_numbering):
|
def _next_ver_num(self, use_timestamp_numbering):
|
||||||
if use_timestamp_numbering == True:
|
if use_timestamp_numbering == True:
|
||||||
|
2
test-requirements-py2.txt
Normal file
2
test-requirements-py2.txt
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
ibm_db_sa>=0.3.0
|
||||||
|
MySQL-python
|
1
test-requirements-py3.txt
Normal file
1
test-requirements-py3.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
ibm-db-sa-py3
|
@ -8,21 +8,17 @@ coverage>=3.6
|
|||||||
discover
|
discover
|
||||||
feedparser
|
feedparser
|
||||||
fixtures>=0.3.14
|
fixtures>=0.3.14
|
||||||
ibm_db_sa>=0.3.0
|
|
||||||
mock>=1.0
|
mock>=1.0
|
||||||
mox>=0.5.3
|
mox>=0.5.3
|
||||||
MySQL-python
|
|
||||||
psycopg2
|
psycopg2
|
||||||
pylint==0.25.2
|
|
||||||
python-subunit>=0.0.18
|
python-subunit>=0.0.18
|
||||||
sphinx>=1.1.2,<1.2
|
sphinx>=1.1.2,<1.2
|
||||||
sphinxcontrib_issuetracker
|
sphinxcontrib_issuetracker
|
||||||
testrepository>=0.0.17
|
testrepository>=0.0.17
|
||||||
testtools>=0.9.34
|
testtools>=0.9.34
|
||||||
|
|
||||||
# NOTE: scripttest 1.0.1 removes base_path argument to ScriptTest
|
scripttest
|
||||||
scripttest==1.0
|
|
||||||
# NOTE(rpodolyaka): This version identifier is currently necessary as
|
# NOTE(rpodolyaka): This version identifier is currently necessary as
|
||||||
# pytz otherwise does not install on pip 1.4 or higher
|
# pytz otherwise does not install on pip 1.4 or higher
|
||||||
|
pylint
|
||||||
pytz>=2010h
|
pytz>=2010h
|
||||||
pysqlite
|
|
||||||
|
15
test_db_py3.cfg
Normal file
15
test_db_py3.cfg
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
# test_db.cfg
|
||||||
|
#
|
||||||
|
# This file contains a list of connection strings which will be used by
|
||||||
|
# database tests. Tests will be executed once for each string in this file.
|
||||||
|
# You should be sure that the database used for the test doesn't contain any
|
||||||
|
# important data. See README for more information.
|
||||||
|
#
|
||||||
|
# The string '__tmp__' is substituted for a temporary file in each connection
|
||||||
|
# string. This is useful for sqlite tests.
|
||||||
|
sqlite:///__tmp__
|
||||||
|
#postgresql://openstack_citest:openstack_citest@localhost/openstack_citest
|
||||||
|
#mysql://openstack_citest:openstack_citest@localhost/openstack_citest
|
||||||
|
#oracle://scott:tiger@localhost
|
||||||
|
#firebird://scott:tiger@localhost//var/lib/firebird/databases/test_migrate
|
||||||
|
#ibm_db_sa://migrate:migrate@localhost:50000/migrate
|
13
tox.ini
13
tox.ini
@ -15,40 +15,53 @@ commands =
|
|||||||
[testenv:py26]
|
[testenv:py26]
|
||||||
deps = sqlalchemy>=0.9
|
deps = sqlalchemy>=0.9
|
||||||
-r{toxinidir}/test-requirements.txt
|
-r{toxinidir}/test-requirements.txt
|
||||||
|
-r{toxinidir}/test-requirements-py2.txt
|
||||||
|
|
||||||
[testenv:py27]
|
[testenv:py27]
|
||||||
deps = sqlalchemy>=0.9
|
deps = sqlalchemy>=0.9
|
||||||
-r{toxinidir}/test-requirements.txt
|
-r{toxinidir}/test-requirements.txt
|
||||||
|
-r{toxinidir}/test-requirements-py2.txt
|
||||||
|
|
||||||
[testenv:py26sa07]
|
[testenv:py26sa07]
|
||||||
basepython = python2.6
|
basepython = python2.6
|
||||||
deps = sqlalchemy>=0.7,<=0.7.99
|
deps = sqlalchemy>=0.7,<=0.7.99
|
||||||
-r{toxinidir}/test-requirements.txt
|
-r{toxinidir}/test-requirements.txt
|
||||||
|
-r{toxinidir}/test-requirements-py2.txt
|
||||||
|
|
||||||
[testenv:py26sa08]
|
[testenv:py26sa08]
|
||||||
basepython = python2.6
|
basepython = python2.6
|
||||||
deps = sqlalchemy>=0.8,<=0.8.99
|
deps = sqlalchemy>=0.8,<=0.8.99
|
||||||
-r{toxinidir}/test-requirements.txt
|
-r{toxinidir}/test-requirements.txt
|
||||||
|
-r{toxinidir}/test-requirements-py2.txt
|
||||||
|
|
||||||
[testenv:py26sa09]
|
[testenv:py26sa09]
|
||||||
basepython = python2.6
|
basepython = python2.6
|
||||||
deps = sqlalchemy>=0.9,<=0.9.99
|
deps = sqlalchemy>=0.9,<=0.9.99
|
||||||
-r{toxinidir}/test-requirements.txt
|
-r{toxinidir}/test-requirements.txt
|
||||||
|
-r{toxinidir}/test-requirements-py2.txt
|
||||||
|
|
||||||
[testenv:py27sa07]
|
[testenv:py27sa07]
|
||||||
basepython = python2.7
|
basepython = python2.7
|
||||||
deps = sqlalchemy>=0.7,<=0.7.99
|
deps = sqlalchemy>=0.7,<=0.7.99
|
||||||
-r{toxinidir}/test-requirements.txt
|
-r{toxinidir}/test-requirements.txt
|
||||||
|
-r{toxinidir}/test-requirements-py2.txt
|
||||||
|
|
||||||
[testenv:py27sa08]
|
[testenv:py27sa08]
|
||||||
basepython = python2.7
|
basepython = python2.7
|
||||||
deps = sqlalchemy>=0.8,<=0.8.99
|
deps = sqlalchemy>=0.8,<=0.8.99
|
||||||
-r{toxinidir}/test-requirements.txt
|
-r{toxinidir}/test-requirements.txt
|
||||||
|
-r{toxinidir}/test-requirements-py2.txt
|
||||||
|
|
||||||
[testenv:py27sa09]
|
[testenv:py27sa09]
|
||||||
basepython = python2.7
|
basepython = python2.7
|
||||||
deps = sqlalchemy>=0.9,<=0.9.99
|
deps = sqlalchemy>=0.9,<=0.9.99
|
||||||
-r{toxinidir}/test-requirements.txt
|
-r{toxinidir}/test-requirements.txt
|
||||||
|
-r{toxinidir}/test-requirements-py2.txt
|
||||||
|
|
||||||
|
[testenv:py33]
|
||||||
|
deps = sqlalchemy>=0.9
|
||||||
|
-r{toxinidir}/test-requirements.txt
|
||||||
|
-r{toxinidir}/test-requirements-py3.txt
|
||||||
|
|
||||||
[testenv:pep8]
|
[testenv:pep8]
|
||||||
commands = flake8
|
commands = flake8
|
||||||
|
Loading…
x
Reference in New Issue
Block a user