diff options
Diffstat (limited to 'lib/sqlalchemy')
44 files changed, 842 insertions, 468 deletions
diff --git a/lib/sqlalchemy/dialects/mssql/base.py b/lib/sqlalchemy/dialects/mssql/base.py index d0f02bb23..5c446341c 100644 --- a/lib/sqlalchemy/dialects/mssql/base.py +++ b/lib/sqlalchemy/dialects/mssql/base.py @@ -2377,12 +2377,8 @@ class MSDialect(default.DefaultDialect): "and ind.is_primary_key=0 and ind.type != 0" ) .bindparams( - sql.bindparam( - "tabname", tablename, sqltypes.String(convert_unicode=True) - ), - sql.bindparam( - "schname", owner, sqltypes.String(convert_unicode=True) - ), + sql.bindparam("tabname", tablename, sqltypes.String()), + sql.bindparam("schname", owner, sqltypes.String()), ) .columns(name=sqltypes.Unicode()) ) @@ -2406,12 +2402,8 @@ class MSDialect(default.DefaultDialect): "and sch.name=:schname" ) .bindparams( - sql.bindparam( - "tabname", tablename, sqltypes.String(convert_unicode=True) - ), - sql.bindparam( - "schname", owner, sqltypes.String(convert_unicode=True) - ), + sql.bindparam("tabname", tablename, sqltypes.String()), + sql.bindparam("schname", owner, sqltypes.String()), ) .columns(name=sqltypes.Unicode()) ) @@ -2436,12 +2428,8 @@ class MSDialect(default.DefaultDialect): "views.schema_id=sch.schema_id and " "views.name=:viewname and sch.name=:schname" ).bindparams( - sql.bindparam( - "viewname", viewname, sqltypes.String(convert_unicode=True) - ), - sql.bindparam( - "schname", owner, sqltypes.String(convert_unicode=True) - ), + sql.bindparam("viewname", viewname, sqltypes.String()), + sql.bindparam("schname", owner, sqltypes.String()), ) ) diff --git a/lib/sqlalchemy/dialects/mssql/information_schema.py b/lib/sqlalchemy/dialects/mssql/information_schema.py index 88628e6a7..b72dbfe93 100644 --- a/lib/sqlalchemy/dialects/mssql/information_schema.py +++ b/lib/sqlalchemy/dialects/mssql/information_schema.py @@ -69,7 +69,7 @@ tables = Table( Column("TABLE_CATALOG", CoerceUnicode, key="table_catalog"), Column("TABLE_SCHEMA", CoerceUnicode, key="table_schema"), Column("TABLE_NAME", CoerceUnicode, key="table_name"), - Column("TABLE_TYPE", String(convert_unicode=True), key="table_type"), + Column("TABLE_TYPE", CoerceUnicode, key="table_type"), schema="INFORMATION_SCHEMA", ) @@ -98,9 +98,7 @@ constraints = Table( Column("TABLE_SCHEMA", CoerceUnicode, key="table_schema"), Column("TABLE_NAME", CoerceUnicode, key="table_name"), Column("CONSTRAINT_NAME", CoerceUnicode, key="constraint_name"), - Column( - "CONSTRAINT_TYPE", String(convert_unicode=True), key="constraint_type" - ), + Column("CONSTRAINT_TYPE", CoerceUnicode, key="constraint_type"), schema="INFORMATION_SCHEMA", ) diff --git a/lib/sqlalchemy/dialects/mysql/oursql.py b/lib/sqlalchemy/dialects/mysql/oursql.py index 165a8f4e2..80313a2fc 100644 --- a/lib/sqlalchemy/dialects/mysql/oursql.py +++ b/lib/sqlalchemy/dialects/mysql/oursql.py @@ -175,7 +175,7 @@ class MySQLDialect_oursql(MySQLDialect): ): return MySQLDialect._show_create_table( self, - connection.contextual_connect( + connection._contextual_connect( close_with_result=True ).execution_options(_oursql_plain_query=True), table, diff --git a/lib/sqlalchemy/dialects/oracle/cx_oracle.py b/lib/sqlalchemy/dialects/oracle/cx_oracle.py index 1164c09f7..abeef39d2 100644 --- a/lib/sqlalchemy/dialects/oracle/cx_oracle.py +++ b/lib/sqlalchemy/dialects/oracle/cx_oracle.py @@ -736,6 +736,17 @@ class OracleDialect_cx_oracle(OracleDialect): _cx_oracle_threaded = None + @util.deprecated_params( + threaded=( + "1.3", + "The 'threaded' parameter to the cx_oracle dialect " + "is deprecated as a dialect-level argument, and will be removed " + "in a future release. As of version 1.3, it defaults to False " + "rather than True. The 'threaded' option can be passed to " + "cx_Oracle directly in the URL query string passed to " + ":func:`.create_engine`.", + ) + ) def __init__( self, auto_convert_lobs=True, @@ -749,13 +760,6 @@ class OracleDialect_cx_oracle(OracleDialect): OracleDialect.__init__(self, **kwargs) self.arraysize = arraysize if threaded is not None: - util.warn_deprecated( - "The 'threaded' parameter to the cx_oracle dialect " - "itself is deprecated. The value now defaults to False in " - "any case. To pass an explicit True value, use the " - "create_engine connect_args dictionary or add ?threaded=true " - "to the URL string." - ) self._cx_oracle_threaded = threaded self.auto_convert_lobs = auto_convert_lobs self.coerce_to_unicode = coerce_to_unicode @@ -811,23 +815,6 @@ class OracleDialect_cx_oracle(OracleDialect): self._is_cx_oracle_6 = self.cx_oracle_ver >= (6,) - def _pop_deprecated_kwargs(self, kwargs): - auto_setinputsizes = kwargs.pop("auto_setinputsizes", None) - exclude_setinputsizes = kwargs.pop("exclude_setinputsizes", None) - if auto_setinputsizes or exclude_setinputsizes: - util.warn_deprecated( - "auto_setinputsizes and exclude_setinputsizes are deprecated. " - "Modern cx_Oracle only requires that LOB types are part " - "of this behavior, and these parameters no longer have any " - "effect." - ) - allow_twophase = kwargs.pop("allow_twophase", None) - if allow_twophase is not None: - util.warn.deprecated( - "allow_twophase is deprecated. The cx_Oracle dialect no " - "longer supports two-phase transaction mode." - ) - def _parse_cx_oracle_ver(self, version): m = re.match(r"(\d+)\.(\d+)(?:\.(\d+))?", version) if m: @@ -982,6 +969,7 @@ class OracleDialect_cx_oracle(OracleDialect): def create_connect_args(self, url): opts = dict(url.query) + # deprecated in 1.3 for opt in ("use_ansi", "auto_convert_lobs"): if opt in opts: util.warn_deprecated( @@ -1067,15 +1055,20 @@ class OracleDialect_cx_oracle(OracleDialect): else: return False + @util.deprecated( + "1.2", + "The create_xid() method of the cx_Oracle dialect is deprecated and " + "will be removed in a future release. " + "Two-phase transaction support is no longer functional " + "in SQLAlchemy's cx_Oracle dialect as of cx_Oracle 6.0b1, which no " + "longer supports the API that SQLAlchemy relied upon.", + ) def create_xid(self): """create a two-phase transaction ID. this id will be passed to do_begin_twophase(), do_rollback_twophase(), do_commit_twophase(). its format is unspecified. - .. deprecated:: two-phase transaction support is no longer functional - in SQLAlchemy's cx_Oracle dialect as of cx_Oracle 6.0b1 - """ id_ = random.randint(0, 2 ** 128) diff --git a/lib/sqlalchemy/dialects/postgresql/psycopg2.py b/lib/sqlalchemy/dialects/postgresql/psycopg2.py index 98e703fb5..8a15a8559 100644 --- a/lib/sqlalchemy/dialects/postgresql/psycopg2.py +++ b/lib/sqlalchemy/dialects/postgresql/psycopg2.py @@ -407,11 +407,17 @@ class _PGNumeric(sqltypes.Numeric): class _PGEnum(ENUM): def result_processor(self, dialect, coltype): - if util.py2k and self.convert_unicode is True: - # we can't easily use PG's extensions here because - # the OID is on the fly, and we need to give it a python - # function anyway - not really worth it. - self.convert_unicode = "force_nocheck" + if util.py2k and self._expect_unicode is True: + # for py2k, if the enum type needs unicode data (which is set up as + # part of the Enum() constructor based on values passed as py2k + # unicode objects) we have to use our own converters since + # psycopg2's don't work, a rare exception to the "modern DBAPIs + # support unicode everywhere" theme of deprecating + # convert_unicode=True. Use the special "force_nocheck" directive + # which forces unicode conversion to happen on the Python side + # without an isinstance() check. in py3k psycopg2 does the right + # thing automatically. + self._expect_unicode = "force_nocheck" return super(_PGEnum, self).result_processor(dialect, coltype) diff --git a/lib/sqlalchemy/engine/__init__.py b/lib/sqlalchemy/engine/__init__.py index 5f77d17a1..fbf346cec 100644 --- a/lib/sqlalchemy/engine/__init__.py +++ b/lib/sqlalchemy/engine/__init__.py @@ -147,6 +147,13 @@ def create_engine(*args, **kwargs): columns to accommodate Python Unicode objects directly as though the datatype were the :class:`.Unicode` type. + .. deprecated:: The :paramref:`.create_engine.convert_unicode` flag + and related Unicode conversion features are legacy Python 2 + mechanisms which no longer have relevance under Python 3. + As all modern DBAPIs now support Python Unicode fully even + under Python 2, these flags will be removed in an upcoming + release. + .. note:: SQLAlchemy's unicode-conversion flags and features only apply diff --git a/lib/sqlalchemy/engine/base.py b/lib/sqlalchemy/engine/base.py index ebf8dd28a..64303f290 100644 --- a/lib/sqlalchemy/engine/base.py +++ b/lib/sqlalchemy/engine/base.py @@ -493,19 +493,7 @@ class Connection(Connectable): return self._branch() - def contextual_connect(self, **kwargs): - """Returns a branched version of this :class:`.Connection`. - - The :meth:`.Connection.close` method on the returned - :class:`.Connection` can be called and this - :class:`.Connection` will remain open. - - This method provides usage symmetry with - :meth:`.Engine.contextual_connect`, including for usage - with context managers. - - """ - + def _contextual_connect(self, **kwargs): return self._branch() def invalidate(self, exception=None): @@ -1993,13 +1981,13 @@ class Engine(Connectable, log.Identified): self.dispatch.engine_disposed(self) def _execute_default(self, default): - with self.contextual_connect() as conn: + with self._contextual_connect() as conn: return conn._execute_default(default, (), {}) @contextlib.contextmanager def _optional_conn_ctx_manager(self, connection=None): if connection is None: - with self.contextual_connect() as conn: + with self._contextual_connect() as conn: yield conn else: yield connection @@ -2058,7 +2046,7 @@ class Engine(Connectable, log.Identified): for a particular :class:`.Connection`. """ - conn = self.contextual_connect(close_with_result=close_with_result) + conn = self._contextual_connect(close_with_result=close_with_result) try: trans = conn.begin() except: @@ -2105,7 +2093,7 @@ class Engine(Connectable, log.Identified): """ - with self.contextual_connect() as conn: + with self._contextual_connect() as conn: return conn.transaction(callable_, *args, **kwargs) def run_callable(self, callable_, *args, **kwargs): @@ -2121,7 +2109,7 @@ class Engine(Connectable, log.Identified): which one is being dealt with. """ - with self.contextual_connect() as conn: + with self._contextual_connect() as conn: return conn.run_callable(callable_, *args, **kwargs) def execute(self, statement, *multiparams, **params): @@ -2140,18 +2128,18 @@ class Engine(Connectable, log.Identified): """ - connection = self.contextual_connect(close_with_result=True) + connection = self._contextual_connect(close_with_result=True) return connection.execute(statement, *multiparams, **params) def scalar(self, statement, *multiparams, **params): return self.execute(statement, *multiparams, **params).scalar() def _execute_clauseelement(self, elem, multiparams=None, params=None): - connection = self.contextual_connect(close_with_result=True) + connection = self._contextual_connect(close_with_result=True) return connection._execute_clauseelement(elem, multiparams, params) def _execute_compiled(self, compiled, multiparams, params): - connection = self.contextual_connect(close_with_result=True) + connection = self._contextual_connect(close_with_result=True) return connection._execute_compiled(compiled, multiparams, params) def connect(self, **kwargs): @@ -2170,6 +2158,13 @@ class Engine(Connectable, log.Identified): return self._connection_cls(self, **kwargs) + @util.deprecated( + "1.3", + "The :meth:`.Engine.contextual_connect` method is deprecated. This " + "method is an artifact of the threadlocal engine strategy which is " + "also to be deprecated. For explicit connections from an " + ":class:`.Engine`, use the :meth:`.Engine.connect` method.", + ) def contextual_connect(self, close_with_result=False, **kwargs): """Return a :class:`.Connection` object which may be part of some ongoing context. @@ -2187,6 +2182,11 @@ class Engine(Connectable, log.Identified): """ + return self._contextual_connect( + close_with_result=close_with_result, **kwargs + ) + + def _contextual_connect(self, close_with_result=False, **kwargs): return self._connection_cls( self, self._wrap_pool_connect(self.pool.connect, None), diff --git a/lib/sqlalchemy/engine/default.py b/lib/sqlalchemy/engine/default.py index 9c8069ff1..e54e99b75 100644 --- a/lib/sqlalchemy/engine/default.py +++ b/lib/sqlalchemy/engine/default.py @@ -184,6 +184,15 @@ class DefaultDialect(interfaces.Dialect): """ + @util.deprecated_params( + convert_unicode=( + "1.3", + "The :paramref:`.create_engine.convert_unicode` parameter " + "and corresponding dialect-level parameters are deprecated, " + "and will be removed in a future release. Modern DBAPIs support " + "Python Unicode natively and this parameter is unnecessary.", + ) + ) def __init__( self, convert_unicode=False, diff --git a/lib/sqlalchemy/engine/interfaces.py b/lib/sqlalchemy/engine/interfaces.py index d4cd55b2f..d579e6fdb 100644 --- a/lib/sqlalchemy/engine/interfaces.py +++ b/lib/sqlalchemy/engine/interfaces.py @@ -251,15 +251,15 @@ class Dialect(object): raise NotImplementedError() + @util.deprecated( + "0.8", + "The :meth:`.Dialect.get_primary_keys` method is deprecated and " + "will be removed in a future release. Please refer to the " + ":meth:`.Dialect.get_pk_constraint` method. ", + ) def get_primary_keys(self, connection, table_name, schema=None, **kw): """Return information about primary keys in `table_name`. - .. deprecated:: 0.8 - - The :meth:`.Dialect.get_primary_keys` method is deprecated and - will be removed in a future release. Please refer to the - :meth:`.Dialect.get_pk_constraint` method. - """ raise NotImplementedError() @@ -1117,7 +1117,15 @@ class Connectable(object): """ - def contextual_connect(self): + @util.deprecated( + "1.3", + "The :meth:`.Engine.contextual_connect` and " + ":meth:`.Connection.contextual_connect` methods are deprecated. This " + "method is an artifact of the threadlocal engine strategy which is " + "also to be deprecated. For explicit connections from an " + ":class:`.Engine`, use the :meth:`.Engine.connect` method.", + ) + def contextual_connect(self, *arg, **kw): """Return a :class:`.Connection` object which may be part of an ongoing context. @@ -1128,6 +1136,9 @@ class Connectable(object): """ + return self._contextual_connect(*arg, **kw) + + def _contextual_connect(self): raise NotImplementedError() @util.deprecated( @@ -1136,7 +1147,7 @@ class Connectable(object): "removed in a future release. Please use the ``.create()`` method " "on specific schema objects to emit DDL sequences, including " ":meth:`.Table.create`, :meth:`.Index.create`, and " - ":meth:`.MetaData.create_all`." + ":meth:`.MetaData.create_all`.", ) def create(self, entity, **kwargs): """Emit CREATE statements for the given schema entity. @@ -1150,7 +1161,8 @@ class Connectable(object): "removed in a future release. Please use the ``.drop()`` method " "on specific schema objects to emit DDL sequences, including " ":meth:`.Table.drop`, :meth:`.Index.drop`, and " - ":meth:`.MetaData.drop_all`.") + ":meth:`.MetaData.drop_all`.", + ) def drop(self, entity, **kwargs): """Emit DROP statements for the given schema entity. """ diff --git a/lib/sqlalchemy/engine/reflection.py b/lib/sqlalchemy/engine/reflection.py index 4e4ddab6d..14d647b9a 100644 --- a/lib/sqlalchemy/engine/reflection.py +++ b/lib/sqlalchemy/engine/reflection.py @@ -160,6 +160,16 @@ class Inspector(object): ) return [] + @util.deprecated_params( + order_by=( + "1.0", + "The :paramref:`get_table_names.order_by` parameter is deprecated " + "and will be removed in a future release. Please refer to " + ":meth:`.Inspector.get_sorted_table_and_fkc_names` for a " + "more comprehensive solution to resolving foreign key cycles " + "between tables.", + ) + ) def get_table_names(self, schema=None, order_by=None): """Return all table names in referred to within a particular schema. @@ -179,14 +189,6 @@ class Inspector(object): resolve cycles, and will raise :class:`.CircularDependencyError` if cycles exist. - .. deprecated:: 1.0 - - The :paramref:`get_table_names.order_by` parameter is deprecated - and will be removed in a future release. Please refer to - :meth:`.Inspector.get_sorted_table_and_fkc_names` for a - more comprehensive solution to resolving foreign key cycles - between tables. - .. seealso:: :meth:`.Inspector.get_sorted_table_and_fkc_names` @@ -380,7 +382,7 @@ class Inspector(object): "0.7", "The :meth:`.Inspector.get_primary_keys` method is deprecated and " "will be removed in a future release. Please refer to the " - ":meth:`.Inspector.get_pk_constraint` method." + ":meth:`.Inspector.get_pk_constraint` method.", ) def get_primary_keys(self, table_name, schema=None, **kw): """Return information about primary keys in `table_name`. diff --git a/lib/sqlalchemy/engine/result.py b/lib/sqlalchemy/engine/result.py index 55310d8b0..9255343e1 100644 --- a/lib/sqlalchemy/engine/result.py +++ b/lib/sqlalchemy/engine/result.py @@ -512,7 +512,6 @@ class ResultMetaData(object): "smaller than number of columns requested (%d)" % (num_ctx_cols, len(cursor_description)) ) - seen = set() for ( idx, diff --git a/lib/sqlalchemy/engine/strategies.py b/lib/sqlalchemy/engine/strategies.py index 2ae48acbd..e367ef890 100644 --- a/lib/sqlalchemy/engine/strategies.py +++ b/lib/sqlalchemy/engine/strategies.py @@ -271,6 +271,9 @@ class MockEngineStrategy(EngineStrategy): def contextual_connect(self, **kwargs): return self + def connect(self, **kwargs): + return self + def execution_options(self, **kw): return self diff --git a/lib/sqlalchemy/engine/threadlocal.py b/lib/sqlalchemy/engine/threadlocal.py index 01d6ecbb2..8e8663ccc 100644 --- a/lib/sqlalchemy/engine/threadlocal.py +++ b/lib/sqlalchemy/engine/threadlocal.py @@ -46,11 +46,22 @@ class TLEngine(base.Engine): _tl_connection_cls = TLConnection + @util.deprecated( + "1.3", + "The 'threadlocal' engine strategy is deprecated, and will be " + "removed in a future release. The strategy is no longer relevant " + "to modern usage patterns (including that of the ORM " + ":class:`.Session` object) which make use of a :class:`.Connection` " + "object in order to invoke statements.", + ) def __init__(self, *args, **kwargs): super(TLEngine, self).__init__(*args, **kwargs) self._connections = util.threading.local() def contextual_connect(self, **kw): + return self._contextual_connect(**kw) + + def _contextual_connect(self, **kw): if not hasattr(self._connections, "conn"): connection = None else: @@ -72,7 +83,7 @@ class TLEngine(base.Engine): if not hasattr(self._connections, "trans"): self._connections.trans = [] self._connections.trans.append( - self.contextual_connect().begin_twophase(xid=xid) + self._contextual_connect().begin_twophase(xid=xid) ) return self @@ -80,14 +91,14 @@ class TLEngine(base.Engine): if not hasattr(self._connections, "trans"): self._connections.trans = [] self._connections.trans.append( - self.contextual_connect().begin_nested() + self._contextual_connect().begin_nested() ) return self def begin(self): if not hasattr(self._connections, "trans"): self._connections.trans = [] - self._connections.trans.append(self.contextual_connect().begin()) + self._connections.trans.append(self._contextual_connect().begin()) return self def __enter__(self): @@ -139,7 +150,7 @@ class TLEngine(base.Engine): def close(self): if not self.closed: - self.contextual_connect().close() + self._contextual_connect().close() connection = self._connections.conn() connection._force_close() del self._connections.conn diff --git a/lib/sqlalchemy/event/base.py b/lib/sqlalchemy/event/base.py index aa5de7af0..9364714ab 100644 --- a/lib/sqlalchemy/event/base.py +++ b/lib/sqlalchemy/event/base.py @@ -275,6 +275,10 @@ class _JoinedDispatcher(object): def _listen(self): return self.parent._listen + @property + def _events(self): + return self.parent._events + class dispatcher(object): """Descriptor used by target classes to diff --git a/lib/sqlalchemy/event/registry.py b/lib/sqlalchemy/event/registry.py index 382e640eb..07b961c01 100644 --- a/lib/sqlalchemy/event/registry.py +++ b/lib/sqlalchemy/event/registry.py @@ -206,6 +206,12 @@ class _EventKey(object): self = self.with_wrapper(adjusted_fn) + stub_function = getattr( + self.dispatch_target.dispatch._events, self.identifier + ) + if hasattr(stub_function, "_sa_warn"): + stub_function._sa_warn() + if once: self.with_wrapper(util.only_once(self._listen_fn)).listen( *args, **kw diff --git a/lib/sqlalchemy/events.py b/lib/sqlalchemy/events.py index 5eb84a156..11d3402c5 100644 --- a/lib/sqlalchemy/events.py +++ b/lib/sqlalchemy/events.py @@ -9,6 +9,7 @@ from . import event from . import exc +from . import util from .engine import Connectable from .engine import Dialect from .engine import Engine @@ -749,6 +750,13 @@ class ConnectionEvents(event.Events): """ + @util.deprecated( + "0.9", + "The :meth:`.ConnectionEvents.dbapi_error` " + "event is deprecated and will be removed in a future release. " + "Please refer to the :meth:`.ConnectionEvents.handle_error` " + "event.", + ) def dbapi_error( self, conn, cursor, statement, parameters, context, exception ): @@ -792,11 +800,6 @@ class ConnectionEvents(event.Events): :param exception: The **unwrapped** exception emitted directly from the DBAPI. The class here is specific to the DBAPI module in use. - .. deprecated:: 0.9 - The :meth:`.ConnectionEvents.dbapi_error` - event is deprecated and will be removed in a future release. - Please refer to the :meth:`.ConnectionEvents.handle_error` - event. - """ def handle_error(self, exception_context): diff --git a/lib/sqlalchemy/ext/horizontal_shard.py b/lib/sqlalchemy/ext/horizontal_shard.py index 0628415ae..c263b1734 100644 --- a/lib/sqlalchemy/ext/horizontal_shard.py +++ b/lib/sqlalchemy/ext/horizontal_shard.py @@ -242,7 +242,7 @@ class ShardedSession(Session): else: return self.get_bind( mapper, shard_id=shard_id, instance=instance - ).contextual_connect(**kwargs) + )._contextual_connect(**kwargs) def get_bind( self, mapper, shard_id=None, instance=None, clause=None, **kw diff --git a/lib/sqlalchemy/interfaces.py b/lib/sqlalchemy/interfaces.py index 0caf85a23..374199143 100644 --- a/lib/sqlalchemy/interfaces.py +++ b/lib/sqlalchemy/interfaces.py @@ -86,10 +86,23 @@ class PoolListener(object): """ - listener = util.as_interface( - listener, - methods=("connect", "first_connect", "checkout", "checkin"), - ) + 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"): @@ -195,6 +208,33 @@ class ConnectionProxy(object): @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 diff --git a/lib/sqlalchemy/orm/attributes.py b/lib/sqlalchemy/orm/attributes.py index c7c242501..cd81d759d 100644 --- a/lib/sqlalchemy/orm/attributes.py +++ b/lib/sqlalchemy/orm/attributes.py @@ -452,50 +452,57 @@ class AttributeImpl(object): ): r"""Construct an AttributeImpl. - \class_ - associated class + :param \class_: associated class - key - string name of the attribute + :param key: string name of the attribute - \callable_ + :param \callable_: optional function which generates a callable based on a parent instance, which produces the "default" values for a scalar or collection attribute when it's first accessed, if not present already. - trackparent + :param trackparent: if True, attempt to track if an instance has a parent attached to it via this attribute. - extension + :param extension: a single or list of AttributeExtension object(s) which will - receive set/delete/append/remove/etc. events. Deprecated. + receive set/delete/append/remove/etc. events. The event package is now used. - compare_function + .. 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. - active_history + :param active_history: indicates that get_history() should always return the "old" value, even if it means executing a lazy callable upon attribute change. - parent_token + :param parent_token: Usually references the MapperProperty, used as a key for the hasparent() function to identify an "owning" attribute. Allows multiple AttributeImpls to all match a single owner attribute. - expire_missing + :param expire_missing: if False, don't add an "expiry" callable to this attribute during state.expire_attributes(None), if no value is present for this key. - send_modified_events + :param send_modified_events: if False, the InstanceState._modified_event method will have no effect; this means the attribute will never show up as changed in a history entry. + """ self.class_ = class_ self.key = key @@ -1841,9 +1848,9 @@ def init_collection(obj, key): For an easier way to do the above, see :func:`~sqlalchemy.orm.attributes.set_committed_value`. - obj is an instrumented object instance. An InstanceState - is accepted directly for backwards compatibility but - this usage is deprecated. + :param obj: a mapped object + + :param key: string attribute name where the collection is located. """ state = instance_state(obj) diff --git a/lib/sqlalchemy/orm/collections.py b/lib/sqlalchemy/orm/collections.py index 1e561369f..b9297e15c 100644 --- a/lib/sqlalchemy/orm/collections.py +++ b/lib/sqlalchemy/orm/collections.py @@ -460,7 +460,7 @@ class collection(object): @staticmethod @util.deprecated( "1.3", - "The :meth:`.collection.converter` method is deprecated and will " + "The :meth:`.collection.converter` handler is deprecated and will " "be removed in a future release. Please refer to the " ":class:`.AttributeEvents.bulk_replace` listener interface in " "conjunction with the :func:`.event.listen` function.", diff --git a/lib/sqlalchemy/orm/deprecated_interfaces.py b/lib/sqlalchemy/orm/deprecated_interfaces.py index 50ef8448a..4069b43a5 100644 --- a/lib/sqlalchemy/orm/deprecated_interfaces.py +++ b/lib/sqlalchemy/orm/deprecated_interfaces.py @@ -87,6 +87,14 @@ class MapperExtension(object): 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): @@ -359,6 +367,13 @@ class SessionExtension(object): 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): @@ -492,6 +507,19 @@ class AttributeExtension(object): @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", diff --git a/lib/sqlalchemy/orm/descriptor_props.py b/lib/sqlalchemy/orm/descriptor_props.py index 45600928f..c1e5866b5 100644 --- a/lib/sqlalchemy/orm/descriptor_props.py +++ b/lib/sqlalchemy/orm/descriptor_props.py @@ -93,6 +93,15 @@ 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. @@ -141,13 +150,6 @@ class CompositeProperty(DescriptorProperty): attribute listeners for the resulting descriptor placed on the class. - .. deprecated:: 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. - """ super(CompositeProperty, self).__init__() @@ -698,6 +700,12 @@ class SynonymProperty(DescriptorProperty): @util.langhelpers.dependency_for("sqlalchemy.orm.properties", add_to_all=True) +@util.deprecated_cls( + "0.7", + ":func:`.comparable_property` is deprecated and will be removed in a " + "future release. Please refer to the :mod:`~sqlalchemy.ext.hybrid` " + "extension.", +) class ComparableProperty(DescriptorProperty): """Instruments a Python property for use in query expressions.""" @@ -707,10 +715,6 @@ class ComparableProperty(DescriptorProperty): """Provides a method of applying a :class:`.PropComparator` to any Python descriptor attribute. - .. deprecated:: 0.7 - :func:`.comparable_property` is superseded by - the :mod:`~sqlalchemy.ext.hybrid` extension. See the example - at :ref:`hybrid_custom_comparators`. Allows any Python descriptor to behave like a SQL-enabled attribute when used at the class level in queries, allowing diff --git a/lib/sqlalchemy/orm/mapper.py b/lib/sqlalchemy/orm/mapper.py index a394ec06e..0c8ab0b10 100644 --- a/lib/sqlalchemy/orm/mapper.py +++ b/lib/sqlalchemy/orm/mapper.py @@ -104,6 +104,22 @@ class Mapper(InspectionAttr): _new_mappers = False _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 " + "is deprecated, and will be removed in a future release. " + "Use :meth:`.Query.order_by` to determine the ordering of a " + "result set.", + ), + ) def __init__( self, class_, @@ -272,13 +288,6 @@ class Mapper(InspectionAttr): list of :class:`.MapperExtension` instances which will be applied to all operations by this :class:`.Mapper`. - .. deprecated:: 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. - :param include_properties: An inclusive list or set of string column names to map. @@ -339,11 +348,6 @@ class Mapper(InspectionAttr): ordering for entities. By default mappers have no pre-defined ordering. - .. deprecated:: 1.1 The :paramref:`.Mapper.order_by` parameter - is deprecated, and will be removed in a future release. - Use :meth:`.Query.order_by` to determine the ordering of a - result set. - :param passive_deletes: Indicates DELETE behavior of foreign key columns when a joined-table inheritance entity is being deleted. Defaults to ``False`` for a base mapper; for an inheriting mapper, @@ -604,12 +608,6 @@ class Mapper(InspectionAttr): if order_by is not False: self.order_by = util.to_list(order_by) - util.warn_deprecated( - "Mapper.order_by is deprecated." - "Use Query.order_by() in order to affect the ordering of ORM " - "result sets." - ) - else: self.order_by = order_by diff --git a/lib/sqlalchemy/orm/properties.py b/lib/sqlalchemy/orm/properties.py index 328c9b1b4..530eadb6b 100644 --- a/lib/sqlalchemy/orm/properties.py +++ b/lib/sqlalchemy/orm/properties.py @@ -55,6 +55,15 @@ 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. @@ -120,13 +129,6 @@ class ColumnProperty(StrategizedProperty): which will be prepended to the list of attribute listeners for the resulting descriptor placed on the class. - .. deprecated:: 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. - """ super(ColumnProperty, self).__init__() self._orig_columns = [expression._labeled(c) for c in columns] diff --git a/lib/sqlalchemy/orm/query.py b/lib/sqlalchemy/orm/query.py index 387a72d0b..150347995 100644 --- a/lib/sqlalchemy/orm/query.py +++ b/lib/sqlalchemy/orm/query.py @@ -1569,16 +1569,16 @@ class Query(object): self._execution_options = self._execution_options.union(kwargs) @_generative() + @util.deprecated( + "0.9", + "The :meth:`.Query.with_lockmode` method is deprecated and will " + "be removed in a future release. Please refer to " + ":meth:`.Query.with_for_update`. ", + ) def with_lockmode(self, mode): """Return a new :class:`.Query` object with the specified "locking mode", which essentially refers to the ``FOR UPDATE`` clause. - .. deprecated:: 0.9 - - The :meth:`.Query.with_lockmode` method is deprecated and will - be removed in a future release. Please refer to - :meth:`.Query.with_for_update`. - :param mode: a string representing the desired locking mode. Valid values are: diff --git a/lib/sqlalchemy/orm/relationships.py b/lib/sqlalchemy/orm/relationships.py index af4d9a782..be2093fb9 100644 --- a/lib/sqlalchemy/orm/relationships.py +++ b/lib/sqlalchemy/orm/relationships.py @@ -105,6 +105,15 @@ 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, @@ -402,13 +411,6 @@ class RelationshipProperty(StrategizedProperty): which will be prepended to the list of attribute listeners for the resulting descriptor placed on the class. - .. deprecated:: 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. - :param foreign_keys: a list of columns which are to be used as "foreign key" diff --git a/lib/sqlalchemy/orm/session.py b/lib/sqlalchemy/orm/session.py index 5993e91b8..53f99b99d 100644 --- a/lib/sqlalchemy/orm/session.py +++ b/lib/sqlalchemy/orm/session.py @@ -427,7 +427,7 @@ class SessionTransaction(object): "given Connection's Engine" ) else: - conn = bind.contextual_connect() + conn = bind._contextual_connect() if execution_options: conn = conn.execution_options(**execution_options) @@ -642,6 +642,30 @@ class Session(_SessionClassMethods): "scalar", ) + @util.deprecated_params( + weak_identity_map=( + "1.0", + "The :paramref:`.Session.weak_identity_map` parameter as well as " + "the strong-referencing identity map are deprecated, and will be " + "removed in a future release. For the use case where objects " + "present in a :class:`.Session` need to be automatically strong " + "referenced, see the recipe at " + ":ref:`session_referencing_behavior` for an event-based approach " + "to maintaining strong identity references. ", + ), + _enable_transaction_accounting=( + "0.7", + "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, bind=None, @@ -650,12 +674,12 @@ class Session(_SessionClassMethods): _enable_transaction_accounting=True, autocommit=False, twophase=False, - weak_identity_map=True, + weak_identity_map=None, binds=None, extension=None, enable_baked_queries=True, info=None, - query_cls=query.Query, + query_cls=None, ): r"""Construct a new Session. @@ -754,15 +778,10 @@ class Session(_SessionClassMethods): .. versionadded:: 1.2 - :param _enable_transaction_accounting: Defaults to ``True``. A + :param _enable_transaction_accounting: A legacy-only flag which when ``False`` disables *all* 0.5-style object accounting on transaction boundaries. - .. deprecated:: 0.7 - - the :paramref:`.Session._enable_transaction_accounting` - parameter will be removed in a future release. - :param expire_on_commit: Defaults to ``True``. When ``True``, all instances will be fully expired after each :meth:`~.commit`, so that all attribute/object access subsequent to a completed @@ -773,13 +792,6 @@ class Session(_SessionClassMethods): of such instances, which will receive pre- and post- commit and flush events, as well as a post-rollback event. - .. deprecated:: 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. - :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 @@ -807,30 +819,14 @@ class Session(_SessionClassMethods): strongly referenced until explicitly removed or the :class:`.Session` is closed. - .. deprecated:: 1.0 - - The :paramref:`.Session.weak_identity_map` parameter as well as - the strong-referencing identity map are deprecated, and will be - removed in a future release. For the use case where objects - present in a :class:`.Session` need to be automatically strong - referenced, see the recipe at - :ref:`session_referencing_behavior` for an event-based approach - to maintaining strong identity references. - """ - if weak_identity_map: + if weak_identity_map in (True, None): self._identity_cls = identity.WeakInstanceDict else: - util.warn_deprecated( - "weak_identity_map=False is deprecated. " - "See the documentation on 'Session Referencing Behavior' " - "for an event-based approach to maintaining strong identity " - "references." - ) - self._identity_cls = identity.StrongInstanceDict + self.identity_map = self._identity_cls() self._new = {} # InstanceState->object, strong refs object @@ -846,8 +842,9 @@ class Session(_SessionClassMethods): self.expire_on_commit = expire_on_commit self.enable_baked_queries = enable_baked_queries self._enable_transaction_accounting = _enable_transaction_accounting + self.twophase = twophase - self._query_cls = query_cls + self._query_cls = query_cls if query_cls else query.Query if info: self.info.update(info) @@ -1068,7 +1065,7 @@ class Session(_SessionClassMethods): Alternatively, if this :class:`.Session` is configured with ``autocommit=True``, an ad-hoc :class:`.Connection` is returned - using :meth:`.Engine.contextual_connect` on the underlying + using :meth:`.Engine.connect` on the underlying :class:`.Engine`. Ambiguity in multi-bind or unbound :class:`.Session` objects can be @@ -1132,7 +1129,7 @@ class Session(_SessionClassMethods): engine, execution_options ) else: - conn = engine.contextual_connect(**kw) + conn = engine._contextual_connect(**kw) if execution_options: conn = conn.execution_options(**execution_options) return conn @@ -2872,7 +2869,15 @@ class Session(_SessionClassMethods): finally: self._flushing = False - def is_modified(self, instance, include_collections=True, passive=True): + @util.deprecated_params( + passive=( + "0.8", + "The :paramref:`.Session.is_modified.passive` flag is deprecated " + "and will be removed in a future release. The flag is no longer " + "used and is ignored.", + ) + ) + def is_modified(self, instance, include_collections=True, passive=None): r"""Return ``True`` if the given instance has locally modified attributes. @@ -2921,11 +2926,7 @@ class Session(_SessionClassMethods): way to detect only local-column based properties (i.e. scalar columns or many-to-one foreign keys) that would result in an UPDATE for this instance upon flush. - :param passive: - - .. deprecated:: 0.8 - The ``passive`` flag is deprecated and will be removed - in a future release. The flag is no longer used and is ignored. + :param passive: not used """ state = object_state(instance) diff --git a/lib/sqlalchemy/orm/strategy_options.py b/lib/sqlalchemy/orm/strategy_options.py index b3f52a2f7..6f9746daa 100644 --- a/lib/sqlalchemy/orm/strategy_options.py +++ b/lib/sqlalchemy/orm/strategy_options.py @@ -818,7 +818,6 @@ See :func:`.orm.%(name)s` for usage examples. return self def _add_unbound_all_fn(self, fn): - self._unbound_all_fn = fn fn.__doc__ = """Produce a standalone "all" option for :func:`.orm.%(name)s`. .. deprecated:: 0.9 @@ -834,6 +833,15 @@ See :func:`.orm.%(name)s` for usage examples. """ % { "name": self.name } + fn = util.deprecated( + "0.9", + "The :func:`.%(name)s_all` function is deprecated, and will be " + "removed in a future release. Please use method chaining with " + ":func:`.%(name)s` instead" % {"name": self.name}, + add_deprecation_to_docstring=False, + )(fn) + + self._unbound_all_fn = fn return self @@ -1307,8 +1315,8 @@ def defaultload(*keys): @loader_option() def defer(loadopt, key): - r"""Indicate that the given column-oriented attribute should be deferred, e.g. - not loaded until accessed. + r"""Indicate that the given column-oriented attribute should be deferred, + e.g. not loaded until accessed. This function is part of the :class:`.Load` interface and supports both method-chained and standalone operation. @@ -1346,10 +1354,16 @@ def defer(loadopt, key): :param key: Attribute to be deferred. - :param \*addl_attrs: Deprecated; this option supports the old 0.8 style + :param \*addl_attrs: This option supports the old 0.8 style of specifying a path as a series of attributes, which is now superseded by the method-chained style. + .. deprecated:: 0.9 The \*addl_attrs on :func:`.orm.defer` is + deprecated and will be removed in a future release. Please + use method chaining in conjunction with defaultload() to + indicate a path. + + .. seealso:: :ref:`deferred` @@ -1364,6 +1378,12 @@ def defer(loadopt, key): @defer._add_unbound_fn def defer(key, *addl_attrs): + if addl_attrs: + util.warn_deprecated( + "The *addl_attrs on orm.defer is deprecated. Please use " + "method chaining in conjunction with defaultload() to " + "indicate a path." + ) return _UnboundLoad._from_keys( _UnboundLoad.defer, (key,) + addl_attrs, False, {} ) @@ -1389,12 +1409,21 @@ def undefer(loadopt, key): session.query(MyClass, MyOtherClass).options( Load(MyClass).undefer("*")) + # undefer a column on a related object + session.query(MyClass).options( + defaultload(MyClass.items).undefer('text')) + :param key: Attribute to be undeferred. - :param \*addl_attrs: Deprecated; this option supports the old 0.8 style + :param \*addl_attrs: This option supports the old 0.8 style of specifying a path as a series of attributes, which is now superseded by the method-chained style. + .. deprecated:: 0.9 The \*addl_attrs on :func:`.orm.undefer` is + deprecated and will be removed in a future release. Please + use method chaining in conjunction with defaultload() to + indicate a path. + .. seealso:: :ref:`deferred` @@ -1411,6 +1440,12 @@ def undefer(loadopt, key): @undefer._add_unbound_fn def undefer(key, *addl_attrs): + if addl_attrs: + util.warn_deprecated( + "The *addl_attrs on orm.undefer is deprecated. Please use " + "method chaining in conjunction with defaultload() to " + "indicate a path." + ) return _UnboundLoad._from_keys( _UnboundLoad.undefer, (key,) + addl_attrs, False, {} ) diff --git a/lib/sqlalchemy/pool/base.py b/lib/sqlalchemy/pool/base.py index 15ac49561..40d97515e 100644 --- a/lib/sqlalchemy/pool/base.py +++ b/lib/sqlalchemy/pool/base.py @@ -60,6 +60,20 @@ class Pool(log.Identified): _dialect = _ConnDialect() + @util.deprecated_params( + use_threadlocal=( + "1.3", + "The :paramref:`.Pool.use_threadlocal` parameter is " + "deprecated and will be removed in a future release.", + ), + 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, @@ -99,35 +113,9 @@ class Pool(log.Identified): :param use_threadlocal: If set to True, repeated calls to :meth:`connect` within the same application thread will be - guaranteed to return the same connection object, if one has - already been retrieved from the pool and has not been - returned yet. Offers a slight performance advantage at the - cost of individual transactions by default. The - :meth:`.Pool.unique_connection` method is provided to return - a consistently unique connection to bypass this behavior - when the flag is set. - - .. warning:: The :paramref:`.Pool.use_threadlocal` flag - **does not affect the behavior** of :meth:`.Engine.connect`. - :meth:`.Engine.connect` makes use of the - :meth:`.Pool.unique_connection` method which **does not use thread - local context**. To produce a :class:`.Connection` which refers - to the :meth:`.Pool.connect` method, use - :meth:`.Engine.contextual_connect`. - - Note that other SQLAlchemy connectivity systems such as - :meth:`.Engine.execute` as well as the orm - :class:`.Session` make use of - :meth:`.Engine.contextual_connect` internally, so these functions - are compatible with the :paramref:`.Pool.use_threadlocal` setting. - - .. seealso:: - - :ref:`threadlocal_strategy` - contains detail on the - "threadlocal" engine strategy, which provides a more comprehensive - approach to "threadlocal" connectivity for the specific - use case of using :class:`.Engine` and :class:`.Connection` objects - directly. + guaranteed to return the same connection object that is already + checked out. This is a legacy use case and the flag has no + effect when using the pool with a :class:`.Engine` object. :param reset_on_return: Determine steps to take on connections as they are returned to the pool. @@ -175,13 +163,6 @@ class Pool(log.Identified): connections are created, checked out and checked in to the pool. - .. deprecated:: 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. - :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 @@ -235,12 +216,6 @@ class Pool(log.Identified): for fn, target in events: event.listen(self, target, fn) if listeners: - util.warn_deprecated( - "The 'listeners' argument to Pool and create_engine() is " - "deprecated and will be removed in a future release. " - "Please refer to the PoolEvents class in conjunction " - "with event.listen()" - ) for l in listeners: self.add_listener(l) @@ -290,9 +265,10 @@ class Pool(log.Identified): ) @util.deprecated( - "0.7", "The :meth:`.Pool.add_listener` method is deprecated and " + "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." + ":class:`.PoolEvents` listener interface.", ) def add_listener(self, listener): """Add a :class:`.PoolListener`-like object to this pool. diff --git a/lib/sqlalchemy/pool/dbapi_proxy.py b/lib/sqlalchemy/pool/dbapi_proxy.py index 6049c8e28..d78d85d1f 100644 --- a/lib/sqlalchemy/pool/dbapi_proxy.py +++ b/lib/sqlalchemy/pool/dbapi_proxy.py @@ -16,12 +16,17 @@ today. """ from .impl import QueuePool +from .. import util from ..util import threading - proxies = {} +@util.deprecated( + "1.3", + "The :func:`.pool.manage` function is deprecated, and will be " + "removed in a future release.", +) def manage(module, **params): r"""Return a proxy for a DB-API module that automatically pools connections. diff --git a/lib/sqlalchemy/pool/impl.py b/lib/sqlalchemy/pool/impl.py index ebbbfdb3d..768921423 100644 --- a/lib/sqlalchemy/pool/impl.py +++ b/lib/sqlalchemy/pool/impl.py @@ -283,7 +283,6 @@ class SingletonThreadPool(Pool): """ def __init__(self, creator, pool_size=5, **kw): - kw["use_threadlocal"] = True Pool.__init__(self, creator, **kw) self._conn = threading.local() self._all_conns = set() diff --git a/lib/sqlalchemy/sql/compiler.py b/lib/sqlalchemy/sql/compiler.py index a3184f270..b703c59f2 100644 --- a/lib/sqlalchemy/sql/compiler.py +++ b/lib/sqlalchemy/sql/compiler.py @@ -315,7 +315,7 @@ class Compiled(object): "The :meth:`.Compiled.compile` method is deprecated and will be " "removed in a future release. The :class:`.Compiled` object " "now runs its compilation within the constructor, and this method " - "does nothing." + "does nothing.", ) def compile(self): """Produce the internal string representation of this element. @@ -3442,6 +3442,7 @@ class IdentifierPreparer(object): def quote_schema(self, schema, force=None): """Conditionally quote a schema name. + The name is quoted if it is a reserved word, contains quote-necessary characters, or is an instance of :class:`.quoted_name` which includes ``quote`` set to ``True``. @@ -3450,17 +3451,30 @@ class IdentifierPreparer(object): quoting behavior for schema names. :param schema: string schema name - :param force: this parameter is no longer used. + :param force: unused - .. deprecated:: 0.9 + .. deprecated:: 0.9 - The :paramref:`.IdentifierPreparer.force` parameter is deprecated - and will be removed in a future release. Quoting preference - is now intrinsic to the string being quoted by making use of the - :class:`.quoted_name` class. + The :paramref:`.IdentifierPreparer.quote_schema.force` + parameter is deprecated and will be removed in a future + release. This flag has no effect on the behavior of the + :meth:`.IdentifierPreparer.quote` method; please refer to + :class:`.quoted_name`. """ - return self.quote(schema, force) + if force is not None: + # not using the util.deprecated_params() decorator in this + # case because of the additional function call overhead on this + # very performance-critical spot. + util.warn_deprecated( + "The IdentifierPreparer.quote_schema.force parameter is " + "deprecated and will be removed in a future release. This " + "flag has no effect on the behavior of the " + "IdentifierPreparer.quote method; please refer to " + "quoted_name()." + ) + + return self.quote(schema) def quote(self, ident, force=None): """Conditionally quote an identfier. @@ -3473,16 +3487,28 @@ class IdentifierPreparer(object): quoting behavior for identifier names. :param ident: string identifier - :param force: this parameter is no longer used. + :param force: unused - .. deprecated:: 0.9 + .. deprecated:: 0.9 - The :paramref:`.IdentifierPreparer.force` parameter is deprecated - and will be removed in a future release. Quoting preference - is now intrinsic to the string being quoted by making use of the - :class:`.quoted_name` class. + The :paramref:`.IdentifierPreparer.quote.force` + parameter is deprecated and will be removed in a future + release. This flag has no effect on the behavior of the + :meth:`.IdentifierPreparer.quote` method; please refer to + :class:`.quoted_name`. """ + if force is not None: + # not using the util.deprecated_params() decorator in this + # case because of the additional function call overhead on this + # very performance-critical spot. + util.warn_deprecated( + "The IdentifierPreparer.quote.force parameter is " + "deprecated and will be removed in a future release. This " + "flag has no effect on the behavior of the " + "IdentifierPreparer.quote method; please refer to " + "quoted_name()." + ) force = getattr(ident, "quote", None) @@ -3580,10 +3606,10 @@ class IdentifierPreparer(object): result = self.quote_schema(effective_schema) + "." + result return result - def format_schema(self, name, quote=None): + def format_schema(self, name): """Prepare a quoted schema name.""" - return self.quote(name, quote) + return self.quote(name) def format_column( self, diff --git a/lib/sqlalchemy/sql/ddl.py b/lib/sqlalchemy/sql/ddl.py index 3deb588ab..954f769ef 100644 --- a/lib/sqlalchemy/sql/ddl.py +++ b/lib/sqlalchemy/sql/ddl.py @@ -106,7 +106,7 @@ class DDLElement(Executable, _DDLCompiles): "The :meth:`.DDLElement.execute_at` method is deprecated and will " "be removed in a future release. Please use the :class:`.DDLEvents` " "listener interface in conjunction with the " - ":meth:`.DDLElement.execute_if` method." + ":meth:`.DDLElement.execute_if` method.", ) def execute_at(self, event_name, target): """Link execution of this DDL to the DDL lifecycle of a SchemaItem. @@ -317,6 +317,14 @@ class DDL(DDLElement): __visit_name__ = "ddl" + @util.deprecated_params( + on=( + "0.7", + "The :paramref:`.DDL.on` parameter is deprecated and will be " + "removed in a future release. Please refer to " + ":meth:`.DDLElement.execute_if`.", + ) + ) def __init__(self, statement, on=None, context=None, bind=None): """Create a DDL statement. @@ -331,12 +339,6 @@ class DDL(DDLElement): :param on: - .. deprecated:: 0.7 - - The :paramref:`.DDL.on` parameter is deprecated and will be - removed in a future release. Please refer to - :meth:`.DDLElement.execute_if`. - Optional filtering criteria. May be a string, tuple or a callable predicate. If a string, it will be compared to the name of the executing database dialect:: diff --git a/lib/sqlalchemy/sql/elements.py b/lib/sqlalchemy/sql/elements.py index fae25cc2c..71f346f45 100644 --- a/lib/sqlalchemy/sql/elements.py +++ b/lib/sqlalchemy/sql/elements.py @@ -461,21 +461,27 @@ class ClauseElement(Visitable): "ascii", "backslashreplace" ) # noqa + @util.deprecated( + "0.9", + "The :meth:`.ClauseElement.__and__` method is deprecated and will " + "be removed in a future release. Conjunctions should only be " + "used from a :class:`.ColumnElement` subclass, e.g. " + ":meth:`.ColumnElement.__and__`.", + ) def __and__(self, other): """'and' at the ClauseElement level. - - .. deprecated:: 0.9.5 - conjunctions are intended to be - at the :class:`.ColumnElement`. level - """ return and_(self, other) + @util.deprecated( + "0.9", + "The :meth:`.ClauseElement.__or__` method is deprecated and will " + "be removed in a future release. Conjunctions should only be " + "used from a :class:`.ColumnElement` subclass, e.g. " + ":meth:`.ColumnElement.__or__`.", + ) def __or__(self, other): """'or' at the ClauseElement level. - - .. deprecated:: 0.9.5 - conjunctions are intended to be - at the :class:`.ColumnElement`. level - """ return or_(self, other) @@ -1280,6 +1286,10 @@ class TextClause(Executable, ClauseElement): ) _is_implicitly_boolean = False + def __and__(self, other): + # support use in select.where(), query.filter() + return and_(self, other) + @property def _select_iterable(self): return (self,) @@ -1311,6 +1321,28 @@ class TextClause(Executable, ClauseElement): self.text = self._bind_params_regex.sub(repl, text) @classmethod + @util.deprecated_params( + autocommit=( + "0.6", + "The :paramref:`.text.autocommit` parameter is deprecated and " + "will be removed in a future release. Please use the " + ":paramref:`.Connection.execution_options.autocommit` parameter " + "in conjunction with the :meth:`.Executable.execution_options` " + "method.", + ), + bindparams=( + "0.9", + "The :paramref:`.text.bindparams` parameter " + "is deprecated and will be removed in a future release. Please " + "refer to the :meth:`.TextClause.bindparams` method.", + ), + typemap=( + "0.9", + "The :paramref:`.text.typemap` parameter is " + "deprecated and will be removed in a future release. Please " + "refer to the :meth:`.TextClause.columns` method.", + ), + ) def _create_text( self, text, bind=None, bindparams=None, typemap=None, autocommit=None ): @@ -1389,15 +1421,8 @@ class TextClause(Executable, ClauseElement): to specify bind parameters; they will be compiled to their engine-specific format. - :param autocommit: - - .. deprecated:: 0.6 - - The :paramref:`.text.autocommit` flag is deprecated and - will be removed in a future release. Please use the - :paramref:`.Connection.execution_options.autocommit` parameter - in conjunction with the :meth:`.Executable.execution_options` - method. + :param autocommit: whether or not to set the "autocommit" execution + option for this :class:`.TextClause` object. :param bind: an optional connection or engine to be used for this text query. @@ -1405,27 +1430,22 @@ class TextClause(Executable, ClauseElement): :param bindparams: A list of :func:`.bindparam` instances used to provide information about parameters embedded in the statement. + E.g.:: stmt = text("SELECT * FROM table WHERE id=:id", bindparams=[bindparam('id', value=5, type_=Integer)]) - .. deprecated:: 0.9 the :paramref:`.TextClause.bindparams` parameter - is deprecated and will be removed in a future release. Please - refer to the :meth:`.TextClause.bindparams` method. - :param typemap: A dictionary mapping the names of columns represented in the columns - clause of a ``SELECT`` statement to type objects, e.g.:: + clause of a ``SELECT`` statement to type objects. + + E.g.:: stmt = text("SELECT * FROM table", typemap={'id': Integer, 'name': String}, ) - .. deprecated:: 0.9 The :paramref:`.TextClause.typemap` argument is - deprecated and will be removed in a future release. Please - refer to the :meth:`.TextClause.columns` method. - .. seealso:: :ref:`sqlexpression_text` - in the Core tutorial @@ -1439,10 +1459,6 @@ class TextClause(Executable, ClauseElement): if typemap: stmt = stmt.columns(**typemap) if autocommit is not None: - util.warn_deprecated( - "autocommit on text() is deprecated. " - "Use .execution_options(autocommit=True)" - ) stmt = stmt.execution_options(autocommit=autocommit) return stmt @@ -1511,12 +1527,6 @@ class TextClause(Executable, ClauseElement): timestamp=datetime.datetime(2012, 10, 8, 15, 12, 5) ) - - .. versionadded:: 0.9.0 The :meth:`.TextClause.bindparams` method - supersedes the argument ``bindparams`` passed to - :func:`~.expression.text`. - - """ self._bindparams = new_params = self._bindparams.copy() @@ -1631,8 +1641,7 @@ class TextClause(Executable, ClauseElement): .. versionadded:: 0.9.0 :func:`.text` can now be converted into a fully featured "selectable" construct using the - :meth:`.TextClause.columns` method. This method supersedes the - ``typemap`` argument to :func:`.text`. + :meth:`.TextClause.columns` method. """ @@ -3364,13 +3373,16 @@ class Over(ColumnElement): return lower, upper @property + @util.deprecated( + "1.1", + "the :attr:`.Over.func` member of the :class:`.Over` " + "class is deprecated and will be removed in a future release. " + "Please refer to the :attr:`.Over.element` attribute.", + ) def func(self): """the element referred to by this :class:`.Over` clause. - .. deprecated:: 1.1 the :attr:`.Over.func` member of the :class:`.Over` - class is deprecated and will be removed in a future release. Please - refer to the :attr:`.Over.element` attribute. """ return self.element diff --git a/lib/sqlalchemy/sql/schema.py b/lib/sqlalchemy/sql/schema.py index 43a2e5d0f..d9555b196 100644 --- a/lib/sqlalchemy/sql/schema.py +++ b/lib/sqlalchemy/sql/schema.py @@ -118,7 +118,8 @@ class SchemaItem(SchemaEventTarget, visitors.Visitable): "The :attr:`.SchemaItem.quote` attribute is deprecated and will be " "removed in a future release. Use the :attr:`.quoted_name.quote` " "attribute on the ``name`` field of the target schema item to retrieve" - "quoted status.") + "quoted status.", + ) def quote(self): """Return the value of the ``quote`` flag passed to this schema object, for those schema items which @@ -394,7 +395,7 @@ class Table(DialectKWArgs, SchemaItem, TableClause): name, specify the flag ``quote_schema=True`` to the constructor, or use the :class:`.quoted_name` construct to specify the name. - :param useexisting: Deprecated. Use :paramref:`.Table.extend_existing`. + :param useexisting: the same as :paramref:`.Table.extend_existing`. :param comment: Optional string that will render an SQL comment on table creation. @@ -411,6 +412,14 @@ class Table(DialectKWArgs, SchemaItem, TableClause): __visit_name__ = "table" + @util.deprecated_params( + useexisting=( + "0.7", + "The :paramref:`.Table.useexisting` parameter is deprecated and " + "will be removed in a future release. Please use " + ":paramref:`.Table.extend_existing`.", + ) + ) def __new__(cls, *args, **kw): if not args: # python3k pickle seems to call this @@ -429,8 +438,6 @@ class Table(DialectKWArgs, SchemaItem, TableClause): keep_existing = kw.pop("keep_existing", False) extend_existing = kw.pop("extend_existing", False) if "useexisting" in kw: - msg = "useexisting is deprecated. Use extend_existing." - util.warn_deprecated(msg) if extend_existing: msg = "useexisting is synonymous with extend_existing." raise exc.ArgumentError(msg) @@ -475,7 +482,8 @@ class Table(DialectKWArgs, SchemaItem, TableClause): "The :meth:`.SchemaItem.quote` method is deprecated and will be " "removed in a future release. Use the :attr:`.quoted_name.quote` " "attribute on the ``schema`` field of the target schema item to " - "retrieve quoted status.") + "retrieve quoted status.", + ) def quote_schema(self): """Return the value of the ``quote_schema`` flag passed to this :class:`.Table`. @@ -763,12 +771,15 @@ class Table(DialectKWArgs, SchemaItem, TableClause): constraint._set_parent_with_dispatch(self) + @util.deprecated( + "0.7", + "the :meth:`.Table.append_ddl_listener` method is deprecated and " + "will be removed in a future release. Please refer to " + ":class:`.DDLEvents`.", + ) def append_ddl_listener(self, event_name, listener): """Append a DDL event listener to this ``Table``. - .. deprecated:: 0.7 - See :class:`.DDLEvents`. - """ def adapt_listener(target, connection, **kw): @@ -2579,22 +2590,15 @@ class DefaultClause(FetchedValue): return "DefaultClause(%r, for_update=%r)" % (self.arg, self.for_update) +@util.deprecated_cls( + "0.6", + ":class:`.PassiveDefault` is deprecated and will be removed in a " + "future release. Please refer to :class:`.DefaultClause`.", +) class PassiveDefault(DefaultClause): """A DDL-specified DEFAULT column value. - - .. deprecated:: 0.6 - - The :class:`.PassiveDefault` class is deprecated and will be removed - in a future release. Please use :class:`.DefaultClause`. - """ - @util.deprecated( - "0.6", - ":class:`.PassiveDefault` is deprecated and will be removed in a " - "future release. Use :class:`.DefaultClause`.", - False, - ) def __init__(self, *arg, **kw): DefaultClause.__init__(self, *arg, **kw) @@ -3711,13 +3715,21 @@ class MetaData(SchemaItem): __visit_name__ = "metadata" + @util.deprecated_params( + reflect=( + "0.8", + "The :paramref:`.MetaData.reflect` flag is deprecated and will " + "be removed in a future release. Please use the " + ":meth:`.MetaData.reflect` method.", + ) + ) def __init__( self, bind=None, reflect=False, schema=None, quote_schema=None, - naming_convention=DEFAULT_NAMING_CONVENTION, + naming_convention=None, info=None, ): """Create a new MetaData object. @@ -3731,12 +3743,6 @@ class MetaData(SchemaItem): Optional, automatically load all tables from the bound database. Defaults to False. ``bind`` is required when this option is set. - .. deprecated:: 0.8 - - The :paramref:`.MetaData.reflect` flag is deprecated and will - be removed in a future release. Please use the - :meth:`.MetaData.reflect` method. - :param schema: The default schema to use for the :class:`.Table`, :class:`.Sequence`, and potentially other objects associated with @@ -3877,7 +3883,11 @@ class MetaData(SchemaItem): """ self.tables = util.immutabledict() self.schema = quoted_name(schema, quote_schema) - self.naming_convention = naming_convention + self.naming_convention = ( + naming_convention + if naming_convention + else DEFAULT_NAMING_CONVENTION + ) if info: self.info = info self._schemas = set() @@ -3886,10 +3896,6 @@ class MetaData(SchemaItem): self.bind = bind if reflect: - util.warn_deprecated( - "reflect=True is deprecate; please " - "use the reflect() method." - ) if not bind: raise exc.ArgumentError( "A bind must be supplied in conjunction " @@ -4180,11 +4186,15 @@ class MetaData(SchemaItem): except exc.UnreflectableTableError as uerr: util.warn("Skipping table %s: %s" % (name, uerr)) + @util.deprecated( + "0.7", + "the :meth:`.MetaData.append_ddl_listener` method is deprecated and " + "will be removed in a future release. Please refer to " + ":class:`.DDLEvents`.", + ) def append_ddl_listener(self, event_name, listener): """Append a DDL event listener to this ``MetaData``. - .. deprecated:: 0.7 - See :class:`.DDLEvents`. """ diff --git a/lib/sqlalchemy/sql/selectable.py b/lib/sqlalchemy/sql/selectable.py index 0b2155a68..f48fa6f57 100644 --- a/lib/sqlalchemy/sql/selectable.py +++ b/lib/sqlalchemy/sql/selectable.py @@ -391,7 +391,7 @@ class FromClause(Selectable): message="The :meth:`.FromClause.count` method is deprecated, " "and will be removed in a future release. Please use the " ":class:`.functions.count` function available from the " - ":attr:`.func` namespace." + ":attr:`.func` namespace.", ) @util.dependencies("sqlalchemy.sql.functions") def count(self, functions, whereclause=None, **params): @@ -973,6 +973,15 @@ class Join(FromClause): return self._join_condition(left, right, a_subset=left_right) @classmethod + @util.deprecated_params( + ignore_nonexistent_tables=( + "0.9", + "The :paramref:`.join_condition.ignore_nonexistent_tables` " + "parameter is deprecated and will be removed in a future " + "release. Tables outside of the two tables being handled " + "are no longer considered.", + ) + ) def _join_condition( cls, a, @@ -995,15 +1004,8 @@ class Join(FromClause): between the two selectables. If there are multiple ways to join, or no way to join, an error is raised. - :param ignore_nonexistent_tables: - - .. deprecated:: 0.9 - - The :paramref:`_join_condition.ignore_nonexistent_tables` - parameter is deprecated and will be removed in a future - release. Tables outside of the two tables being handled - are no longer considered. - + :param ignore_nonexistent_tables: unused - tables outside of the + two tables being handled are not considered. :param a_subset: An optional expression that is a sub-component of ``a``. An attempt will be made to join to just this sub-component @@ -2026,7 +2028,7 @@ class SelectBase(HasCTE, Executable, FromClause): "and will be removed in a future release. Please use the " "the :paramref:`.Connection.execution_options.autocommit` " "parameter in conjunction with the " - ":meth:`.Executable.execution_options` method." + ":meth:`.Executable.execution_options` method.", ) def autocommit(self): """return a new selectable with the 'autocommit' flag set to @@ -2642,6 +2644,24 @@ class Select(HasPrefixes, HasSuffixes, GenerativeSelect): _memoized_property = SelectBase._memoized_property _is_select = True + @util.deprecated_params( + autocommit=( + "0.6", + "The :paramref:`.select.autocommit` parameter is deprecated " + "and will be removed in a future release. Please refer to " + "the :paramref:`.Connection.execution_options.autocommit` " + "parameter in conjunction with the the " + ":meth:`.Executable.execution_options` method in order to " + "affect the autocommit behavior for a statement.", + ), + for_update=( + "0.9", + "The :paramref:`.select.for_update` parameter is deprecated and " + "will be removed in a future release. Please refer to the " + ":meth:`.Select.with_for_update` to specify the " + "structure of the ``FOR UPDATE`` clause.", + ), + ) def __init__( self, columns=None, @@ -2712,16 +2732,7 @@ class Select(HasPrefixes, HasSuffixes, GenerativeSelect): :meth:`.Select.select_from` - full description of explicit FROM clause specification. - :param autocommit: - - .. deprecated:: 0.6 - - The :paramref:`.select.autocommit` parameter is deprecated - and will be removed in a future release. Please refer to - the :paramref:`.Connection.execution_options.autocommit` - parameter in conjunction with the the - :meth:`.Executable.execution_options` method in order to - affect the autocommit behavior for a statement. + :param autocommit: legacy autocommit parameter. :param bind=None: an :class:`~.Engine` or :class:`~.Connection` instance @@ -2762,13 +2773,6 @@ class Select(HasPrefixes, HasSuffixes, GenerativeSelect): when ``True``, applies ``FOR UPDATE`` to the end of the resulting statement. - .. deprecated:: 0.9 - - The :paramref:`.select.for_update` parameter is deprecated and - will be removed in a future release. Please refer to the - :meth:`.Select.with_for_update` to specify the - structure of the ``FOR UPDATE`` clause. - ``for_update`` accepts various string values interpreted by specific backends, including: diff --git a/lib/sqlalchemy/sql/sqltypes.py b/lib/sqlalchemy/sql/sqltypes.py index 1d97bf35c..8131be443 100644 --- a/lib/sqlalchemy/sql/sqltypes.py +++ b/lib/sqlalchemy/sql/sqltypes.py @@ -137,6 +137,22 @@ class String(Concatenable, TypeEngine): __visit_name__ = "string" + @util.deprecated_params( + convert_unicode=( + "1.3", + "The :paramref:`.String.convert_unicode` parameter is deprecated " + "and will be removed in a future release. All modern DBAPIs " + "now support Python Unicode directly and this parameter is " + "unnecessary.", + ), + unicode_error=( + "1.3", + "The :paramref:`.String.unicode_errors` parameter is deprecated " + "and will be removed in a future release. This parameter is " + "unnecessary for modern Python DBAPIs and degrades performance " + "significantly.", + ), + ) def __init__( self, length=None, @@ -144,6 +160,7 @@ class String(Concatenable, TypeEngine): convert_unicode=False, unicode_error=None, _warn_on_bytestring=False, + _expect_unicode=False, ): """ Create a string-holding type. @@ -207,15 +224,9 @@ class String(Concatenable, TypeEngine): :param unicode_error: Optional, a method to use to handle Unicode conversion errors. Behaves like the ``errors`` keyword argument to - the standard library's ``string.decode()`` functions. This flag - requires that :paramref:`.String.convert_unicode` is set to - ``"force"`` - otherwise, - SQLAlchemy is not guaranteed to handle the task of unicode - conversion. Note that this flag adds significant performance - overhead to row-fetching operations for backends that already - return unicode objects natively (which most DBAPIs do). This - flag should only be used as a last resort for reading - strings from a column with varied or corrupted encodings. + the standard library's ``string.decode()`` functions, requires + that :paramref:`.String.convert_unicode` is set to + ``"force"`` """ if unicode_error is not None and convert_unicode != "force": @@ -225,8 +236,9 @@ class String(Concatenable, TypeEngine): self.length = length self.collation = collation - self.convert_unicode = convert_unicode - self.unicode_error = unicode_error + self._expect_unicode = convert_unicode or _expect_unicode + self._expect_unicode_error = unicode_error + self._warn_on_bytestring = _warn_on_bytestring def literal_processor(self, dialect): @@ -241,10 +253,10 @@ class String(Concatenable, TypeEngine): return process def bind_processor(self, dialect): - if self.convert_unicode or dialect.convert_unicode: + if self._expect_unicode or dialect.convert_unicode: if ( dialect.supports_unicode_binds - and self.convert_unicode != "force" + and self._expect_unicode != "force" ): if self._warn_on_bytestring: @@ -266,7 +278,7 @@ class String(Concatenable, TypeEngine): def process(value): if isinstance(value, util.text_type): - return encoder(value, self.unicode_error)[0] + return encoder(value, self._expect_unicode_error)[0] elif warn_on_bytestring and value is not None: util.warn_limited( "Unicode type received non-unicode bind " @@ -280,31 +292,31 @@ class String(Concatenable, TypeEngine): return None def result_processor(self, dialect, coltype): - wants_unicode = self.convert_unicode or dialect.convert_unicode + wants_unicode = self._expect_unicode or dialect.convert_unicode needs_convert = wants_unicode and ( dialect.returns_unicode_strings is not True - or self.convert_unicode in ("force", "force_nocheck") + or self._expect_unicode in ("force", "force_nocheck") ) needs_isinstance = ( needs_convert and dialect.returns_unicode_strings - and self.convert_unicode != "force_nocheck" + and self._expect_unicode != "force_nocheck" ) if needs_convert: if needs_isinstance: return processors.to_conditional_unicode_processor_factory( - dialect.encoding, self.unicode_error + dialect.encoding, self._expect_unicode_error ) else: return processors.to_unicode_processor_factory( - dialect.encoding, self.unicode_error + dialect.encoding, self._expect_unicode_error ) else: return None @property def python_type(self): - if self.convert_unicode: + if self._expect_unicode: return util.text_type else: return str @@ -312,6 +324,16 @@ class String(Concatenable, TypeEngine): def get_dbapi_type(self, dbapi): return dbapi.STRING + @classmethod + def _warn_deprecated_unicode(cls): + util.warn_deprecated( + "The convert_unicode on Engine and String as well as the " + "unicode_error flag on String are deprecated. All modern " + "DBAPIs now support Python Unicode natively under Python 2, and " + "under Python 3 all strings are inherently Unicode. These flags " + "will be removed in a future release." + ) + class Text(String): @@ -395,7 +417,7 @@ class Unicode(String): defaults to ``True``. """ - kwargs.setdefault("convert_unicode", True) + kwargs.setdefault("_expect_unicode", True) kwargs.setdefault("_warn_on_bytestring", True) super(Unicode, self).__init__(length=length, **kwargs) @@ -424,10 +446,13 @@ class UnicodeText(Text): defaults to ``True``. """ - kwargs.setdefault("convert_unicode", True) + kwargs.setdefault("_expect_unicode", True) kwargs.setdefault("_warn_on_bytestring", True) super(UnicodeText, self).__init__(length=length, **kwargs) + def _warn_deprecated_unicode(self): + pass + class Integer(_LookupExpressionAdapter, TypeEngine): @@ -697,11 +722,7 @@ class Float(Numeric): scale = None def __init__( - self, - precision=None, - asdecimal=False, - decimal_return_scale=None, - **kwargs + self, precision=None, asdecimal=False, decimal_return_scale=None ): r""" Construct a Float. @@ -724,25 +745,10 @@ class Float(Numeric): .. versionadded:: 0.9.0 - :param \**kwargs: - - .. deprecated:: 0.9 - - Additional keyword arguments are ignored by the base - :class:`.Float` type, and keyword arguments will no longer - be accepted in a future release. For database specific floats - that support additional arguments, see that dialect's - documentation for details, such as - :class:`sqlalchemy.dialects.mysql.FLOAT`. - """ self.precision = precision self.asdecimal = asdecimal self.decimal_return_scale = decimal_return_scale - if kwargs: - util.warn_deprecated( - "Additional keyword arguments " "passed to Float ignored." - ) def result_processor(self, dialect, coltype): if self.asdecimal: @@ -975,19 +981,13 @@ class LargeBinary(_Binary): _Binary.__init__(self, length=length) +@util.deprecated_cls( + "0.6", + "The :class:`.Binary` class is deprecated and will be removed " + "in a future relase. Please use :class:`.LargeBinary`.", +) class Binary(LargeBinary): - - """.. deprecated:: 0.6 - - The :class:`.Binary` class is deprecated and will be removed - in a future relase. Please use :class:`.LargeBinary`. - - """ - def __init__(self, *arg, **kw): - util.warn_deprecated( - "The Binary type has been renamed to " "LargeBinary." - ) LargeBinary.__init__(self, *arg, **kw) @@ -1264,6 +1264,15 @@ class Enum(Emulated, String, SchemaType): __visit_name__ = "enum" + @util.deprecated_params( + convert_unicode=( + "1.3", + "The :paramref:`.Enum.convert_unicode` parameter is deprecated " + "and will be removed in a future release. All modern DBAPIs " + "now support Python Unicode directly and this parameter is " + "unnecessary.", + ) + ) def __init__(self, *enums, **kw): r"""Construct an enum. @@ -1376,11 +1385,15 @@ class Enum(Emulated, String, SchemaType): if convert_unicode is None: for e in self.enums: + # this is all py2k logic that can go away for py3k only, + # "expect unicode" will always be implicitly true if isinstance(e, util.text_type): - convert_unicode = True + _expect_unicode = True break else: - convert_unicode = False + _expect_unicode = False + else: + _expect_unicode = convert_unicode if self.enums: length = max(len(x) for x in self.enums) @@ -1389,7 +1402,7 @@ class Enum(Emulated, String, SchemaType): self._valid_lookup[None] = self._object_lookup[None] = None super(Enum, self).__init__( - length=length, convert_unicode=convert_unicode + length=length, _expect_unicode=_expect_unicode ) if self.enum_class: @@ -1469,7 +1482,7 @@ class Enum(Emulated, String, SchemaType): ) if op is operators.concat_op: typ = String( - self.type.length, convert_unicode=self.type.convert_unicode + self.type.length, _expect_unicode=self.type._expect_unicode ) return op, typ @@ -1491,7 +1504,7 @@ class Enum(Emulated, String, SchemaType): ) def adapt_to_emulated(self, impltype, **kw): - kw.setdefault("convert_unicode", self.convert_unicode) + kw.setdefault("_expect_unicode", self._expect_unicode) kw.setdefault("validate_strings", self.validate_strings) kw.setdefault("name", self.name) kw.setdefault("schema", self.schema) @@ -2205,7 +2218,7 @@ class JSON(Indexable, TypeEngine): @util.memoized_property def _str_impl(self): - return String(convert_unicode=True) + return String(_expect_unicode=True) def bind_processor(self, dialect): string_process = self._str_impl.bind_processor(dialect) diff --git a/lib/sqlalchemy/testing/engines.py b/lib/sqlalchemy/testing/engines.py index 22faa2394..232eebeb3 100644 --- a/lib/sqlalchemy/testing/engines.py +++ b/lib/sqlalchemy/testing/engines.py @@ -12,6 +12,7 @@ import warnings import weakref from . import config +from . import uses_deprecated from .util import decorator from .. import event from .. import pool @@ -74,6 +75,7 @@ class ConnectionKiller(object): else: self._stop_test_ctx_aggressive() + @uses_deprecated() def _stop_test_ctx_minimal(self): self.close_all() @@ -83,6 +85,7 @@ class ConnectionKiller(object): if rec is not config.db: rec.dispose() + @uses_deprecated() def _stop_test_ctx_aggressive(self): self.close_all() for conn, rec in list(self.conns): diff --git a/lib/sqlalchemy/testing/suite/test_reflection.py b/lib/sqlalchemy/testing/suite/test_reflection.py index aa04b6073..e8d75bafa 100644 --- a/lib/sqlalchemy/testing/suite/test_reflection.py +++ b/lib/sqlalchemy/testing/suite/test_reflection.py @@ -313,11 +313,15 @@ class ComponentReflectionTest(fixtures.TablesTest): answer = ["email_addresses_v", "users_v"] eq_(sorted(table_names), answer) else: - table_names = [ - t - for t in insp.get_table_names(schema, order_by=order_by) - if t not in _ignore_tables - ] + if order_by: + tables = [ + rec[0] + for rec in insp.get_sorted_table_and_fkc_names(schema) + if rec[0] + ] + else: + tables = insp.get_table_names(schema) + table_names = [t for t in tables if t not in _ignore_tables] if order_by == "foreign_key": answer = ["users", "email_addresses", "dingalings"] diff --git a/lib/sqlalchemy/testing/suite/test_results.py b/lib/sqlalchemy/testing/suite/test_results.py index aa98a5088..a07e45df2 100644 --- a/lib/sqlalchemy/testing/suite/test_results.py +++ b/lib/sqlalchemy/testing/suite/test_results.py @@ -320,7 +320,7 @@ class ServerSideCursorsTest( def test_for_update_expr(self): engine = self._fixture(True) - s1 = select([1], for_update=True) + s1 = select([1]).with_for_update() result = engine.execute(s1) assert self._is_server_side(result.cursor) diff --git a/lib/sqlalchemy/testing/suite/test_types.py b/lib/sqlalchemy/testing/suite/test_types.py index 4791671f3..1e02c0e74 100644 --- a/lib/sqlalchemy/testing/suite/test_types.py +++ b/lib/sqlalchemy/testing/suite/test_types.py @@ -933,10 +933,7 @@ class JSONTest(_LiteralRoundTripFixture, fixtures.TablesTest): ) eq_( s.query( - cast( - self.tables.data_table.c.data, - String(convert_unicode="force"), - ), + cast(self.tables.data_table.c.data, String()), cast(self.tables.data_table.c.nulldata, String), ) .filter(self.tables.data_table.c.name == "d1") @@ -945,10 +942,7 @@ class JSONTest(_LiteralRoundTripFixture, fixtures.TablesTest): ) eq_( s.query( - cast( - self.tables.data_table.c.data, - String(convert_unicode="force"), - ), + cast(self.tables.data_table.c.data, String()), cast(self.tables.data_table.c.nulldata, String), ) .filter(self.tables.data_table.c.name == "d2") diff --git a/lib/sqlalchemy/util/__init__.py b/lib/sqlalchemy/util/__init__.py index f812671d3..383c143c4 100644 --- a/lib/sqlalchemy/util/__init__.py +++ b/lib/sqlalchemy/util/__init__.py @@ -87,6 +87,8 @@ from .compat import win32 # noqa from .compat import with_metaclass # noqa from .compat import zip_longest # noqa from .deprecations import deprecated # noqa +from .deprecations import deprecated_cls # noqa +from .deprecations import deprecated_params # noqa from .deprecations import inject_docstring_text # noqa from .deprecations import pending_deprecation # noqa from .deprecations import warn_deprecated # noqa diff --git a/lib/sqlalchemy/util/compat.py b/lib/sqlalchemy/util/compat.py index abd36d9b0..ff644cd36 100644 --- a/lib/sqlalchemy/util/compat.py +++ b/lib/sqlalchemy/util/compat.py @@ -44,6 +44,19 @@ FullArgSpec = collections.namedtuple( ], ) +FullArgSpec = collections.namedtuple( + "FullArgSpec", + [ + "args", + "varargs", + "varkw", + "defaults", + "kwonlyargs", + "kwonlydefaults", + "annotations", + ], +) + try: import threading except ImportError: diff --git a/lib/sqlalchemy/util/deprecations.py b/lib/sqlalchemy/util/deprecations.py index e7b972deb..a43acc72e 100644 --- a/lib/sqlalchemy/util/deprecations.py +++ b/lib/sqlalchemy/util/deprecations.py @@ -12,6 +12,7 @@ import re import textwrap import warnings +from . import compat from .langhelpers import decorator from .. import exc @@ -24,6 +25,21 @@ def warn_pending_deprecation(msg, stacklevel=3): warnings.warn(msg, exc.SAPendingDeprecationWarning, stacklevel=stacklevel) +def deprecated_cls(version, message, constructor="__init__"): + header = ".. deprecated:: %s %s" % (version, (message or "")) + + def decorate(cls): + return _decorate_cls_with_warning( + cls, + constructor, + exc.SADeprecationWarning, + message % dict(func=constructor), + header, + ) + + return decorate + + def deprecated(version, message=None, add_deprecation_to_docstring=True): """Decorates a function and issues a deprecation warning on use. @@ -60,6 +76,74 @@ def deprecated(version, message=None, add_deprecation_to_docstring=True): return decorate +def deprecated_params(**specs): + """Decorates a function to warn on use of certain parameters. + + e.g. :: + + @deprecated_params( + weak_identity_map=( + "0.7", + "the :paramref:`.Session.weak_identity_map parameter " + "is deprecated." + ) + + ) + + """ + + messages = {} + for param, (version, message) in specs.items(): + messages[param] = _sanitize_restructured_text(message) + + def decorate(fn): + spec = compat.inspect_getfullargspec(fn) + if spec.defaults is not None: + defaults = dict( + zip( + spec.args[(len(spec.args) - len(spec.defaults)) :], + spec.defaults, + ) + ) + check_defaults = set(defaults).intersection(messages) + check_kw = set(messages).difference(defaults) + else: + check_defaults = () + check_kw = set(messages) + + has_kw = spec.varkw is not None + + @decorator + def warned(fn, *args, **kwargs): + for m in check_defaults: + if kwargs[m] != defaults[m]: + warnings.warn( + messages[m], exc.SADeprecationWarning, stacklevel=3 + ) + for m in check_kw: + if m in kwargs: + warnings.warn( + messages[m], exc.SADeprecationWarning, stacklevel=3 + ) + + return fn(*args, **kwargs) + + doc = fn.__doc__ is not None and fn.__doc__ or "" + if doc: + doc = inject_param_text( + doc, + { + param: ".. deprecated:: %s %s" % (version, (message or "")) + for param, (version, message) in specs.items() + }, + ) + decorated = warned(fn) + decorated.__doc__ = doc + return decorated + + return decorate + + def pending_deprecation( version, message=None, add_deprecation_to_docstring=True ): @@ -98,6 +182,14 @@ def pending_deprecation( return decorate +def deprecated_option_value(parameter_value, default_value, warning_text): + if parameter_value is None: + return default_value + else: + warn_deprecated(warning_text) + return parameter_value + + def _sanitize_restructured_text(text): def repl(m): type_, name = m.group(1, 2) @@ -108,6 +200,33 @@ def _sanitize_restructured_text(text): return re.sub(r"\:(\w+)\:`~?\.?(.+?)`", repl, text) +def _decorate_cls_with_warning( + cls, constructor, wtype, message, docstring_header=None +): + doc = cls.__doc__ is not None and cls.__doc__ or "" + if docstring_header is not None: + docstring_header %= dict(func=constructor) + + doc = inject_docstring_text(doc, docstring_header, 1) + + if type(cls) is type: + clsdict = dict(cls.__dict__) + clsdict["__doc__"] = doc + cls = type(cls.__name__, cls.__bases__, clsdict) + constructor_fn = clsdict[constructor] + else: + cls.__doc__ = doc + constructor_fn = getattr(cls, constructor) + + setattr( + cls, + constructor, + _decorate_with_warning(constructor_fn, wtype, message, None), + ) + + return cls + + def _decorate_with_warning(func, wtype, message, docstring_header=None): """Wrap a function with a warnings.warn and augmented docstring.""" @@ -126,6 +245,7 @@ def _decorate_with_warning(func, wtype, message, docstring_header=None): decorated = warned(func) decorated.__doc__ = doc + decorated._sa_warn = lambda: warnings.warn(message, wtype, stacklevel=3) return decorated @@ -155,3 +275,36 @@ def inject_docstring_text(doctext, injecttext, pos): lines = lines[0:inject_pos] + injectlines + lines[inject_pos:] return "\n".join(lines) + + +def inject_param_text(doctext, inject_params): + doclines = doctext.splitlines() + lines = [] + + to_inject = None + while doclines: + line = doclines.pop(0) + if to_inject is None: + m = re.match(r"(\s+):param (.+?):", line) + if m: + param = m.group(2) + if param in inject_params: + # default indent to that of :param: plus one + indent = " " * len(m.group(1)) + " " + + # but if the next line has text, use that line's + # indentntation + if doclines: + m2 = re.match(r"(\s+)\S", doclines[0]) + if m2: + indent = " " * len(m2.group(1)) + + to_inject = indent + inject_params[param] + elif not line.rstrip(): + lines.append(line) + lines.append(to_inject) + lines.append("\n") + to_inject = None + lines.append(line) + + return "\n".join(lines) |
