summaryrefslogtreecommitdiff
path: root/migrate/changeset
diff options
context:
space:
mode:
authorCyril Roelandt <cyril.roelandt@enovance.com>2014-03-19 15:02:40 +0100
committerCyril Roelandt <cyril.roelandt@enovance.com>2014-04-09 17:32:52 +0200
commita03b141a954c7e644f0033defdb1b5b434a7c49a (patch)
tree3515ecc8ab1b2a2218b0fa60aec04de09def8ce5 /migrate/changeset
parent07909159ae22dc0d399b9618dcf0f79a1d0332bf (diff)
downloadsqalchemy-migrate-a03b141a954c7e644f0033defdb1b5b434a7c49a.tar.gz
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>
Diffstat (limited to 'migrate/changeset')
-rw-r--r--migrate/changeset/ansisql.py7
-rw-r--r--migrate/changeset/databases/sqlite.py5
-rw-r--r--migrate/changeset/schema.py58
3 files changed, 58 insertions, 12 deletions
diff --git a/migrate/changeset/ansisql.py b/migrate/changeset/ansisql.py
index b4509ae..a18d4ed 100644
--- a/migrate/changeset/ansisql.py
+++ b/migrate/changeset/ansisql.py
@@ -4,7 +4,6 @@
At the moment, this isn't so much based off of ANSI as much as
things that just happen to work with multiple databases.
"""
-import StringIO
import sqlalchemy as sa
from sqlalchemy.schema import SchemaVisitor
@@ -20,6 +19,7 @@ from migrate import exceptions
import sqlalchemy.sql.compiler
from migrate.changeset import constraint
from migrate.changeset import util
+from six.moves import StringIO
from sqlalchemy.schema import AddConstraint, DropConstraint
from sqlalchemy.sql.compiler import DDLCompiler
@@ -43,11 +43,12 @@ class AlterTableVisitor(SchemaVisitor):
try:
return self.connection.execute(self.buffer.getvalue())
finally:
- self.buffer.truncate(0)
+ self.buffer.seek(0)
+ self.buffer.truncate()
def __init__(self, dialect, connection, **kw):
self.connection = connection
- self.buffer = StringIO.StringIO()
+ self.buffer = StringIO()
self.preparer = dialect.identifier_preparer
self.dialect = dialect
diff --git a/migrate/changeset/databases/sqlite.py b/migrate/changeset/databases/sqlite.py
index 6453422..a601593 100644
--- a/migrate/changeset/databases/sqlite.py
+++ b/migrate/changeset/databases/sqlite.py
@@ -3,7 +3,10 @@
.. _`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 sqlalchemy.databases import sqlite as sa_base
diff --git a/migrate/changeset/schema.py b/migrate/changeset/schema.py
index 913b90f..a0e42cc 100644
--- a/migrate/changeset/schema.py
+++ b/migrate/changeset/schema.py
@@ -1,10 +1,14 @@
"""
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
-from UserDict import DictMixin
-
+import six
import sqlalchemy
from sqlalchemy.schema import ForeignKeyConstraint
@@ -163,7 +167,39 @@ def _to_index(index, table=None, engine=None):
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
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)
else:
# 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")
diffs = self.compare_parameters(*p, **kw)
@@ -254,6 +290,12 @@ class ColumnDelta(DictMixin, sqlalchemy.schema.SchemaItem):
def __delitem__(self, key):
raise NotImplementedError
+ def __len__(self):
+ raise NotImplementedError
+
+ def __iter__(self):
+ raise NotImplementedError
+
def keys(self):
return self.diffs.keys()
@@ -332,7 +374,7 @@ class ColumnDelta(DictMixin, sqlalchemy.schema.SchemaItem):
"""Extracts data from p and modifies diffs"""
p = list(p)
while len(p):
- if isinstance(p[0], basestring):
+ if isinstance(p[0], six.string_types):
k.setdefault('name', p.pop(0))
elif isinstance(p[0], sqlalchemy.types.TypeEngine):
k.setdefault('type', p.pop(0))
@@ -370,7 +412,7 @@ class ColumnDelta(DictMixin, sqlalchemy.schema.SchemaItem):
return getattr(self, '_table', None)
def _set_table(self, table):
- if isinstance(table, basestring):
+ if isinstance(table, six.string_types):
if self.alter_metadata:
if not self.meta:
raise ValueError("metadata must be specified for table"
@@ -587,7 +629,7 @@ populated with defaults
if isinstance(cons,(ForeignKeyConstraint,
UniqueConstraint)):
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
if self.name==col_name:
to_drop.add(cons)
@@ -622,7 +664,7 @@ populated with defaults
if (getattr(self, name[:-5]) and not obj):
raise InvalidConstraintError("Column.create() accepts index_name,"
" 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(
"%s argument for column must be constraint name" % name)