From 112fe404f50f75e4cb8dbaebfd3ca27811df9872 Mon Sep 17 00:00:00 2001 From: Randy Syring Date: Wed, 17 Apr 2019 19:30:30 -0400 Subject: [PATCH] deprecate config/options that are replaced by engine configuration --- docs/config.rst | 23 +++++++++- flask_sqlalchemy/__init__.py | 32 +++++++++++++- flask_sqlalchemy/utils.py | 11 +++++ tests/test_config.py | 81 ++++++++++++++++++++++++++++++++---- 4 files changed, 134 insertions(+), 13 deletions(-) diff --git a/docs/config.rst b/docs/config.rst index b2dea196..f4800b57 100644 --- a/docs/config.rst +++ b/docs/config.rst @@ -39,10 +39,16 @@ A list of configuration keys currently understood by the extension: on some Ubuntu versions) when used with improper database defaults that specify encoding-less databases. + + **Deprecated** as of v2.4 and will be removed in v3.0. ``SQLALCHEMY_POOL_SIZE`` The size of the database pool. Defaults - to the engine's default (usually 5) + to the engine's default (usually 5). + + **Deprecated** as of v2.4 and will be removed in v3.0. ``SQLALCHEMY_POOL_TIMEOUT`` Specifies the connection timeout in seconds for the pool. + + **Deprecated** as of v2.4 and will be removed in v3.0. ``SQLALCHEMY_POOL_RECYCLE`` Number of seconds after which a connection is automatically recycled. This is required for MySQL, which removes @@ -53,11 +59,15 @@ A list of configuration keys currently understood by the extension: different default timeout value. For more information about timeouts see :ref:`timeouts`. + + **Deprecated** as of v2.4 and will be removed in v3.0. ``SQLALCHEMY_MAX_OVERFLOW`` Controls the number of connections that can be created after the pool reached its maximum size. When those additional connections are returned to the pool, they are disconnected and discarded. + + **Deprecated** as of v2.4 and will be removed in v3.0. ``SQLALCHEMY_TRACK_MODIFICATIONS`` If set to ``True``, Flask-SQLAlchemy will track modifications of objects and emit signals. The default is ``None``, which @@ -88,7 +98,16 @@ A list of configuration keys currently understood by the extension: ``SQLALCHEMY_TRACK_MODIFICATIONS`` will warn if unset. .. versionchanged:: 2.4 - ``SQLALCHEMY_ENGINE_OPTIONS`` configuration key was added. + +* ``SQLALCHEMY_ENGINE_OPTIONS`` configuration key was added. +* Deprecated keys + + * ``SQLALCHEMY_NATIVE_UNICODE`` + * ``SQLALCHEMY_POOL_SIZE`` + * ``SQLALCHEMY_POOL_TIMEOUT`` + * ``SQLALCHEMY_POOL_RECYCLE`` + * ``SQLALCHEMY_MAX_OVERFLOW`` + Connection URI Format --------------------- diff --git a/flask_sqlalchemy/__init__.py b/flask_sqlalchemy/__init__.py index 23a6c4c4..0dcf6fad 100644 --- a/flask_sqlalchemy/__init__.py +++ b/flask_sqlalchemy/__init__.py @@ -31,6 +31,7 @@ from flask_sqlalchemy.model import Model from ._compat import itervalues, string_types, xrange from .model import DefaultMeta +from . import utils __version__ = '2.3.2' @@ -634,13 +635,18 @@ def create_app(): the second case a :meth:`flask.Flask.app_context` has to exist. By default Flask-SQLAlchemy will apply some backend-specific settings - to improve your experience with them. As of SQLAlchemy 0.6 SQLAlchemy + to improve your experience with them. + + As of SQLAlchemy 0.6 SQLAlchemy will probe the library for native unicode support. If it detects unicode it will let the library handle that, otherwise do that itself. Sometimes this detection can fail in which case you might want to set ``use_native_unicode`` (or the ``SQLALCHEMY_NATIVE_UNICODE`` configuration key) to ``False``. Note that the configuration key overrides the - value you pass to the constructor. + value you pass to the constructor. Direct support for ``use_native_unicode`` + and SQLALCHEMY_NATIVE_UNICODE are deprecated as of v2.4 and will be removed + in v3.0. ``engine_options`` and ``SQLALCHEMY_ENGINE_OPTIONS`` may be used + instead. This class also provides access to all the SQLAlchemy functions and classes from the :mod:`sqlalchemy` and :mod:`sqlalchemy.orm` modules. So you can @@ -696,6 +702,9 @@ class to be used in place of :class:`Model`. .. versionadded:: 2.4 The `engine_options` parameter was added. + + .. versionchanged:: 2.4 + The `use_native_unicode` parameter was deprecated. """ #: Default query class used by :attr:`Model.query` and other queries. @@ -834,6 +843,12 @@ def init_app(self, app): 'or False to suppress this warning.' )) + # Deprecation warnings for config keys that should be replaced by SQLALCHEMY_ENGINE_OPTIONS. + utils.engine_config_warning(app.config, '3.0', 'SQLALCHEMY_POOL_SIZE', 'pool_size') + utils.engine_config_warning(app.config, '3.0', 'SQLALCHEMY_POOL_TIMEOUT', 'pool_timeout') + utils.engine_config_warning(app.config, '3.0', 'SQLALCHEMY_POOL_RECYCLE', 'pool_recycle') + utils.engine_config_warning(app.config, '3.0', 'SQLALCHEMY_MAX_OVERFLOW', 'max_overflow') + app.extensions['sqlalchemy'] = _SQLAlchemyState(self) @app.teardown_appcontext @@ -904,6 +919,19 @@ def apply_driver_hacks(self, app, sa_url, options): if not unu: options['use_native_unicode'] = False + if app.config['SQLALCHEMY_NATIVE_UNICODE'] is not None: + warnings.warn( + "The 'SQLALCHEMY_NATIVE_UNICODE' config option is deprecated and will be removed in" + " v3.0. Use 'SQLALCHEMY_ENGINE_OPTIONS' instead.", + DeprecationWarning + ) + if not self.use_native_unicode: + warnings.warn( + "'use_native_unicode' is deprecated and will be removed in v3.0." + " Use the 'engine_options' parameter instead.", + DeprecationWarning + ) + @property def engine(self): """Gives access to the engine. If the database configuration is bound diff --git a/flask_sqlalchemy/utils.py b/flask_sqlalchemy/utils.py index 52ea94e0..b06ad923 100644 --- a/flask_sqlalchemy/utils.py +++ b/flask_sqlalchemy/utils.py @@ -1,3 +1,4 @@ +import warnings import sqlalchemy @@ -32,3 +33,13 @@ def sqlalchemy_version(op, val): if op == '>=': return sa_ver >= target_ver return sa_ver == target_ver + + +def engine_config_warning(config, version, deprecated_config_key, engine_option): + if config[deprecated_config_key] is not None: + warnings.warn( + 'The `{}` config option is deprecated and will be removed in' + ' v{}. Use `SQLALCHEMY_ENGINE_OPTIONS[\'{}\']` instead.' + .format(deprecated_config_key, version, engine_option), + DeprecationWarning + ) diff --git a/tests/test_config.py b/tests/test_config.py index 49848f20..0f4b2ed1 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -6,6 +6,16 @@ from flask_sqlalchemy import _compat, utils +@pytest.fixture +def app_nr(app): + """ + Signal/event registration with record queries breaks when + sqlalchemy.create_engine() is mocked out. + """ + app.config['SQLALCHEMY_RECORD_QUERIES'] = False + return app + + class TestConfigKeys: def test_defaults(self, app): @@ -81,15 +91,68 @@ def test_engine_creation_ok(self, app, recwarn): assert len(recwarn) == expected_warnings + @mock.patch.object(fsa.sqlalchemy, 'create_engine', autospec=True, spec_set=True) + def test_native_unicode_deprecation_config_opt(self, m_create_engine, app_nr, recwarn): + app_nr.config['SQLALCHEMY_NATIVE_UNICODE'] = False + assert fsa.SQLAlchemy(app_nr).get_engine() + assert len(recwarn) == 1 -@pytest.fixture -def app_nr(app): - """ - Signal/event registration with record queries breaks when - sqlalchemy.create_engine() is mocked out. - """ - app.config['SQLALCHEMY_RECORD_QUERIES'] = False - return app + warning_msg = recwarn[0].message.args[0] + assert 'SQLALCHEMY_NATIVE_UNICODE' in warning_msg + assert 'deprecated and will be removed in v3.0' in warning_msg + + @mock.patch.object(fsa.sqlalchemy, 'create_engine', autospec=True, spec_set=True) + def test_native_unicode_deprecation_init_opt(self, m_create_engine, app_nr, recwarn): + assert fsa.SQLAlchemy(app_nr, use_native_unicode=False).get_engine() + assert len(recwarn) == 1 + + warning_msg = recwarn[0].message.args[0] + assert 'use_native_unicode' in warning_msg + assert 'deprecated and will be removed in v3.0' in warning_msg + + @mock.patch.object(fsa.sqlalchemy, 'create_engine', autospec=True, spec_set=True) + def test_deprecation_config_opt_pool_size(self, m_create_engine, app_nr, recwarn): + app_nr.config['SQLALCHEMY_POOL_SIZE'] = 5 + assert fsa.SQLAlchemy(app_nr).get_engine() + assert len(recwarn) == 1 + + warning_msg = recwarn[0].message.args[0] + assert 'SQLALCHEMY_POOL_SIZE' in warning_msg + assert 'deprecated and will be removed in v3.0.' in warning_msg + assert 'pool_size' in warning_msg + + @mock.patch.object(fsa.sqlalchemy, 'create_engine', autospec=True, spec_set=True) + def test_deprecation_config_opt_pool_timeout(self, m_create_engine, app_nr, recwarn): + app_nr.config['SQLALCHEMY_POOL_TIMEOUT'] = 5 + assert fsa.SQLAlchemy(app_nr).get_engine() + assert len(recwarn) == 1 + + warning_msg = recwarn[0].message.args[0] + assert 'SQLALCHEMY_POOL_TIMEOUT' in warning_msg + assert 'deprecated and will be removed in v3.0.' in warning_msg + assert 'pool_timeout' in warning_msg + + @mock.patch.object(fsa.sqlalchemy, 'create_engine', autospec=True, spec_set=True) + def test_deprecation_config_opt_pool_recycle(self, m_create_engine, app_nr, recwarn): + app_nr.config['SQLALCHEMY_POOL_RECYCLE'] = 5 + assert fsa.SQLAlchemy(app_nr).get_engine() + assert len(recwarn) == 1 + + warning_msg = recwarn[0].message.args[0] + assert 'SQLALCHEMY_POOL_RECYCLE' in warning_msg + assert 'deprecated and will be removed in v3.0.' in warning_msg + assert 'pool_recycle' in warning_msg + + @mock.patch.object(fsa.sqlalchemy, 'create_engine', autospec=True, spec_set=True) + def test_deprecation_config_opt_max_overflow(self, m_create_engine, app_nr, recwarn): + app_nr.config['SQLALCHEMY_MAX_OVERFLOW'] = 5 + assert fsa.SQLAlchemy(app_nr).get_engine() + assert len(recwarn) == 1 + + warning_msg = recwarn[0].message.args[0] + assert 'SQLALCHEMY_MAX_OVERFLOW' in warning_msg + assert 'deprecated and will be removed in v3.0.' in warning_msg + assert 'max_overflow' in warning_msg @mock.patch.object(fsa.sqlalchemy, 'create_engine', autospec=True, spec_set=True) @@ -139,7 +202,7 @@ def test_pool_class_default(self, m_create_engine, app_nr): args, options = m_create_engine.call_args assert options['poolclass'].__name__ == 'StaticPool' - def test_pool_class_with_pool_size_zero(self, m_create_engine, app_nr): + def test_pool_class_with_pool_size_zero(self, m_create_engine, app_nr, recwarn): app_nr.config['SQLALCHEMY_POOL_SIZE'] = 0 with pytest.raises(RuntimeError) as exc_info: fsa.SQLAlchemy(app_nr).get_engine()