--- /dev/null
+.. change::
+ :tags: change, general
+ :tickets: 4638
+
+ All long-deprecated "extension" classes have been removed, including
+ MapperExtension, SessionExtension, PoolListener, ConnectionProxy,
+ AttributExtension. These classes have been deprecated since version 0.7
+ long superseded by the event listener system.
+
+++ /dev/null
-.. _dep_interfaces_core_toplevel:
-
-Deprecated Event Interfaces
-===========================
-
-.. module:: sqlalchemy.interfaces
-
-This section describes the class-based core event interface introduced in
-SQLAlchemy 0.5. The ORM analogue is described at :ref:`dep_interfaces_orm_toplevel`.
-
-.. deprecated:: 0.7
- The new event system described in :ref:`event_toplevel` replaces
- the extension/proxy/listener system, providing a consistent interface
- to all events without the need for subclassing.
-
-Execution, Connection and Cursor Events
----------------------------------------
-
-.. autoclass:: ConnectionProxy
- :members:
- :undoc-members:
-
-Connection Pool Events
-----------------------
-
-.. autoclass:: PoolListener
- :members:
- :undoc-members:
-
-
+++ /dev/null
-:orphan:
-
-.. _dep_interfaces_orm_toplevel:
-
-Deprecated ORM Event Interfaces
-===============================
-
-.. module:: sqlalchemy.orm.interfaces
-
-This section describes the class-based ORM event interface which first
-existed in SQLAlchemy 0.1, which progressed with more kinds of events up
-until SQLAlchemy 0.5. The non-ORM analogue is described at :ref:`dep_interfaces_core_toplevel`.
-
-.. deprecated:: 0.7
- As of SQLAlchemy 0.7, the new event system described in
- :ref:`event_toplevel` replaces the extension/proxy/listener system, providing
- a consistent interface to all events without the need for subclassing.
-
-Mapper Events
--------------
-
-.. autoclass:: MapperExtension
- :members:
-
-Session Events
---------------
-
-.. autoclass:: SessionExtension
- :members:
-
-Attribute Events
-----------------
-
-.. autoclass:: AttributeExtension
- :members:
-
from .util import _distill_params
from .. import exc
from .. import inspection
-from .. import interfaces
from .. import log
from .. import util
from ..sql import schema
url,
logging_name=None,
echo=None,
- proxy=None,
execution_options=None,
hide_parameters=False,
):
self.echo = echo
self.hide_parameters = hide_parameters
log.instance_logger(self, echoflag=echo)
- if proxy:
- interfaces.ConnectionProxy._adapt_listener(self, proxy)
if execution_options:
self.update_execution_options(**execution_options)
+++ /dev/null
-# sqlalchemy/interfaces.py
-# Copyright (C) 2007-2019 the SQLAlchemy authors and contributors
-# <see AUTHORS file>
-# Copyright (C) 2007 Jason Kirtland jek@discorporate.us
-#
-# This module is part of SQLAlchemy and is released under
-# the MIT License: http://www.opensource.org/licenses/mit-license.php
-
-"""Deprecated core event interfaces.
-
-
-.. deprecated:: 0.7
- As of SQLAlchemy 0.7, the new event system described in
- :ref:`event_toplevel` replaces the extension/proxy/listener system,
- providing a consistent interface to all events without the need for
- subclassing.
-
-"""
-
-from . import event
-from . import util
-
-
-class PoolListener(object):
- """Hooks into the lifecycle of connections in a :class:`.Pool`.
-
- .. deprecated:: 0.7
-
- :class:`.PoolListener` is deprecated and will be removed in a future
- release. Please refer to :func:`.event.listen` in conjunction with
- the :class:`.PoolEvents` listener interface.
-
- Usage::
-
- class MyListener(PoolListener):
- def connect(self, dbapi_con, con_record):
- '''perform connect operations'''
- # etc.
-
- # create a new pool with a listener
- p = QueuePool(..., listeners=[MyListener()])
-
- # add a listener after the fact
- p.add_listener(MyListener())
-
- # usage with create_engine()
- e = create_engine("url://", listeners=[MyListener()])
-
- All of the standard connection :class:`~sqlalchemy.pool.Pool` types can
- accept event listeners for key connection lifecycle events:
- creation, pool check-out and check-in. There are no events fired
- when a connection closes.
-
- For any given DB-API connection, there will be one ``connect``
- event, `n` number of ``checkout`` events, and either `n` or `n - 1`
- ``checkin`` events. (If a ``Connection`` is detached from its
- pool via the ``detach()`` method, it won't be checked back in.)
-
- These are low-level events for low-level objects: raw Python
- DB-API connections, without the conveniences of the SQLAlchemy
- ``Connection`` wrapper, ``Dialect`` services or ``ClauseElement``
- execution. If you execute SQL through the connection, explicitly
- closing all cursors and other resources is recommended.
-
- Events also receive a ``_ConnectionRecord``, a long-lived internal
- ``Pool`` object that basically represents a "slot" in the
- connection pool. ``_ConnectionRecord`` objects have one public
- attribute of note: ``info``, a dictionary whose contents are
- scoped to the lifetime of the DB-API connection managed by the
- record. You can use this shared storage area however you like.
-
- There is no need to subclass ``PoolListener`` to handle events.
- Any class that implements one or more of these methods can be used
- as a pool listener. The ``Pool`` will inspect the methods
- provided by a listener object and add the listener to one or more
- internal event queues based on its capabilities. In terms of
- efficiency and function call overhead, you're much better off only
- providing implementations for the hooks you'll be using.
-
- """
-
- @classmethod
- def _adapt_listener(cls, self, listener):
- """Adapt a :class:`.PoolListener` to individual
- :class:`event.Dispatch` events.
-
- """
-
- methods = ["connect", "first_connect", "checkout", "checkin"]
- listener = util.as_interface(listener, methods=methods)
-
- for meth in methods:
- me_meth = getattr(PoolListener, meth)
- ls_meth = getattr(listener, meth, None)
-
- if ls_meth is not None and not util.methods_equivalent(
- me_meth, ls_meth
- ):
- util.warn_deprecated(
- "PoolListener.%s is deprecated. The "
- "PoolListener class will be removed in a future "
- "release. Please transition to the @event interface, "
- "using @event.listens_for(Engine, '%s')." % (meth, meth)
- )
-
- if hasattr(listener, "connect"):
- event.listen(self, "connect", listener.connect)
- if hasattr(listener, "first_connect"):
- event.listen(self, "first_connect", listener.first_connect)
- if hasattr(listener, "checkout"):
- event.listen(self, "checkout", listener.checkout)
- if hasattr(listener, "checkin"):
- event.listen(self, "checkin", listener.checkin)
-
- def connect(self, dbapi_con, con_record):
- """Called once for each new DB-API connection or Pool's ``creator()``.
-
- dbapi_con
- A newly connected raw DB-API connection (not a SQLAlchemy
- ``Connection`` wrapper).
-
- con_record
- The ``_ConnectionRecord`` that persistently manages the connection
-
- """
-
- def first_connect(self, dbapi_con, con_record):
- """Called exactly once for the first DB-API connection.
-
- dbapi_con
- A newly connected raw DB-API connection (not a SQLAlchemy
- ``Connection`` wrapper).
-
- con_record
- The ``_ConnectionRecord`` that persistently manages the connection
-
- """
-
- def checkout(self, dbapi_con, con_record, con_proxy):
- """Called when a connection is retrieved from the Pool.
-
- dbapi_con
- A raw DB-API connection
-
- con_record
- The ``_ConnectionRecord`` that persistently manages the connection
-
- con_proxy
- The ``_ConnectionFairy`` which manages the connection for the span of
- the current checkout.
-
- If you raise an ``exc.DisconnectionError``, the current
- connection will be disposed and a fresh connection retrieved.
- Processing of all checkout listeners will abort and restart
- using the new connection.
- """
-
- def checkin(self, dbapi_con, con_record):
- """Called when a connection returns to the pool.
-
- Note that the connection may be closed, and may be None if the
- connection has been invalidated. ``checkin`` will not be called
- for detached connections. (They do not return to the pool.)
-
- dbapi_con
- A raw DB-API connection
-
- con_record
- The ``_ConnectionRecord`` that persistently manages the connection
-
- """
-
-
-class ConnectionProxy(object):
- """Allows interception of statement execution by Connections.
-
- .. deprecated:: 0.7
-
- :class:`.ConnectionProxy` is deprecated and will be removed in a future
- release. Please refer to :func:`.event.listen` in conjunction with
- the :class:`.ConnectionEvents` listener interface.
-
- Either or both of the ``execute()`` and ``cursor_execute()``
- may be implemented to intercept compiled statement and
- cursor level executions, e.g.::
-
- class MyProxy(ConnectionProxy):
- def execute(self, conn, execute, clauseelement,
- *multiparams, **params):
- print "compiled statement:", clauseelement
- return execute(clauseelement, *multiparams, **params)
-
- def cursor_execute(self, execute, cursor, statement,
- parameters, context, executemany):
- print "raw statement:", statement
- return execute(cursor, statement, parameters, context)
-
- The ``execute`` argument is a function that will fulfill the default
- execution behavior for the operation. The signature illustrated
- in the example should be used.
-
- The proxy is installed into an :class:`~sqlalchemy.engine.Engine` via
- the ``proxy`` argument::
-
- e = create_engine('someurl://', proxy=MyProxy())
-
- """
-
- @classmethod
- def _adapt_listener(cls, self, listener):
-
- methods = [
- "execute",
- "cursor_execute",
- "begin",
- "rollback",
- "commit",
- "savepoint",
- "rollback_savepoint",
- "release_savepoint",
- "begin_twophase",
- "prepare_twophase",
- "rollback_twophase",
- "commit_twophase",
- ]
- for meth in methods:
- me_meth = getattr(ConnectionProxy, meth)
- ls_meth = getattr(listener, meth)
-
- if not util.methods_equivalent(me_meth, ls_meth):
- util.warn_deprecated(
- "ConnectionProxy.%s is deprecated. The "
- "ConnectionProxy class will be removed in a future "
- "release. Please transition to the @event interface, "
- "using @event.listens_for(Engine, '%s')." % (meth, meth)
- )
-
- def adapt_execute(conn, clauseelement, multiparams, params):
- def execute_wrapper(clauseelement, *multiparams, **params):
- return clauseelement, multiparams, params
-
- return listener.execute(
- conn, execute_wrapper, clauseelement, *multiparams, **params
- )
-
- event.listen(self, "before_execute", adapt_execute)
-
- def adapt_cursor_execute(
- conn, cursor, statement, parameters, context, executemany
- ):
- def execute_wrapper(cursor, statement, parameters, context):
- return statement, parameters
-
- return listener.cursor_execute(
- execute_wrapper,
- cursor,
- statement,
- parameters,
- context,
- executemany,
- )
-
- event.listen(self, "before_cursor_execute", adapt_cursor_execute)
-
- def do_nothing_callback(*arg, **kw):
- pass
-
- def adapt_listener(fn):
- def go(conn, *arg, **kw):
- fn(conn, do_nothing_callback, *arg, **kw)
-
- return util.update_wrapper(go, fn)
-
- event.listen(self, "begin", adapt_listener(listener.begin))
- event.listen(self, "rollback", adapt_listener(listener.rollback))
- event.listen(self, "commit", adapt_listener(listener.commit))
- event.listen(self, "savepoint", adapt_listener(listener.savepoint))
- event.listen(
- self,
- "rollback_savepoint",
- adapt_listener(listener.rollback_savepoint),
- )
- event.listen(
- self,
- "release_savepoint",
- adapt_listener(listener.release_savepoint),
- )
- event.listen(
- self, "begin_twophase", adapt_listener(listener.begin_twophase)
- )
- event.listen(
- self, "prepare_twophase", adapt_listener(listener.prepare_twophase)
- )
- event.listen(
- self,
- "rollback_twophase",
- adapt_listener(listener.rollback_twophase),
- )
- event.listen(
- self, "commit_twophase", adapt_listener(listener.commit_twophase)
- )
-
- def execute(self, conn, execute, clauseelement, *multiparams, **params):
- """Intercept high level execute() events."""
-
- return execute(clauseelement, *multiparams, **params)
-
- def cursor_execute(
- self, execute, cursor, statement, parameters, context, executemany
- ):
- """Intercept low-level cursor execute() events."""
-
- return execute(cursor, statement, parameters, context)
-
- def begin(self, conn, begin):
- """Intercept begin() events."""
-
- return begin()
-
- def rollback(self, conn, rollback):
- """Intercept rollback() events."""
-
- return rollback()
-
- def commit(self, conn, commit):
- """Intercept commit() events."""
-
- return commit()
-
- def savepoint(self, conn, savepoint, name=None):
- """Intercept savepoint() events."""
-
- return savepoint(name=name)
-
- def rollback_savepoint(self, conn, rollback_savepoint, name, context):
- """Intercept rollback_savepoint() events."""
-
- return rollback_savepoint(name, context)
-
- def release_savepoint(self, conn, release_savepoint, name, context):
- """Intercept release_savepoint() events."""
-
- return release_savepoint(name, context)
-
- def begin_twophase(self, conn, begin_twophase, xid):
- """Intercept begin_twophase() events."""
-
- return begin_twophase(xid)
-
- def prepare_twophase(self, conn, prepare_twophase, xid):
- """Intercept prepare_twophase() events."""
-
- return prepare_twophase(xid)
-
- def rollback_twophase(self, conn, rollback_twophase, xid, is_prepared):
- """Intercept rollback_twophase() events."""
-
- return rollback_twophase(xid, is_prepared)
-
- def commit_twophase(self, conn, commit_twophase, xid, is_prepared):
- """Intercept commit_twophase() events."""
-
- return commit_twophase(xid, is_prepared)
from . import exc # noqa
from . import mapper as mapperlib # noqa
from . import strategy_options
-from .deprecated_interfaces import AttributeExtension # noqa
-from .deprecated_interfaces import MapperExtension # noqa
-from .deprecated_interfaces import SessionExtension # noqa
from .descriptor_props import ComparableProperty # noqa
from .descriptor_props import CompositeProperty # noqa
from .descriptor_props import SynonymProperty # noqa
callable_,
dispatch,
trackparent=False,
- extension=None,
compare_function=None,
active_history=False,
parent_token=None,
if True, attempt to track if an instance has a parent attached
to it via this attribute.
- :param extension:
- a single or list of AttributeExtension object(s) which will
- receive set/delete/append/remove/etc. events.
- The event package is now used.
-
- .. deprecated:: 1.3
-
- The :paramref:`.AttributeImpl.extension` parameter is deprecated
- and will be removed in a future release, corresponding to the
- "extension" parameter on the :class:`.MapperProprty` classes
- like :func:`.column_property` and :func:`.relationship` The
- events system is now used.
-
:param compare_function:
a function that compares two values which are normally
assignable to this attribute.
else:
self.accepts_scalar_loader = self.default_accepts_scalar_loader
- # TODO: pass in the manager here
- # instead of doing a lookup
- attr = manager_of_class(class_)[key]
-
- for ext in util.to_list(extension or []):
- ext._adapt_listener(attr, ext)
-
if active_history:
self.dispatch._active_history = True
dispatch,
typecallable=None,
trackparent=False,
- extension=None,
copy_function=None,
compare_function=None,
**kwargs
callable_,
dispatch,
trackparent=trackparent,
- extension=extension,
compare_function=compare_function,
**kwargs
)
+++ /dev/null
-# orm/deprecated_interfaces.py
-# Copyright (C) 2005-2019 the SQLAlchemy authors and contributors
-# <see AUTHORS file>
-#
-# This module is part of SQLAlchemy and is released under
-# the MIT License: http://www.opensource.org/licenses/mit-license.php
-
-from .interfaces import EXT_CONTINUE
-from .. import event
-from .. import util
-
-
-@util.langhelpers.dependency_for("sqlalchemy.orm.interfaces")
-class MapperExtension(object):
- """Base implementation for :class:`.Mapper` event hooks.
-
- .. deprecated:: 0.7
-
- :class:`.MapperExtension` is deprecated and will be removed in a future
- release. Please refer to :func:`.event.listen` in conjunction with
- the :class:`.MapperEvents` listener interface.
-
- New extension classes subclass :class:`.MapperExtension` and are specified
- using the ``extension`` mapper() argument, which is a single
- :class:`.MapperExtension` or a list of such::
-
- from sqlalchemy.orm.interfaces import MapperExtension
-
- class MyExtension(MapperExtension):
- def before_insert(self, mapper, connection, instance):
- print "instance %s before insert !" % instance
-
- m = mapper(User, users_table, extension=MyExtension())
-
- A single mapper can maintain a chain of ``MapperExtension``
- objects. When a particular mapping event occurs, the
- corresponding method on each ``MapperExtension`` is invoked
- serially, and each method has the ability to halt the chain
- from proceeding further::
-
- m = mapper(User, users_table, extension=[ext1, ext2, ext3])
-
- Each ``MapperExtension`` method returns the symbol
- EXT_CONTINUE by default. This symbol generally means "move
- to the next ``MapperExtension`` for processing". For methods
- that return objects like translated rows or new object
- instances, EXT_CONTINUE means the result of the method
- should be ignored. In some cases it's required for a
- default mapper activity to be performed, such as adding a
- new instance to a result list.
-
- The symbol EXT_STOP has significance within a chain
- of ``MapperExtension`` objects that the chain will be stopped
- when this symbol is returned. Like EXT_CONTINUE, it also
- has additional significance in some cases that a default
- mapper activity will not be performed.
-
- """
-
- @classmethod
- def _adapt_instrument_class(cls, self, listener):
- cls._adapt_listener_methods(self, listener, ("instrument_class",))
-
- @classmethod
- def _adapt_listener(cls, self, listener):
- cls._adapt_listener_methods(
- self,
- listener,
- (
- "init_instance",
- "init_failed",
- "reconstruct_instance",
- "before_insert",
- "after_insert",
- "before_update",
- "after_update",
- "before_delete",
- "after_delete",
- ),
- )
-
- @classmethod
- def _adapt_listener_methods(cls, self, listener, methods):
-
- for meth in methods:
- me_meth = getattr(MapperExtension, meth)
- ls_meth = getattr(listener, meth)
-
- if not util.methods_equivalent(me_meth, ls_meth):
- util.warn_deprecated(
- "MapperExtension.%s is deprecated. The "
- "MapperExtension class will be removed in a future "
- "release. Please transition to the @event interface, "
- "using @event.listens_for(mapped_class, '%s')."
- % (meth, meth)
- )
-
- if meth == "reconstruct_instance":
-
- def go(ls_meth):
- def reconstruct(instance, ctx):
- ls_meth(self, instance)
-
- return reconstruct
-
- event.listen(
- self.class_manager,
- "load",
- go(ls_meth),
- raw=False,
- propagate=True,
- )
- elif meth == "init_instance":
-
- def go(ls_meth):
- def init_instance(instance, args, kwargs):
- ls_meth(
- self,
- self.class_,
- self.class_manager.original_init,
- instance,
- args,
- kwargs,
- )
-
- return init_instance
-
- event.listen(
- self.class_manager,
- "init",
- go(ls_meth),
- raw=False,
- propagate=True,
- )
- elif meth == "init_failed":
-
- def go(ls_meth):
- def init_failed(instance, args, kwargs):
- util.warn_exception(
- ls_meth,
- self,
- self.class_,
- self.class_manager.original_init,
- instance,
- args,
- kwargs,
- )
-
- return init_failed
-
- event.listen(
- self.class_manager,
- "init_failure",
- go(ls_meth),
- raw=False,
- propagate=True,
- )
- else:
- event.listen(
- self,
- "%s" % meth,
- ls_meth,
- raw=False,
- retval=True,
- propagate=True,
- )
-
- def instrument_class(self, mapper, class_):
- """Receive a class when the mapper is first constructed, and has
- applied instrumentation to the mapped class.
-
- The return value is only significant within the ``MapperExtension``
- chain; the parent mapper's behavior isn't modified by this method.
-
- """
- return EXT_CONTINUE
-
- def init_instance(self, mapper, class_, oldinit, instance, args, kwargs):
- """Receive an instance when its constructor is called.
-
- This method is only called during a userland construction of
- an object. It is not called when an object is loaded from the
- database.
-
- The return value is only significant within the ``MapperExtension``
- chain; the parent mapper's behavior isn't modified by this method.
-
- """
- return EXT_CONTINUE
-
- def init_failed(self, mapper, class_, oldinit, instance, args, kwargs):
- """Receive an instance when its constructor has been called,
- and raised an exception.
-
- This method is only called during a userland construction of
- an object. It is not called when an object is loaded from the
- database.
-
- The return value is only significant within the ``MapperExtension``
- chain; the parent mapper's behavior isn't modified by this method.
-
- """
- return EXT_CONTINUE
-
- def reconstruct_instance(self, mapper, instance):
- """Receive an object instance after it has been created via
- ``__new__``, and after initial attribute population has
- occurred.
-
- This typically occurs when the instance is created based on
- incoming result rows, and is only called once for that
- instance's lifetime.
-
- Note that during a result-row load, this method is called upon
- the first row received for this instance. Note that some
- attributes and collections may or may not be loaded or even
- initialized, depending on what's present in the result rows.
-
- The return value is only significant within the ``MapperExtension``
- chain; the parent mapper's behavior isn't modified by this method.
-
- """
- return EXT_CONTINUE
-
- def before_insert(self, mapper, connection, instance):
- """Receive an object instance before that instance is inserted
- into its table.
-
- This is a good place to set up primary key values and such
- that aren't handled otherwise.
-
- Column-based attributes can be modified within this method
- which will result in the new value being inserted. However
- *no* changes to the overall flush plan can be made, and
- manipulation of the ``Session`` will not have the desired effect.
- To manipulate the ``Session`` within an extension, use
- ``SessionExtension``.
-
- The return value is only significant within the ``MapperExtension``
- chain; the parent mapper's behavior isn't modified by this method.
-
- """
-
- return EXT_CONTINUE
-
- def after_insert(self, mapper, connection, instance):
- """Receive an object instance after that instance is inserted.
-
- The return value is only significant within the ``MapperExtension``
- chain; the parent mapper's behavior isn't modified by this method.
-
- """
-
- return EXT_CONTINUE
-
- def before_update(self, mapper, connection, instance):
- """Receive an object instance before that instance is updated.
-
- Note that this method is called for all instances that are marked as
- "dirty", even those which have no net changes to their column-based
- attributes. An object is marked as dirty when any of its column-based
- attributes have a "set attribute" operation called or when any of its
- collections are modified. If, at update time, no column-based
- attributes have any net changes, no UPDATE statement will be issued.
- This means that an instance being sent to before_update is *not* a
- guarantee that an UPDATE statement will be issued (although you can
- affect the outcome here).
-
- To detect if the column-based attributes on the object have net
- changes, and will therefore generate an UPDATE statement, use
- ``object_session(instance).is_modified(instance,
- include_collections=False)``.
-
- Column-based attributes can be modified within this method
- which will result in the new value being updated. However
- *no* changes to the overall flush plan can be made, and
- manipulation of the ``Session`` will not have the desired effect.
- To manipulate the ``Session`` within an extension, use
- ``SessionExtension``.
-
- The return value is only significant within the ``MapperExtension``
- chain; the parent mapper's behavior isn't modified by this method.
-
- """
-
- return EXT_CONTINUE
-
- def after_update(self, mapper, connection, instance):
- """Receive an object instance after that instance is updated.
-
- The return value is only significant within the ``MapperExtension``
- chain; the parent mapper's behavior isn't modified by this method.
-
- """
-
- return EXT_CONTINUE
-
- def before_delete(self, mapper, connection, instance):
- """Receive an object instance before that instance is deleted.
-
- Note that *no* changes to the overall flush plan can be made
- here; and manipulation of the ``Session`` will not have the
- desired effect. To manipulate the ``Session`` within an
- extension, use ``SessionExtension``.
-
- The return value is only significant within the ``MapperExtension``
- chain; the parent mapper's behavior isn't modified by this method.
-
- """
-
- return EXT_CONTINUE
-
- def after_delete(self, mapper, connection, instance):
- """Receive an object instance after that instance is deleted.
-
- The return value is only significant within the ``MapperExtension``
- chain; the parent mapper's behavior isn't modified by this method.
-
- """
-
- return EXT_CONTINUE
-
-
-@util.langhelpers.dependency_for("sqlalchemy.orm.interfaces")
-class SessionExtension(object):
-
- """Base implementation for :class:`.Session` event hooks.
-
- .. deprecated:: 0.7
-
- :class:`.SessionExtension` is deprecated and will be removed in a future
- release. Please refer to :func:`.event.listen` in conjunction with
- the :class:`.SessionEvents` listener interface.
-
- Subclasses may be installed into a :class:`.Session` (or
- :class:`.sessionmaker`) using the ``extension`` keyword
- argument::
-
- from sqlalchemy.orm.interfaces import SessionExtension
-
- class MySessionExtension(SessionExtension):
- def before_commit(self, session):
- print "before commit!"
-
- Session = sessionmaker(extension=MySessionExtension())
-
- The same :class:`.SessionExtension` instance can be used
- with any number of sessions.
-
- """
-
- @classmethod
- def _adapt_listener(cls, self, listener):
- for meth in [
- "before_commit",
- "after_commit",
- "after_rollback",
- "before_flush",
- "after_flush",
- "after_flush_postexec",
- "after_begin",
- "after_attach",
- "after_bulk_update",
- "after_bulk_delete",
- ]:
- me_meth = getattr(SessionExtension, meth)
- ls_meth = getattr(listener, meth)
-
- if not util.methods_equivalent(me_meth, ls_meth):
- util.warn_deprecated(
- "SessionExtension.%s is deprecated. The "
- "SessionExtension class will be removed in a future "
- "release. Please transition to the @event interface, "
- "using @event.listens_for(Session, '%s')." % (meth, meth)
- )
-
- event.listen(self, meth, getattr(listener, meth))
-
- def before_commit(self, session):
- """Execute right before commit is called.
-
- Note that this may not be per-flush if a longer running
- transaction is ongoing."""
-
- def after_commit(self, session):
- """Execute after a commit has occurred.
-
- Note that this may not be per-flush if a longer running
- transaction is ongoing."""
-
- def after_rollback(self, session):
- """Execute after a rollback has occurred.
-
- Note that this may not be per-flush if a longer running
- transaction is ongoing."""
-
- def before_flush(self, session, flush_context, instances):
- """Execute before flush process has started.
-
- `instances` is an optional list of objects which were passed to
- the ``flush()`` method. """
-
- def after_flush(self, session, flush_context):
- """Execute after flush has completed, but before commit has been
- called.
-
- Note that the session's state is still in pre-flush, i.e. 'new',
- 'dirty', and 'deleted' lists still show pre-flush state as well
- as the history settings on instance attributes."""
-
- def after_flush_postexec(self, session, flush_context):
- """Execute after flush has completed, and after the post-exec
- state occurs.
-
- This will be when the 'new', 'dirty', and 'deleted' lists are in
- their final state. An actual commit() may or may not have
- occurred, depending on whether or not the flush started its own
- transaction or participated in a larger transaction. """
-
- def after_begin(self, session, transaction, connection):
- """Execute after a transaction is begun on a connection
-
- `transaction` is the SessionTransaction. This method is called
- after an engine level transaction is begun on a connection. """
-
- def after_attach(self, session, instance):
- """Execute after an instance is attached to a session.
-
- This is called after an add, delete or merge. """
-
- def after_bulk_update(self, session, query, query_context, result):
- """Execute after a bulk update operation to the session.
-
- This is called after a session.query(...).update()
-
- `query` is the query object that this update operation was
- called on. `query_context` was the query context object.
- `result` is the result object returned from the bulk operation.
- """
-
- def after_bulk_delete(self, session, query, query_context, result):
- """Execute after a bulk delete operation to the session.
-
- This is called after a session.query(...).delete()
-
- `query` is the query object that this delete operation was
- called on. `query_context` was the query context object.
- `result` is the result object returned from the bulk operation.
- """
-
-
-@util.langhelpers.dependency_for("sqlalchemy.orm.interfaces")
-class AttributeExtension(object):
- """Base implementation for :class:`.AttributeImpl` event hooks, events
- that fire upon attribute mutations in user code.
-
- .. deprecated:: 0.7
-
- :class:`.AttributeExtension` is deprecated and will be removed in a
- future release. Please refer to :func:`.event.listen` in conjunction
- with the :class:`.AttributeEvents` listener interface.
-
- :class:`.AttributeExtension` is used to listen for set,
- remove, and append events on individual mapped attributes.
- It is established on an individual mapped attribute using
- the `extension` argument, available on
- :func:`.column_property`, :func:`.relationship`, and
- others::
-
- from sqlalchemy.orm.interfaces import AttributeExtension
- from sqlalchemy.orm import mapper, relationship, column_property
-
- class MyAttrExt(AttributeExtension):
- def append(self, state, value, initiator):
- print "append event !"
- return value
-
- def set(self, state, value, oldvalue, initiator):
- print "set event !"
- return value
-
- mapper(SomeClass, sometable, properties={
- 'foo':column_property(sometable.c.foo, extension=MyAttrExt()),
- 'bar':relationship(Bar, extension=MyAttrExt())
- })
-
- Note that the :class:`.AttributeExtension` methods
- :meth:`~.AttributeExtension.append` and
- :meth:`~.AttributeExtension.set` need to return the
- ``value`` parameter. The returned value is used as the
- effective value, and allows the extension to change what is
- ultimately persisted.
-
- AttributeExtension is assembled within the descriptors associated
- with a mapped class.
-
- """
-
- active_history = True
- """indicates that the set() method would like to receive the 'old' value,
- even if it means firing lazy callables.
-
- Note that ``active_history`` can also be set directly via
- :func:`.column_property` and :func:`.relationship`.
-
- """
-
- @classmethod
- def _adapt_listener(cls, self, listener):
- for meth in ["append", "remove", "set"]:
- me_meth = getattr(AttributeExtension, meth)
- ls_meth = getattr(listener, meth)
-
- if not util.methods_equivalent(me_meth, ls_meth):
- util.warn_deprecated(
- "AttributeExtension.%s is deprecated. The "
- "AttributeExtension class will be removed in a future "
- "release. Please transition to the @event interface, "
- "using @event.listens_for(Class.attribute, '%s')."
- % (meth, meth)
- )
-
- event.listen(
- self,
- "append",
- listener.append,
- active_history=listener.active_history,
- raw=True,
- retval=True,
- )
- event.listen(
- self,
- "remove",
- listener.remove,
- active_history=listener.active_history,
- raw=True,
- retval=True,
- )
- event.listen(
- self,
- "set",
- listener.set,
- active_history=listener.active_history,
- raw=True,
- retval=True,
- )
-
- def append(self, state, value, initiator):
- """Receive a collection append event.
-
- The returned value will be used as the actual value to be
- appended.
-
- """
- return value
-
- def remove(self, state, value, initiator):
- """Receive a remove event.
-
- No return value is defined.
-
- """
- pass
-
- def set(self, state, value, oldvalue, initiator):
- """Receive a set event.
-
- The returned value will be used as the actual value to be
- set.
-
- """
- return value
"""
- @util.deprecated_params(
- extension=(
- "0.7",
- ":class:`.AttributeExtension` is deprecated in favor of the "
- ":class:`.AttributeEvents` listener interface. The "
- ":paramref:`.composite.extension` parameter will be "
- "removed in a future release.",
- )
- )
def __init__(self, class_, *attrs, **kwargs):
r"""Return a composite column-based property for use with a Mapper.
:param info: Optional data dictionary which will be populated into the
:attr:`.MapperProperty.info` attribute of this object.
- :param extension:
- an :class:`.AttributeExtension` instance,
- or list of extensions, which will be prepended to the list of
- attribute listeners for the resulting descriptor placed on the
- class.
-
"""
super(CompositeProperty, self).__init__()
Contains various base classes used throughout the ORM.
-Defines some key base classes prominent within the internals,
-as well as the now-deprecated ORM extension classes.
+Defines some key base classes prominent within the internals.
-Other than the deprecated extensions, this module and the
-classes within are mostly private, though some attributes
+This module and the classes within are mostly private, though some attributes
are exposed when inspecting mappings.
"""
from ..sql import operators
-# imported later
-MapperExtension = SessionExtension = AttributeExtension = None
-
__all__ = (
- "AttributeExtension",
"EXT_CONTINUE",
"EXT_STOP",
"EXT_SKIP",
"MANYTOONE",
"NOT_EXTENSION",
"LoaderStrategy",
- "MapperExtension",
"MapperOption",
"MapperProperty",
"PropComparator",
- "SessionExtension",
"StrategizedProperty",
)
_dispose_called = False
@util.deprecated_params(
- extension=(
- "0.7",
- ":class:`.MapperExtension` is deprecated in favor of the "
- ":class:`.MapperEvents` listener interface. The "
- ":paramref:`.mapper.extension` parameter will be "
- "removed in a future release.",
- ),
order_by=(
"1.1",
"The :paramref:`.mapper.order_by` parameter "
inherits=None,
inherit_condition=None,
inherit_foreign_keys=None,
- extension=None,
order_by=False,
always_refresh=False,
version_id_col=None,
See :ref:`include_exclude_cols` for an example.
- :param extension: A :class:`.MapperExtension` instance or
- list of :class:`.MapperExtension` instances which will be applied
- to all operations by this :class:`.Mapper`.
-
:param include_properties: An inclusive list or set of string column
names to map.
self._memoized_values = {}
self._compiled_cache_size = _compiled_cache_size
self._reconstructor = None
- self._deprecated_extensions = util.to_list(extension or [])
self.allow_partial_pks = allow_partial_pks
if self.inherits and not self.concrete:
try:
self.dispatch._events._new_mapper_instance(class_, self)
self._configure_inheritance()
- self._configure_legacy_instrument_class()
self._configure_class_instrumentation()
- self._configure_listeners()
self._configure_properties()
self._configure_polymorphic_setter()
self._configure_pks()
"Class '%s' does not inherit from '%s'"
% (self.class_.__name__, self.inherits.class_.__name__)
)
+
+ self.dispatch._update(self.inherits.dispatch)
+
if self.non_primary != self.inherits.non_primary:
np = not self.non_primary and "primary" or "non-primary"
raise sa_exc.ArgumentError(
self.polymorphic_on = polymorphic_on
self._configure_polymorphic_setter(True)
- def _configure_legacy_instrument_class(self):
-
- if self.inherits:
- self.dispatch._update(self.inherits.dispatch)
- super_extensions = set(
- chain(
- *[
- m._deprecated_extensions
- for m in self.inherits.iterate_to_root()
- ]
- )
- )
- else:
- super_extensions = set()
-
- for ext in self._deprecated_extensions:
- if ext not in super_extensions:
- ext._adapt_instrument_class(self, ext)
-
- def _configure_listeners(self):
- if self.inherits:
- super_extensions = set(
- chain(
- *[
- m._deprecated_extensions
- for m in self.inherits.iterate_to_root()
- ]
- )
- )
- else:
- super_extensions = set()
-
- for ext in self._deprecated_extensions:
- if ext not in super_extensions:
- ext._adapt_listener(self, ext)
-
def _configure_class_instrumentation(self):
"""If this mapper is to be a primary mapper (i.e. the
non_primary flag is not set), associate this Mapper with the
"instrument",
"comparator_factory",
"descriptor",
- "extension",
"active_history",
"expire_on_flush",
"info",
"_deferred_column_loader",
)
- @util.deprecated_params(
- extension=(
- "0.7",
- ":class:`.AttributeExtension` is deprecated in favor of the "
- ":class:`.AttributeEvents` listener interface. The "
- ":paramref:`.column_property.extension` parameter will be "
- "removed in a future release.",
- )
- )
def __init__(self, *columns, **kwargs):
r"""Provide a column-level property for use with a Mapper.
:param info: Optional data dictionary which will be populated into the
:attr:`.MapperProperty.info` attribute of this object.
- :param extension:
- an :class:`.AttributeExtension` instance, or list of extensions,
- which will be prepended to the list of attribute listeners for the
- resulting descriptor placed on the class.
"""
super(ColumnProperty, self).__init__()
"comparator_factory", self.__class__.Comparator
)
self.descriptor = kwargs.pop("descriptor", None)
- self.extension = kwargs.pop("extension", None)
self.active_history = kwargs.pop("active_history", False)
self.expire_on_flush = kwargs.pop("expire_on_flush", True)
_dependency_processor = None
- @util.deprecated_params(
- extension=(
- "0.7",
- ":class:`.AttributeExtension` is deprecated in favor of the "
- ":class:`.AttributeEvents` listener interface. The "
- ":paramref:`.relationship.extension` parameter will be "
- "removed in a future release.",
- )
- )
def __init__(
self,
argument,
back_populates=None,
post_update=False,
cascade=False,
- extension=None,
viewonly=False,
lazy="select",
collection_class=None,
:param doc:
docstring which will be applied to the resulting descriptor.
- :param extension:
- an :class:`.AttributeExtension` instance, or list of extensions,
- which will be prepended to the list of attribute listeners for
- the resulting descriptor placed on the class.
-
:param foreign_keys:
a list of columns which are to be used as "foreign key"
self.join_depth = join_depth
self.omit_join = omit_join
self.local_remote_pairs = _local_remote_pairs
- self.extension = extension
self.bake_queries = bake_queries
self.load_on_pending = load_on_pending
self.comparator_factory = (
from .base import object_mapper
from .base import object_state
from .base import state_str
-from .deprecated_interfaces import SessionExtension
from .unitofwork import UOWTransaction
from .. import engine
from .. import exc as sa_exc
from ..sql import util as sql_util
-__all__ = ["Session", "SessionTransaction", "SessionExtension", "sessionmaker"]
+__all__ = ["Session", "SessionTransaction", "sessionmaker"]
_sessions = weakref.WeakValueDictionary()
"""Weak-referencing dictionary of :class:`.Session` objects.
"The :paramref:`.Session._enable_transaction_accounting` "
"parameter is deprecated and will be removed in a future release.",
),
- extension=(
- "0.7",
- ":class:`.SessionExtension` is deprecated in favor of the "
- ":class:`.SessionEvents` listener interface. The "
- ":paramref:`.Session.extension` parameter will be "
- "removed in a future release.",
- ),
)
def __init__(
self,
twophase=False,
weak_identity_map=None,
binds=None,
- extension=None,
enable_baked_queries=True,
info=None,
query_cls=None,
so that all attribute/object access subsequent to a completed
transaction will load from the most recent database state.
- :param extension: An optional
- :class:`~.SessionExtension` instance, or a list
- of such instances, which will receive pre- and post- commit and
- flush events, as well as a post-rollback event.
-
:param info: optional dictionary of arbitrary data to be associated
with this :class:`.Session`. Is available via the
:attr:`.Session.info` attribute. Note the dictionary is copied at
if info:
self.info.update(info)
- if extension:
- for ext in util.to_list(extension):
- SessionExtension._adapt_listener(self, ext)
-
if binds is not None:
for key, bind in binds.items():
self._add_bind(key, bind)
**kw
):
- attribute_ext = list(util.to_list(prop.extension, default=[]))
-
listen_hooks = []
uselist = useobject and prop.uselist
uselist=uselist,
compare_function=compare_function,
useobject=useobject,
- extension=attribute_ext,
trackparent=useobject
and (
prop.single_parent
from .. import event
from .. import exc
-from .. import interfaces
from .. import log
from .. import util
from ..util import threading
_dialect = _ConnDialect()
- @util.deprecated_params(
- listeners=(
- "0.7",
- ":class:`.PoolListener` is deprecated in favor of the "
- ":class:`.PoolEvents` listener interface. The "
- ":paramref:`.Pool.listeners` parameter will be removed in a "
- "future release.",
- )
- )
def __init__(
self,
creator,
echo=None,
logging_name=None,
reset_on_return=True,
- listeners=None,
events=None,
dialect=None,
pre_ping=False,
can be assigned via :func:`.create_engine` before dialect-level
listeners are applied.
- :param listeners: A list of :class:`.PoolListener`-like objects or
- dictionaries of callables that receive events when DB-API
- connections are created, checked out and checked in to the
- pool.
-
:param dialect: a :class:`.Dialect` that will handle the job
of calling rollback(), close(), or commit() on DBAPI connections.
If omitted, a built-in "stub" dialect is used. Applications that
if events:
for fn, target in events:
event.listen(self, target, fn)
- if listeners:
- for l in listeners:
- self.add_listener(l)
@property
def _creator(self):
"Exception closing connection %r", connection, exc_info=True
)
- @util.deprecated(
- "0.7",
- "The :meth:`.Pool.add_listener` method is deprecated and "
- "will be removed in a future release. Please use the "
- ":class:`.PoolEvents` listener interface.",
- )
- def add_listener(self, listener):
- """Add a :class:`.PoolListener`-like object to this pool.
-
- ``listener`` may be an object that implements some or all of
- PoolListener, or a dictionary of callables containing implementations
- of some or all of the named methods in PoolListener.
-
- """
- interfaces.PoolListener._adapt_listener(self, listener)
-
def _create_connection(self):
"""Called by subclasses to create a new ConnectionRecord."""
-import re
-
import sqlalchemy as tsa
from sqlalchemy import column
from sqlalchemy import create_engine
from sqlalchemy import TypeDecorator
from sqlalchemy.engine.base import Engine
from sqlalchemy.engine.mock import MockConnection
-from sqlalchemy.interfaces import ConnectionProxy
from sqlalchemy.testing import assert_raises
from sqlalchemy.testing import assert_raises_message
from sqlalchemy.testing import engines
from sqlalchemy.testing.mock import Mock
from sqlalchemy.testing.schema import Column
from sqlalchemy.testing.schema import Table
-from sqlalchemy.testing.util import lazy_gc
class SomeException(Exception):
)
-def _proxy_execute_deprecated():
- return (
- testing.expect_deprecated("ConnectionProxy.execute is deprecated."),
- testing.expect_deprecated(
- "ConnectionProxy.cursor_execute is deprecated."
- ),
- )
-
-
class TransactionTest(fixtures.TestBase):
__backend__ = True
eq_(conn.execute(users.select()).fetchall(), [(1, "user1")])
-class ProxyConnectionTest(fixtures.TestBase):
-
- """These are the same tests as EngineEventsTest, except using
- the deprecated ConnectionProxy interface.
-
- """
-
- __requires__ = ("ad_hoc_engines",)
- __prefer_requires__ = ("two_phase_transactions",)
-
- @testing.uses_deprecated(r".*Use event.listen")
- @testing.fails_on("firebird", "Data type unknown")
- def test_proxy(self):
-
- stmts = []
- cursor_stmts = []
-
- class MyProxy(ConnectionProxy):
- def execute(
- self, conn, execute, clauseelement, *multiparams, **params
- ):
- stmts.append((str(clauseelement), params, multiparams))
- return execute(clauseelement, *multiparams, **params)
-
- def cursor_execute(
- self,
- execute,
- cursor,
- statement,
- parameters,
- context,
- executemany,
- ):
- cursor_stmts.append((str(statement), parameters, None))
- return execute(cursor, statement, parameters, context)
-
- def assert_stmts(expected, received):
- for stmt, params, posn in expected:
- if not received:
- assert False, "Nothing available for stmt: %s" % stmt
- while received:
- teststmt, testparams, testmultiparams = received.pop(0)
- teststmt = (
- re.compile(r"[\n\t ]+", re.M)
- .sub(" ", teststmt)
- .strip()
- )
- if teststmt.startswith(stmt) and (
- testparams == params or testparams == posn
- ):
- break
-
- with testing.expect_deprecated(
- "ConnectionProxy.execute is deprecated.",
- "ConnectionProxy.cursor_execute is deprecated.",
- ):
- plain_engine = engines.testing_engine(
- options=dict(implicit_returning=False, proxy=MyProxy())
- )
-
- for engine in (plain_engine,):
- m = MetaData(engine)
- t1 = Table(
- "t1",
- m,
- Column("c1", Integer, primary_key=True),
- Column(
- "c2",
- String(50),
- default=func.lower("Foo"),
- primary_key=True,
- ),
- )
- m.create_all()
- try:
- t1.insert().execute(c1=5, c2="some data")
- t1.insert().execute(c1=6)
- eq_(
- engine.execute("select * from t1").fetchall(),
- [(5, "some data"), (6, "foo")],
- )
- finally:
- m.drop_all()
- engine.dispose()
- compiled = [
- ("CREATE TABLE t1", {}, None),
- (
- "INSERT INTO t1 (c1, c2)",
- {"c2": "some data", "c1": 5},
- None,
- ),
- ("INSERT INTO t1 (c1, c2)", {"c1": 6}, None),
- ("select * from t1", {}, None),
- ("DROP TABLE t1", {}, None),
- ]
-
- cursor = [
- ("CREATE TABLE t1", {}, ()),
- (
- "INSERT INTO t1 (c1, c2)",
- {"c2": "some data", "c1": 5},
- (5, "some data"),
- ),
- ("SELECT lower", {"lower_1": "Foo"}, ("Foo",)),
- (
- "INSERT INTO t1 (c1, c2)",
- {"c2": "foo", "c1": 6},
- (6, "foo"),
- ),
- ("select * from t1", {}, ()),
- ("DROP TABLE t1", {}, ()),
- ]
-
- assert_stmts(compiled, stmts)
- assert_stmts(cursor, cursor_stmts)
-
- @testing.uses_deprecated(r".*Use event.listen")
- def test_options(self):
- canary = []
-
- class TrackProxy(ConnectionProxy):
- def __getattribute__(self, key):
- fn = object.__getattribute__(self, key)
-
- def go(*arg, **kw):
- canary.append(fn.__name__)
- return fn(*arg, **kw)
-
- return go
-
- with testing.expect_deprecated(
- *[
- "ConnectionProxy.%s is deprecated" % name
- for name in [
- "execute",
- "cursor_execute",
- "begin",
- "rollback",
- "commit",
- "savepoint",
- "rollback_savepoint",
- "release_savepoint",
- "begin_twophase",
- "prepare_twophase",
- "rollback_twophase",
- "commit_twophase",
- ]
- ]
- ):
- engine = engines.testing_engine(options={"proxy": TrackProxy()})
- conn = engine.connect()
- c2 = conn.execution_options(foo="bar")
- eq_(c2._execution_options, {"foo": "bar"})
- c2.execute(select([1]))
- c3 = c2.execution_options(bar="bat")
- eq_(c3._execution_options, {"foo": "bar", "bar": "bat"})
- eq_(canary, ["execute", "cursor_execute"])
-
- @testing.uses_deprecated(r".*Use event.listen")
- def test_transactional(self):
- canary = []
-
- class TrackProxy(ConnectionProxy):
- def __getattribute__(self, key):
- fn = object.__getattribute__(self, key)
-
- def go(*arg, **kw):
- canary.append(fn.__name__)
- return fn(*arg, **kw)
-
- return go
-
- with testing.expect_deprecated(
- *[
- "ConnectionProxy.%s is deprecated" % name
- for name in [
- "execute",
- "cursor_execute",
- "begin",
- "rollback",
- "commit",
- "savepoint",
- "rollback_savepoint",
- "release_savepoint",
- "begin_twophase",
- "prepare_twophase",
- "rollback_twophase",
- "commit_twophase",
- ]
- ]
- ):
- engine = engines.testing_engine(options={"proxy": TrackProxy()})
- conn = engine.connect()
- trans = conn.begin()
- conn.execute(select([1]))
- trans.rollback()
- trans = conn.begin()
- conn.execute(select([1]))
- trans.commit()
-
- eq_(
- canary,
- [
- "begin",
- "execute",
- "cursor_execute",
- "rollback",
- "begin",
- "execute",
- "cursor_execute",
- "commit",
- ],
- )
-
- @testing.uses_deprecated(r".*Use event.listen")
- @testing.requires.savepoints
- @testing.requires.two_phase_transactions
- def test_transactional_advanced(self):
- canary = []
-
- class TrackProxy(ConnectionProxy):
- def __getattribute__(self, key):
- fn = object.__getattribute__(self, key)
-
- def go(*arg, **kw):
- canary.append(fn.__name__)
- return fn(*arg, **kw)
-
- return go
-
- with testing.expect_deprecated(
- *[
- "ConnectionProxy.%s is deprecated" % name
- for name in [
- "execute",
- "cursor_execute",
- "begin",
- "rollback",
- "commit",
- "savepoint",
- "rollback_savepoint",
- "release_savepoint",
- "begin_twophase",
- "prepare_twophase",
- "rollback_twophase",
- "commit_twophase",
- ]
- ]
- ):
- engine = engines.testing_engine(options={"proxy": TrackProxy()})
- conn = engine.connect()
-
- trans = conn.begin()
- trans2 = conn.begin_nested()
- conn.execute(select([1]))
- trans2.rollback()
- trans2 = conn.begin_nested()
- conn.execute(select([1]))
- trans2.commit()
- trans.rollback()
-
- trans = conn.begin_twophase()
- conn.execute(select([1]))
- trans.prepare()
- trans.commit()
-
- canary = [t for t in canary if t not in ("cursor_execute", "execute")]
- eq_(
- canary,
- [
- "begin",
- "savepoint",
- "rollback_savepoint",
- "savepoint",
- "release_savepoint",
- "rollback",
- "begin_twophase",
- "prepare_twophase",
- "commit_twophase",
- ],
- )
-
-
class HandleInvalidatedOnConnectTest(fixtures.TestBase):
__requires__ = ("sqlite",)
)
-class DeprecatedPoolListenerTest(PoolTestBase):
- @testing.requires.predictable_gc
- @testing.uses_deprecated(
- r".*Use the PoolEvents", r".*'listeners' argument .* is deprecated"
- )
- def test_listeners(self):
- class InstrumentingListener(object):
- def __init__(self):
- if hasattr(self, "connect"):
- self.connect = self.inst_connect
- if hasattr(self, "first_connect"):
- self.first_connect = self.inst_first_connect
- if hasattr(self, "checkout"):
- self.checkout = self.inst_checkout
- if hasattr(self, "checkin"):
- self.checkin = self.inst_checkin
- self.clear()
-
- def clear(self):
- self.connected = []
- self.first_connected = []
- self.checked_out = []
- self.checked_in = []
-
- def assert_total(self, conn, fconn, cout, cin):
- eq_(len(self.connected), conn)
- eq_(len(self.first_connected), fconn)
- eq_(len(self.checked_out), cout)
- eq_(len(self.checked_in), cin)
-
- def assert_in(self, item, in_conn, in_fconn, in_cout, in_cin):
- eq_((item in self.connected), in_conn)
- eq_((item in self.first_connected), in_fconn)
- eq_((item in self.checked_out), in_cout)
- eq_((item in self.checked_in), in_cin)
-
- def inst_connect(self, con, record):
- print("connect(%s, %s)" % (con, record))
- assert con is not None
- assert record is not None
- self.connected.append(con)
-
- def inst_first_connect(self, con, record):
- print("first_connect(%s, %s)" % (con, record))
- assert con is not None
- assert record is not None
- self.first_connected.append(con)
-
- def inst_checkout(self, con, record, proxy):
- print("checkout(%s, %s, %s)" % (con, record, proxy))
- assert con is not None
- assert record is not None
- assert proxy is not None
- self.checked_out.append(con)
-
- def inst_checkin(self, con, record):
- print("checkin(%s, %s)" % (con, record))
- # con can be None if invalidated
- assert record is not None
- self.checked_in.append(con)
-
- class ListenAll(tsa.interfaces.PoolListener, InstrumentingListener):
- pass
-
- class ListenConnect(InstrumentingListener):
- def connect(self, con, record):
- pass
-
- class ListenFirstConnect(InstrumentingListener):
- def first_connect(self, con, record):
- pass
-
- class ListenCheckOut(InstrumentingListener):
- def checkout(self, con, record, proxy, num):
- pass
-
- class ListenCheckIn(InstrumentingListener):
- def checkin(self, con, record):
- pass
-
- def assert_listeners(p, total, conn, fconn, cout, cin):
- for instance in (p, p.recreate()):
- self.assert_(len(instance.dispatch.connect) == conn)
- self.assert_(len(instance.dispatch.first_connect) == fconn)
- self.assert_(len(instance.dispatch.checkout) == cout)
- self.assert_(len(instance.dispatch.checkin) == cin)
-
- p = self._queuepool_fixture()
- assert_listeners(p, 0, 0, 0, 0, 0)
-
- with testing.expect_deprecated(
- *[
- "PoolListener.%s is deprecated." % name
- for name in ["connect", "first_connect", "checkout", "checkin"]
- ]
- ):
- p.add_listener(ListenAll())
- assert_listeners(p, 1, 1, 1, 1, 1)
-
- with testing.expect_deprecated(
- *["PoolListener.%s is deprecated." % name for name in ["connect"]]
- ):
- p.add_listener(ListenConnect())
- assert_listeners(p, 2, 2, 1, 1, 1)
-
- with testing.expect_deprecated(
- *[
- "PoolListener.%s is deprecated." % name
- for name in ["first_connect"]
- ]
- ):
- p.add_listener(ListenFirstConnect())
- assert_listeners(p, 3, 2, 2, 1, 1)
-
- with testing.expect_deprecated(
- *["PoolListener.%s is deprecated." % name for name in ["checkout"]]
- ):
- p.add_listener(ListenCheckOut())
- assert_listeners(p, 4, 2, 2, 2, 1)
-
- with testing.expect_deprecated(
- *["PoolListener.%s is deprecated." % name for name in ["checkin"]]
- ):
- p.add_listener(ListenCheckIn())
- assert_listeners(p, 5, 2, 2, 2, 2)
- del p
-
- snoop = ListenAll()
-
- with testing.expect_deprecated(
- *[
- "PoolListener.%s is deprecated." % name
- for name in ["connect", "first_connect", "checkout", "checkin"]
- ]
- + [
- "PoolListener is deprecated in favor of the PoolEvents "
- "listener interface. The Pool.listeners parameter "
- "will be removed"
- ]
- ):
- p = self._queuepool_fixture(listeners=[snoop])
- assert_listeners(p, 1, 1, 1, 1, 1)
-
- c = p.connect()
- snoop.assert_total(1, 1, 1, 0)
- cc = c.connection
- snoop.assert_in(cc, True, True, True, False)
- c.close()
- snoop.assert_in(cc, True, True, True, True)
- del c, cc
-
- snoop.clear()
-
- # this one depends on immediate gc
- c = p.connect()
- cc = c.connection
- snoop.assert_in(cc, False, False, True, False)
- snoop.assert_total(0, 0, 1, 0)
- del c, cc
- lazy_gc()
- snoop.assert_total(0, 0, 1, 1)
-
- p.dispose()
- snoop.clear()
-
- c = p.connect()
- c.close()
- c = p.connect()
- snoop.assert_total(1, 0, 2, 1)
- c.close()
- snoop.assert_total(1, 0, 2, 2)
-
- # invalidation
- p.dispose()
- snoop.clear()
-
- c = p.connect()
- snoop.assert_total(1, 0, 1, 0)
- c.invalidate()
- snoop.assert_total(1, 0, 1, 1)
- c.close()
- snoop.assert_total(1, 0, 1, 1)
- del c
- lazy_gc()
- snoop.assert_total(1, 0, 1, 1)
- c = p.connect()
- snoop.assert_total(2, 0, 2, 1)
- c.close()
- del c
- lazy_gc()
- snoop.assert_total(2, 0, 2, 2)
-
- # detached
- p.dispose()
- snoop.clear()
-
- c = p.connect()
- snoop.assert_total(1, 0, 1, 0)
- c.detach()
- snoop.assert_total(1, 0, 1, 0)
- c.close()
- del c
- snoop.assert_total(1, 0, 1, 0)
- c = p.connect()
- snoop.assert_total(2, 0, 2, 0)
- c.close()
- del c
- snoop.assert_total(2, 0, 2, 1)
-
- # recreated
- p = p.recreate()
- snoop.clear()
-
- c = p.connect()
- snoop.assert_total(1, 1, 1, 0)
- c.close()
- snoop.assert_total(1, 1, 1, 1)
- c = p.connect()
- snoop.assert_total(1, 1, 2, 1)
- c.close()
- snoop.assert_total(1, 1, 2, 2)
-
- @testing.uses_deprecated(r".*Use the PoolEvents")
- def test_listeners_callables(self):
- def connect(dbapi_con, con_record):
- counts[0] += 1
-
- def checkout(dbapi_con, con_record, con_proxy):
- counts[1] += 1
-
- def checkin(dbapi_con, con_record):
- counts[2] += 1
-
- i_all = dict(connect=connect, checkout=checkout, checkin=checkin)
- i_connect = dict(connect=connect)
- i_checkout = dict(checkout=checkout)
- i_checkin = dict(checkin=checkin)
-
- for cls in (pool.QueuePool, pool.StaticPool):
- counts = [0, 0, 0]
-
- def assert_listeners(p, total, conn, cout, cin):
- for instance in (p, p.recreate()):
- eq_(len(instance.dispatch.connect), conn)
- eq_(len(instance.dispatch.checkout), cout)
- eq_(len(instance.dispatch.checkin), cin)
-
- p = self._queuepool_fixture()
- assert_listeners(p, 0, 0, 0, 0)
-
- with testing.expect_deprecated(
- *[
- "PoolListener.%s is deprecated." % name
- for name in ["connect", "checkout", "checkin"]
- ]
- ):
- p.add_listener(i_all)
- assert_listeners(p, 1, 1, 1, 1)
-
- with testing.expect_deprecated(
- *[
- "PoolListener.%s is deprecated." % name
- for name in ["connect"]
- ]
- ):
- p.add_listener(i_connect)
- assert_listeners(p, 2, 1, 1, 1)
-
- with testing.expect_deprecated(
- *[
- "PoolListener.%s is deprecated." % name
- for name in ["checkout"]
- ]
- ):
- p.add_listener(i_checkout)
- assert_listeners(p, 3, 1, 1, 1)
-
- with testing.expect_deprecated(
- *[
- "PoolListener.%s is deprecated." % name
- for name in ["checkin"]
- ]
- ):
- p.add_listener(i_checkin)
- assert_listeners(p, 4, 1, 1, 1)
- del p
-
- with testing.expect_deprecated(
- *[
- "PoolListener.%s is deprecated." % name
- for name in ["connect", "checkout", "checkin"]
- ]
- + [".*The Pool.listeners parameter will be removed"]
- ):
- p = self._queuepool_fixture(listeners=[i_all])
- assert_listeners(p, 1, 1, 1, 1)
-
- c = p.connect()
- assert counts == [1, 1, 0]
- c.close()
- assert counts == [1, 1, 1]
-
- c = p.connect()
- assert counts == [1, 2, 1]
- with testing.expect_deprecated(
- *[
- "PoolListener.%s is deprecated." % name
- for name in ["checkin"]
- ]
- ):
- p.add_listener(i_checkin)
- c.close()
- assert counts == [1, 2, 2]
-
-
class ExplicitAutoCommitDeprecatedTest(fixtures.TestBase):
"""test the 'autocommit' flag on select() and text() objects.
from sqlalchemy import exc
from sqlalchemy import func
from sqlalchemy import Integer
-from sqlalchemy import MetaData
from sqlalchemy import select
from sqlalchemy import String
from sqlalchemy import testing
from sqlalchemy.ext.declarative import comparable_using
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import aliased
-from sqlalchemy.orm import AttributeExtension
from sqlalchemy.orm import attributes
from sqlalchemy.orm import collections
from sqlalchemy.orm import column_property
from sqlalchemy.orm import comparable_property
-from sqlalchemy.orm import composite
from sqlalchemy.orm import configure_mappers
from sqlalchemy.orm import contains_alias
from sqlalchemy.orm import contains_eager
from sqlalchemy.orm import create_session
from sqlalchemy.orm import defer
from sqlalchemy.orm import deferred
-from sqlalchemy.orm import EXT_CONTINUE
from sqlalchemy.orm import identity
from sqlalchemy.orm import instrumentation
from sqlalchemy.orm import joinedload
from sqlalchemy.orm import joinedload_all
from sqlalchemy.orm import mapper
-from sqlalchemy.orm import MapperExtension
from sqlalchemy.orm import PropComparator
from sqlalchemy.orm import relationship
from sqlalchemy.orm import Session
-from sqlalchemy.orm import SessionExtension
from sqlalchemy.orm import sessionmaker
from sqlalchemy.orm import synonym
from sqlalchemy.orm import undefer
from sqlalchemy.orm import with_polymorphic
from sqlalchemy.orm.collections import collection
from sqlalchemy.orm.util import polymorphic_union
-from sqlalchemy.testing import assert_raises
from sqlalchemy.testing import assert_raises_message
from sqlalchemy.testing import assertions
from sqlalchemy.testing import AssertsCompiledSQL
from sqlalchemy.testing import is_
from sqlalchemy.testing import is_true
from sqlalchemy.testing.schema import Column
-from sqlalchemy.testing.schema import Table
from sqlalchemy.testing.util import gc_collect
from sqlalchemy.util.compat import pypy
from . import _fixtures
run_define_tables = "each"
run_create_tables = None
- def test_attribute_extension(self):
- class SomeExtension(AttributeExtension):
- def append(self, obj, value, initiator):
- pass
-
- def remove(self, obj, value, initiator):
- pass
-
- def set(self, obj, value, oldvalue, initiator):
- pass
-
- with assertions.expect_deprecated(
- ".*The column_property.extension parameter will be removed in a "
- "future release."
- ):
-
- class Foo(self.DeclarativeBasic):
- __tablename__ = "foo"
-
- id = Column(Integer, primary_key=True)
- foo = column_property(
- Column("q", Integer), extension=SomeExtension()
- )
-
- with assertions.expect_deprecated(
- "AttributeExtension.append is deprecated. The "
- "AttributeExtension class will be removed in a future release.",
- "AttributeExtension.remove is deprecated. The "
- "AttributeExtension class will be removed in a future release.",
- "AttributeExtension.set is deprecated. The "
- "AttributeExtension class will be removed in a future release.",
- ):
- configure_mappers()
-
- def test_attribute_extension_parameter(self):
- class SomeExtension(AttributeExtension):
- def append(self, obj, value, initiator):
- pass
-
- with assertions.expect_deprecated(
- ".*The relationship.extension parameter will be removed in a "
- "future release."
- ):
- relationship("Bar", extension=SomeExtension)
-
- with assertions.expect_deprecated(
- ".*The column_property.extension parameter will be removed in a "
- "future release."
- ):
- column_property(Column("q", Integer), extension=SomeExtension)
-
- with assertions.expect_deprecated(
- ".*The composite.extension parameter will be removed in a "
- "future release."
- ):
- composite("foo", extension=SomeExtension)
-
- def test_session_extension(self):
- class SomeExtension(SessionExtension):
- def after_commit(self, session):
- pass
-
- def after_rollback(self, session):
- pass
-
- def before_flush(self, session, flush_context, instances):
- pass
-
- with assertions.expect_deprecated(
- ".*The Session.extension parameter will be removed",
- "SessionExtension.after_commit is deprecated. "
- "The SessionExtension class",
- "SessionExtension.before_flush is deprecated. "
- "The SessionExtension class",
- "SessionExtension.after_rollback is deprecated. "
- "The SessionExtension class",
- ):
- Session(extension=SomeExtension())
-
- def test_mapper_extension(self):
- class SomeExtension(MapperExtension):
- def init_instance(
- self, mapper, class_, oldinit, instance, args, kwargs
- ):
- pass
-
- def init_failed(
- self, mapper, class_, oldinit, instance, args, kwargs
- ):
- pass
-
- with assertions.expect_deprecated(
- "MapperExtension.init_instance is deprecated. "
- "The MapperExtension class",
- "MapperExtension.init_failed is deprecated. "
- "The MapperExtension class",
- ".*The mapper.extension parameter will be removed",
- ):
-
- class Foo(self.DeclarativeBasic):
- __tablename__ = "foo"
-
- id = Column(Integer, primary_key=True)
-
- __mapper_args__ = {"extension": SomeExtension()}
-
def test_session_weak_identity_map(self):
with testing.expect_deprecated(
".*Session.weak_identity_map parameter as well as the"
eq_(rt, u1)
-class DeprecatedMapperExtensionTest(_fixtures.FixtureTest):
-
- """Superseded by MapperEventsTest - test backwards
- compatibility of MapperExtension."""
-
- run_inserts = None
-
- def extension(self):
- methods = []
-
- class Ext(MapperExtension):
- def instrument_class(self, mapper, cls):
- methods.append("instrument_class")
- return EXT_CONTINUE
-
- def init_instance(
- self, mapper, class_, oldinit, instance, args, kwargs
- ):
- methods.append("init_instance")
- return EXT_CONTINUE
-
- def init_failed(
- self, mapper, class_, oldinit, instance, args, kwargs
- ):
- methods.append("init_failed")
- return EXT_CONTINUE
-
- def reconstruct_instance(self, mapper, instance):
- methods.append("reconstruct_instance")
- return EXT_CONTINUE
-
- def before_insert(self, mapper, connection, instance):
- methods.append("before_insert")
- return EXT_CONTINUE
-
- def after_insert(self, mapper, connection, instance):
- methods.append("after_insert")
- return EXT_CONTINUE
-
- def before_update(self, mapper, connection, instance):
- methods.append("before_update")
- return EXT_CONTINUE
-
- def after_update(self, mapper, connection, instance):
- methods.append("after_update")
- return EXT_CONTINUE
-
- def before_delete(self, mapper, connection, instance):
- methods.append("before_delete")
- return EXT_CONTINUE
-
- def after_delete(self, mapper, connection, instance):
- methods.append("after_delete")
- return EXT_CONTINUE
-
- return Ext, methods
-
- def test_basic(self):
- """test that common user-defined methods get called."""
-
- User, users = self.classes.User, self.tables.users
-
- Ext, methods = self.extension()
-
- with testing.expect_deprecated(
- "MapperExtension is deprecated in favor of the MapperEvents",
- "MapperExtension.before_insert is deprecated",
- "MapperExtension.instrument_class is deprecated",
- "MapperExtension.init_instance is deprecated",
- "MapperExtension.after_insert is deprecated",
- "MapperExtension.reconstruct_instance is deprecated",
- "MapperExtension.before_delete is deprecated",
- "MapperExtension.after_delete is deprecated",
- "MapperExtension.before_update is deprecated",
- "MapperExtension.after_update is deprecated",
- "MapperExtension.init_failed is deprecated",
- ):
- mapper(User, users, extension=Ext())
- sess = create_session()
- u = User(name="u1")
- sess.add(u)
- sess.flush()
- u = sess.query(User).populate_existing().get(u.id)
- sess.expunge_all()
- u = sess.query(User).get(u.id)
- u.name = "u1 changed"
- sess.flush()
- sess.delete(u)
- sess.flush()
- eq_(
- methods,
- [
- "instrument_class",
- "init_instance",
- "before_insert",
- "after_insert",
- "reconstruct_instance",
- "before_update",
- "after_update",
- "before_delete",
- "after_delete",
- ],
- )
-
- def test_inheritance(self):
- users, addresses, User = (
- self.tables.users,
- self.tables.addresses,
- self.classes.User,
- )
-
- Ext, methods = self.extension()
-
- class AdminUser(User):
- pass
-
- with testing.expect_deprecated(
- "MapperExtension is deprecated in favor of the MapperEvents",
- "MapperExtension.before_insert is deprecated",
- "MapperExtension.instrument_class is deprecated",
- "MapperExtension.init_instance is deprecated",
- "MapperExtension.after_insert is deprecated",
- "MapperExtension.reconstruct_instance is deprecated",
- "MapperExtension.before_delete is deprecated",
- "MapperExtension.after_delete is deprecated",
- "MapperExtension.before_update is deprecated",
- "MapperExtension.after_update is deprecated",
- "MapperExtension.init_failed is deprecated",
- ):
- mapper(User, users, extension=Ext())
- mapper(
- AdminUser,
- addresses,
- inherits=User,
- properties={"address_id": addresses.c.id},
- )
-
- sess = create_session()
- am = AdminUser(name="au1", email_address="au1@e1")
- sess.add(am)
- sess.flush()
- am = sess.query(AdminUser).populate_existing().get(am.id)
- sess.expunge_all()
- am = sess.query(AdminUser).get(am.id)
- am.name = "au1 changed"
- sess.flush()
- sess.delete(am)
- sess.flush()
- eq_(
- methods,
- [
- "instrument_class",
- "instrument_class",
- "init_instance",
- "before_insert",
- "after_insert",
- "reconstruct_instance",
- "before_update",
- "after_update",
- "before_delete",
- "after_delete",
- ],
- )
-
- def test_before_after_only_collection(self):
- """before_update is called on parent for collection modifications,
- after_update is called even if no columns were updated.
-
- """
-
- keywords, items, item_keywords, Keyword, Item = (
- self.tables.keywords,
- self.tables.items,
- self.tables.item_keywords,
- self.classes.Keyword,
- self.classes.Item,
- )
-
- Ext1, methods1 = self.extension()
- Ext2, methods2 = self.extension()
-
- with testing.expect_deprecated(
- "MapperExtension is deprecated in favor of the MapperEvents",
- "MapperExtension.before_insert is deprecated",
- "MapperExtension.instrument_class is deprecated",
- "MapperExtension.init_instance is deprecated",
- "MapperExtension.after_insert is deprecated",
- "MapperExtension.reconstruct_instance is deprecated",
- "MapperExtension.before_delete is deprecated",
- "MapperExtension.after_delete is deprecated",
- "MapperExtension.before_update is deprecated",
- "MapperExtension.after_update is deprecated",
- "MapperExtension.init_failed is deprecated",
- ):
- mapper(
- Item,
- items,
- extension=Ext1(),
- properties={
- "keywords": relationship(Keyword, secondary=item_keywords)
- },
- )
- with testing.expect_deprecated(
- "MapperExtension is deprecated in favor of the MapperEvents",
- "MapperExtension.before_insert is deprecated",
- "MapperExtension.instrument_class is deprecated",
- "MapperExtension.init_instance is deprecated",
- "MapperExtension.after_insert is deprecated",
- "MapperExtension.reconstruct_instance is deprecated",
- "MapperExtension.before_delete is deprecated",
- "MapperExtension.after_delete is deprecated",
- "MapperExtension.before_update is deprecated",
- "MapperExtension.after_update is deprecated",
- "MapperExtension.init_failed is deprecated",
- ):
- mapper(Keyword, keywords, extension=Ext2())
-
- sess = create_session()
- i1 = Item(description="i1")
- k1 = Keyword(name="k1")
- sess.add(i1)
- sess.add(k1)
- sess.flush()
- eq_(
- methods1,
- [
- "instrument_class",
- "init_instance",
- "before_insert",
- "after_insert",
- ],
- )
- eq_(
- methods2,
- [
- "instrument_class",
- "init_instance",
- "before_insert",
- "after_insert",
- ],
- )
-
- del methods1[:]
- del methods2[:]
- i1.keywords.append(k1)
- sess.flush()
- eq_(methods1, ["before_update", "after_update"])
- eq_(methods2, [])
-
- def test_inheritance_with_dupes(self):
- """Inheritance with the same extension instance on both mappers."""
-
- users, addresses, User = (
- self.tables.users,
- self.tables.addresses,
- self.classes.User,
- )
-
- Ext, methods = self.extension()
-
- class AdminUser(User):
- pass
-
- ext = Ext()
- with testing.expect_deprecated(
- "MapperExtension is deprecated in favor of the MapperEvents",
- "MapperExtension.before_insert is deprecated",
- "MapperExtension.instrument_class is deprecated",
- "MapperExtension.init_instance is deprecated",
- "MapperExtension.after_insert is deprecated",
- "MapperExtension.reconstruct_instance is deprecated",
- "MapperExtension.before_delete is deprecated",
- "MapperExtension.after_delete is deprecated",
- "MapperExtension.before_update is deprecated",
- "MapperExtension.after_update is deprecated",
- "MapperExtension.init_failed is deprecated",
- ):
- mapper(User, users, extension=ext)
-
- with testing.expect_deprecated(
- "MapperExtension is deprecated in favor of the MapperEvents"
- ):
- mapper(
- AdminUser,
- addresses,
- inherits=User,
- extension=ext,
- properties={"address_id": addresses.c.id},
- )
-
- sess = create_session()
- am = AdminUser(name="au1", email_address="au1@e1")
- sess.add(am)
- sess.flush()
- am = sess.query(AdminUser).populate_existing().get(am.id)
- sess.expunge_all()
- am = sess.query(AdminUser).get(am.id)
- am.name = "au1 changed"
- sess.flush()
- sess.delete(am)
- sess.flush()
- eq_(
- methods,
- [
- "instrument_class",
- "instrument_class",
- "init_instance",
- "before_insert",
- "after_insert",
- "reconstruct_instance",
- "before_update",
- "after_update",
- "before_delete",
- "after_delete",
- ],
- )
-
- def test_unnecessary_methods_not_evented(self):
- users = self.tables.users
-
- class MyExtension(MapperExtension):
- def before_insert(self, mapper, connection, instance):
- pass
-
- class Foo(object):
- pass
-
- with testing.expect_deprecated(
- "MapperExtension is deprecated in favor of the MapperEvents",
- "MapperExtension.before_insert is deprecated",
- ):
- m = mapper(Foo, users, extension=MyExtension())
- assert not m.class_manager.dispatch.load
- assert not m.dispatch.before_update
- assert len(m.dispatch.before_insert) == 1
-
-
-class DeprecatedSessionExtensionTest(_fixtures.FixtureTest):
- run_inserts = None
-
- def test_extension(self):
- User, users = self.classes.User, self.tables.users
-
- mapper(User, users)
- log = []
-
- class MyExt(SessionExtension):
- def before_commit(self, session):
- log.append("before_commit")
-
- def after_commit(self, session):
- log.append("after_commit")
-
- def after_rollback(self, session):
- log.append("after_rollback")
-
- def before_flush(self, session, flush_context, objects):
- log.append("before_flush")
-
- def after_flush(self, session, flush_context):
- log.append("after_flush")
-
- def after_flush_postexec(self, session, flush_context):
- log.append("after_flush_postexec")
-
- def after_begin(self, session, transaction, connection):
- log.append("after_begin")
-
- def after_attach(self, session, instance):
- log.append("after_attach")
-
- def after_bulk_update(self, session, query, query_context, result):
- log.append("after_bulk_update")
-
- def after_bulk_delete(self, session, query, query_context, result):
- log.append("after_bulk_delete")
-
- with testing.expect_deprecated(
- "SessionExtension is deprecated in favor of " "the SessionEvents",
- "SessionExtension.before_commit is deprecated",
- "SessionExtension.after_commit is deprecated",
- "SessionExtension.after_begin is deprecated",
- "SessionExtension.after_attach is deprecated",
- "SessionExtension.before_flush is deprecated",
- "SessionExtension.after_flush is deprecated",
- "SessionExtension.after_flush_postexec is deprecated",
- "SessionExtension.after_rollback is deprecated",
- "SessionExtension.after_bulk_update is deprecated",
- "SessionExtension.after_bulk_delete is deprecated",
- ):
- sess = create_session(extension=MyExt())
- u = User(name="u1")
- sess.add(u)
- sess.flush()
- assert log == [
- "after_attach",
- "before_flush",
- "after_begin",
- "after_flush",
- "after_flush_postexec",
- "before_commit",
- "after_commit",
- ]
- log = []
- with testing.expect_deprecated(
- "SessionExtension is deprecated in favor of " "the SessionEvents",
- "SessionExtension.before_commit is deprecated",
- "SessionExtension.after_commit is deprecated",
- "SessionExtension.after_begin is deprecated",
- "SessionExtension.after_attach is deprecated",
- "SessionExtension.before_flush is deprecated",
- "SessionExtension.after_flush is deprecated",
- "SessionExtension.after_flush_postexec is deprecated",
- "SessionExtension.after_rollback is deprecated",
- "SessionExtension.after_bulk_update is deprecated",
- "SessionExtension.after_bulk_delete is deprecated",
- ):
- sess = create_session(autocommit=False, extension=MyExt())
- u = User(name="u1")
- sess.add(u)
- sess.flush()
- assert log == [
- "after_attach",
- "before_flush",
- "after_begin",
- "after_flush",
- "after_flush_postexec",
- ]
- log = []
- u.name = "ed"
- sess.commit()
- assert log == [
- "before_commit",
- "before_flush",
- "after_flush",
- "after_flush_postexec",
- "after_commit",
- ]
- log = []
- sess.commit()
- assert log == ["before_commit", "after_commit"]
- log = []
- sess.query(User).delete()
- assert log == ["after_begin", "after_bulk_delete"]
- log = []
- sess.query(User).update({"name": "foo"})
- assert log == ["after_bulk_update"]
- log = []
- with testing.expect_deprecated(
- "SessionExtension is deprecated in favor of " "the SessionEvents",
- "SessionExtension.before_commit is deprecated",
- "SessionExtension.after_commit is deprecated",
- "SessionExtension.after_begin is deprecated",
- "SessionExtension.after_attach is deprecated",
- "SessionExtension.before_flush is deprecated",
- "SessionExtension.after_flush is deprecated",
- "SessionExtension.after_flush_postexec is deprecated",
- "SessionExtension.after_rollback is deprecated",
- "SessionExtension.after_bulk_update is deprecated",
- "SessionExtension.after_bulk_delete is deprecated",
- ):
- sess = create_session(
- autocommit=False, extension=MyExt(), bind=testing.db
- )
- sess.connection()
- assert log == ["after_begin"]
- sess.close()
-
- def test_multiple_extensions(self):
- User, users = self.classes.User, self.tables.users
-
- log = []
-
- class MyExt1(SessionExtension):
- def before_commit(self, session):
- log.append("before_commit_one")
-
- class MyExt2(SessionExtension):
- def before_commit(self, session):
- log.append("before_commit_two")
-
- mapper(User, users)
- with testing.expect_deprecated(
- "SessionExtension is deprecated in favor of " "the SessionEvents",
- "SessionExtension.before_commit is deprecated",
- ):
- sess = create_session(extension=[MyExt1(), MyExt2()])
- u = User(name="u1")
- sess.add(u)
- sess.flush()
- assert log == ["before_commit_one", "before_commit_two"]
-
- def test_unnecessary_methods_not_evented(self):
- class MyExtension(SessionExtension):
- def before_commit(self, session):
- pass
-
- with testing.expect_deprecated(
- "SessionExtension is deprecated in favor of " "the SessionEvents",
- "SessionExtension.before_commit is deprecated.",
- ):
- s = Session(extension=MyExtension())
- assert not s.dispatch.after_commit
- assert len(s.dispatch.before_commit) == 1
-
-
-class DeprecatedAttributeExtensionTest1(fixtures.ORMTest):
- def test_extension_commit_attr(self):
- """test that an extension which commits attribute history
- maintains the end-result history.
-
- This won't work in conjunction with some unitofwork extensions.
-
- """
-
- class Foo(fixtures.BasicEntity):
- pass
-
- class Bar(fixtures.BasicEntity):
- pass
-
- class ReceiveEvents(AttributeExtension):
- def __init__(self, key):
- self.key = key
-
- def append(self, state, child, initiator):
- if commit:
- state._commit_all(state.dict)
- return child
-
- def remove(self, state, child, initiator):
- if commit:
- state._commit_all(state.dict)
- return child
-
- def set(self, state, child, oldchild, initiator):
- if commit:
- state._commit_all(state.dict)
- return child
-
- instrumentation.register_class(Foo)
- instrumentation.register_class(Bar)
-
- b1, b2, b3, b4 = Bar(id="b1"), Bar(id="b2"), Bar(id="b3"), Bar(id="b4")
-
- def loadcollection(state, passive):
- if passive is attributes.PASSIVE_NO_FETCH:
- return attributes.PASSIVE_NO_RESULT
- return [b1, b2]
-
- def loadscalar(state, passive):
- if passive is attributes.PASSIVE_NO_FETCH:
- return attributes.PASSIVE_NO_RESULT
- return b2
-
- with testing.expect_deprecated(
- "AttributeExtension.append is deprecated.",
- "AttributeExtension.remove is deprecated.",
- "AttributeExtension.set is deprecated.",
- ):
- attributes.register_attribute(
- Foo,
- "bars",
- uselist=True,
- useobject=True,
- callable_=loadcollection,
- extension=[ReceiveEvents("bars")],
- )
-
- with testing.expect_deprecated(
- "AttributeExtension.append is deprecated.",
- "AttributeExtension.remove is deprecated.",
- "AttributeExtension.set is deprecated.",
- ):
- attributes.register_attribute(
- Foo,
- "bar",
- uselist=False,
- useobject=True,
- callable_=loadscalar,
- extension=[ReceiveEvents("bar")],
- )
-
- with testing.expect_deprecated(
- "AttributeExtension.append is deprecated.",
- "AttributeExtension.remove is deprecated.",
- "AttributeExtension.set is deprecated.",
- ):
- attributes.register_attribute(
- Foo,
- "scalar",
- uselist=False,
- useobject=False,
- extension=[ReceiveEvents("scalar")],
- )
-
- def create_hist():
- def hist(key, fn, *arg):
- attributes.instance_state(f1)._commit_all(
- attributes.instance_dict(f1)
- )
- fn(*arg)
- histories.append(attributes.get_history(f1, key))
-
- f1 = Foo()
- hist("bars", f1.bars.append, b3)
- hist("bars", f1.bars.append, b4)
- hist("bars", f1.bars.remove, b2)
- hist("bar", setattr, f1, "bar", b3)
- hist("bar", setattr, f1, "bar", None)
- hist("bar", setattr, f1, "bar", b4)
- hist("scalar", setattr, f1, "scalar", 5)
- hist("scalar", setattr, f1, "scalar", None)
- hist("scalar", setattr, f1, "scalar", 4)
-
- histories = []
- commit = False
- create_hist()
- without_commit = list(histories)
- histories[:] = []
- commit = True
- create_hist()
- with_commit = histories
- for without, with_ in zip(without_commit, with_commit):
- woc = without
- wic = with_
- eq_(woc, wic)
-
- def test_extension_lazyload_assertion(self):
- class Foo(fixtures.BasicEntity):
- pass
-
- class Bar(fixtures.BasicEntity):
- pass
-
- class ReceiveEvents(AttributeExtension):
- def append(self, state, child, initiator):
- state.obj().bars
- return child
-
- def remove(self, state, child, initiator):
- state.obj().bars
- return child
-
- def set(self, state, child, oldchild, initiator):
- return child
-
- instrumentation.register_class(Foo)
- instrumentation.register_class(Bar)
-
- bar1, bar2, bar3 = [Bar(id=1), Bar(id=2), Bar(id=3)]
-
- def func1(state, passive):
- if passive is attributes.PASSIVE_NO_FETCH:
- return attributes.PASSIVE_NO_RESULT
-
- return [bar1, bar2, bar3]
-
- with testing.expect_deprecated(
- "AttributeExtension.append is deprecated.",
- "AttributeExtension.remove is deprecated.",
- "AttributeExtension.set is deprecated.",
- ):
- attributes.register_attribute(
- Foo,
- "bars",
- uselist=True,
- callable_=func1,
- useobject=True,
- extension=[ReceiveEvents()],
- )
- attributes.register_attribute(
- Bar, "foos", uselist=True, useobject=True, backref="bars"
- )
-
- x = Foo()
- assert_raises(AssertionError, Bar(id=4).foos.append, x)
-
- x.bars
- b = Bar(id=4)
- b.foos.append(x)
- attributes.instance_state(x)._expire_attributes(
- attributes.instance_dict(x), ["bars"]
- )
- assert_raises(AssertionError, b.foos.remove, x)
-
- def test_scalar_listener(self):
-
- # listeners on ScalarAttributeImpl aren't used normally. test that
- # they work for the benefit of user extensions
-
- class Foo(object):
-
- pass
-
- results = []
-
- class ReceiveEvents(AttributeExtension):
- def append(self, state, child, initiator):
- assert False
-
- def remove(self, state, child, initiator):
- results.append(("remove", state.obj(), child))
-
- def set(self, state, child, oldchild, initiator):
- results.append(("set", state.obj(), child, oldchild))
- return child
-
- instrumentation.register_class(Foo)
- with testing.expect_deprecated(
- "AttributeExtension.append is deprecated.",
- "AttributeExtension.remove is deprecated.",
- "AttributeExtension.set is deprecated.",
- ):
- attributes.register_attribute(
- Foo,
- "x",
- uselist=False,
- useobject=False,
- extension=ReceiveEvents(),
- )
-
- f = Foo()
- f.x = 5
- f.x = 17
- del f.x
-
- eq_(
- results,
- [
- ("set", f, 5, attributes.NEVER_SET),
- ("set", f, 17, 5),
- ("remove", f, 17),
- ],
- )
-
- def test_cascading_extensions(self):
- t1 = Table(
- "t1",
- MetaData(),
- Column("id", Integer, primary_key=True),
- Column("type", String(40)),
- Column("data", String(50)),
- )
-
- ext_msg = []
-
- class Ex1(AttributeExtension):
- def set(self, state, value, oldvalue, initiator):
- ext_msg.append("Ex1 %r" % value)
- return "ex1" + value
-
- class Ex2(AttributeExtension):
- def set(self, state, value, oldvalue, initiator):
- ext_msg.append("Ex2 %r" % value)
- return "ex2" + value
-
- class A(fixtures.BasicEntity):
- pass
-
- class B(A):
- pass
-
- class C(B):
- pass
-
- with testing.expect_deprecated(
- "AttributeExtension is deprecated in favor of the "
- "AttributeEvents listener interface. "
- "The column_property.extension parameter"
- ):
- mapper(
- A,
- t1,
- polymorphic_on=t1.c.type,
- polymorphic_identity="a",
- properties={
- "data": column_property(t1.c.data, extension=Ex1())
- },
- )
- mapper(B, polymorphic_identity="b", inherits=A)
- with testing.expect_deprecated(
- "AttributeExtension is deprecated in favor of the "
- "AttributeEvents listener interface. "
- "The column_property.extension parameter"
- ):
- mapper(
- C,
- polymorphic_identity="c",
- inherits=B,
- properties={
- "data": column_property(t1.c.data, extension=Ex2())
- },
- )
-
- with testing.expect_deprecated(
- "AttributeExtension.set is deprecated. "
- ):
- configure_mappers()
-
- a1 = A(data="a1")
- b1 = B(data="b1")
- c1 = C(data="c1")
-
- eq_(a1.data, "ex1a1")
- eq_(b1.data, "ex1b1")
- eq_(c1.data, "ex2c1")
-
- a1.data = "a2"
- b1.data = "b2"
- c1.data = "c2"
- eq_(a1.data, "ex1a2")
- eq_(b1.data, "ex1b2")
- eq_(c1.data, "ex2c2")
-
- eq_(
- ext_msg,
- [
- "Ex1 'a1'",
- "Ex1 'b1'",
- "Ex2 'c1'",
- "Ex1 'a2'",
- "Ex1 'b2'",
- "Ex2 'c2'",
- ],
- )
-
-
class DeprecatedOptionAllTest(OptionsPathTest, _fixtures.FixtureTest):
run_inserts = "once"
run_deletes = None