From: Mike Bayer Date: Tue, 9 Nov 2010 22:35:24 +0000 (-0500) Subject: - basic docs X-Git-Tag: rel_0_7b1~253^2~9 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=a9b270a3ed4faf85f772897a867caf6762ff9160;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git - basic docs - poolevent accepts Engine as a target --- diff --git a/doc/build/core/event.rst b/doc/build/core/event.rst index ec36ee5990..946aa7901f 100644 --- a/doc/build/core/event.rst +++ b/doc/build/core/event.rst @@ -1,19 +1,87 @@ +.. _event_toplevel: + Events ====== -.. automodule:: sqlalchemy.event +SQLAlchemy includes an event API which publishes a wide variety of hooks into +the internals of both SQLAlchemy Core and ORM. The system is all new +as of version 0.7 and supercedes the previous system of "extension", "proxy", +and "listener" classes. -.. autofunction:: sqlalchemy.event.listen +Core events are described in :ref:`core_event_toplevel` and ORM events in :ref:`orm_event_toplevel`. + +Event Registration +------------------ + +Subscribing to an event occurs through a single API point, the :func:`.listen` function. This function +accepts a user-defined listening function, a string identifier which identifies the event to be +intercepted, and a target. Additional positional and keyword arguments may be supported by +specific types of events, which may specify alternate interfaces for the given event function, or provide +instructions regarding secondary event targets based on the given target. + +The name of an event and the argument signature of a corresponding listener function is derived from +a class bound specification method, which exists bound to a marker class that's described in the documentation. +For example, the documentation for :ref:`.PoolEvents.on_connect` indicates that the event name is ``"on_connect"`` +and that a user-defined listener function should receive two positional arguments:: + + from sqlalchemy.event import listen + from sqlalchemy.pool import Pool + + def my_on_connect(dbapi_con, connection_record): + print "New DBAPI connection:", dbapi_con + + listen(my_on_connect, 'on_connect', Pool) + +Targets +------- -Connection Pool Events ----------------------- +The :func:`.listen` function is very flexible regarding targets. It generally accepts classes, instances of those +classes, and related classes or objects from which the appropriate target can be derived. For example, +the above mentioned ``"on_connect"`` event accepts :class:`.Engine` classes and objects as well as :class:`.Pool` +classes and objects:: -.. autoclass:: sqlalchemy.pool.PoolEvents - :members: + from sqlalchemy.event import listen + from sqlalchemy.pool import Pool, QueuePool + from sqlalchemy import create_engine + from sqlalchemy.engine import Engine + import psycopg2 + + def connect(): + return psycopg2.connect(username='ed', host='127.0.0.1', dbname='test') -Connection Events ------------------------- + my_pool = QueuePool(connect) + my_engine = create_engine('postgresql://ed@localhost/test') + + # associate listener with all instances of Pool + listen(my_on_connect, 'on_connect', Pool) -.. autoclass:: sqlalchemy.engine.base.EngineEvents - :members: + # associate listener with all instances of Pool + # via the Engine class + listen(my_on_connect, 'on_connect', Engine) + + # associate listener with my_pool + listen(my_on_connect, 'on_connect', my_pool) + + # associate listener with my_engine.pool + listen(my_on_connect, 'on_connect', my_engine) + +Modifiers +---------- + +Some listeners allow modifiers to be passed to :func:`.listen`. These modifiers sometimes provide alternate +calling signatures for listeners. Such as with ORM events, some event listeners can have a return value +which modifies the subsequent handling. By default, no listener ever requires a return value, but by passing +``retval=True`` this value can be supported:: + + def validate_phone(target, value, oldvalue, initiator): + """Strip non-numeric characters from a phone number""" + + return re.sub(r'(?![0-9])', '', value) + + # setup listener on UserContact.phone attribute, instructing + # it to use the return value + listen(validate_phone, 'on_set', UserContact.phone, retval=True) + + +.. autofunction:: sqlalchemy.event.listen diff --git a/doc/build/core/events.rst b/doc/build/core/events.rst new file mode 100644 index 0000000000..7f0d2d530f --- /dev/null +++ b/doc/build/core/events.rst @@ -0,0 +1,26 @@ +.. _core_event_toplevel: + +Core Events +============ + +This section describes the event interfaces provided in SQLAlchemy Core. For an introduction +to the event listening API, see :ref:`event_toplevel`. ORM events are described in :ref:`orm_event_toplevel`. + +Connection Pool Events +----------------------- + +.. autoclass:: sqlalchemy.events.PoolEvents + :members: + +Connection Events +----------------------- + +.. autoclass:: sqlalchemy.events.EngineEvents + :members: + +Schema Events +----------------------- + +.. autoclass:: sqlalchemy.events.DDLEvents + :members: + diff --git a/doc/build/core/index.rst b/doc/build/core/index.rst index 377d475e72..53420777e9 100644 --- a/doc/build/core/index.rst +++ b/doc/build/core/index.rst @@ -13,6 +13,8 @@ SQLAlchemy Core pooling schema types + event + events interfaces exceptions compiler diff --git a/doc/build/core/interfaces.rst b/doc/build/core/interfaces.rst index c6254491a0..393ef9be80 100644 --- a/doc/build/core/interfaces.rst +++ b/doc/build/core/interfaces.rst @@ -1,17 +1,13 @@ -. _interfaces_core_toplevel: +.. _interfaces_core_toplevel: -Core Event Interfaces -====================== +Deprecated Event Interfaces +============================ .. module:: sqlalchemy.interfaces -This section describes the various categories of events which can be intercepted -in SQLAlchemy core, including execution and connection pool events. - -For ORM event documentation, see :ref:`interfaces_orm_toplevel`. - -A new version of this API with a significantly more flexible and consistent -interface will be available in version 0.7. +This section describes the class-based event interface introduced in +SQLAlchemy 0.5. As of SQLAlchemy 0.7, the new event system described in +:ref:`event_toplevel` should be used. Execution, Connection and Cursor Events --------------------------------------- diff --git a/doc/build/orm/events.rst b/doc/build/orm/events.rst new file mode 100644 index 0000000000..87dbe5d7d6 --- /dev/null +++ b/doc/build/orm/events.rst @@ -0,0 +1,38 @@ +.. _orm_event_toplevel: + +ORM Events +========== + +The ORM includes a wide variety of hooks available for subscription. The event +system in 0.7 is all new and supercedes the previous system of "extension" classes. +For an introduction to the event API, see :ref:`core_event_toplevel`. + +Attribute Events +---------------- + +.. autoclass:: sqlalchemy.orm.events.AttributeEvents + :members: + +Instrumentation Events +----------------------- + +.. autoclass:: sqlalchemy.orm.events.InstrumentationEvents + :members: + +Mapper Events +--------------- + +.. autoclass:: sqlalchemy.orm.events.MapperEvents + :members: + +Instance Events +--------------- + +.. autoclass:: sqlalchemy.orm.events.InstanceEvents + :members: + +Session Events +-------------- + +TODO + diff --git a/doc/build/orm/interfaces.rst b/doc/build/orm/interfaces.rst index 321660ac92..b69f958d8f 100644 --- a/doc/build/orm/interfaces.rst +++ b/doc/build/orm/interfaces.rst @@ -1,8 +1,7 @@ .. _interfaces_orm_toplevel: -.. _events_orm_toplevel: -ORM Event Interfaces -==================== +Deprecated ORM Event Interfaces +================================ .. module:: sqlalchemy.orm.interfaces diff --git a/lib/sqlalchemy/event.py b/lib/sqlalchemy/event.py index 75512f7d29..c39aff63e1 100644 --- a/lib/sqlalchemy/event.py +++ b/lib/sqlalchemy/event.py @@ -1,9 +1,4 @@ -""" -The event system handles all events throughout the :mod:`sqlalchemy` -and :mod:`sqlalchemy.orm` packages. - - -""" +"""Base event API.""" from sqlalchemy import util, exc diff --git a/lib/sqlalchemy/events.py b/lib/sqlalchemy/events.py index 55d5932af3..355cf22358 100644 --- a/lib/sqlalchemy/events.py +++ b/lib/sqlalchemy/events.py @@ -13,16 +13,16 @@ 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 PoolEvents(event.Events): @@ -41,8 +41,29 @@ class PoolEvents(event.Events): events.listen(my_on_checkout, 'on_checkout', Pool) + In addition to the :class:`.Pool` class and :class:`.Pool` instances, + :class:`.PoolEvents` also accepts :class:`.Engine` objects and + the :class:`.Engine` class as targets, which will be resolved + to the ``.pool`` attribute of the given engine or the :class:`.Pool` + class. + """ + @classmethod + def accept_with(cls, target): + from sqlalchemy.engine import Engine + from sqlalchemy.pool import Pool + + if isinstance(target, type): + if issubclass(target, Engine): + return Pool + elif issubclass(target, Pool): + return target + elif isinstance(target, Engine): + return target.pool + else: + return target + def on_connect(self, dbapi_connection, connection_record): """Called once for each new DB-API connection or Pool's ``creator()``. diff --git a/lib/sqlalchemy/orm/events.py b/lib/sqlalchemy/orm/events.py index 4007ffc662..77af97b5fc 100644 --- a/lib/sqlalchemy/orm/events.py +++ b/lib/sqlalchemy/orm/events.py @@ -156,22 +156,22 @@ class MapperEvents(event.Events): Several modifiers are available to the listen() function. :param propagate=False: When True, the event listener should - be applied to all inheriting mappers as well. + be applied to all inheriting mappers as well. :param raw=False: When True, the "target" argument to the - event, if applicable will be the :class:`.InstanceState` management - object, rather than the mapped instance itself. + event, if applicable will be the :class:`.InstanceState` management + object, rather than the mapped instance itself. :param retval=False: when True, the user-defined event listening - must have a return value, the purpose of which is either to - control subsequent event propagation, or to otherwise alter - the operation in progress by the mapper. Possible values - here are:: + must have a return value, the purpose of which is either to + control subsequent event propagation, or to otherwise alter + the operation in progress by the mapper. Possible values + here are: - * `sqlalchemy.orm.interfaces.EXT_CONTINUE` - continue event - processing normally. - * `sqlalchemy.orm.interfaces.EXT_STOP` - cancel all subsequent - event handlers in the chain. - * other values - the return value specified by specific listeners, - such as "translate_row" or "create_instance". + * ``sqlalchemy.orm.interfaces.EXT_CONTINUE`` - continue event + processing normally. + * ``sqlalchemy.orm.interfaces.EXT_STOP`` - cancel all subsequent + event handlers in the chain. + * other values - the return value specified by specific listeners, + such as "translate_row" or "create_instance". """ diff --git a/test/engine/test_pool.py b/test/engine/test_pool.py index 8d0039cb89..26fef371c0 100644 --- a/test/engine/test_pool.py +++ b/test/engine/test_pool.py @@ -186,6 +186,8 @@ class PoolTest(PoolTestBase): self.assert_(c.connection is not c2.connection) self.assert_(not c2.info) self.assert_('foo2' in c.info) + +class PoolEventsTest(PoolTestBase): @testing.uses_deprecated(r".*Use event.listen") def test_listeners(self): @@ -428,6 +430,7 @@ class PoolTest(PoolTestBase): def test_listener_after_oninit(self): """Test that listeners are called after OnInit is removed""" + called = [] def listener(*args): called.append(True) @@ -436,6 +439,33 @@ class PoolTest(PoolTestBase): engine.execute(select([1])).close() assert called, "Listener not called on connect" + def test_targets(self): + canary = [] + def listen_one(*args): + canary.append("listen_one") + def listen_two(*args): + canary.append("listen_two") + def listen_three(*args): + canary.append("listen_three") + def listen_four(*args): + canary.append("listen_four") + + engine = create_engine(testing.db.url) + event.listen(listen_one, 'on_connect', pool.Pool) + event.listen(listen_two, 'on_connect', engine.pool) + event.listen(listen_three, 'on_connect', engine) + event.listen(listen_four, 'on_connect', engine.__class__) + + engine.execute(select([1])).close() + eq_( + canary, ["listen_one","listen_four", "listen_two","listen_three"] + ) + + def teardown(self): + # TODO: need to get remove() functionality + # going + pool.Pool.dispatch.clear() + class QueuePoolTest(PoolTestBase):