From 3a4a6f3f55a05969f02f3bef972971a33ea55174 Mon Sep 17 00:00:00 2001 From: Karen Chen <64801825+karenc-bq@users.noreply.github.com> Date: Wed, 12 Nov 2025 15:35:26 -0800 Subject: [PATCH 1/4] fix: better handling for when c extension isn't available for mysql connector/python instead of erroring out --- .../mysql_driver_dialect.py | 41 ++++++++++++++----- 1 file changed, 30 insertions(+), 11 deletions(-) diff --git a/aws_advanced_python_wrapper/mysql_driver_dialect.py b/aws_advanced_python_wrapper/mysql_driver_dialect.py index 76a6ff11..a0aa9645 100644 --- a/aws_advanced_python_wrapper/mysql_driver_dialect.py +++ b/aws_advanced_python_wrapper/mysql_driver_dialect.py @@ -23,9 +23,20 @@ from concurrent.futures import Executor, ThreadPoolExecutor, TimeoutError from inspect import signature -from mysql.connector import CMySQLConnection, MySQLConnection +CMYSQL_ENABLED = False + +from mysql.connector import MySQLConnection from mysql.connector.cursor import MySQLCursor -from mysql.connector.cursor_cext import CMySQLCursor + +try: + from mysql.connector import CMySQLConnection + from mysql.connector.cursor_cext import CMySQLCursor + + CMYSQL_ENABLED = True + +except ImportError as exc: + # Do nothing + pass from aws_advanced_python_wrapper.driver_dialect import DriverDialect from aws_advanced_python_wrapper.driver_dialect_codes import DriverDialectCodes @@ -62,13 +73,21 @@ class MySQLDriverDialect(DriverDialect): "Cursor.fetchall" } + @staticmethod + def _is_mysql_connection(conn: Connection | object) -> bool: + return isinstance(conn, MySQLConnection) or (CMYSQL_ENABLED and isinstance(conn, CMySQLConnection)) + + @staticmethod + def _is_cmysql_cursor(obj: object) -> bool: + return CMYSQL_ENABLED and isinstance(obj, CMySQLCursor) + def is_dialect(self, connect_func: Callable) -> bool: if MySQLDriverDialect.TARGET_DRIVER_CODE not in str(signature(connect_func)): return MySQLDriverDialect.TARGET_DRIVER_CODE.lower() in (connect_func.__module__ + connect_func.__qualname__).lower() return True def is_closed(self, conn: Connection) -> bool: - if isinstance(conn, CMySQLConnection) or isinstance(conn, MySQLConnection): + if MySQLDriverDialect._is_mysql_connection(conn): # is_connected validates the connection using a ping(). # If there are any unread results from previous executions an error will be thrown. @@ -86,14 +105,14 @@ def is_closed(self, conn: Connection) -> bool: raise UnsupportedOperationError(Messages.get_formatted("DriverDialect.UnsupportedOperationError", self._driver_name, "is_connected")) def get_autocommit(self, conn: Connection) -> bool: - if isinstance(conn, CMySQLConnection) or isinstance(conn, MySQLConnection): + if MySQLDriverDialect._is_mysql_connection(conn): return conn.autocommit raise UnsupportedOperationError( Messages.get_formatted("DriverDialect.UnsupportedOperationError", self._driver_name, "autocommit")) def set_autocommit(self, conn: Connection, autocommit: bool): - if isinstance(conn, CMySQLConnection) or isinstance(conn, MySQLConnection): + if MySQLDriverDialect._is_mysql_connection(conn): conn.autocommit = autocommit return @@ -112,13 +131,13 @@ def abort_connection(self, conn: Connection): "abort_connection")) def can_execute_query(self, conn: Connection) -> bool: - if isinstance(conn, CMySQLConnection) or isinstance(conn, MySQLConnection): + if MySQLDriverDialect._is_mysql_connection(conn): if conn.unread_result: return conn.can_consume_results return True def is_in_transaction(self, conn: Connection) -> bool: - if isinstance(conn, CMySQLConnection) or isinstance(conn, MySQLConnection): + if MySQLDriverDialect._is_mysql_connection(conn): return bool(conn.in_transaction) raise UnsupportedOperationError( @@ -126,10 +145,10 @@ def is_in_transaction(self, conn: Connection) -> bool: "in_transaction")) def get_connection_from_obj(self, obj: object) -> Any: - if isinstance(obj, CMySQLConnection) or isinstance(obj, MySQLConnection): + if MySQLDriverDialect._is_mysql_connection(obj): return obj - if isinstance(obj, CMySQLCursor): + if MySQLDriverDialect._is_cmysql_cursor(obj): try: conn = None @@ -140,7 +159,7 @@ def get_connection_from_obj(self, obj: object) -> Any: if conn is None: return None - if isinstance(conn, CMySQLConnection) or isinstance(conn, MySQLConnection): + if MySQLDriverDialect._is_mysql_connection(conn): return conn except ReferenceError: @@ -148,7 +167,7 @@ def get_connection_from_obj(self, obj: object) -> Any: if isinstance(obj, MySQLCursor): try: - if isinstance(obj._connection, CMySQLConnection) or isinstance(obj._connection, MySQLConnection): + if MySQLDriverDialect._is_mysql_connection(obj._connection): return obj._connection except ReferenceError: return None From 18d0d00bc0f10da41ef6c6116278ae4e25b14925 Mon Sep 17 00:00:00 2001 From: Karen Chen <64801825+karenc-bq@users.noreply.github.com> Date: Wed, 12 Nov 2025 19:33:04 -0800 Subject: [PATCH 2/4] fix: blue green deployment test --- aws_advanced_python_wrapper/mysql_driver_dialect.py | 5 ++--- tests/integration/container/test_blue_green_deployment.py | 3 +-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/aws_advanced_python_wrapper/mysql_driver_dialect.py b/aws_advanced_python_wrapper/mysql_driver_dialect.py index a0aa9645..728e7976 100644 --- a/aws_advanced_python_wrapper/mysql_driver_dialect.py +++ b/aws_advanced_python_wrapper/mysql_driver_dialect.py @@ -34,7 +34,7 @@ CMYSQL_ENABLED = True -except ImportError as exc: +except ImportError: # Do nothing pass @@ -175,8 +175,7 @@ def get_connection_from_obj(self, obj: object) -> Any: return None def transfer_session_state(self, from_conn: Connection, to_conn: Connection): - if (isinstance(from_conn, CMySQLConnection) or isinstance(from_conn, MySQLConnection)) and ( - isinstance(to_conn, CMySQLConnection) or isinstance(to_conn, MySQLConnection)): + if MySQLDriverDialect._is_mysql_connection(from_conn) and MySQLDriverDialect._is_mysql_connection(to_conn): to_conn.autocommit = from_conn.autocommit def ping(self, conn: Connection) -> bool: diff --git a/tests/integration/container/test_blue_green_deployment.py b/tests/integration/container/test_blue_green_deployment.py index 6be6c7f3..8a732642 100644 --- a/tests/integration/container/test_blue_green_deployment.py +++ b/tests/integration/container/test_blue_green_deployment.py @@ -30,7 +30,6 @@ import mysql.connector import psycopg -from mysql.connector import CMySQLConnection, MySQLConnection from aws_advanced_python_wrapper.mysql_driver_dialect import MySQLDriverDialect from aws_advanced_python_wrapper.pg_driver_dialect import PgDriverDialect @@ -458,7 +457,7 @@ def close_connection(self, conn: Optional[Connection]): def is_closed(self, conn: Connection) -> bool: if isinstance(conn, psycopg.Connection): return self.pg_dialect.is_closed(conn) - elif isinstance(conn, CMySQLConnection) or isinstance(conn, MySQLConnection): + elif MySQLDriverDialect._is_mysql_connection(conn): return self.mysql_dialect.is_closed(conn) elif isinstance(conn, AwsWrapperConnection): return conn.is_closed From 41c4c7c0b457c7c10cfd85e1d5a8f7ce9fcf57eb Mon Sep 17 00:00:00 2001 From: Karen Chen <64801825+karenc-bq@users.noreply.github.com> Date: Thu, 13 Nov 2025 11:15:16 -0800 Subject: [PATCH 3/4] chore: ignore mypy errors in mysql_driver_dialect.py --- .../mysql_driver_dialect.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/aws_advanced_python_wrapper/mysql_driver_dialect.py b/aws_advanced_python_wrapper/mysql_driver_dialect.py index 728e7976..02a18388 100644 --- a/aws_advanced_python_wrapper/mysql_driver_dialect.py +++ b/aws_advanced_python_wrapper/mysql_driver_dialect.py @@ -94,7 +94,7 @@ def is_closed(self, conn: Connection) -> bool: if self.can_execute_query(conn): socket_timeout = WrapperProperties.SOCKET_TIMEOUT_SEC.get_float(self._props) timeout_sec = socket_timeout if socket_timeout > 0 else MySQLDriverDialect.IS_CLOSED_TIMEOUT_SEC - is_connected_with_timeout = timeout(MySQLDriverDialect._executor, timeout_sec)(conn.is_connected) + is_connected_with_timeout = timeout(MySQLDriverDialect._executor, timeout_sec)(conn.is_connected) # type: ignore try: return not is_connected_with_timeout() @@ -106,14 +106,14 @@ def is_closed(self, conn: Connection) -> bool: def get_autocommit(self, conn: Connection) -> bool: if MySQLDriverDialect._is_mysql_connection(conn): - return conn.autocommit + return conn.autocommit # type: ignore raise UnsupportedOperationError( Messages.get_formatted("DriverDialect.UnsupportedOperationError", self._driver_name, "autocommit")) def set_autocommit(self, conn: Connection, autocommit: bool): if MySQLDriverDialect._is_mysql_connection(conn): - conn.autocommit = autocommit + conn.autocommit = autocommit # type: ignore return raise UnsupportedOperationError( @@ -132,13 +132,13 @@ def abort_connection(self, conn: Connection): def can_execute_query(self, conn: Connection) -> bool: if MySQLDriverDialect._is_mysql_connection(conn): - if conn.unread_result: - return conn.can_consume_results + if conn.unread_result: # type: ignore + return conn.can_consume_results # type: ignore return True def is_in_transaction(self, conn: Connection) -> bool: if MySQLDriverDialect._is_mysql_connection(conn): - return bool(conn.in_transaction) + return bool(conn.in_transaction) # type: ignore raise UnsupportedOperationError( Messages.get_formatted("DriverDialect.UnsupportedOperationError", self._driver_name, @@ -176,7 +176,7 @@ def get_connection_from_obj(self, obj: object) -> Any: def transfer_session_state(self, from_conn: Connection, to_conn: Connection): if MySQLDriverDialect._is_mysql_connection(from_conn) and MySQLDriverDialect._is_mysql_connection(to_conn): - to_conn.autocommit = from_conn.autocommit + to_conn.autocommit = from_conn.autocommit # type: ignore def ping(self, conn: Connection) -> bool: return not self.is_closed(conn) From bb028f9955c8579294cf4f3f76f5c2d9406fb267 Mon Sep 17 00:00:00 2001 From: Karen Chen <64801825+karenc-bq@users.noreply.github.com> Date: Thu, 13 Nov 2025 11:21:37 -0800 Subject: [PATCH 4/4] chore: disable flake8 warnings for special imports --- .../mysql_driver_dialect.py | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/aws_advanced_python_wrapper/mysql_driver_dialect.py b/aws_advanced_python_wrapper/mysql_driver_dialect.py index 02a18388..dd7055c5 100644 --- a/aws_advanced_python_wrapper/mysql_driver_dialect.py +++ b/aws_advanced_python_wrapper/mysql_driver_dialect.py @@ -23,14 +23,23 @@ from concurrent.futures import Executor, ThreadPoolExecutor, TimeoutError from inspect import signature +from aws_advanced_python_wrapper.driver_dialect import DriverDialect +from aws_advanced_python_wrapper.driver_dialect_codes import DriverDialectCodes +from aws_advanced_python_wrapper.errors import UnsupportedOperationError +from aws_advanced_python_wrapper.utils.decorators import timeout +from aws_advanced_python_wrapper.utils.messages import Messages +from aws_advanced_python_wrapper.utils.properties import (Properties, + PropertiesUtils, + WrapperProperties) + CMYSQL_ENABLED = False -from mysql.connector import MySQLConnection -from mysql.connector.cursor import MySQLCursor +from mysql.connector import MySQLConnection # noqa: E402 +from mysql.connector.cursor import MySQLCursor # noqa: E402 try: - from mysql.connector import CMySQLConnection - from mysql.connector.cursor_cext import CMySQLCursor + from mysql.connector import CMySQLConnection # noqa: E402 + from mysql.connector.cursor_cext import CMySQLCursor # noqa: E402 CMYSQL_ENABLED = True @@ -38,15 +47,6 @@ # Do nothing pass -from aws_advanced_python_wrapper.driver_dialect import DriverDialect -from aws_advanced_python_wrapper.driver_dialect_codes import DriverDialectCodes -from aws_advanced_python_wrapper.errors import UnsupportedOperationError -from aws_advanced_python_wrapper.utils.decorators import timeout -from aws_advanced_python_wrapper.utils.messages import Messages -from aws_advanced_python_wrapper.utils.properties import (Properties, - PropertiesUtils, - WrapperProperties) - class MySQLDriverDialect(DriverDialect): _driver_name = "MySQL Connector Python" @@ -94,7 +94,7 @@ def is_closed(self, conn: Connection) -> bool: if self.can_execute_query(conn): socket_timeout = WrapperProperties.SOCKET_TIMEOUT_SEC.get_float(self._props) timeout_sec = socket_timeout if socket_timeout > 0 else MySQLDriverDialect.IS_CLOSED_TIMEOUT_SEC - is_connected_with_timeout = timeout(MySQLDriverDialect._executor, timeout_sec)(conn.is_connected) # type: ignore + is_connected_with_timeout = timeout(MySQLDriverDialect._executor, timeout_sec)(conn.is_connected) # type: ignore try: return not is_connected_with_timeout() @@ -106,14 +106,14 @@ def is_closed(self, conn: Connection) -> bool: def get_autocommit(self, conn: Connection) -> bool: if MySQLDriverDialect._is_mysql_connection(conn): - return conn.autocommit # type: ignore + return conn.autocommit # type: ignore raise UnsupportedOperationError( Messages.get_formatted("DriverDialect.UnsupportedOperationError", self._driver_name, "autocommit")) def set_autocommit(self, conn: Connection, autocommit: bool): if MySQLDriverDialect._is_mysql_connection(conn): - conn.autocommit = autocommit # type: ignore + conn.autocommit = autocommit # type: ignore return raise UnsupportedOperationError( @@ -132,13 +132,13 @@ def abort_connection(self, conn: Connection): def can_execute_query(self, conn: Connection) -> bool: if MySQLDriverDialect._is_mysql_connection(conn): - if conn.unread_result: # type: ignore - return conn.can_consume_results # type: ignore + if conn.unread_result: # type: ignore + return conn.can_consume_results # type: ignore return True def is_in_transaction(self, conn: Connection) -> bool: if MySQLDriverDialect._is_mysql_connection(conn): - return bool(conn.in_transaction) # type: ignore + return bool(conn.in_transaction) # type: ignore raise UnsupportedOperationError( Messages.get_formatted("DriverDialect.UnsupportedOperationError", self._driver_name, @@ -176,7 +176,7 @@ def get_connection_from_obj(self, obj: object) -> Any: def transfer_session_state(self, from_conn: Connection, to_conn: Connection): if MySQLDriverDialect._is_mysql_connection(from_conn) and MySQLDriverDialect._is_mysql_connection(to_conn): - to_conn.autocommit = from_conn.autocommit # type: ignore + to_conn.autocommit = from_conn.autocommit # type: ignore def ping(self, conn: Connection) -> bool: return not self.is_closed(conn)