From: Mike Bayer Date: Wed, 1 Sep 2010 01:46:14 +0000 (-0400) Subject: doh, forgot about ddl events. need some work with the pickling. X-Git-Tag: rel_0_7b1~253^2~27 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=3a86cea34abbcf1ca852ca8925b9866be3cd86f8;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git doh, forgot about ddl events. need some work with the pickling. --- diff --git a/lib/sqlalchemy/engine/ddl.py b/lib/sqlalchemy/engine/ddl.py index 74205dbb03..0e165b8f59 100644 --- a/lib/sqlalchemy/engine/ddl.py +++ b/lib/sqlalchemy/engine/ddl.py @@ -35,21 +35,20 @@ class SchemaGenerator(DDLBase): tables = metadata.tables.values() collection = [t for t in sql_util.sort_tables(tables) if self._can_create(t)] - for listener in metadata.ddl_listeners['before-create']: - listener('before-create', metadata, self.connection, tables=collection) - + metadata.dispatch.on_before_create(metadata, self.connection, + tables=collection) + for table in collection: self.traverse_single(table, create_ok=True) - - for listener in metadata.ddl_listeners['after-create']: - listener('after-create', metadata, self.connection, tables=collection) + + metadata.dispatch.on_after_create(metadata, self.connection, + tables=collection) def visit_table(self, table, create_ok=False): if not create_ok and not self._can_create(table): return - - for listener in table.ddl_listeners['before-create']: - listener('before-create', table, self.connection) + + table.dispatch.on_before_create(table, self.connection) for column in table.columns: if column.default is not None: @@ -61,8 +60,7 @@ class SchemaGenerator(DDLBase): for index in table.indexes: self.traverse_single(index) - for listener in table.ddl_listeners['after-create']: - listener('after-create', table, self.connection) + table.dispatch.on_after_create(table, self.connection) def visit_sequence(self, sequence): if self.dialect.supports_sequences: @@ -91,14 +89,14 @@ class SchemaDropper(DDLBase): tables = metadata.tables.values() collection = [t for t in reversed(sql_util.sort_tables(tables)) if self._can_drop(t)] - for listener in metadata.ddl_listeners['before-drop']: - listener('before-drop', metadata, self.connection, tables=collection) + metadata.dispatch.on_before_drop(metadata, self.connection, + tables=collection) for table in collection: self.traverse_single(table, drop_ok=True) - for listener in metadata.ddl_listeners['after-drop']: - listener('after-drop', metadata, self.connection, tables=collection) + metadata.dispatch.on_after_drop(metadata, self.connection, + tables=collection) def _can_drop(self, table): self.dialect.validate_identifier(table.name) @@ -112,18 +110,16 @@ class SchemaDropper(DDLBase): def visit_table(self, table, drop_ok=False): if not drop_ok and not self._can_drop(table): return - - for listener in table.ddl_listeners['before-drop']: - listener('before-drop', table, self.connection) + + table.dispatch.on_before_drop(table, self.connection) for column in table.columns: if column.default is not None: self.traverse_single(column.default) self.connection.execute(schema.DropTable(table)) - - for listener in table.ddl_listeners['after-drop']: - listener('after-drop', table, self.connection) + + table.dispatch.on_after_drop(table, self.connection) def visit_sequence(self, sequence): if self.dialect.supports_sequences: diff --git a/lib/sqlalchemy/event.py b/lib/sqlalchemy/event.py index 5448503b26..0e99eb3744 100644 --- a/lib/sqlalchemy/event.py +++ b/lib/sqlalchemy/event.py @@ -5,7 +5,7 @@ and :mod:`sqlalchemy.orm` packages. """ -from sqlalchemy import util +from sqlalchemy import util, exc CANCEL = util.symbol('CANCEL') NO_RETVAL = util.symbol('NO_RETVAL') @@ -26,7 +26,9 @@ def listen(fn, identifier, target, *args, **kw): for evt_cls in _registrars[identifier]: for tgt in evt_cls.accept_with(target): tgt.dispatch.listen(fn, identifier, tgt, *args, **kw) - break + return + raise exc.InvalidRequestError("No such event %s for target %s" % + (identifier,target)) _registrars = util.defaultdict(list) @@ -178,7 +180,7 @@ class dispatcher(object): """ def __init__(self, events): self.dispatch_cls = events.dispatch - + self.events = events def __get__(self, obj, cls): if obj is None: diff --git a/lib/sqlalchemy/schema.py b/lib/sqlalchemy/schema.py index 5b59838587..fdc9616197 100644 --- a/lib/sqlalchemy/schema.py +++ b/lib/sqlalchemy/schema.py @@ -31,6 +31,7 @@ as components in SQL expressions. import re, inspect from sqlalchemy import exc, util, dialects from sqlalchemy.sql import expression, visitors +from sqlalchemy import event URL = None @@ -81,6 +82,20 @@ def _get_table_key(name, schema): else: return schema + "." + name +class DDLEvents(event.Events): + def on_before_create(self, target, connection, **kw): + pass + + def on_after_create(self, target, connection, **kw): + pass + + def on_before_drop(self, target, connection, **kw): + pass + + def on_after_drop(self, target, connection, **kw): + pass + + class Table(SchemaItem, expression.TableClause): """Represent a table in a database. @@ -174,8 +189,7 @@ class Table(SchemaItem, expression.TableClause): __visit_name__ = 'table' - ddl_events = ('before-create', 'after-create', - 'before-drop', 'after-drop') + dispatch = event.dispatcher(DDLEvents) def __new__(cls, *args, **kw): if not args: @@ -227,7 +241,6 @@ class Table(SchemaItem, expression.TableClause): self._set_primary_key(PrimaryKeyConstraint()) self._foreign_keys = util.OrderedSet() self._extra_dependencies = set() - self.ddl_listeners = util.defaultdict(list) self.kwargs = {} if self.schema is not None: self.fullname = "%s.%s" % (self.schema, self.name) @@ -371,37 +384,18 @@ class Table(SchemaItem, expression.TableClause): constraint._set_parent(self) - def append_ddl_listener(self, event, listener): + def append_ddl_listener(self, event_name, listener): """Append a DDL event listener to this ``Table``. - - The ``listener`` callable will be triggered when this ``Table`` is - created or dropped, either directly before or after the DDL is issued - to the database. The listener may modify the Table, but may not abort - the event itself. - :param event: - One of ``Table.ddl_events``; e.g. 'before-create', 'after-create', - 'before-drop' or 'after-drop'. - - :param listener: - A callable, invoked with three positional arguments: - - :event: - The event currently being handled - - :target: - The ``Table`` object being created or dropped - - :bind: - The ``Connection`` bueing used for DDL execution. - - Listeners are added to the Table's ``ddl_listeners`` attribute. + Deprecated. See :class:`.DDLEvents`. """ - - if event not in self.ddl_events: - raise LookupError(event) - self.ddl_listeners[event].append(listener) + + def adapt_listener(target, connection, **kw): + listener(event_name, target, connection, **kw) + + event.listen(adapt_listener, + "on_" + event_name.replace('-', '_'), self) def _set_parent(self, metadata): metadata.tables[_get_table_key(self.name, self.schema)] = self @@ -1835,8 +1829,7 @@ class MetaData(SchemaItem): __visit_name__ = 'metadata' - ddl_events = ('before-create', 'after-create', - 'before-drop', 'after-drop') + dispatch = event.dispatcher(DDLEvents) def __init__(self, bind=None, reflect=False): """Create a new MetaData object. @@ -1856,7 +1849,6 @@ class MetaData(SchemaItem): self.tables = {} self.bind = bind self.metadata = self - self.ddl_listeners = util.defaultdict(list) if reflect: if not bind: raise exc.ArgumentError( @@ -1993,44 +1985,17 @@ class MetaData(SchemaItem): for name in load: Table(name, self, **reflect_opts) - def append_ddl_listener(self, event, listener): + def append_ddl_listener(self, event_name, listener): """Append a DDL event listener to this ``MetaData``. - The ``listener`` callable will be triggered when this ``MetaData`` is - involved in DDL creates or drops, and will be invoked either before - all Table-related actions or after. - - :param event: - One of ``MetaData.ddl_events``; 'before-create', 'after-create', - 'before-drop' or 'after-drop'. - - :param listener: - A callable, invoked with three positional arguments: - - :event: - The event currently being handled - - :target: - The ``MetaData`` object being operated upon - - :bind: - The ``Connection`` bueing used for DDL execution. - - Listeners are added to the MetaData's ``ddl_listeners`` attribute. - - Note: MetaData listeners are invoked even when ``Tables`` are created - in isolation. This may change in a future release. I.e.:: - - # triggers all MetaData and Table listeners: - metadata.create_all() - - # triggers MetaData listeners too: - some.table.create() + Deprecated. See :class:`.DDLEvents`. """ - if event not in self.ddl_events: - raise LookupError(event) - self.ddl_listeners[event].append(listener) + def adapt_listener(target, connection, **kw): + listener(event, target, connection, **kw) + + event.listen(adapt_listener, + "on_" + event_name.replace('-', '_'), self) def create_all(self, bind=None, tables=None, checkfirst=True): """Create all tables stored in this metadata. @@ -2194,7 +2159,7 @@ class DDLElement(expression.Executable, expression.ClauseElement): bind.engine.logger.info( "DDL execution skipped, criteria not met.") - def execute_at(self, event, target): + def execute_at(self, event_name, target): """Link execution of this DDL to the DDL lifecycle of a SchemaItem. Links this ``DDLElement`` to a ``Table`` or ``MetaData`` instance, @@ -2203,9 +2168,10 @@ class DDLElement(expression.Executable, expression.ClauseElement): context as the Table create/drop itself. The ``.bind`` property of this statement is ignored. - :param event: - One of the events defined in the schema item's ``.ddl_events``; - e.g. 'before-create', 'after-create', 'before-drop' or 'after-drop' + :param event_name: + Name of an event from :class:`.DDLEvents`. e.g.: + 'on_before_create', 'on_after_create', 'on_before_drop', + 'on_after_drop'. :param target: The Table or MetaData instance for which this DDLElement will @@ -2220,16 +2186,12 @@ class DDLElement(expression.Executable, expression.ClauseElement): any DDL set to ``execute_at`` that Table's MetaData. This may change in a future release. """ - - if not hasattr(target, 'ddl_listeners'): - raise exc.ArgumentError( - "%s does not support DDL events" % type(target).__name__) - if event not in target.ddl_events: - raise exc.ArgumentError( - "Unknown event, expected one of (%s), got '%r'" % - (', '.join(target.ddl_events), event)) - target.ddl_listeners[event].append(self) - return self + + event_name = "on_" + event_name.replace('-', '_') + def call_event(target, connection, **kw): + self(event_name, target, connection, **kw) + + event.listen(call_event, event_name, target) @expression._generative def against(self, target): @@ -2346,7 +2308,8 @@ class DDL(DDLElement): :event: The name of the event that has triggered this DDL, such as - 'after-create' Will be None if the DDL is executed explicitly. + 'on_after_create' Will be None if the DDL is executed + explicitly. :target: The ``Table`` or ``MetaData`` object which is the target of diff --git a/test/engine/test_ddlevents.py b/test/engine/test_ddlevents.py index ccbbfd82d0..5c1ecb1dd3 100644 --- a/test/engine/test_ddlevents.py +++ b/test/engine/test_ddlevents.py @@ -2,7 +2,7 @@ from sqlalchemy.test.testing import assert_raises, assert_raises_message from sqlalchemy.schema import DDL, CheckConstraint, AddConstraint, \ DropConstraint from sqlalchemy import create_engine -from sqlalchemy import MetaData, Integer, String +from sqlalchemy import MetaData, Integer, String, event, exc from sqlalchemy.test.schema import Table from sqlalchemy.test.schema import Column import sqlalchemy as tsa @@ -17,29 +17,29 @@ class DDLEventTest(TestBase): self.schema_item = schema_item self.bind = bind - def before_create(self, action, schema_item, bind, **kw): + def before_create(self, schema_item, bind, **kw): assert self.state is None assert schema_item is self.schema_item assert bind is self.bind - self.state = action + self.state = 'before-create' - def after_create(self, action, schema_item, bind, **kw): + def after_create(self, schema_item, bind, **kw): assert self.state in ('before-create', 'skipped') assert schema_item is self.schema_item assert bind is self.bind - self.state = action + self.state = 'after-create' - def before_drop(self, action, schema_item, bind, **kw): + def before_drop(self, schema_item, bind, **kw): assert self.state is None assert schema_item is self.schema_item assert bind is self.bind - self.state = action + self.state = 'before-drop' - def after_drop(self, action, schema_item, bind, **kw): + def after_drop(self, schema_item, bind, **kw): assert self.state in ('before-drop', 'skipped') assert schema_item is self.schema_item assert bind is self.bind - self.state = action + self.state = 'after-drop' def setup(self): self.bind = engines.mock_engine() @@ -49,7 +49,7 @@ class DDLEventTest(TestBase): def test_table_create_before(self): table, bind = self.table, self.bind canary = self.Canary(table, bind) - table.ddl_listeners['before-create'].append(canary.before_create) + event.listen(canary.before_create, 'on_before_create', table) table.create(bind) assert canary.state == 'before-create' @@ -59,7 +59,7 @@ class DDLEventTest(TestBase): def test_table_create_after(self): table, bind = self.table, self.bind canary = self.Canary(table, bind) - table.ddl_listeners['after-create'].append(canary.after_create) + event.listen(canary.after_create, 'on_after_create', table) canary.state = 'skipped' table.create(bind) @@ -70,9 +70,9 @@ class DDLEventTest(TestBase): def test_table_create_both(self): table, bind = self.table, self.bind canary = self.Canary(table, bind) - table.ddl_listeners['before-create'].append(canary.before_create) - table.ddl_listeners['after-create'].append(canary.after_create) - + event.listen(canary.before_create, 'on_before_create', table) + event.listen(canary.after_create, 'on_after_create', table) + table.create(bind) assert canary.state == 'after-create' table.drop(bind) @@ -81,7 +81,7 @@ class DDLEventTest(TestBase): def test_table_drop_before(self): table, bind = self.table, self.bind canary = self.Canary(table, bind) - table.ddl_listeners['before-drop'].append(canary.before_drop) + event.listen(canary.before_drop, 'on_before_drop', table) table.create(bind) assert canary.state is None @@ -91,7 +91,7 @@ class DDLEventTest(TestBase): def test_table_drop_after(self): table, bind = self.table, self.bind canary = self.Canary(table, bind) - table.ddl_listeners['after-drop'].append(canary.after_drop) + event.listen(canary.after_drop, 'on_after_drop', table) table.create(bind) assert canary.state is None @@ -102,8 +102,9 @@ class DDLEventTest(TestBase): def test_table_drop_both(self): table, bind = self.table, self.bind canary = self.Canary(table, bind) - table.ddl_listeners['before-drop'].append(canary.before_drop) - table.ddl_listeners['after-drop'].append(canary.after_drop) + + event.listen(canary.before_drop, 'on_before_drop', table) + event.listen(canary.after_drop, 'on_after_drop', table) table.create(bind) assert canary.state is None @@ -113,10 +114,11 @@ class DDLEventTest(TestBase): def test_table_all(self): table, bind = self.table, self.bind canary = self.Canary(table, bind) - table.ddl_listeners['before-create'].append(canary.before_create) - table.ddl_listeners['after-create'].append(canary.after_create) - table.ddl_listeners['before-drop'].append(canary.before_drop) - table.ddl_listeners['after-drop'].append(canary.after_drop) + + event.listen(canary.before_create, 'on_before_create', table) + event.listen(canary.after_create, 'on_after_create', table) + event.listen(canary.before_drop, 'on_before_drop', table) + event.listen(canary.after_drop, 'on_after_drop', table) assert canary.state is None table.create(bind) @@ -128,7 +130,7 @@ class DDLEventTest(TestBase): def test_table_create_before(self): metadata, bind = self.metadata, self.bind canary = self.Canary(metadata, bind) - metadata.ddl_listeners['before-create'].append(canary.before_create) + event.listen(canary.before_create, 'on_before_create', metadata) metadata.create_all(bind) assert canary.state == 'before-create' @@ -138,7 +140,7 @@ class DDLEventTest(TestBase): def test_metadata_create_after(self): metadata, bind = self.metadata, self.bind canary = self.Canary(metadata, bind) - metadata.ddl_listeners['after-create'].append(canary.after_create) + event.listen(canary.after_create, 'on_after_create', metadata) canary.state = 'skipped' metadata.create_all(bind) @@ -149,8 +151,9 @@ class DDLEventTest(TestBase): def test_metadata_create_both(self): metadata, bind = self.metadata, self.bind canary = self.Canary(metadata, bind) - metadata.ddl_listeners['before-create'].append(canary.before_create) - metadata.ddl_listeners['after-create'].append(canary.after_create) + + event.listen(canary.before_create, 'on_before_create', metadata) + event.listen(canary.after_create, 'on_after_create', metadata) metadata.create_all(bind) assert canary.state == 'after-create' @@ -160,11 +163,12 @@ class DDLEventTest(TestBase): def test_metadata_table_isolation(self): metadata, table, bind = self.metadata, self.table, self.bind table_canary = self.Canary(table, bind) - table.ddl_listeners['before-create' - ].append(table_canary.before_create) + + event.listen(table_canary.before_create, 'on_before_create', table) + metadata_canary = self.Canary(metadata, bind) - metadata.ddl_listeners['before-create' - ].append(metadata_canary.before_create) + event.listen(metadata_canary.before_create, 'on_before_create', + metadata) self.table.create(self.bind) assert metadata_canary.state == None @@ -174,10 +178,12 @@ class DDLEventTest(TestBase): fn = lambda *a: None table.append_ddl_listener('before-create', fn) - assert_raises(LookupError, table.append_ddl_listener, 'blah', fn) + assert_raises(exc.InvalidRequestError, table.append_ddl_listener, + 'blah', fn) metadata.append_ddl_listener('before-create', fn) - assert_raises(LookupError, metadata.append_ddl_listener, 'blah', fn) + assert_raises(exc.InvalidRequestError, metadata.append_ddl_listener, + 'blah', fn) class DDLExecutionTest(TestBase):