summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2018-12-20 22:05:36 -0500
committerMike Bayer <mike_mp@zzzcomputing.com>2019-01-23 18:10:06 -0500
commit4c2c2c40fde17c85013e00a6f3303a99e2b32c12 (patch)
tree324a2c22eb61cb913e3e162e163f7baff14152cf /lib/sqlalchemy
parent5832f7172907a8151345d95061f93784ce4bb9b1 (diff)
downloadsqlalchemy-4c2c2c40fde17c85013e00a6f3303a99e2b32c12.tar.gz
Add deprecation warnings to all deprecated APIs
A large change throughout the library has ensured that all objects, parameters, and behaviors which have been noted as deprecated or legacy now emit ``DeprecationWarning`` warnings when invoked. As the Python 3 interpreter now defaults to displaying deprecation warnings, as well as that modern test suites based on tools like tox and pytest tend to display deprecation warnings, this change should make it easier to note what API features are obsolete. See the notes added to the changelog and migration notes for further details. Fixes: #4393 Change-Id: If0ea11a1fc24f9a8029352eeadfc49a7a54c0a1b
Diffstat (limited to 'lib/sqlalchemy')
-rw-r--r--lib/sqlalchemy/dialects/mssql/base.py24
-rw-r--r--lib/sqlalchemy/dialects/mssql/information_schema.py6
-rw-r--r--lib/sqlalchemy/dialects/mysql/oursql.py2
-rw-r--r--lib/sqlalchemy/dialects/oracle/cx_oracle.py47
-rw-r--r--lib/sqlalchemy/dialects/postgresql/psycopg2.py16
-rw-r--r--lib/sqlalchemy/engine/__init__.py7
-rw-r--r--lib/sqlalchemy/engine/base.py42
-rw-r--r--lib/sqlalchemy/engine/default.py9
-rw-r--r--lib/sqlalchemy/engine/interfaces.py30
-rw-r--r--lib/sqlalchemy/engine/reflection.py20
-rw-r--r--lib/sqlalchemy/engine/result.py1
-rw-r--r--lib/sqlalchemy/engine/strategies.py3
-rw-r--r--lib/sqlalchemy/engine/threadlocal.py19
-rw-r--r--lib/sqlalchemy/event/base.py4
-rw-r--r--lib/sqlalchemy/event/registry.py6
-rw-r--r--lib/sqlalchemy/events.py13
-rw-r--r--lib/sqlalchemy/ext/horizontal_shard.py2
-rw-r--r--lib/sqlalchemy/interfaces.py48
-rw-r--r--lib/sqlalchemy/orm/attributes.py39
-rw-r--r--lib/sqlalchemy/orm/collections.py2
-rw-r--r--lib/sqlalchemy/orm/deprecated_interfaces.py28
-rw-r--r--lib/sqlalchemy/orm/descriptor_props.py26
-rw-r--r--lib/sqlalchemy/orm/mapper.py34
-rw-r--r--lib/sqlalchemy/orm/properties.py16
-rw-r--r--lib/sqlalchemy/orm/query.py12
-rw-r--r--lib/sqlalchemy/orm/relationships.py16
-rw-r--r--lib/sqlalchemy/orm/session.py87
-rw-r--r--lib/sqlalchemy/orm/strategy_options.py45
-rw-r--r--lib/sqlalchemy/pool/base.py64
-rw-r--r--lib/sqlalchemy/pool/dbapi_proxy.py7
-rw-r--r--lib/sqlalchemy/pool/impl.py1
-rw-r--r--lib/sqlalchemy/sql/compiler.py58
-rw-r--r--lib/sqlalchemy/sql/ddl.py16
-rw-r--r--lib/sqlalchemy/sql/elements.py94
-rw-r--r--lib/sqlalchemy/sql/schema.py78
-rw-r--r--lib/sqlalchemy/sql/selectable.py60
-rw-r--r--lib/sqlalchemy/sql/sqltypes.py131
-rw-r--r--lib/sqlalchemy/testing/engines.py3
-rw-r--r--lib/sqlalchemy/testing/suite/test_reflection.py14
-rw-r--r--lib/sqlalchemy/testing/suite/test_results.py2
-rw-r--r--lib/sqlalchemy/testing/suite/test_types.py10
-rw-r--r--lib/sqlalchemy/util/__init__.py2
-rw-r--r--lib/sqlalchemy/util/compat.py13
-rw-r--r--lib/sqlalchemy/util/deprecations.py153
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)