]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- basic docs
authorMike Bayer <mike_mp@zzzcomputing.com>
Tue, 9 Nov 2010 22:35:24 +0000 (17:35 -0500)
committerMike Bayer <mike_mp@zzzcomputing.com>
Tue, 9 Nov 2010 22:35:24 +0000 (17:35 -0500)
- poolevent accepts Engine as a target

doc/build/core/event.rst
doc/build/core/events.rst [new file with mode: 0644]
doc/build/core/index.rst
doc/build/core/interfaces.rst
doc/build/orm/events.rst [new file with mode: 0644]
doc/build/orm/interfaces.rst
lib/sqlalchemy/event.py
lib/sqlalchemy/events.py
lib/sqlalchemy/orm/events.py
test/engine/test_pool.py

index ec36ee5990d3bbcfdd30f622b18011c052ca13ad..946aa7901f87bc44ac9b1041ba0d39326affc716 100644 (file)
@@ -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 (file)
index 0000000..7f0d2d5
--- /dev/null
@@ -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:
+
index 377d475e722f3373b3ab9e5e94fc623539cd8b4e..53420777e95b1885ab942fe6f2fd0926250ffb92 100644 (file)
@@ -13,6 +13,8 @@ SQLAlchemy Core
     pooling
     schema
     types
+    event
+    events
     interfaces
     exceptions
     compiler
index c6254491a05b043977f242685d33953899a4cd81..393ef9be802ae4f27d275e078a06be5c3da31d0d 100644 (file)
@@ -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 (file)
index 0000000..87dbe5d
--- /dev/null
@@ -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
+
index 321660ac926f1e7c94686da2fa19299c3d05158e..b69f958d8f382c42ba0cfb15c331fcba2b0d24fc 100644 (file)
@@ -1,8 +1,7 @@
 .. _interfaces_orm_toplevel:
-.. _events_orm_toplevel:
 
-ORM Event Interfaces
-====================
+Deprecated ORM Event Interfaces
+================================
 
 .. module:: sqlalchemy.orm.interfaces
 
index 75512f7d2902e359b57f871ba0645981e492f06a..c39aff63e1d6b7538486e2a41f8ec8144c52810b 100644 (file)
@@ -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
 
index 55d5932af3612003b7c8c9d6f806b232ff199d93..355cf22358f2263e3e112bfb43376d70eab5fe5c 100644 (file)
@@ -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()``.
 
index 4007ffc6620c3833d74858a507155dfcb934e801..77af97b5fce286a0a2977168f1cdccd95ba2d6cc 100644 (file)
@@ -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".
      
     """
 
index 8d0039cb897d9152411a887247ebdb08875d6a01..26fef371c016b4c200dfd32afe8b96dd44ff39ea 100644 (file)
@@ -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):