diff options
Diffstat (limited to 'lib/sqlalchemy')
-rw-r--r-- | lib/sqlalchemy/cextension/resultproxy.c | 14 | ||||
-rw-r--r-- | lib/sqlalchemy/engine/__init__.py | 2 | ||||
-rw-r--r-- | lib/sqlalchemy/engine/base.py | 137 | ||||
-rw-r--r-- | lib/sqlalchemy/engine/cursor.py | 243 | ||||
-rw-r--r-- | lib/sqlalchemy/engine/default.py | 41 | ||||
-rw-r--r-- | lib/sqlalchemy/engine/events.py | 9 | ||||
-rw-r--r-- | lib/sqlalchemy/engine/interfaces.py | 57 | ||||
-rw-r--r-- | lib/sqlalchemy/engine/mock.py | 24 | ||||
-rw-r--r-- | lib/sqlalchemy/engine/result.py | 21 | ||||
-rw-r--r-- | lib/sqlalchemy/engine/row.py | 210 | ||||
-rw-r--r-- | lib/sqlalchemy/sql/base.py | 40 | ||||
-rw-r--r-- | lib/sqlalchemy/sql/compiler.py | 2 | ||||
-rw-r--r-- | lib/sqlalchemy/sql/schema.py | 13 | ||||
-rw-r--r-- | lib/sqlalchemy/testing/assertions.py | 6 | ||||
-rw-r--r-- | lib/sqlalchemy/testing/profiling.py | 7 | ||||
-rw-r--r-- | lib/sqlalchemy/testing/suite/test_select.py | 4 |
16 files changed, 111 insertions, 719 deletions
diff --git a/lib/sqlalchemy/cextension/resultproxy.c b/lib/sqlalchemy/cextension/resultproxy.c index 2de672f22..99b2d36f3 100644 --- a/lib/sqlalchemy/cextension/resultproxy.c +++ b/lib/sqlalchemy/cextension/resultproxy.c @@ -55,8 +55,6 @@ static PyObject *sqlalchemy_engine_result = NULL; static int KEY_INTEGER_ONLY = 0; static int KEY_OBJECTS_ONLY = 1; -static int KEY_OBJECTS_BUT_WARN = 2; -//static int KEY_OBJECTS_NO_WARN = 3; /**************** * BaseRow * @@ -409,16 +407,6 @@ BaseRow_getitem_by_object(BaseRow *self, PyObject *key, int asmapping) /* -1 can be either the actual value, or an error flag. */ return NULL; - if (!asmapping && self->key_style == KEY_OBJECTS_BUT_WARN) { - PyObject *tmp; - - tmp = PyObject_CallMethod(self->parent, "_warn_for_nonint", "O", key); - if (tmp == NULL) { - return NULL; - } - Py_DECREF(tmp); - } - return BaseRow_getitem(self, index); } @@ -503,7 +491,7 @@ BaseRow_subscript(BaseRow *self, PyObject *key) static PyObject * BaseRow_subscript_mapping(BaseRow *self, PyObject *key) { - if (self->key_style == KEY_OBJECTS_BUT_WARN || self->key_style == KEY_INTEGER_ONLY) { + if (self->key_style == KEY_INTEGER_ONLY) { return BaseRow_subscript_impl(self, key, 0); } else { diff --git a/lib/sqlalchemy/engine/__init__.py b/lib/sqlalchemy/engine/__init__.py index 6306e201d..ba57eee51 100644 --- a/lib/sqlalchemy/engine/__init__.py +++ b/lib/sqlalchemy/engine/__init__.py @@ -31,7 +31,6 @@ from .cursor import BufferedColumnRow from .cursor import BufferedRowResultProxy from .cursor import CursorResult from .cursor import FullyBufferedResultProxy -from .cursor import LegacyCursorResult from .cursor import ResultProxy from .interfaces import AdaptedConnection from .interfaces import Compiled @@ -52,7 +51,6 @@ from .result import Result from .result import result_tuple from .result import ScalarResult from .row import BaseRow -from .row import LegacyRow from .row import Row from .row import RowMapping from .url import make_url diff --git a/lib/sqlalchemy/engine/base.py b/lib/sqlalchemy/engine/base.py index 2444b5c7f..c60a8383f 100644 --- a/lib/sqlalchemy/engine/base.py +++ b/lib/sqlalchemy/engine/base.py @@ -10,6 +10,7 @@ import contextlib import sys from .interfaces import Connectable +from .interfaces import ConnectionEventsTarget from .interfaces import ExceptionContext from .util import _distill_params from .util import _distill_params_20 @@ -64,11 +65,15 @@ class Connection(Connectable): # used by sqlalchemy.engine.util.TransactionalContext _trans_context_manager = None + # legacy as of 2.0, should be eventually deprecated and + # removed. was used in the "pre_ping" recipe that's been in the docs + # a long time + should_close_with_result = False + def __init__( self, engine, connection=None, - close_with_result=False, _branch_from=None, _execution_options=None, _dispatch=None, @@ -86,7 +91,6 @@ class Connection(Connectable): self._dbapi_connection = connection self._execution_options = _execution_options self._echo = _branch_from._echo - self.should_close_with_result = False self.dispatch = _dispatch self._has_events = _branch_from._has_events else: @@ -99,7 +103,6 @@ class Connection(Connectable): self._transaction = self._nested_transaction = None self.__savepoint_seq = 0 self.__in_begin = False - self.should_close_with_result = close_with_result self.__can_reconnect = _allow_revalidate self._echo = self.engine._should_log_info() @@ -169,8 +172,7 @@ class Connection(Connectable): def _branch(self): """Return a new Connection which references this Connection's - engine and connection; but does not have close_with_result enabled, - and also whose close() method does nothing. + engine and connection; whose close() method does nothing. .. deprecated:: 1.4 the "branching" concept will be removed in SQLAlchemy 2.0 as well as the "Connection.connect()" method which @@ -590,7 +592,9 @@ class Connection(Connectable): return self.connection.info @util.deprecated_20(":meth:`.Connection.connect`") - def connect(self, close_with_result=False): + def connect( + self, + ): """Returns a branched version of this :class:`_engine.Connection`. The :meth:`_engine.Connection.close` method on the returned @@ -1333,8 +1337,6 @@ class Connection(Connectable): self._handle_dbapi_exception(e, None, None, None, None) ret = ctx._exec_default(None, default, None) - if self.should_close_with_result: - self.close() if self._has_events or self.engine._has_events: self.dispatch.after_execute( @@ -1684,7 +1686,6 @@ class Connection(Connectable): """Create an :class:`.ExecutionContext` and execute, returning a :class:`_engine.CursorResult`.""" - branched = self if self.__branch_from: # if this is a "branched" connection, do everything in terms # of the "root" connection, *except* for .close(), which is @@ -1705,6 +1706,7 @@ class Connection(Connectable): self._handle_dbapi_exception( e, util.text_type(statement), parameters, None, None ) + return # not reached if ( self._transaction @@ -1815,10 +1817,6 @@ class Connection(Connectable): result = context._setup_result_proxy() if not self._is_future: - should_close_with_result = branched.should_close_with_result - - if not result._soft_closed and should_close_with_result: - result._autoclose_connection = True if ( # usually we're in a transaction so avoid relatively @@ -1828,16 +1826,6 @@ class Connection(Connectable): ): self._commit_impl(autocommit=True) - # for "connectionless" execution, we have to close this - # Connection after the statement is complete. - # legacy stuff. - if should_close_with_result and context._soft_closed: - assert not self._is_future - - # CursorResult already exhausted rows / has no rows. - # close us now - branched.close() - except BaseException as e: self._handle_dbapi_exception( e, statement, parameters, cursor, context @@ -2035,9 +2023,6 @@ class Connection(Connectable): if invalidate_pool_on_disconnect: self.engine.pool._invalidate(dbapi_conn_wrapper, e) self.invalidate(e) - if self.should_close_with_result: - assert not self._is_future - self.close() @classmethod def _handle_dbapi_exception_noconnection(cls, e, dialect, engine): @@ -2710,7 +2695,7 @@ class TwoPhaseTransaction(RootTransaction): self.connection._commit_twophase_impl(self.xid, self._is_prepared) -class Engine(Connectable, log.Identified): +class Engine(ConnectionEventsTarget, log.Identified): """ Connects a :class:`~sqlalchemy.pool.Pool` and :class:`~sqlalchemy.engine.interfaces.Dialect` together to provide a @@ -2965,10 +2950,9 @@ class Engine(Connectable, log.Identified): yield connection class _trans_ctx(object): - def __init__(self, conn, transaction, close_with_result): + def __init__(self, conn, transaction): self.conn = conn self.transaction = transaction - self.close_with_result = close_with_result def __enter__(self): self.transaction.__enter__() @@ -2978,10 +2962,9 @@ class Engine(Connectable, log.Identified): try: self.transaction.__exit__(type_, value, traceback) finally: - if not self.close_with_result: - self.conn.close() + self.conn.close() - def begin(self, close_with_result=False): + def begin(self): """Return a context manager delivering a :class:`_engine.Connection` with a :class:`.Transaction` established. @@ -2997,15 +2980,6 @@ class Engine(Connectable, log.Identified): is committed. If an error is raised, the :class:`.Transaction` is rolled back. - Legacy use only: the ``close_with_result`` flag is normally ``False``, - and indicates that the :class:`_engine.Connection` will be closed when - the operation is complete. When set to ``True``, it indicates the - :class:`_engine.Connection` is in "single use" mode, where the - :class:`_engine.CursorResult` returned by the first call to - :meth:`_engine.Connection.execute` will close the - :class:`_engine.Connection` when that :class:`_engine.CursorResult` has - exhausted all result rows. - .. seealso:: :meth:`_engine.Engine.connect` - procure a @@ -3016,16 +2990,13 @@ class Engine(Connectable, log.Identified): for a particular :class:`_engine.Connection`. """ - if self._connection_cls._is_future: - conn = self.connect() - else: - conn = self.connect(close_with_result=close_with_result) + conn = self.connect() try: trans = conn.begin() except: with util.safe_reraise(): conn.close() - return Engine._trans_ctx(conn, trans, close_with_result) + return Engine._trans_ctx(conn, trans) @util.deprecated( "1.4", @@ -3106,77 +3077,7 @@ class Engine(Connectable, log.Identified): with self.begin() as conn: conn._run_ddl_visitor(visitorcallable, element, **kwargs) - @util.deprecated_20( - ":meth:`_engine.Engine.execute`", - alternative="All statement execution in SQLAlchemy 2.0 is performed " - "by the :meth:`_engine.Connection.execute` method of " - ":class:`_engine.Connection`, " - "or in the ORM by the :meth:`.Session.execute` method of " - ":class:`.Session`.", - ) - def execute(self, statement, *multiparams, **params): - """Executes the given construct and returns a - :class:`_engine.CursorResult`. - - The arguments are the same as those used by - :meth:`_engine.Connection.execute`. - - Here, a :class:`_engine.Connection` is acquired using the - :meth:`_engine.Engine.connect` method, and the statement executed - with that connection. The returned :class:`_engine.CursorResult` - is flagged - such that when the :class:`_engine.CursorResult` is exhausted and its - underlying cursor is closed, the :class:`_engine.Connection` - created here - will also be closed, which allows its associated DBAPI connection - resource to be returned to the connection pool. - - """ - connection = self.connect(close_with_result=True) - return connection.execute(statement, *multiparams, **params) - - @util.deprecated_20( - ":meth:`_engine.Engine.scalar`", - alternative="All statement execution in SQLAlchemy 2.0 is performed " - "by the :meth:`_engine.Connection.execute` method of " - ":class:`_engine.Connection`, " - "or in the ORM by the :meth:`.Session.execute` method of " - ":class:`.Session`; the :meth:`_future.Result.scalar` " - "method can then be " - "used to return a scalar result.", - ) - def scalar(self, statement, *multiparams, **params): - """Executes and returns the first column of the first row. - - The underlying result/cursor is closed after execution. - """ - return self.execute(statement, *multiparams, **params).scalar() - - def _execute_clauseelement( - self, - elem, - multiparams=None, - params=None, - execution_options=_EMPTY_EXECUTION_OPTS, - ): - connection = self.connect(close_with_result=True) - return connection._execute_clauseelement( - elem, multiparams, params, execution_options - ) - - def _execute_compiled( - self, - compiled, - multiparams, - params, - execution_options=_EMPTY_EXECUTION_OPTS, - ): - connection = self.connect(close_with_result=True) - return connection._execute_compiled( - compiled, multiparams, params, execution_options - ) - - def connect(self, close_with_result=False): + def connect(self): """Return a new :class:`_engine.Connection` object. The :class:`_engine.Connection` object is a facade that uses a DBAPI @@ -3191,7 +3092,7 @@ class Engine(Connectable, log.Identified): """ - return self._connection_cls(self, close_with_result=close_with_result) + return self._connection_cls(self) @util.deprecated( "1.4", diff --git a/lib/sqlalchemy/engine/cursor.py b/lib/sqlalchemy/engine/cursor.py index 5e6078f86..049422617 100644 --- a/lib/sqlalchemy/engine/cursor.py +++ b/lib/sqlalchemy/engine/cursor.py @@ -16,10 +16,10 @@ from .result import Result from .result import ResultMetaData from .result import SimpleResultMetaData from .result import tuplegetter -from .row import LegacyRow +from .row import Row from .. import exc from .. import util -from ..sql import expression +from ..sql import elements from ..sql import sqltypes from ..sql import util as sql_util from ..sql.base import _generative @@ -53,7 +53,8 @@ class CursorResultMetaData(ResultMetaData): "_keymap_by_result_column_idx", "_tuplefilter", "_translated_indexes", - "_safe_for_cache" + "_safe_for_cache", + "_unpickled" # don't need _unique_filters support here for now. Can be added # if a need arises. ) @@ -82,6 +83,7 @@ class CursorResultMetaData(ResultMetaData): new_metadata = self.__class__.__new__(self.__class__) new_metadata.case_sensitive = self.case_sensitive + new_metadata._unpickled = self._unpickled new_metadata._processors = self._processors new_metadata._keys = new_keys new_metadata._tuplefilter = tup @@ -143,6 +145,7 @@ class CursorResultMetaData(ResultMetaData): md._keymap[new] = rec md.case_sensitive = self.case_sensitive + md._unpickled = self._unpickled md._processors = self._processors assert not self._tuplefilter md._tuplefilter = None @@ -158,7 +161,7 @@ class CursorResultMetaData(ResultMetaData): self._tuplefilter = None self._translated_indexes = None self.case_sensitive = dialect.case_sensitive - self._safe_for_cache = False + self._safe_for_cache = self._unpickled = False if context.result_column_struct: ( @@ -610,14 +613,34 @@ class CursorResultMetaData(ResultMetaData): ) def _key_fallback(self, key, err, raiseerr=True): + + # we apparently have not marked .case_sensitive as + # RemovedIn20. I still think we should remove it as I can't + # imagine anyone is using it, however lets make that a separate + # commit. + if not self.case_sensitive and isinstance(key, util.string_types): + map_ = self._keymap + result = map_.get(key.lower()) + if result is not None: + return result + if raiseerr: - util.raise_( - exc.NoSuchColumnError( - "Could not locate column in row for column '%s'" - % util.string_or_unprintable(key) - ), - replace_context=err, - ) + if self._unpickled and isinstance(key, elements.ColumnElement): + util.raise_( + exc.NoSuchColumnError( + "Row was unpickled; lookup by ColumnElement " + "is unsupported" + ), + replace_context=err, + ) + else: + util.raise_( + exc.NoSuchColumnError( + "Could not locate column in row for column '%s'" + % util.string_or_unprintable(key) + ), + replace_context=err, + ) else: return None @@ -694,7 +717,7 @@ class CursorResultMetaData(ResultMetaData): } self._keys = state["_keys"] self.case_sensitive = state["case_sensitive"] - + self._unpickled = True if state["_translated_indexes"]: self._translated_indexes = state["_translated_indexes"] self._tuplefilter = tuplegetter(*self._translated_indexes) @@ -702,116 +725,6 @@ class CursorResultMetaData(ResultMetaData): self._translated_indexes = self._tuplefilter = None -class LegacyCursorResultMetaData(CursorResultMetaData): - __slots__ = () - - def _contains(self, value, row): - key = value - if key in self._keymap: - util.warn_deprecated_20( - "Using the 'in' operator to test for string or column " - "keys, or integer indexes, in a :class:`.Row` object is " - "deprecated and will " - "be removed in a future release. " - "Use the `Row._fields` or `Row._mapping` attribute, i.e. " - "'key in row._fields'", - ) - return True - else: - return self._key_fallback(key, None, False) is not None - - def _key_fallback(self, key, err, raiseerr=True): - map_ = self._keymap - result = None - - if isinstance(key, util.string_types): - result = map_.get(key if self.case_sensitive else key.lower()) - elif isinstance(key, expression.ColumnElement): - if ( - key._tq_label - and ( - key._tq_label - if self.case_sensitive - else key._tq_label.lower() - ) - in map_ - ): - result = map_[ - key._tq_label - if self.case_sensitive - else key._tq_label.lower() - ] - elif ( - hasattr(key, "name") - and (key.name if self.case_sensitive else key.name.lower()) - in map_ - ): - # match is only on name. - result = map_[ - key.name if self.case_sensitive else key.name.lower() - ] - - # search extra hard to make sure this - # isn't a column/label name overlap. - # this check isn't currently available if the row - # was unpickled. - if result is not None and result[MD_OBJECTS] not in ( - None, - _UNPICKLED, - ): - for obj in result[MD_OBJECTS]: - if key._compare_name_for_result(obj): - break - else: - result = None - if result is not None: - if result[MD_OBJECTS] is _UNPICKLED: - util.warn_deprecated( - "Retrieving row values using Column objects from a " - "row that was unpickled is deprecated; adequate " - "state cannot be pickled for this to be efficient. " - "This usage will raise KeyError in a future release.", - version="1.4", - ) - else: - util.warn_deprecated( - "Retrieving row values using Column objects with only " - "matching names as keys is deprecated, and will raise " - "KeyError in a future release; only Column " - "objects that are explicitly part of the statement " - "object should be used.", - version="1.4", - ) - if result is None: - if raiseerr: - util.raise_( - exc.NoSuchColumnError( - "Could not locate column in row for column '%s'" - % util.string_or_unprintable(key) - ), - replace_context=err, - ) - else: - return None - else: - map_[key] = result - return result - - def _warn_for_nonint(self, key): - util.warn_deprecated_20( - "Using non-integer/slice indices on Row is deprecated and will " - "be removed in version 2.0; please use row._mapping[<key>], or " - "the mappings() accessor on the Result object.", - stacklevel=4, - ) - - def _has_key(self, key): - if key in self._keymap: - return True - else: - return self._key_fallback(key, None, False) is not None - - class ResultFetchStrategy(object): """Define a fetching strategy for a result object. @@ -1205,19 +1118,7 @@ class _NoResultMetaData(ResultMetaData): self._we_dont_return_rows() -class _LegacyNoResultMetaData(_NoResultMetaData): - @property - def keys(self): - util.warn_deprecated_20( - "Calling the .keys() method on a result set that does not return " - "rows is deprecated and will raise ResourceClosedError in " - "SQLAlchemy 2.0.", - ) - return [] - - _NO_RESULT_METADATA = _NoResultMetaData() -_LEGACY_NO_RESULT_METADATA = _LegacyNoResultMetaData() class BaseCursorResult(object): @@ -1750,10 +1651,9 @@ class BaseCursorResult(object): class CursorResult(BaseCursorResult, Result): """A Result that is representing state from a DBAPI cursor. - .. versionchanged:: 1.4 The :class:`.CursorResult` and - :class:`.LegacyCursorResult` - classes replace the previous :class:`.ResultProxy` interface. - These classes are based on the :class:`.Result` calling API + .. versionchanged:: 1.4 The :class:`.CursorResult`` + class replaces the previous :class:`.ResultProxy` interface. + This classes are based on the :class:`.Result` calling API which provides an updated usage model and calling facade for SQLAlchemy Core and SQLAlchemy ORM. @@ -1762,14 +1662,6 @@ class CursorResult(BaseCursorResult, Result): the DBAPI. Through the use of filters such as the :meth:`.Result.scalars` method, other kinds of objects may also be returned. - Within the scope of the 1.x series of SQLAlchemy, Core SQL results in - version 1.4 return an instance of :class:`._engine.LegacyCursorResult` - which takes the place of the ``CursorResult`` class used for the 1.3 series - and previously. This object returns rows as :class:`.LegacyRow` objects, - which maintains Python mapping (i.e. dictionary) like behaviors upon the - object itself. Going forward, the :attr:`.Row._mapping` attribute should - be used for dictionary behaviors. - .. seealso:: :ref:`coretutorial_selecting` - introductory material for accessing @@ -1839,62 +1731,7 @@ class CursorResult(BaseCursorResult, Result): self.cursor_strategy.yield_per(self, self.cursor, num) -class LegacyCursorResult(CursorResult): - """Legacy version of :class:`.CursorResult`. - - This class includes connection "connection autoclose" behavior for use with - "connectionless" execution, as well as delivers rows using the - :class:`.LegacyRow` row implementation. - - .. versionadded:: 1.4 - - """ - - _autoclose_connection = False - _process_row = LegacyRow - _cursor_metadata = LegacyCursorResultMetaData - _cursor_strategy_cls = CursorFetchStrategy - - _no_result_metadata = _LEGACY_NO_RESULT_METADATA - - def close(self): - """Close this :class:`_engine.LegacyCursorResult`. - - This method has the same behavior as that of - :meth:`._engine.CursorResult`, but it also may close - the underlying :class:`.Connection` for the case of "connectionless" - execution. - - .. deprecated:: 2.0 "connectionless" execution is deprecated and will - be removed in version 2.0. Version 2.0 will feature the - :class:`_future.Result` - object that will no longer affect the status - of the originating connection in any case. - - After this method is called, it is no longer valid to call upon - the fetch methods, which will raise a :class:`.ResourceClosedError` - on subsequent use. - - .. seealso:: - - :ref:`connections_toplevel` - - :ref:`dbengine_implicit` - """ - self._soft_close(hard=True) - - def _soft_close(self, hard=False): - soft_closed = self._soft_closed - super(LegacyCursorResult, self)._soft_close(hard=hard) - if ( - not soft_closed - and self._soft_closed - and self._autoclose_connection - ): - self.connection.close() - - -ResultProxy = LegacyCursorResult +ResultProxy = CursorResult class BufferedRowResultProxy(ResultProxy): @@ -1919,7 +1756,7 @@ class FullyBufferedResultProxy(ResultProxy): _cursor_strategy_cls = FullyBufferedCursorFetchStrategy -class BufferedColumnRow(LegacyRow): +class BufferedColumnRow(Row): """Row is now BufferedColumn in all cases""" diff --git a/lib/sqlalchemy/engine/default.py b/lib/sqlalchemy/engine/default.py index c379ec673..5046d4035 100644 --- a/lib/sqlalchemy/engine/default.py +++ b/lib/sqlalchemy/engine/default.py @@ -855,7 +855,6 @@ class DefaultExecutionContext(interfaces.ExecutionContext): _is_implicit_returning = False _is_explicit_returning = False - _is_future_result = False _is_server_side = False _soft_closed = False @@ -890,11 +889,6 @@ class DefaultExecutionContext(interfaces.ExecutionContext): self.execution_options = execution_options - self._is_future_result = ( - connection._is_future - or self.execution_options.get("future_result", False) - ) - self.unicode_statement = util.text_type(compiled) if compiled.schema_translate_map: schema_translate_map = self.execution_options.get( @@ -947,11 +941,6 @@ class DefaultExecutionContext(interfaces.ExecutionContext): self.execution_options = execution_options - self._is_future_result = ( - connection._is_future - or self.execution_options.get("future_result", False) - ) - self.result_column_struct = ( compiled._result_columns, compiled._ordered_columns, @@ -1106,11 +1095,6 @@ class DefaultExecutionContext(interfaces.ExecutionContext): self.execution_options = execution_options - self._is_future_result = ( - connection._is_future - or self.execution_options.get("future_result", False) - ) - if not parameters: if self.dialect.positional: self.parameters = [dialect.execute_sequence_format()] @@ -1157,11 +1141,6 @@ class DefaultExecutionContext(interfaces.ExecutionContext): self.execution_options = execution_options - self._is_future_result = ( - connection._is_future - or self.execution_options.get("future_result", False) - ) - self.cursor = self.create_cursor() return self @@ -1420,18 +1399,7 @@ class DefaultExecutionContext(interfaces.ExecutionContext): if cursor_description is None: strategy = _cursor._NO_CURSOR_DQL - if self._is_future_result: - if self.root_connection.should_close_with_result: - raise exc.InvalidRequestError( - "can't use future_result=True with close_with_result" - ) - result = _cursor.CursorResult( - self, strategy, cursor_description - ) - else: - result = _cursor.LegacyCursorResult( - self, strategy, cursor_description - ) + result = _cursor.CursorResult(self, strategy, cursor_description) if ( self.compiled @@ -1493,12 +1461,7 @@ class DefaultExecutionContext(interfaces.ExecutionContext): if cursor_description is None: strategy = _cursor._NO_CURSOR_DML - if self._is_future_result: - result = _cursor.CursorResult(self, strategy, cursor_description) - else: - result = _cursor.LegacyCursorResult( - self, strategy, cursor_description - ) + result = _cursor.CursorResult(self, strategy, cursor_description) if self.isinsert: if self._is_implicit_returning: diff --git a/lib/sqlalchemy/engine/events.py b/lib/sqlalchemy/engine/events.py index f091c7733..effebb4cb 100644 --- a/lib/sqlalchemy/engine/events.py +++ b/lib/sqlalchemy/engine/events.py @@ -7,20 +7,21 @@ from .base import Engine -from .interfaces import Connectable +from .interfaces import ConnectionEventsTarget from .interfaces import Dialect from .. import event from .. import exc class ConnectionEvents(event.Events): - """Available events for :class:`.Connectable`, which includes + """Available events for :class:`_engine.Connection` and :class:`_engine.Engine`. The methods here define the name of an event as well as the names of members that are passed to listener functions. - An event listener can be associated with any :class:`.Connectable` + An event listener can be associated with any + :class:`_engine.Connection` or :class:`_engine.Engine` class or instance, such as an :class:`_engine.Engine`, e.g.:: from sqlalchemy import event, create_engine @@ -90,7 +91,7 @@ class ConnectionEvents(event.Events): """ _target_class_doc = "SomeEngine" - _dispatch_target = Connectable + _dispatch_target = ConnectionEventsTarget @classmethod def _listen(cls, event_key, retval=False): diff --git a/lib/sqlalchemy/engine/interfaces.py b/lib/sqlalchemy/engine/interfaces.py index fdaeaddcd..3fd245e44 100644 --- a/lib/sqlalchemy/engine/interfaces.py +++ b/lib/sqlalchemy/engine/interfaces.py @@ -7,7 +7,6 @@ """Define core interfaces used by the engine system.""" -from .. import util from ..sql.compiler import Compiled # noqa from ..sql.compiler import TypeCompiler # noqa @@ -1327,9 +1326,7 @@ class ExecutionContext(object): root_connection. root_connection - Connection object which is the source of this ExecutionContext. This - Connection may have close_with_result=True set, in which case it can - only be used once. + Connection object which is the source of this ExecutionContext. dialect dialect which created this ExecutionContext. @@ -1534,48 +1531,43 @@ class ExecutionContext(object): raise NotImplementedError() -@util.deprecated_20_cls( - ":class:`.Connectable`", - alternative=( - "The :class:`_engine.Engine` will be the only Core " - "object that features a .connect() method, and the " - ":class:`_engine.Connection` will be the only object that features " - "an .execute() method." - ), - constructor=None, -) -class Connectable(object): - """Interface for an object which supports execution of SQL constructs. +class ConnectionEventsTarget: + """An object which can accept events from :class:`.ConnectionEvents`. - The two implementations of :class:`.Connectable` are - :class:`_engine.Connection` and :class:`_engine.Engine`. + Includes :class:`_engine.Connection` and :class:`_engine.Engine`. - Connectable must also implement the 'dialect' member which references a - :class:`.Dialect` instance. + .. versionadded:: 2.0 """ - def connect(self, **kwargs): - """Return a :class:`_engine.Connection` object. - Depending on context, this may be ``self`` if this object - is already an instance of :class:`_engine.Connection`, or a newly - procured :class:`_engine.Connection` if this object is an instance - of :class:`_engine.Engine`. +class Connectable(ConnectionEventsTarget): + """Interface for an object which supports execution of SQL constructs. + + This is the base for :class:`_engine.Connection` and similar objects. - """ + .. versionchanged:: 2.0 :class:`_engine.Connectable` is no longer the + base class for :class:`_engine.Engine`, replaced with + :class:`_engine.ConnectionEventsTarget`. + + """ engine = None """The :class:`_engine.Engine` instance referred to by this :class:`.Connectable`. - May be ``self`` if this is already an :class:`_engine.Engine`. + """ + + dialect = None + """The :class:`_engine.Dialect` instance referred to by this + :class:`.Connectable`. """ def execute(self, object_, *multiparams, **params): """Executes the given construct and returns a - :class:`_engine.CursorResult`. + :class:`_result.Result`. + """ raise NotImplementedError() @@ -1583,13 +1575,8 @@ class Connectable(object): """Executes and returns the first column of the first row. The underlying cursor is closed after execution. - """ - raise NotImplementedError() - def _run_visitor(self, visitorcallable, element, **kwargs): - raise NotImplementedError() - - def _execute_clauseelement(self, elem, multiparams=None, params=None): + """ raise NotImplementedError() diff --git a/lib/sqlalchemy/engine/mock.py b/lib/sqlalchemy/engine/mock.py index 803fe30a2..5da716b6b 100644 --- a/lib/sqlalchemy/engine/mock.py +++ b/lib/sqlalchemy/engine/mock.py @@ -10,7 +10,6 @@ from operator import attrgetter from . import base from . import url as _url from .. import util -from ..sql import ddl class MockConnection(base.Connectable): @@ -22,32 +21,15 @@ class MockConnection(base.Connectable): dialect = property(attrgetter("_dialect")) name = property(lambda s: s._dialect.name) - def schema_for_object(self, obj): - return obj.schema - def connect(self, **kwargs): return self + def schema_for_object(self, obj): + return obj.schema + def execution_options(self, **kw): return self - def compiler(self, statement, parameters, **kwargs): - return self._dialect.compiler( - statement, parameters, engine=self, **kwargs - ) - - def create(self, entity, **kwargs): - kwargs["checkfirst"] = False - - ddl.SchemaGenerator(self.dialect, self, **kwargs).traverse_single( - entity - ) - - def drop(self, entity, **kwargs): - kwargs["checkfirst"] = False - - ddl.SchemaDropper(self.dialect, self, **kwargs).traverse_single(entity) - def _run_ddl_visitor( self, visitorcallable, element, connection=None, **kwargs ): diff --git a/lib/sqlalchemy/engine/result.py b/lib/sqlalchemy/engine/result.py index 3c2e682be..48572c7fe 100644 --- a/lib/sqlalchemy/engine/result.py +++ b/lib/sqlalchemy/engine/result.py @@ -25,8 +25,6 @@ from ..util import py2k if _baserow_usecext: from sqlalchemy.cresultproxy import tuplegetter - - _row_as_tuple = tuplegetter else: def tuplegetter(*indexes): @@ -37,16 +35,6 @@ else: else: return lambda row: (it(row),) - def _row_as_tuple(*indexes): - # circumvent LegacyRow.__getitem__ pointing to - # _get_by_key_impl_mapping for now. otherwise we could - # use itemgetter - getters = [ - operator.methodcaller("_get_by_int_impl", index) - for index in indexes - ] - return lambda rec: tuple([getter(rec) for getter in getters]) - class ResultMetaData(object): """Base for metadata about result rows.""" @@ -71,13 +59,6 @@ class ResultMetaData(object): assert raiseerr util.raise_(KeyError(key), replace_context=err) - def _warn_for_nonint(self, key): - util.warn_deprecated_20( - "Retrieving row members using strings or other non-integers is " - "deprecated; use row._mapping for a dictionary interface " - "to the row" - ) - def _raise_for_nonint(self, key): raise TypeError( "TypeError: tuple indices must be integers or slices, not %s" @@ -104,7 +85,7 @@ class ResultMetaData(object): def _row_as_tuple_getter(self, keys): indexes = self._indexes_for_keys(keys) - return _row_as_tuple(*indexes) + return tuplegetter(*indexes) class RMKeyView(collections_abc.KeysView): diff --git a/lib/sqlalchemy/engine/row.py b/lib/sqlalchemy/engine/row.py index dc11e3548..e268cfec9 100644 --- a/lib/sqlalchemy/engine/row.py +++ b/lib/sqlalchemy/engine/row.py @@ -40,19 +40,12 @@ except ImportError: KEY_INTEGER_ONLY = 0 -"""__getitem__ only allows integer values, raises TypeError otherwise""" +"""__getitem__ only allows integer values and slices, raises TypeError + otherwise""" KEY_OBJECTS_ONLY = 1 """__getitem__ only allows string/object values, raises TypeError otherwise""" -KEY_OBJECTS_BUT_WARN = 2 -"""__getitem__ allows integer or string/object values, but emits a 2.0 -deprecation warning if string/object is passed""" - -KEY_OBJECTS_NO_WARN = 3 -"""__getitem__ allows integer or string/object values with no warnings -or errors.""" - try: from sqlalchemy.cresultproxy import BaseRow @@ -116,31 +109,12 @@ except ImportError: if int in key.__class__.__mro__: return self._data[key] - if self._key_style == KEY_INTEGER_ONLY: - self._parent._raise_for_nonint(key) - - # the following is all LegacyRow support. none of this - # should be called if not LegacyRow - # assert isinstance(self, LegacyRow) - - try: - rec = self._keymap[key] - except KeyError as ke: - rec = self._parent._key_fallback(key, ke) - except TypeError: - if isinstance(key, slice): - return tuple(self._data[key]) - else: - raise - - mdindex = rec[MD_INDEX] - if mdindex is None: - self._parent._raise_for_ambiguous_column_name(rec) + assert self._key_style == KEY_INTEGER_ONLY - elif self._key_style == KEY_OBJECTS_BUT_WARN and mdindex != key: - self._parent._warn_for_nonint(key) + if isinstance(key, slice): + return tuple(self._data[key]) - return self._data[mdindex] + self._parent._raise_for_nonint(key) # The original 1.4 plan was that Row would not allow row["str"] # access, however as the C extensions were inadvertently allowing @@ -190,26 +164,19 @@ class Row(BaseRow, collections_abc.Sequence): :ref:`coretutorial_selecting` - includes examples of selecting rows from SELECT statements. - :class:`.LegacyRow` - Compatibility interface introduced in SQLAlchemy - 1.4. - .. versionchanged:: 1.4 - Renamed ``RowProxy`` to :class:`.Row`. :class:`.Row` is no longer a + Renamed ``RowProxy`` to :class:`.Row`. :class:`.Row` is no longer a "proxy" object in that it contains the final form of data within it, - and now acts mostly like a named tuple. Mapping-like functionality is - moved to the :attr:`.Row._mapping` attribute, but will remain available - in SQLAlchemy 1.x series via the :class:`.LegacyRow` class that is used - by :class:`_engine.LegacyCursorResult`. - See :ref:`change_4710_core` for background - on this change. + and now acts mostly like a named tuple. Mapping-like functionality is + moved to the :attr:`.Row._mapping` attribute. See + :ref:`change_4710_core` for background on this change. """ __slots__ = () - # in 2.0, this should be KEY_INTEGER_ONLY - _default_key_style = KEY_OBJECTS_BUT_WARN + _default_key_style = KEY_INTEGER_ONLY @property def _mapping(self): @@ -217,10 +184,7 @@ class Row(BaseRow, collections_abc.Sequence): This object provides a consistent Python mapping (i.e. dictionary) interface for the data contained within the row. The :class:`.Row` - by itself behaves like a named tuple, however in the 1.4 series of - SQLAlchemy, the :class:`.LegacyRow` class is still used by Core which - continues to have mapping-like behaviors against the row object - itself. + by itself behaves like a named tuple. .. seealso:: @@ -304,32 +268,6 @@ class Row(BaseRow, collections_abc.Sequence): def __repr__(self): return repr(sql_util._repr_row(self)) - @util.deprecated_20( - ":meth:`.Row.keys`", - alternative="Use the namedtuple standard accessor " - ":attr:`.Row._fields`, or for full mapping behavior use " - "row._mapping.keys() ", - ) - def keys(self): - """Return the list of keys as strings represented by this - :class:`.Row`. - - The keys can represent the labels of the columns returned by a core - statement or the names of the orm classes returned by an orm - execution. - - This method is analogous to the Python dictionary ``.keys()`` method, - except that it returns a list, not an iterator. - - .. seealso:: - - :attr:`.Row._fields` - - :attr:`.Row._mapping` - - """ - return self._parent.keys - @property def _fields(self): """Return a tuple of string keys as represented by this @@ -376,130 +314,6 @@ class Row(BaseRow, collections_abc.Sequence): raise NotImplementedError() -class LegacyRow(Row): - """A subclass of :class:`.Row` that delivers 1.x SQLAlchemy behaviors - for Core. - - The :class:`.LegacyRow` class is where most of the Python mapping - (i.e. dictionary-like) - behaviors are implemented for the row object. The mapping behavior - of :class:`.Row` going forward is accessible via the :class:`.Row._mapping` - attribute. - - .. versionadded:: 1.4 - added :class:`.LegacyRow` which encapsulates most - of the deprecated behaviors of :class:`.Row`. - - """ - - __slots__ = () - - if util.SQLALCHEMY_WARN_20: - _default_key_style = KEY_OBJECTS_BUT_WARN - else: - _default_key_style = KEY_OBJECTS_NO_WARN - - def __contains__(self, key): - return self._parent._contains(key, self) - - # prior to #6218, LegacyRow would redirect the behavior of __getitem__ - # for the non C version of BaseRow. This is now set up by Python BaseRow - # in all cases - # if not _baserow_usecext: - # __getitem__ = BaseRow._get_by_key_impl - - @util.deprecated( - "1.4", - "The :meth:`.LegacyRow.has_key` method is deprecated and will be " - "removed in a future release. To test for key membership, use " - "the :attr:`Row._mapping` attribute, i.e. 'key in row._mapping`.", - ) - def has_key(self, key): - """Return True if this :class:`.LegacyRow` contains the given key. - - Through the SQLAlchemy 1.x series, the ``__contains__()`` method of - :class:`.Row` (or :class:`.LegacyRow` as of SQLAlchemy 1.4) also links - to :meth:`.Row.has_key`, in that an expression such as :: - - "some_col" in row - - Will return True if the row contains a column named ``"some_col"``, - in the way that a Python mapping works. - - However, it is planned that the 2.0 series of SQLAlchemy will reverse - this behavior so that ``__contains__()`` will refer to a value being - present in the row, in the way that a Python tuple works. - - .. seealso:: - - :ref:`change_4710_core` - - """ - - return self._parent._has_key(key) - - @util.deprecated( - "1.4", - "The :meth:`.LegacyRow.items` method is deprecated and will be " - "removed in a future release. Use the :attr:`Row._mapping` " - "attribute, i.e., 'row._mapping.items()'.", - ) - def items(self): - """Return a list of tuples, each tuple containing a key/value pair. - - This method is analogous to the Python dictionary ``.items()`` method, - except that it returns a list, not an iterator. - - """ - - return [(key, self[key]) for key in self.keys()] - - @util.deprecated( - "1.4", - "The :meth:`.LegacyRow.iterkeys` method is deprecated and will be " - "removed in a future release. Use the :attr:`Row._mapping` " - "attribute, i.e., 'row._mapping.keys()'.", - ) - def iterkeys(self): - """Return a an iterator against the :meth:`.Row.keys` method. - - This method is analogous to the Python-2-only dictionary - ``.iterkeys()`` method. - - """ - return iter(self._parent.keys) - - @util.deprecated( - "1.4", - "The :meth:`.LegacyRow.itervalues` method is deprecated and will be " - "removed in a future release. Use the :attr:`Row._mapping` " - "attribute, i.e., 'row._mapping.values()'.", - ) - def itervalues(self): - """Return a an iterator against the :meth:`.Row.values` method. - - This method is analogous to the Python-2-only dictionary - ``.itervalues()`` method. - - """ - return iter(self) - - @util.deprecated( - "1.4", - "The :meth:`.LegacyRow.values` method is deprecated and will be " - "removed in a future release. Use the :attr:`Row._mapping` " - "attribute, i.e., 'row._mapping.values()'.", - ) - def values(self): - """Return the values represented by this :class:`.Row` as a list. - - This method is analogous to the Python dictionary ``.values()`` method, - except that it returns a list, not an iterator. - - """ - - return self._values_impl() - - BaseRowProxy = BaseRow RowProxy = Row diff --git a/lib/sqlalchemy/sql/base.py b/lib/sqlalchemy/sql/base.py index aba80222a..4bcb655d6 100644 --- a/lib/sqlalchemy/sql/base.py +++ b/lib/sqlalchemy/sql/base.py @@ -937,46 +937,6 @@ class Executable(roles.StatementRole, Generative): """ return self._execution_options - @util.deprecated_20( - ":meth:`.Executable.execute`", - alternative="All statement execution in SQLAlchemy 2.0 is performed " - "by the :meth:`_engine.Connection.execute` method of " - ":class:`_engine.Connection`, " - "or in the ORM by the :meth:`.Session.execute` method of " - ":class:`.Session`.", - ) - def execute(self, *multiparams, **params): - """Compile and execute this :class:`.Executable`.""" - e = self.bind - if e is None: - label = ( - getattr(self, "description", None) or self.__class__.__name__ - ) - msg = ( - "This %s is not directly bound to a Connection or Engine. " - "Use the .execute() method of a Connection or Engine " - "to execute this construct." % label - ) - raise exc.UnboundExecutionError(msg) - return e._execute_clauseelement( - self, multiparams, params, util.immutabledict() - ) - - @util.deprecated_20( - ":meth:`.Executable.scalar`", - alternative="Scalar execution in SQLAlchemy 2.0 is performed " - "by the :meth:`_engine.Connection.scalar` method of " - ":class:`_engine.Connection`, " - "or in the ORM by the :meth:`.Session.scalar` method of " - ":class:`.Session`.", - ) - def scalar(self, *multiparams, **params): - """Compile and execute this :class:`.Executable`, returning the - result's scalar representation. - - """ - return self.execute(*multiparams, **params).scalar() - @property @util.deprecated_20( ":attr:`.Executable.bind`", diff --git a/lib/sqlalchemy/sql/compiler.py b/lib/sqlalchemy/sql/compiler.py index 0cd568fcc..5c3fbb2b1 100644 --- a/lib/sqlalchemy/sql/compiler.py +++ b/lib/sqlalchemy/sql/compiler.py @@ -3186,6 +3186,8 @@ class SQLCompiler(Compiled): # passed in. for ORM use this will convert from an ORM-state # SELECT to a regular "Core" SELECT. other composed operations # such as computation of joins will be performed. + kwargs["within_columns_clause"] = False + compile_state = select_stmt._compile_state_factory( select_stmt, self, **kwargs ) diff --git a/lib/sqlalchemy/sql/schema.py b/lib/sqlalchemy/sql/schema.py index 166ad98cd..e45e22564 100644 --- a/lib/sqlalchemy/sql/schema.py +++ b/lib/sqlalchemy/sql/schema.py @@ -2517,19 +2517,6 @@ class DefaultGenerator(Executable, SchemaItem): else: self.column.default = self - @util.deprecated_20( - ":meth:`.DefaultGenerator.execute`", - alternative="All statement execution in SQLAlchemy 2.0 is performed " - "by the :meth:`_engine.Connection.execute` method of " - ":class:`_engine.Connection`, " - "or in the ORM by the :meth:`.Session.execute` method of " - ":class:`.Session`.", - ) - def execute(self, bind=None): - if bind is None: - bind = _bind_or_error(self) - return bind._execute_default(self, (), util.EMPTY_DICT) - def _execute_on_connection( self, connection, multiparams, params, execution_options ): diff --git a/lib/sqlalchemy/testing/assertions.py b/lib/sqlalchemy/testing/assertions.py index 6bf14aecd..c30fdf823 100644 --- a/lib/sqlalchemy/testing/assertions.py +++ b/lib/sqlalchemy/testing/assertions.py @@ -204,11 +204,7 @@ def _expect_warnings( with mock.patch("warnings.warn", our_warn), mock.patch( "sqlalchemy.util.SQLALCHEMY_WARN_20", True - ), mock.patch( - "sqlalchemy.util.deprecations.SQLALCHEMY_WARN_20", True - ), mock.patch( - "sqlalchemy.engine.row.LegacyRow._default_key_style", 2 - ): + ), mock.patch("sqlalchemy.util.deprecations.SQLALCHEMY_WARN_20", True): try: yield finally: diff --git a/lib/sqlalchemy/testing/profiling.py b/lib/sqlalchemy/testing/profiling.py index de4847f2f..dd5040205 100644 --- a/lib/sqlalchemy/testing/profiling.py +++ b/lib/sqlalchemy/testing/profiling.py @@ -243,17 +243,12 @@ def function_call_count(variance=0.05, times=1, warmup=0): from sqlalchemy.util import decorator from sqlalchemy.util import deprecations - from sqlalchemy.engine import row from sqlalchemy.testing import mock @decorator def wrap(fn, *args, **kw): - with mock.patch.object( - deprecations, "SQLALCHEMY_WARN_20", False - ), mock.patch.object( - row.LegacyRow, "_default_key_style", row.KEY_OBJECTS_NO_WARN - ): + with mock.patch.object(deprecations, "SQLALCHEMY_WARN_20", False): for warm in range(warmup): fn(*args, **kw) diff --git a/lib/sqlalchemy/testing/suite/test_select.py b/lib/sqlalchemy/testing/suite/test_select.py index a3475f651..63502b077 100644 --- a/lib/sqlalchemy/testing/suite/test_select.py +++ b/lib/sqlalchemy/testing/suite/test_select.py @@ -206,8 +206,8 @@ class FetchLimitOffsetTest(fixtures.TablesTest): eq_(connection.execute(select, params).fetchall(), result) def _assert_result_str(self, select, result, params=()): - conn = config.db.connect(close_with_result=True) - eq_(conn.exec_driver_sql(select, params).fetchall(), result) + with config.db.connect() as conn: + eq_(conn.exec_driver_sql(select, params).fetchall(), result) def test_simple_limit(self, connection): table = self.tables.some_table |