diff options
author | Cyril Roelandt <cyril.roelandt@enovance.com> | 2014-03-19 15:02:40 +0100 |
---|---|---|
committer | Cyril Roelandt <cyril.roelandt@enovance.com> | 2014-04-09 17:32:52 +0200 |
commit | a03b141a954c7e644f0033defdb1b5b434a7c49a (patch) | |
tree | 3515ecc8ab1b2a2218b0fa60aec04de09def8ce5 /migrate/changeset | |
parent | 07909159ae22dc0d399b9618dcf0f79a1d0332bf (diff) | |
download | sqalchemy-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.py | 7 | ||||
-rw-r--r-- | migrate/changeset/databases/sqlite.py | 5 | ||||
-rw-r--r-- | migrate/changeset/schema.py | 58 |
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) |