From: Mike Bayer Date: Sun, 6 Oct 2019 01:28:48 +0000 (-0400) Subject: Remove deprecated extension and similar classes X-Git-Tag: rel_1_4_0b1~696 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=08757f05e027ead82a24cd8b7d4a3e90c5b01b59;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git Remove deprecated extension and similar classes 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. Fixes: #4638 Change-Id: If4156d4956b10847bd93b6408a7c52ff5168db9b --- diff --git a/doc/build/changelog/unreleased_14/4638.rst b/doc/build/changelog/unreleased_14/4638.rst new file mode 100644 index 0000000000..1a799cf91f --- /dev/null +++ b/doc/build/changelog/unreleased_14/4638.rst @@ -0,0 +1,9 @@ +.. 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. + diff --git a/doc/build/core/interfaces.rst b/doc/build/core/interfaces.rst deleted file mode 100644 index 706db625ee..0000000000 --- a/doc/build/core/interfaces.rst +++ /dev/null @@ -1,30 +0,0 @@ -.. _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: - - diff --git a/doc/build/orm/deprecated.rst b/doc/build/orm/deprecated.rst deleted file mode 100644 index 088705a0e6..0000000000 --- a/doc/build/orm/deprecated.rst +++ /dev/null @@ -1,36 +0,0 @@ -: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: - diff --git a/lib/sqlalchemy/engine/base.py b/lib/sqlalchemy/engine/base.py index a0a2680ca0..27da8c295c 100644 --- a/lib/sqlalchemy/engine/base.py +++ b/lib/sqlalchemy/engine/base.py @@ -14,7 +14,6 @@ from .interfaces import ExceptionContext from .util import _distill_params from .. import exc from .. import inspection -from .. import interfaces from .. import log from .. import util from ..sql import schema @@ -1910,7 +1909,6 @@ class Engine(Connectable, log.Identified): url, logging_name=None, echo=None, - proxy=None, execution_options=None, hide_parameters=False, ): @@ -1922,8 +1920,6 @@ class Engine(Connectable, log.Identified): 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) diff --git a/lib/sqlalchemy/interfaces.py b/lib/sqlalchemy/interfaces.py deleted file mode 100644 index 374199143a..0000000000 --- a/lib/sqlalchemy/interfaces.py +++ /dev/null @@ -1,363 +0,0 @@ -# sqlalchemy/interfaces.py -# Copyright (C) 2007-2019 the SQLAlchemy authors and contributors -# -# 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) diff --git a/lib/sqlalchemy/orm/__init__.py b/lib/sqlalchemy/orm/__init__.py index a596d1408a..8bd68b4171 100644 --- a/lib/sqlalchemy/orm/__init__.py +++ b/lib/sqlalchemy/orm/__init__.py @@ -16,9 +16,6 @@ documentation for an overview of how this module is used. 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 diff --git a/lib/sqlalchemy/orm/attributes.py b/lib/sqlalchemy/orm/attributes.py index 117dd4cea4..1466f5f47c 100644 --- a/lib/sqlalchemy/orm/attributes.py +++ b/lib/sqlalchemy/orm/attributes.py @@ -465,7 +465,6 @@ class AttributeImpl(object): callable_, dispatch, trackparent=False, - extension=None, compare_function=None, active_history=False, parent_token=None, @@ -490,19 +489,6 @@ class AttributeImpl(object): 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. @@ -545,13 +531,6 @@ class AttributeImpl(object): 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 @@ -1075,7 +1054,6 @@ class CollectionAttributeImpl(AttributeImpl): dispatch, typecallable=None, trackparent=False, - extension=None, copy_function=None, compare_function=None, **kwargs @@ -1086,7 +1064,6 @@ class CollectionAttributeImpl(AttributeImpl): callable_, dispatch, trackparent=trackparent, - extension=extension, compare_function=compare_function, **kwargs ) diff --git a/lib/sqlalchemy/orm/deprecated_interfaces.py b/lib/sqlalchemy/orm/deprecated_interfaces.py deleted file mode 100644 index 4069b43a5f..0000000000 --- a/lib/sqlalchemy/orm/deprecated_interfaces.py +++ /dev/null @@ -1,572 +0,0 @@ -# orm/deprecated_interfaces.py -# Copyright (C) 2005-2019 the SQLAlchemy authors and contributors -# -# -# 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 diff --git a/lib/sqlalchemy/orm/descriptor_props.py b/lib/sqlalchemy/orm/descriptor_props.py index 075638fed9..dd482bf069 100644 --- a/lib/sqlalchemy/orm/descriptor_props.py +++ b/lib/sqlalchemy/orm/descriptor_props.py @@ -97,15 +97,6 @@ class CompositeProperty(DescriptorProperty): """ - @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. @@ -148,12 +139,6 @@ class CompositeProperty(DescriptorProperty): :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__() diff --git a/lib/sqlalchemy/orm/interfaces.py b/lib/sqlalchemy/orm/interfaces.py index d6bdfb9247..09d1858b94 100644 --- a/lib/sqlalchemy/orm/interfaces.py +++ b/lib/sqlalchemy/orm/interfaces.py @@ -9,11 +9,9 @@ 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. """ @@ -40,11 +38,7 @@ from .. import util from ..sql import operators -# imported later -MapperExtension = SessionExtension = AttributeExtension = None - __all__ = ( - "AttributeExtension", "EXT_CONTINUE", "EXT_STOP", "EXT_SKIP", @@ -53,11 +47,9 @@ __all__ = ( "MANYTOONE", "NOT_EXTENSION", "LoaderStrategy", - "MapperExtension", "MapperOption", "MapperProperty", "PropComparator", - "SessionExtension", "StrategizedProperty", ) diff --git a/lib/sqlalchemy/orm/mapper.py b/lib/sqlalchemy/orm/mapper.py index e8b215653a..0d8d454eb4 100644 --- a/lib/sqlalchemy/orm/mapper.py +++ b/lib/sqlalchemy/orm/mapper.py @@ -108,13 +108,6 @@ class Mapper(InspectionAttr): _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 " @@ -141,7 +134,6 @@ class Mapper(InspectionAttr): inherits=None, inherit_condition=None, inherit_foreign_keys=None, - extension=None, order_by=False, always_refresh=False, version_id_col=None, @@ -295,10 +287,6 @@ class Mapper(InspectionAttr): 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. @@ -673,7 +661,6 @@ class Mapper(InspectionAttr): 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: @@ -715,9 +702,7 @@ class Mapper(InspectionAttr): 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() @@ -1012,6 +997,9 @@ class Mapper(InspectionAttr): "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( @@ -1206,42 +1194,6 @@ class Mapper(InspectionAttr): 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 diff --git a/lib/sqlalchemy/orm/properties.py b/lib/sqlalchemy/orm/properties.py index f804d6eed4..f8bf069262 100644 --- a/lib/sqlalchemy/orm/properties.py +++ b/lib/sqlalchemy/orm/properties.py @@ -44,7 +44,6 @@ class ColumnProperty(StrategizedProperty): "instrument", "comparator_factory", "descriptor", - "extension", "active_history", "expire_on_flush", "info", @@ -56,15 +55,6 @@ class ColumnProperty(StrategizedProperty): "_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. @@ -125,10 +115,6 @@ class ColumnProperty(StrategizedProperty): :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__() @@ -148,7 +134,6 @@ class ColumnProperty(StrategizedProperty): "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) diff --git a/lib/sqlalchemy/orm/relationships.py b/lib/sqlalchemy/orm/relationships.py index 731947cbaf..a8013f36dd 100644 --- a/lib/sqlalchemy/orm/relationships.py +++ b/lib/sqlalchemy/orm/relationships.py @@ -107,15 +107,6 @@ class RelationshipProperty(StrategizedProperty): _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, @@ -129,7 +120,6 @@ class RelationshipProperty(StrategizedProperty): back_populates=None, post_update=False, cascade=False, - extension=None, viewonly=False, lazy="select", collection_class=None, @@ -415,11 +405,6 @@ class RelationshipProperty(StrategizedProperty): :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" @@ -865,7 +850,6 @@ class RelationshipProperty(StrategizedProperty): 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 = ( diff --git a/lib/sqlalchemy/orm/session.py b/lib/sqlalchemy/orm/session.py index 3aa392ecfa..5cf31da0f7 100644 --- a/lib/sqlalchemy/orm/session.py +++ b/lib/sqlalchemy/orm/session.py @@ -25,7 +25,6 @@ from .base import instance_str 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 @@ -37,7 +36,7 @@ from ..sql import roles 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. @@ -660,13 +659,6 @@ class Session(_SessionClassMethods): "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, @@ -678,7 +670,6 @@ class Session(_SessionClassMethods): twophase=False, weak_identity_map=None, binds=None, - extension=None, enable_baked_queries=True, info=None, query_cls=None, @@ -789,11 +780,6 @@ class Session(_SessionClassMethods): 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 @@ -850,10 +836,6 @@ class Session(_SessionClassMethods): 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) diff --git a/lib/sqlalchemy/orm/strategies.py b/lib/sqlalchemy/orm/strategies.py index f82973b39d..f82fc2c576 100644 --- a/lib/sqlalchemy/orm/strategies.py +++ b/lib/sqlalchemy/orm/strategies.py @@ -51,8 +51,6 @@ def _register_attribute( **kw ): - attribute_ext = list(util.to_list(prop.extension, default=[])) - listen_hooks = [] uselist = useobject and prop.uselist @@ -105,7 +103,6 @@ def _register_attribute( uselist=uselist, compare_function=compare_function, useobject=useobject, - extension=attribute_ext, trackparent=useobject and ( prop.single_parent diff --git a/lib/sqlalchemy/pool/base.py b/lib/sqlalchemy/pool/base.py index c45f836db2..db1197eeca 100644 --- a/lib/sqlalchemy/pool/base.py +++ b/lib/sqlalchemy/pool/base.py @@ -16,7 +16,6 @@ import weakref from .. import event from .. import exc -from .. import interfaces from .. import log from .. import util from ..util import threading @@ -60,15 +59,6 @@ class Pool(log.Identified): _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, @@ -76,7 +66,6 @@ class Pool(log.Identified): echo=None, logging_name=None, reset_on_return=True, - listeners=None, events=None, dialect=None, pre_ping=False, @@ -155,11 +144,6 @@ class Pool(log.Identified): 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 @@ -211,9 +195,6 @@ class Pool(log.Identified): 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): @@ -260,22 +241,6 @@ class Pool(log.Identified): "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.""" diff --git a/test/engine/test_deprecations.py b/test/engine/test_deprecations.py index 502fe02450..b7c3cf52f8 100644 --- a/test/engine/test_deprecations.py +++ b/test/engine/test_deprecations.py @@ -1,5 +1,3 @@ -import re - import sqlalchemy as tsa from sqlalchemy import column from sqlalchemy import create_engine @@ -17,7 +15,6 @@ from sqlalchemy import text 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 @@ -28,7 +25,6 @@ from sqlalchemy.testing import is_true 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): @@ -64,15 +60,6 @@ class CreateEngineTest(fixtures.TestBase): ) -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 @@ -128,289 +115,6 @@ class TransactionTest(fixtures.TestBase): 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",) @@ -570,321 +274,6 @@ class PoolTestBase(fixtures.TestBase): ) -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. diff --git a/test/orm/test_deprecations.py b/test/orm/test_deprecations.py index 76cfb86674..8fc3c0f0bb 100644 --- a/test/orm/test_deprecations.py +++ b/test/orm/test_deprecations.py @@ -4,7 +4,6 @@ from sqlalchemy import event 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 @@ -12,36 +11,30 @@ from sqlalchemy import text 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 @@ -50,7 +43,6 @@ from sqlalchemy.testing import fixtures 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 @@ -66,112 +58,6 @@ class DeprecationWarningsTest(fixtures.DeclarativeMappedTest): 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" @@ -1388,834 +1274,6 @@ class DeprecatedDeclTest(fixtures.TestBase): 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