From fde58e8f6f2ba3575946aebe1fda9fc54bff5485 Mon Sep 17 00:00:00 2001 From: Nicola Coretti Date: Wed, 31 Jan 2024 10:10:55 +0100 Subject: [PATCH 1/4] Add warnings module and deprecation types --- sqlalchemy_exasol/warnings.py | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 sqlalchemy_exasol/warnings.py diff --git a/sqlalchemy_exasol/warnings.py b/sqlalchemy_exasol/warnings.py new file mode 100644 index 00000000..77b4b506 --- /dev/null +++ b/sqlalchemy_exasol/warnings.py @@ -0,0 +1,9 @@ +from warnings import UserWarning, DeprecationWarning + + +class SqlaExasolWarning(UserWarning): + """Base class for all warnings emited by sqlalchemy_exasol.""" + + +class SqlaExasolDeprecationWarning(SqlaExasolWarning, DeprecationWarning): + """Warning class for features that will be removed in future versions.""" From c501448533a234fe80f1ccd4fe8fb02f794b796a Mon Sep 17 00:00:00 2001 From: Nicola Coretti Date: Wed, 31 Jan 2024 14:19:06 +0100 Subject: [PATCH 2/4] Add deprectation warnings for pyodbc and trubodbc --- CHANGELOG.rst | 1 + sqlalchemy_exasol/pyodbc.py | 23 +++++++++++++++---- sqlalchemy_exasol/turbodbc.py | 13 ++++++++++- sqlalchemy_exasol/warnings.py | 3 --- .../unit/exasol/test_deprectation_warnings.py | 16 +++++++++++++ 5 files changed, 47 insertions(+), 9 deletions(-) create mode 100644 test/unit/exasol/test_deprectation_warnings.py diff --git a/CHANGELOG.rst b/CHANGELOG.rst index deb854d2..3836b292 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -5,6 +5,7 @@ Unreleased 🔧 Changed ----------- +- Added deprecation warnings for pyodbc and trubodbc dialects - Made websockets the default way to use sqlalchemy with exasol - Made pydobc an optional dependency diff --git a/sqlalchemy_exasol/pyodbc.py b/sqlalchemy_exasol/pyodbc.py index 5737b71c..994365f6 100644 --- a/sqlalchemy_exasol/pyodbc.py +++ b/sqlalchemy_exasol/pyodbc.py @@ -10,8 +10,11 @@ import re import sys +from warnings import warn + from packaging import version from sqlalchemy import sql +from sqlalchemy_exasol.warnings import SqlaExasolDeprecationWarning from sqlalchemy.connectors.pyodbc import PyODBCConnector from sqlalchemy.engine import reflection from sqlalchemy.util.langhelpers import asbool @@ -30,13 +33,19 @@ class EXADialect_pyodbc(EXADialect, PyODBCConnector): driver_version = None def __init__(self, **kw): + message = ( + "'pyodbc' support in 'sqlalchemy_exasol' is deprecated and will be removed. " + "Please switch to the websocket driver. See documentation for details." + ) + warn(message, SqlaExasolDeprecationWarning) super().__init__(**kw) def get_driver_version(self, connection): # LooseVersion will also work with interim versions like '4.2.7dev1' or '5.0.rc4' if self.driver_version is None: self.driver_version = version.parse( - connection.connection.getinfo(self.dbapi.SQL_DRIVER_VER) or "2.0.0" + connection.connection.getinfo( + self.dbapi.SQL_DRIVER_VER) or "2.0.0" ) return self.driver_version @@ -81,9 +90,11 @@ def create_connect_args(self, url): if param in keys: connect_args[param.upper()] = asbool(keys.pop(param)) - dsn_connection = "dsn" in keys or ("host" in keys and "port" not in keys) + dsn_connection = "dsn" in keys or ( + "host" in keys and "port" not in keys) if dsn_connection: - connectors = ["DSN=%s" % (keys.pop("dsn", "") or keys.pop("host", ""))] + connectors = ["DSN=%s" % + (keys.pop("dsn", "") or keys.pop("host", ""))] else: connectors = ["DRIVER={%s}" % keys.pop("driver", None)] @@ -110,7 +121,8 @@ def create_connect_args(self, url): # client encoding. This should obviously be set to 'No' if # you query a cp1253 encoded database from a latin1 client... if "odbc_autotranslate" in keys: - connectors.append("AutoTranslate=%s" % keys.pop("odbc_autotranslate")) + connectors.append("AutoTranslate=%s" % + keys.pop("odbc_autotranslate")) connectors.extend([f"{k}={v}" for k, v in sorted(keys.items())]) return [[";".join(connectors)], connect_args] @@ -195,7 +207,8 @@ def get_view_definition(self, connection, view_name, schema=None, **kw): def get_table_names(self, connection, schema, **kw): if self._is_sql_fallback_requested(**kw): return super().get_table_names(connection, schema, **kw) - tables = self._tables_for_schema(connection, schema, table_type="TABLE") + tables = self._tables_for_schema( + connection, schema, table_type="TABLE") return [self.normalize_name(row.table_name) for row in tables] @reflection.cache diff --git a/sqlalchemy_exasol/turbodbc.py b/sqlalchemy_exasol/turbodbc.py index 3b09849a..72f53b5d 100644 --- a/sqlalchemy_exasol/turbodbc.py +++ b/sqlalchemy_exasol/turbodbc.py @@ -1,8 +1,10 @@ import decimal +from warnings import warn from sqlalchemy import types as sqltypes from sqlalchemy import util +from sqlalchemy_exasol.warnings import SqlaExasolDeprecationWarning from sqlalchemy_exasol.base import EXADialect DEFAULT_CONNECTION_PARAMS = { @@ -72,6 +74,14 @@ class EXADialect_turbodbc(EXADialect): colspecs = {sqltypes.Numeric: _ExaDecimal, sqltypes.Integer: _ExaInteger} + def __init__(self, **kw): + message = ( + "'turbodbc' support in 'sqlalchemy_exasol' is deprecated and will be removed. " + "Please switch to the websocket driver. See documentation for details." + ) + warn(message, SqlaExasolDeprecationWarning) + super().__init__(**kw) + @classmethod def dbapi(cls): return __import__("turbodbc") @@ -118,7 +128,8 @@ def _get_options_with_defaults(url): value = util.asint(raw) turbodbc_options[param] = value - options["turbodbc_options"] = real_turbodbc.make_options(**turbodbc_options) + options["turbodbc_options"] = real_turbodbc.make_options( + **turbodbc_options) return options diff --git a/sqlalchemy_exasol/warnings.py b/sqlalchemy_exasol/warnings.py index 77b4b506..a5b22fe0 100644 --- a/sqlalchemy_exasol/warnings.py +++ b/sqlalchemy_exasol/warnings.py @@ -1,6 +1,3 @@ -from warnings import UserWarning, DeprecationWarning - - class SqlaExasolWarning(UserWarning): """Base class for all warnings emited by sqlalchemy_exasol.""" diff --git a/test/unit/exasol/test_deprectation_warnings.py b/test/unit/exasol/test_deprectation_warnings.py new file mode 100644 index 00000000..63f5196b --- /dev/null +++ b/test/unit/exasol/test_deprectation_warnings.py @@ -0,0 +1,16 @@ +import pytest +from sqlalchemy_exasol.pyodbc import EXADialect_pyodbc +from sqlalchemy_exasol.turbodbc import EXADialect_turbodbc + + +@pytest.mark.parametrize( + "klass,kwargs", + [ + (EXADialect_pyodbc, {}), + (EXADialect_turbodbc, {}) + ] +) +def test_deprectation_warnings(klass, kwargs): + with pytest.deprecated_call(): + _ = EXADialect_pyodbc(**kwargs) + From 679a5b7856c741adb6012a5765267d34de52fbfb Mon Sep 17 00:00:00 2001 From: Nicola Coretti Date: Wed, 31 Jan 2024 15:52:39 +0100 Subject: [PATCH 3/4] Fix code formatting --- sqlalchemy_exasol/pyodbc.py | 18 ++++++------------ sqlalchemy_exasol/turbodbc.py | 5 ++--- test/unit/exasol/test_deprectation_warnings.py | 8 ++------ 3 files changed, 10 insertions(+), 21 deletions(-) diff --git a/sqlalchemy_exasol/pyodbc.py b/sqlalchemy_exasol/pyodbc.py index 994365f6..6f4c0018 100644 --- a/sqlalchemy_exasol/pyodbc.py +++ b/sqlalchemy_exasol/pyodbc.py @@ -9,12 +9,10 @@ import logging import re import sys - from warnings import warn from packaging import version from sqlalchemy import sql -from sqlalchemy_exasol.warnings import SqlaExasolDeprecationWarning from sqlalchemy.connectors.pyodbc import PyODBCConnector from sqlalchemy.engine import reflection from sqlalchemy.util.langhelpers import asbool @@ -23,6 +21,7 @@ EXADialect, EXAExecutionContext, ) +from sqlalchemy_exasol.warnings import SqlaExasolDeprecationWarning logger = logging.getLogger("sqlalchemy_exasol") @@ -44,8 +43,7 @@ def get_driver_version(self, connection): # LooseVersion will also work with interim versions like '4.2.7dev1' or '5.0.rc4' if self.driver_version is None: self.driver_version = version.parse( - connection.connection.getinfo( - self.dbapi.SQL_DRIVER_VER) or "2.0.0" + connection.connection.getinfo(self.dbapi.SQL_DRIVER_VER) or "2.0.0" ) return self.driver_version @@ -90,11 +88,9 @@ def create_connect_args(self, url): if param in keys: connect_args[param.upper()] = asbool(keys.pop(param)) - dsn_connection = "dsn" in keys or ( - "host" in keys and "port" not in keys) + dsn_connection = "dsn" in keys or ("host" in keys and "port" not in keys) if dsn_connection: - connectors = ["DSN=%s" % - (keys.pop("dsn", "") or keys.pop("host", ""))] + connectors = ["DSN=%s" % (keys.pop("dsn", "") or keys.pop("host", ""))] else: connectors = ["DRIVER={%s}" % keys.pop("driver", None)] @@ -121,8 +117,7 @@ def create_connect_args(self, url): # client encoding. This should obviously be set to 'No' if # you query a cp1253 encoded database from a latin1 client... if "odbc_autotranslate" in keys: - connectors.append("AutoTranslate=%s" % - keys.pop("odbc_autotranslate")) + connectors.append("AutoTranslate=%s" % keys.pop("odbc_autotranslate")) connectors.extend([f"{k}={v}" for k, v in sorted(keys.items())]) return [[";".join(connectors)], connect_args] @@ -207,8 +202,7 @@ def get_view_definition(self, connection, view_name, schema=None, **kw): def get_table_names(self, connection, schema, **kw): if self._is_sql_fallback_requested(**kw): return super().get_table_names(connection, schema, **kw) - tables = self._tables_for_schema( - connection, schema, table_type="TABLE") + tables = self._tables_for_schema(connection, schema, table_type="TABLE") return [self.normalize_name(row.table_name) for row in tables] @reflection.cache diff --git a/sqlalchemy_exasol/turbodbc.py b/sqlalchemy_exasol/turbodbc.py index 72f53b5d..5e402b3f 100644 --- a/sqlalchemy_exasol/turbodbc.py +++ b/sqlalchemy_exasol/turbodbc.py @@ -4,8 +4,8 @@ from sqlalchemy import types as sqltypes from sqlalchemy import util -from sqlalchemy_exasol.warnings import SqlaExasolDeprecationWarning from sqlalchemy_exasol.base import EXADialect +from sqlalchemy_exasol.warnings import SqlaExasolDeprecationWarning DEFAULT_CONNECTION_PARAMS = { # always enable efficient conversion to Python types: @@ -128,8 +128,7 @@ def _get_options_with_defaults(url): value = util.asint(raw) turbodbc_options[param] = value - options["turbodbc_options"] = real_turbodbc.make_options( - **turbodbc_options) + options["turbodbc_options"] = real_turbodbc.make_options(**turbodbc_options) return options diff --git a/test/unit/exasol/test_deprectation_warnings.py b/test/unit/exasol/test_deprectation_warnings.py index 63f5196b..f5b287b0 100644 --- a/test/unit/exasol/test_deprectation_warnings.py +++ b/test/unit/exasol/test_deprectation_warnings.py @@ -1,16 +1,12 @@ import pytest + from sqlalchemy_exasol.pyodbc import EXADialect_pyodbc from sqlalchemy_exasol.turbodbc import EXADialect_turbodbc @pytest.mark.parametrize( - "klass,kwargs", - [ - (EXADialect_pyodbc, {}), - (EXADialect_turbodbc, {}) - ] + "klass,kwargs", [(EXADialect_pyodbc, {}), (EXADialect_turbodbc, {})] ) def test_deprectation_warnings(klass, kwargs): with pytest.deprecated_call(): _ = EXADialect_pyodbc(**kwargs) - From 00a2238f6deb6868de5339e588a8edb53e3a3319 Mon Sep 17 00:00:00 2001 From: Nicola Coretti Date: Thu, 1 Feb 2024 11:18:46 +0100 Subject: [PATCH 4/4] Make sure added deprectation warnings do not cause the CI to fail --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index 340d8d71..6928c6ce 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -110,6 +110,7 @@ addopts = "--tb native -v -r fxX" filterwarnings = [ "error::DeprecationWarning", "ignore::DeprecationWarning:sqlalchemy.testing.plugin.*", + "ignore::DeprecationWarning:sqlalchemy_exasol.*", ] [tool.black]