From: Mike Bayer Date: Mon, 15 Mar 2010 17:08:31 +0000 (-0400) Subject: - The visit_pool() method of Dialect is removed, and replaced with X-Git-Tag: rel_0_6beta2~54^2 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=5dcc32fd5a81c41b50bc35573d190a60a344c3c6;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git - The visit_pool() method of Dialect is removed, and replaced with on_connect(). This method returns a callable which receives the raw DBAPI connection after each one is created. The callable is assembled into a first_connect/connect pool listener by the connection strategy if non-None. Provides a simpler interface for dialects. --- diff --git a/CHANGES b/CHANGES index 5a8cf7aac8..d2ed4672e1 100644 --- a/CHANGES +++ b/CHANGES @@ -264,6 +264,12 @@ CHANGES within the "name" field of logging messages instead of the default hex identifier string. [ticket:1555] + - The visit_pool() method of Dialect is removed, and replaced with + on_connect(). This method returns a callable which receives + the raw DBAPI connection after each one is created. The callable + is assembled into a first_connect/connect pool listener by the + connection strategy if non-None. Provides a simpler interface + for dialects. - metadata - Added the ability to strip schema information when using diff --git a/lib/sqlalchemy/connectors/mxodbc.py b/lib/sqlalchemy/connectors/mxodbc.py index 49c5a73293..a646473fb7 100644 --- a/lib/sqlalchemy/connectors/mxodbc.py +++ b/lib/sqlalchemy/connectors/mxodbc.py @@ -26,14 +26,13 @@ class MxODBCConnector(Connector): raise ImportError, "Unrecognized platform for mxODBC import" return module - def visit_pool(self, pool): - def connect(conn, rec): + def on_connect(self): + def connect(conn): conn.stringformat = self.dbapi.MIXED_STRINGFORMAT conn.datetimeformat = self.dbapi.PYDATETIME_DATETIMEFORMAT #conn.bindmethod = self.dbapi.BIND_USING_PYTHONTYPE #conn.bindmethod = self.dbapi.BIND_USING_SQLTYPE - - pool.add_listener({'connect':connect}) + return connect def create_connect_args(self, url): """ Return a tuple of *args,**kwargs for creating a connection. diff --git a/lib/sqlalchemy/dialects/postgresql/base.py b/lib/sqlalchemy/dialects/postgresql/base.py index 7d4cbbbd80..cbd92ccfec 100644 --- a/lib/sqlalchemy/dialects/postgresql/base.py +++ b/lib/sqlalchemy/dialects/postgresql/base.py @@ -600,21 +600,19 @@ class PGDialect(default.DefaultDialect): if not self.supports_native_enum: self.colspecs = self.colspecs.copy() del self.colspecs[ENUM] - - def visit_pool(self, pool): - if self.isolation_level is not None: - class SetIsolationLevel(object): - def __init__(self, isolation_level): - self.isolation_level = isolation_level - - def connect(self, conn, rec): - cursor = conn.cursor() - cursor.execute("SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL %s" - % self.isolation_level) - cursor.execute("COMMIT") - cursor.close() - pool.add_listener(SetIsolationLevel(self.isolation_level)) + def on_connect(self): + if self.isolation_level is not None: + def connect(conn): + cursor = conn.cursor() + cursor.execute("SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL %s" + % self.isolation_level) + cursor.execute("COMMIT") + cursor.close() + return connect + else: + return None + def do_begin_twophase(self, connection, xid): self.do_begin(connection.connection) diff --git a/lib/sqlalchemy/dialects/postgresql/psycopg2.py b/lib/sqlalchemy/dialects/postgresql/psycopg2.py index 48349834d2..c239a3ee06 100644 --- a/lib/sqlalchemy/dialects/postgresql/psycopg2.py +++ b/lib/sqlalchemy/dialects/postgresql/psycopg2.py @@ -179,20 +179,18 @@ class PGDialect_psycopg2(PGDialect): psycopg = __import__('psycopg2') return psycopg - _unwrap_connection = None - - def visit_pool(self, pool): + def on_connect(self): + base_on_connect = super(PGDialect_psycopg2, self).on_connect() if self.dbapi and self.use_native_unicode: extensions = __import__('psycopg2.extensions').extensions - def connect(conn, rec): - if self._unwrap_connection: - conn = self._unwrap_connection(conn) - if conn is None: - return + def connect(conn): extensions.register_type(extensions.UNICODE, conn) - pool.add_listener({'first_connect': connect, 'connect':connect}) - super(PGDialect_psycopg2, self).visit_pool(pool) - + if base_on_connect: + base_on_connect(conn) + return connect + else: + return base_on_connect + def create_connect_args(self, url): opts = url.translate_connect_args(username='user') if 'port' in opts: diff --git a/lib/sqlalchemy/dialects/sqlite/base.py b/lib/sqlalchemy/dialects/sqlite/base.py index 5bcf901519..dfc09f0257 100644 --- a/lib/sqlalchemy/dialects/sqlite/base.py +++ b/lib/sqlalchemy/dialects/sqlite/base.py @@ -360,21 +360,21 @@ class SQLiteDialect(default.DefaultDialect): # hypothetical driver ?) self.native_datetime = native_datetime - def visit_pool(self, pool): + def on_connect(self): if self.isolation_level is not None: - class SetIsolationLevel(object): - def __init__(self, isolation_level): - if isolation_level == 'READ UNCOMMITTED': - self.isolation_level = 1 - else: - self.isolation_level = 0 - - def connect(self, conn, rec): - cursor = conn.cursor() - cursor.execute("PRAGMA read_uncommitted = %d" % self.isolation_level) - cursor.close() - pool.add_listener(SetIsolationLevel(self.isolation_level)) - + if self.isolation_level == 'READ UNCOMMITTED': + isolation_level = 1 + else: + isolation_level = 0 + + def connect(conn): + cursor = conn.cursor() + cursor.execute("PRAGMA read_uncommitted = %d" % isolation_level) + cursor.close() + return connect + else: + return None + def table_names(self, connection, schema): if schema is not None: qschema = self.identifier_preparer.quote_identifier(schema) diff --git a/lib/sqlalchemy/engine/base.py b/lib/sqlalchemy/engine/base.py index fa0059130f..095f7a960e 100644 --- a/lib/sqlalchemy/engine/base.py +++ b/lib/sqlalchemy/engine/base.py @@ -169,6 +169,7 @@ class Dialect(object): Given a :class:`~sqlalchemy.engine.url.URL` object, returns a tuple consisting of a `*args`/`**kwargs` suitable to send directly to the dbapi's connect function. + """ raise NotImplementedError() @@ -183,6 +184,7 @@ class Dialect(object): The returned result is cached *per dialect class* so can contain no dialect-instance state. + """ raise NotImplementedError() @@ -192,6 +194,13 @@ class Dialect(object): Allows dialects to configure options based on server version info or other properties. + + The connection passed here is a SQLAlchemy Connection object, + with full capabilities. + + The initalize() method of the base dialect should be called via + super(). + """ pass @@ -204,6 +213,12 @@ class Dialect(object): properties from the database. If include_columns (a list or set) is specified, limit the autoload to the given column names. + + The default implementation uses the + :class:`~sqlalchemy.engine.reflection.Inspector` interface to + provide the output, building upon the granular table/column/ + constraint etc. methods of :class:`Dialect`. + """ raise NotImplementedError() @@ -458,8 +473,22 @@ class Dialect(object): raise NotImplementedError() - def visit_pool(self, pool): - """Executed after a pool is created.""" + def on_connect(self): + """return a callable which sets up a newly created DBAPI connection. + + The callable accepts a single argument "conn" which is the + DBAPI connection itself. It has no return value. + + This is used to set dialect-wide per-connection options such as isolation + modes, unicode modes, etc. + + If a callable is returned, it will be assembled into a pool listener + that receives the direct DBAPI connection, with all wrappers removed. + + If None is returned, no listener will be generated. + + """ + return None class ExecutionContext(object): diff --git a/lib/sqlalchemy/engine/default.py b/lib/sqlalchemy/engine/default.py index 7df858a644..ce24a9ae43 100644 --- a/lib/sqlalchemy/engine/default.py +++ b/lib/sqlalchemy/engine/default.py @@ -138,6 +138,20 @@ class DefaultDialect(base.Dialect): self.do_rollback(connection.connection) + def on_connect(self): + """return a callable which sets up a newly created DBAPI connection. + + This is used to set dialect-wide per-connection options such as isolation + modes, unicode modes, etc. + + If a callable is returned, it will be assembled into a pool listener + that receives the direct DBAPI connection, with all wrappers removed. + + If None is returned, no listener will be generated. + + """ + return None + def _check_unicode_returns(self, connection): # Py2K if self.supports_unicode_statements: diff --git a/lib/sqlalchemy/engine/strategies.py b/lib/sqlalchemy/engine/strategies.py index 7c434105c3..7fc39b91a9 100644 --- a/lib/sqlalchemy/engine/strategies.py +++ b/lib/sqlalchemy/engine/strategies.py @@ -130,8 +130,16 @@ class DefaultEngineStrategy(EngineStrategy): engine = engineclass(pool, dialect, u, **engine_args) if _initialize: - dialect.visit_pool(pool) - + do_on_connect = dialect.on_connect() + if do_on_connect: + def on_connect(conn, rec): + conn = getattr(conn, '_sqla_unwrap', conn) + if conn is None: + return + do_on_connect(conn) + + pool.add_listener({'first_connect': on_connect, 'connect':on_connect}) + def first_connect(conn, rec): c = base.Connection(engine, connection=conn) dialect.initialize(c) diff --git a/lib/sqlalchemy/test/engines.py b/lib/sqlalchemy/test/engines.py index 58bfe2b3c1..0cfd58d207 100644 --- a/lib/sqlalchemy/test/engines.py +++ b/lib/sqlalchemy/test/engines.py @@ -242,7 +242,11 @@ class ReplayableSession(object): else: buffer.append(result) return result - + + @property + def _sqla_unwrap(self): + return self._subject + def __getattribute__(self, key): try: return object.__getattribute__(self, key) @@ -275,7 +279,11 @@ class ReplayableSession(object): return self else: return result - + + @property + def _sqla_unwrap(self): + return None + def __getattribute__(self, key): try: return object.__getattribute__(self, key) @@ -290,10 +298,3 @@ class ReplayableSession(object): else: return result -def unwrap_connection(conn): - if conn.__class__.__name__ == 'Recorder': - return conn._subject - elif conn.__class__.__name__ == 'Player': - return None - else: - return conn diff --git a/test/aaa_profiling/test_zoomark.py b/test/aaa_profiling/test_zoomark.py index b7b77bd02b..0c090acb7c 100644 --- a/test/aaa_profiling/test_zoomark.py +++ b/test/aaa_profiling/test_zoomark.py @@ -37,7 +37,6 @@ class ZooMarkTest(TestBase): recorder = lambda: dbapi_session.recorder(creator()) engine = engines.testing_engine(options={'creator':recorder}) - engine.dialect._unwrap_connection = engines.unwrap_connection metadata = MetaData(engine) engine.connect() @@ -321,7 +320,6 @@ class ZooMarkTest(TestBase): player = lambda: dbapi_session.player() engine = create_engine('postgresql:///', creator=player) - engine.dialect._unwrap_connection = engines.unwrap_connection metadata = MetaData(engine) engine.connect() diff --git a/test/aaa_profiling/test_zoomark_orm.py b/test/aaa_profiling/test_zoomark_orm.py index 62a27eca44..8304c93839 100644 --- a/test/aaa_profiling/test_zoomark_orm.py +++ b/test/aaa_profiling/test_zoomark_orm.py @@ -36,7 +36,6 @@ class ZooMarkTest(TestBase): creator = testing.db.pool._creator recorder = lambda: dbapi_session.recorder(creator()) engine = engines.testing_engine(options={'creator':recorder}) - engine.dialect._unwrap_connection = engines.unwrap_connection metadata = MetaData(engine) session = sessionmaker()() engine.connect() @@ -284,7 +283,6 @@ class ZooMarkTest(TestBase): player = lambda: dbapi_session.player() engine = create_engine('postgresql:///', creator=player) - engine.dialect._unwrap_connection = engines.unwrap_connection metadata = MetaData(engine) session = sessionmaker()() engine.connect()