From eb00570991a2b39b005749f7f7c67643bdcaba59 Mon Sep 17 00:00:00 2001 From: "jan.dittberner" Date: Sun, 25 Jan 2009 12:52:33 +0000 Subject: [PATCH] make migrate.changeset.databases PEP-8 clean and add it to the API docs --- docs/api.rst | 45 +++++++++++- migrate/changeset/databases/__init__.py | 4 ++ migrate/changeset/databases/mysql.py | 56 ++++++++++----- migrate/changeset/databases/oracle.py | 92 ++++++++++++++++--------- migrate/changeset/databases/postgres.py | 55 ++++++++------- migrate/changeset/databases/sqlite.py | 42 +++++++---- migrate/changeset/databases/visitor.py | 32 ++++++--- 7 files changed, 233 insertions(+), 93 deletions(-) diff --git a/docs/api.rst b/docs/api.rst index b33889c..643fbc0 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -9,13 +9,54 @@ Module :mod:`migrate.changeset` :members: :synopsis: Database changeset management -Module :mod:`migrate.changeset.ansisql` ---------------------------------------- +Module :mod:`ansisql ` +------------------------------------------------- .. automodule:: migrate.changeset.ansisql :members: :synopsis: Standard SQL implementation for altering database schemas +Module :mod:`databases ` +----------------------------------------------------- + +.. automodule:: migrate.changeset.databases + :members: + :synopsis: Database specific changeset implementations + +Module :mod:`mysql ` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. automodule:: migrate.changeset.databases.mysql + :members: + :synopsis: MySQL database specific changeset implementations + +Module :mod:`oracle ` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. automodule:: migrate.changeset.databases.oracle + :members: + :synopsis: Oracle database specific changeset implementations + +Module :mod:`postgres ` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. automodule:: migrate.changeset.databases.postgres + :members: + :synopsis: PostgreSQL database specific changeset implementations + +Module :mod:`sqlite ` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. automodule:: migrate.changeset.databases.sqlite + :members: + :synopsis: SQLite database specific changeset implementations + +Module :mod:`visitor ` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. automodule:: migrate.changeset.databases.visitor + :members: + Module :mod:`migrate.versioning` ================================ diff --git a/migrate/changeset/databases/__init__.py b/migrate/changeset/databases/__init__.py index 34b4f9d..17a12d2 100644 --- a/migrate/changeset/databases/__init__.py +++ b/migrate/changeset/databases/__init__.py @@ -1,3 +1,7 @@ +""" + This module contains database dialect specific changeset + implementations. +""" __all__=[ 'postgres', 'sqlite', diff --git a/migrate/changeset/databases/mysql.py b/migrate/changeset/databases/mysql.py index aa65166..94ee4db 100644 --- a/migrate/changeset/databases/mysql.py +++ b/migrate/changeset/databases/mysql.py @@ -1,52 +1,74 @@ -from migrate.changeset import ansisql,exceptions +""" + MySQL database specific implementations of changeset classes. +""" + +from migrate.changeset import ansisql, exceptions from sqlalchemy.databases import mysql as sa_base #import sqlalchemy as sa MySQLSchemaGenerator = sa_base.MySQLSchemaGenerator -class MySQLColumnGenerator(MySQLSchemaGenerator,ansisql.ANSIColumnGenerator): + +class MySQLColumnGenerator(MySQLSchemaGenerator, ansisql.ANSIColumnGenerator): + def _do_quote_table_identifier(self, identifier): return '%s'%identifier pass + + class MySQLColumnDropper(ansisql.ANSIColumnDropper): + def _do_quote_table_identifier(self, identifier): return '%s'%identifier + def _do_quote_column_identifier(self, identifier): return '%s'%identifier -class MySQLSchemaChanger(MySQLSchemaGenerator,ansisql.ANSISchemaChanger): - def visit_column(self,delta): + +class MySQLSchemaChanger(MySQLSchemaGenerator, ansisql.ANSISchemaChanger): + + def visit_column(self, delta): keys = delta.keys() if 'type' in keys or 'nullable' in keys or 'name' in keys: - self._run_subvisit(delta,self._visit_column_change) + self._run_subvisit(delta, self._visit_column_change) if 'server_default' in keys: # Column name might have changed above - col_name = delta.get('name',delta.current_name) - self._run_subvisit(delta,self._visit_column_default,col_name=col_name) - def _visit_column_change(self,table_name,col_name,delta): - if not hasattr(delta,'result_column'): - # Mysql needs the whole column definition, not just a lone name/type + col_name = delta.get('name', delta.current_name) + self._run_subvisit(delta, self._visit_column_default, + col_name=col_name) + + def _visit_column_change(self, table_name, col_name, delta): + if not hasattr(delta, 'result_column'): + # Mysql needs the whole column definition, not just a lone + # name/type raise exceptions.NotSupportedError( "A column object is required to do this") - + column = delta.result_column - if not column.table: column.table = delta.table # needed by get_column_specification + # needed by get_column_specification + if not column.table: + column.table = delta.table colspec = self.get_column_specification(column) self.start_alter_table(table_name) self.append("CHANGE COLUMN ") self.append(col_name) self.append(' ') self.append(colspec) - def visit_index(self,param): + + def visit_index(self, param): # If MySQL can do this, I can't find how raise exceptions.NotSupportedError("MySQL cannot rename indexes") + def _do_quote_table_identifier(self, identifier): return '%s'%identifier + class MySQLConstraintGenerator(ansisql.ANSIConstraintGenerator): + def _do_quote_table_identifier(self, identifier): return '%s'%identifier - + + class MySQLConstraintDropper(ansisql.ANSIConstraintDropper): #def visit_constraint(self,constraint): # if isinstance(constraint,sqlalchemy.schema.PrimaryKeyConstraint): @@ -54,12 +76,13 @@ class MySQLConstraintDropper(ansisql.ANSIConstraintDropper): # elif isinstance(constraint,sqlalchemy.schema.ForeignKeyConstraint): # return self._visit_constraint_fk(constraint) # return super(MySQLConstraintDropper,self).visit_constraint(constraint) - def visit_migrate_primary_key_constraint(self,constraint): + + def visit_migrate_primary_key_constraint(self, constraint): self.start_alter_table(constraint) self.append("DROP PRIMARY KEY") self.execute() - def visit_migrate_foreign_key_constraint(self,constraint): + def visit_migrate_foreign_key_constraint(self, constraint): self.start_alter_table(constraint) self.append("DROP FOREIGN KEY ") self.append(constraint.name) @@ -68,6 +91,7 @@ class MySQLConstraintDropper(ansisql.ANSIConstraintDropper): def _do_quote_table_identifier(self, identifier): return '%s'%identifier + class MySQLDialect(ansisql.ANSIDialect): columngenerator = MySQLColumnGenerator columndropper = MySQLColumnDropper diff --git a/migrate/changeset/databases/oracle.py b/migrate/changeset/databases/oracle.py index 1bb616d..35b146c 100644 --- a/migrate/changeset/databases/oracle.py +++ b/migrate/changeset/databases/oracle.py @@ -1,54 +1,74 @@ -from migrate.changeset import ansisql,exceptions +""" + Oracle database specific implementations of changeset classes. +""" + +from migrate.changeset import ansisql, exceptions from sqlalchemy.databases import oracle as sa_base import sqlalchemy as sa OracleSchemaGenerator = sa_base.OracleSchemaGenerator -class OracleColumnGenerator(OracleSchemaGenerator,ansisql.ANSIColumnGenerator): + +class OracleColumnGenerator(OracleSchemaGenerator, + ansisql.ANSIColumnGenerator): pass + + class OracleColumnDropper(ansisql.ANSIColumnDropper): pass -class OracleSchemaChanger(OracleSchemaGenerator,ansisql.ANSISchemaChanger): - def get_column_specification(self,column,**kwargs): - # Ignore the NOT NULL generated - override_nullable = kwargs.pop('override_nullable',None) + + +class OracleSchemaChanger(OracleSchemaGenerator, ansisql.ANSISchemaChanger): + + def get_column_specification(self, column, **kwargs): + # Ignore the NOT NULL generated + override_nullable = kwargs.pop('override_nullable', None) if override_nullable: orig = column.nullable column.nullable = True - ret=super(OracleSchemaChanger,self).get_column_specification(column,**kwargs) + ret = super(OracleSchemaChanger, self).get_column_specification( + column, **kwargs) if override_nullable: column.nullable = orig return ret - - def visit_column(self,delta): + + def visit_column(self, delta): keys = delta.keys() - if 'type' in keys or 'nullable' in keys or 'default' in keys or 'server_default' in keys: - self._run_subvisit(delta,self._visit_column_change) + if 'type' in keys or 'nullable' in keys or 'default' in keys \ + or 'server_default' in keys: + self._run_subvisit(delta, self._visit_column_change) if 'name' in keys: - self._run_subvisit(delta,self._visit_column_name) - def _visit_column_change(self,table_name,col_name,delta): - if not hasattr(delta,'result_column'): - # Oracle needs the whole column definition, not just a lone name/type + self._run_subvisit(delta, self._visit_column_name) + + def _visit_column_change(self, table_name, col_name, delta): + if not hasattr(delta, 'result_column'): + # Oracle needs the whole column definition, not just a + # lone name/type raise exceptions.NotSupportedError( "A column object is required to do this") - + column = delta.result_column - # Oracle cannot drop a default once created, but it can set it to null. - # We'll do that if default=None - # http://forums.oracle.com/forums/message.jspa?messageID=1273234#1273234 - dropdefault_hack = (column.server_default is None and 'server_default' in delta.keys()) - # Oracle apparently doesn't like it when we say "not null" if the - # column's already not null. Fudge it, so we don't need a new function - notnull_hack = ((not column.nullable) and ('nullable' not in delta.keys())) - # We need to specify NULL if we're removing a NOT NULL constraint + # Oracle cannot drop a default once created, but it can set it + # to null. We'll do that if default=None + # http://forums.oracle.com/forums/message.jspa?\ + # messageID=1273234#1273234 + dropdefault_hack = (column.server_default is None \ + and 'server_default' in delta.keys()) + # Oracle apparently doesn't like it when we say "not null" if + # the column's already not null. Fudge it, so we don't need a + # new function + notnull_hack = ((not column.nullable) \ + and ('nullable' not in delta.keys())) + # We need to specify NULL if we're removing a NOT NULL + # constraint null_hack = (column.nullable and ('nullable' in delta.keys())) - - - if dropdefault_hack: + + if dropdefault_hack: column.server_default = sa.PassiveDefault(sa.sql.null()) if notnull_hack: column.nullable = True - colspec=self.get_column_specification(column,override_nullable=null_hack) + colspec=self.get_column_specification(column, + override_nullable=null_hack) if null_hack: colspec += ' NULL' if notnull_hack: @@ -59,18 +79,28 @@ class OracleSchemaChanger(OracleSchemaGenerator,ansisql.ANSISchemaChanger): self.start_alter_table(table_name) self.append("MODIFY ") self.append(colspec) + + class OracleConstraintCommon(object): - def get_constraint_name(self,cons): + + def get_constraint_name(self, cons): # Oracle constraints can't guess their name like other DBs if not cons.name: raise exceptions.NotSupportedError( "Oracle constraint names must be explicitly stated") return cons.name -class OracleConstraintGenerator(OracleConstraintCommon,ansisql.ANSIConstraintGenerator): + + +class OracleConstraintGenerator(OracleConstraintCommon, + ansisql.ANSIConstraintGenerator): pass -class OracleConstraintDropper(OracleConstraintCommon,ansisql.ANSIConstraintDropper): + + +class OracleConstraintDropper(OracleConstraintCommon, + ansisql.ANSIConstraintDropper): pass + class OracleDialect(ansisql.ANSIDialect): columngenerator = OracleColumnGenerator columndropper = OracleColumnDropper diff --git a/migrate/changeset/databases/postgres.py b/migrate/changeset/databases/postgres.py index 8c907fd..d1d4cc8 100644 --- a/migrate/changeset/databases/postgres.py +++ b/migrate/changeset/databases/postgres.py @@ -1,44 +1,53 @@ +""" + `PostgreSQL`_ database specific implementations of changeset classes. + + .. _`PostgreSQL`: http://www.postgresql.org/ +""" from migrate.changeset import ansisql from sqlalchemy.databases import postgres as sa_base #import sqlalchemy as sa + PGSchemaGenerator = sa_base.PGSchemaGenerator + class PGSchemaGeneratorMixin(object): + """Common code used by the PostgreSQL specific classes.""" + def _do_quote_table_identifier(self, identifier): return identifier + def _do_quote_column_identifier(self, identifier): return '"%s"'%identifier -class PGColumnGenerator(PGSchemaGenerator,ansisql.ANSIColumnGenerator, PGSchemaGeneratorMixin): - def _do_quote_table_identifier(self, identifier): - return identifier - def _do_quote_column_identifier(self, identifier): - return '"%s"'%identifier + +class PGColumnGenerator(PGSchemaGenerator, ansisql.ANSIColumnGenerator, + PGSchemaGeneratorMixin): + """PostgreSQL column generator implementation.""" + pass + class PGColumnDropper(ansisql.ANSIColumnDropper, PGSchemaGeneratorMixin): - def _do_quote_table_identifier(self, identifier): - return identifier - def _do_quote_column_identifier(self, identifier): - return '"%s"'%identifier + """PostgreSQL column dropper implementation.""" + pass + class PGSchemaChanger(ansisql.ANSISchemaChanger, PGSchemaGeneratorMixin): - def _do_quote_table_identifier(self, identifier): - return identifier - def _do_quote_column_identifier(self, identifier): - return '"%s"'%identifier + """PostgreSQL schema changer implementation.""" + pass -class PGConstraintGenerator(ansisql.ANSIConstraintGenerator, PGSchemaGeneratorMixin): - def _do_quote_table_identifier(self, identifier): - return identifier - def _do_quote_column_identifier(self, identifier): - return '"%s"'%identifier -class PGConstraintDropper(ansisql.ANSIConstraintDropper, PGSchemaGeneratorMixin): - def _do_quote_table_identifier(self, identifier): - return identifier - def _do_quote_column_identifier(self, identifier): - return '"%s"'%identifier +class PGConstraintGenerator(ansisql.ANSIConstraintGenerator, + PGSchemaGeneratorMixin): + """PostgreSQL constraint generator implementation.""" + pass + + +class PGConstraintDropper(ansisql.ANSIConstraintDropper, + PGSchemaGeneratorMixin): + """PostgreSQL constaint dropper implementation.""" + pass + class PGDialect(ansisql.ANSIDialect): columngenerator = PGColumnGenerator diff --git a/migrate/changeset/databases/sqlite.py b/migrate/changeset/databases/sqlite.py index 0f560c1..caeb1b5 100644 --- a/migrate/changeset/databases/sqlite.py +++ b/migrate/changeset/databases/sqlite.py @@ -1,11 +1,18 @@ -from migrate.changeset import ansisql,constraint,exceptions +""" + `SQLite`_ database specific implementations of changeset classes. + + .. _`SQLite`: http://www.sqlite.org/ +""" +from migrate.changeset import ansisql, constraint, exceptions from sqlalchemy.databases import sqlite as sa_base from sqlalchemy import Table, MetaData #import sqlalchemy as sa SQLiteSchemaGenerator = sa_base.SQLiteSchemaGenerator + class SQLiteHelper(object): + def visit_column(self, param): try: table = self._to_table(param.table) @@ -24,50 +31,61 @@ class SQLiteHelper(object): self.append('DROP TABLE migration_tmp') self.execute() -class SQLiteColumnGenerator(SQLiteSchemaGenerator,ansisql.ANSIColumnGenerator): + +class SQLiteColumnGenerator(SQLiteSchemaGenerator, + ansisql.ANSIColumnGenerator): pass + class SQLiteColumnDropper(SQLiteHelper, ansisql.ANSIColumnDropper): - def _modify_table(self,table, column): + + def _modify_table(self, table, column): del table.columns[column.name] - columns = ','.join([c.name for c in table.columns]) - return 'INSERT INTO %(table_name)s SELECT ' + columns + ' from migration_tmp' + return 'INSERT INTO %(table_name)s SELECT %s from migration_tmp' % \ + ','.join([c.name for c in table.columns]) + class SQLiteSchemaChanger(SQLiteHelper, ansisql.ANSISchemaChanger): - def _not_supported(self,op): + + def _not_supported(self, op): raise exceptions.NotSupportedError("SQLite does not support " "%s; see http://www.sqlite.org/lang_altertable.html"%op) def _modify_table(self, table, delta): column = table.columns[delta.current_name] - for k,v in delta.items(): + for k, v in delta.items(): setattr(column, k, v) return 'INSERT INTO %(table_name)s SELECT * from migration_tmp' - def visit_index(self,param): + def visit_index(self, param): self._not_supported('ALTER INDEX') def _do_quote_column_identifier(self, identifier): return '"%s"'%identifier + class SQLiteConstraintGenerator(ansisql.ANSIConstraintGenerator): - def visit_migrate_primary_key_constraint(self,constraint): + + def visit_migrate_primary_key_constraint(self, constraint): tmpl = "CREATE UNIQUE INDEX %s ON %s ( %s )" cols = ','.join([c.name for c in constraint.columns]) tname = constraint.table.name name = constraint.name - msg = tmpl%(name,tname,cols) + msg = tmpl % (name, tname, cols) self.append(msg) self.execute() + class SQLiteConstraintDropper(ansisql.ANSIColumnDropper): - def visit_migrate_primary_key_constraint(self,constraint): + + def visit_migrate_primary_key_constraint(self, constraint): tmpl = "DROP INDEX %s " name = constraint.name - msg = tmpl%(name) + msg = tmpl % (name) self.append(msg) self.execute() + class SQLiteDialect(ansisql.ANSIDialect): columngenerator = SQLiteColumnGenerator columndropper = SQLiteColumnDropper diff --git a/migrate/changeset/databases/visitor.py b/migrate/changeset/databases/visitor.py index 437dc68..60216cc 100644 --- a/migrate/changeset/databases/visitor.py +++ b/migrate/changeset/databases/visitor.py @@ -1,20 +1,34 @@ +""" + Module for visitor class mapping. +""" import sqlalchemy as sa -from migrate.changeset.databases import sqlite,postgres,mysql,oracle +from migrate.changeset.databases import sqlite, postgres, mysql, oracle from migrate.changeset import ansisql # Map SA dialects to the corresponding Migrate extensions dialects = { - sa.engine.default.DefaultDialect : ansisql.ANSIDialect, - sa.databases.sqlite.SQLiteDialect : sqlite.SQLiteDialect, - sa.databases.postgres.PGDialect : postgres.PGDialect, - sa.databases.mysql.MySQLDialect : mysql.MySQLDialect, - sa.databases.oracle.OracleDialect : oracle.OracleDialect, + sa.engine.default.DefaultDialect: ansisql.ANSIDialect, + sa.databases.sqlite.SQLiteDialect: sqlite.SQLiteDialect, + sa.databases.postgres.PGDialect: postgres.PGDialect, + sa.databases.mysql.MySQLDialect: mysql.MySQLDialect, + sa.databases.oracle.OracleDialect: oracle.OracleDialect, } -def get_engine_visitor(engine,name): - return get_dialect_visitor(engine.dialect,name) -def get_dialect_visitor(sa_dialect,name): +def get_engine_visitor(engine, name): + """ + Get the visitor implementation for the given database engine. + """ + return get_dialect_visitor(engine.dialect, name) + + +def get_dialect_visitor(sa_dialect, name): + """ + Get the visitor implementation for the given dialect. + + Finds the visitor implementation based on the dialect class and + returns and instance initialized with the given name. + """ sa_dialect_cls = sa_dialect.__class__ migrate_dialect_cls = dialects[sa_dialect_cls] return migrate_dialect_cls.visitor(name)