This is an automated email from the ASF dual-hosted git repository.
potiuk pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow.git
The following commit(s) were added to refs/heads/main by this push:
new 312f7e873d Remove mysql-connector-python (#30487)
312f7e873d is described below
commit 312f7e873d5141ad65e64dbffa6095232c6c29b6
Author: max <[email protected]>
AuthorDate: Thu Apr 13 18:04:19 2023 +0200
Remove mysql-connector-python (#30487)
* Turn the package 'mysql-connector-python' as an optional feature
* Update airflow/providers/mysql/provider.yaml
* Update airflow/providers/mysql/CHANGELOG.rst
---------
Co-authored-by: eladkal <[email protected]>
---
airflow/providers/mysql/hooks/mysql.py | 18 ++++-
airflow/providers/mysql/provider.yaml | 6 +-
docker_tests/test_prod_image.py | 4 +-
docs/apache-airflow/howto/set-up-database.rst | 8 +-
generated/provider_dependencies.json | 1 -
scripts/in_container/verify_providers.py | 4 +-
tests/providers/mysql/hooks/test_mysql.py | 57 --------------
.../mysql/hooks/test_mysql_connector_python.py | 86 ++++++++++++++++++++++
8 files changed, 119 insertions(+), 65 deletions(-)
diff --git a/airflow/providers/mysql/hooks/mysql.py
b/airflow/providers/mysql/hooks/mysql.py
index ea2d912a62..e105d8f96a 100644
--- a/airflow/providers/mysql/hooks/mysql.py
+++ b/airflow/providers/mysql/hooks/mysql.py
@@ -19,13 +19,20 @@
from __future__ import annotations
import json
+import logging
from typing import TYPE_CHECKING, Any, Union
+from airflow.exceptions import AirflowOptionalProviderFeatureException
from airflow.models import Connection
from airflow.providers.common.sql.hooks.sql import DbApiHook
+logger = logging.getLogger(__name__)
+
if TYPE_CHECKING:
- from mysql.connector.abstracts import MySQLConnectionAbstract
+ try:
+ from mysql.connector.abstracts import MySQLConnectionAbstract
+ except ModuleNotFoundError:
+ logger.warning("The package 'mysql-connector-python' is not installed.
Import skipped")
from MySQLdb.connections import Connection as MySQLdbConnection
MySQLConnectionTypes = Union["MySQLdbConnection", "MySQLConnectionAbstract"]
@@ -181,7 +188,14 @@ class MySqlHook(DbApiHook):
return MySQLdb.connect(**conn_config)
if client_name == "mysql-connector-python":
- import mysql.connector
+ try:
+ import mysql.connector
+ except ModuleNotFoundError:
+ raise AirflowOptionalProviderFeatureException(
+ "The pip package 'mysql-connector-python' is not
installed, therefore the connection "
+ "wasn't established. Please, consider using default driver
or pip install the package "
+ "'mysql-connector-python'. Warning! It might cause
dependency conflicts."
+ )
conn_config = self._get_conn_config_mysql_connector_python(conn)
return mysql.connector.connect(**conn_config)
diff --git a/airflow/providers/mysql/provider.yaml
b/airflow/providers/mysql/provider.yaml
index ca2a951939..ca9a30733a 100644
--- a/airflow/providers/mysql/provider.yaml
+++ b/airflow/providers/mysql/provider.yaml
@@ -47,7 +47,6 @@ versions:
dependencies:
- apache-airflow>=2.3.0
- apache-airflow-providers-common-sql>=1.3.1
- - mysql-connector-python>=8.0.11
- mysqlclient>=1.3.6
integrations:
@@ -87,3 +86,8 @@ transfers:
connection-types:
- hook-class-name: airflow.providers.mysql.hooks.mysql.MySqlHook
connection-type: mysql
+
+additional-extras:
+ - name: mysql-connector-python
+ dependencies:
+ - mysql-connector-python>=8.0.11
diff --git a/docker_tests/test_prod_image.py b/docker_tests/test_prod_image.py
index 99ddb4730f..e76f04dfd8 100644
--- a/docker_tests/test_prod_image.py
+++ b/docker_tests/test_prod_image.py
@@ -20,6 +20,7 @@ import json
import os
import subprocess
import tempfile
+from importlib.util import find_spec
from pathlib import Path
import pytest
@@ -161,7 +162,6 @@ class TestPythonPackages:
"grpc": ["grpc", "google.auth", "google_auth_httplib2"],
"hashicorp": ["hvac"],
"ldap": ["ldap"],
- "mysql": ["mysql"],
"postgres": ["psycopg2"],
"pyodbc": ["pyodbc"],
"redis": ["redis"],
@@ -171,6 +171,8 @@ class TestPythonPackages:
"statsd": ["statsd"],
"virtualenv": ["virtualenv"],
}
+ if bool(find_spec("mysql")):
+ PACKAGE_IMPORTS["mysql"] = ["mysql"]
@pytest.mark.skipif(os.environ.get("TEST_SLIM_IMAGE") == "true",
reason="Skipped with slim image")
@pytest.mark.parametrize("package_name,import_names",
PACKAGE_IMPORTS.items())
diff --git a/docs/apache-airflow/howto/set-up-database.rst
b/docs/apache-airflow/howto/set-up-database.rst
index c33f9ac0d9..b9041c8e28 100644
--- a/docs/apache-airflow/howto/set-up-database.rst
+++ b/docs/apache-airflow/howto/set-up-database.rst
@@ -299,7 +299,13 @@ We recommend using the ``mysqlclient`` driver and
specifying it in your SqlAlche
mysql+mysqldb://<user>:<password>@<host>[:<port>]/<dbname>
We also support the ``mysql-connector-python`` driver, which lets you connect
through SSL
-without any cert options provided.
+without any cert options provided. If you wish to use
``mysql-connector-python`` driver, please install it with extras.
+
+.. code-block:: text
+
+ $ pip install mysql-connector-python
+
+The connection string in this case should look like:
.. code-block:: text
diff --git a/generated/provider_dependencies.json
b/generated/provider_dependencies.json
index 0b19116b29..fafcbbb606 100644
--- a/generated/provider_dependencies.json
+++ b/generated/provider_dependencies.json
@@ -520,7 +520,6 @@
"deps": [
"apache-airflow-providers-common-sql>=1.3.1",
"apache-airflow>=2.3.0",
- "mysql-connector-python>=8.0.11",
"mysqlclient>=1.3.6"
],
"cross-providers-deps": [
diff --git a/scripts/in_container/verify_providers.py
b/scripts/in_container/verify_providers.py
index 582cd56d2c..e7153548b9 100755
--- a/scripts/in_container/verify_providers.py
+++ b/scripts/in_container/verify_providers.py
@@ -150,12 +150,12 @@ KNOWN_COMMON_DEPRECATED_MESSAGES: set[str] = {
"Implementing implicit namespace packages (as specified in PEP 420) is "
"preferred to `pkg_resources.declare_namespace`",
"This module is deprecated. Please use
`airflow.providers.cncf.kubernetes.operators.pod` instead.",
- "urllib3 (1.26.6) or chardet (5.1.0)/charset_normalizer (2.0.12) doesn't
match a supported version!",
- "urllib3 (1.26.9) or chardet (5.1.0)/charset_normalizer (2.0.12) doesn't
match a supported version!",
"This operator is deprecated. Please use
`GoogleDisplayVideo360CreateQueryOperator`",
"This operator is deprecated. Please use
`GoogleDisplayVideo360RunQueryOperator`",
"This operator is deprecated. Please use
`GoogleDisplayVideo360RunQuerySensor`",
"This operator is deprecated. Please use
`GoogleDisplayVideo360DownloadReportV2Operator`",
+ "urllib3 (1.26.6) or chardet (5.1.0)/charset_normalizer (2.0.12) doesn't
match a supported version!",
+ "urllib3 (1.26.9) or chardet (5.1.0)/charset_normalizer (2.0.12) doesn't
match a supported version!",
}
# The set of warning messages generated by direct importing of some deprecated
modules. We should only
diff --git a/tests/providers/mysql/hooks/test_mysql.py
b/tests/providers/mysql/hooks/test_mysql.py
index 1ce211e075..4de9201ff5 100644
--- a/tests/providers/mysql/hooks/test_mysql.py
+++ b/tests/providers/mysql/hooks/test_mysql.py
@@ -182,63 +182,6 @@ class TestMySqlHookConn:
)
-class TestMySqlHookConnMySqlConnectorPython:
- def setup_method(self):
- self.connection = Connection(
- login="login",
- password="password",
- host="host",
- schema="schema",
- extra='{"client": "mysql-connector-python"}',
- )
-
- self.db_hook = MySqlHook()
- self.db_hook.get_connection = mock.Mock()
- self.db_hook.get_connection.return_value = self.connection
-
- @mock.patch("mysql.connector.connect")
- def test_get_conn(self, mock_connect):
- self.db_hook.get_conn()
- assert mock_connect.call_count == 1
- args, kwargs = mock_connect.call_args
- assert args == ()
- assert kwargs["user"] == "login"
- assert kwargs["password"] == "password"
- assert kwargs["host"] == "host"
- assert kwargs["database"] == "schema"
-
- @mock.patch("mysql.connector.connect")
- def test_get_conn_port(self, mock_connect):
- self.connection.port = 3307
- self.db_hook.get_conn()
- assert mock_connect.call_count == 1
- args, kwargs = mock_connect.call_args
- assert args == ()
- assert kwargs["port"] == 3307
-
- @mock.patch("mysql.connector.connect")
- def test_get_conn_allow_local_infile(self, mock_connect):
- extra_dict = self.connection.extra_dejson
- self.connection.extra = json.dumps(extra_dict)
- self.db_hook.local_infile = True
- self.db_hook.get_conn()
- assert mock_connect.call_count == 1
- args, kwargs = mock_connect.call_args
- assert args == ()
- assert kwargs["allow_local_infile"] == 1
-
- @mock.patch("mysql.connector.connect")
- def test_get_ssl_mode(self, mock_connect):
- extra_dict = self.connection.extra_dejson
- extra_dict.update(ssl_disabled=True)
- self.connection.extra = json.dumps(extra_dict)
- self.db_hook.get_conn()
- assert mock_connect.call_count == 1
- args, kwargs = mock_connect.call_args
- assert args == ()
- assert kwargs["ssl_disabled"] == 1
-
-
class MockMySQLConnectorConnection:
DEFAULT_AUTOCOMMIT = "default"
diff --git a/tests/providers/mysql/hooks/test_mysql_connector_python.py
b/tests/providers/mysql/hooks/test_mysql_connector_python.py
new file mode 100644
index 0000000000..7359347000
--- /dev/null
+++ b/tests/providers/mysql/hooks/test_mysql_connector_python.py
@@ -0,0 +1,86 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+import json
+from unittest import mock
+
+import pytest
+
+from airflow.models import Connection
+from airflow.providers.mysql.hooks.mysql import MySqlHook
+
+# Make sure that the optional package 'mysql-connector-python' is installed
(which is not by default)
+pytest.importorskip("mysql")
+
+
+class TestMySqlHookConnMySqlConnectorPython:
+ def setup_method(self):
+ self.connection = Connection(
+ login="login",
+ password="password",
+ host="host",
+ schema="schema",
+ extra='{"client": "mysql-connector-python"}',
+ )
+
+ self.db_hook = MySqlHook()
+ self.db_hook.get_connection = mock.Mock()
+ self.db_hook.get_connection.return_value = self.connection
+
+ @mock.patch("mysql.connector.connect")
+ def test_get_conn(self, mock_connect):
+ self.db_hook.get_conn()
+ assert mock_connect.call_count == 1
+ args, kwargs = mock_connect.call_args
+ assert args == ()
+ assert kwargs["user"] == "login"
+ assert kwargs["password"] == "password"
+ assert kwargs["host"] == "host"
+ assert kwargs["database"] == "schema"
+
+ @mock.patch("mysql.connector.connect")
+ def test_get_conn_port(self, mock_connect):
+ self.connection.port = 3307
+ self.db_hook.get_conn()
+ assert mock_connect.call_count == 1
+ args, kwargs = mock_connect.call_args
+ assert args == ()
+ assert kwargs["port"] == 3307
+
+ @mock.patch("mysql.connector.connect")
+ def test_get_conn_allow_local_infile(self, mock_connect):
+ extra_dict = self.connection.extra_dejson
+ self.connection.extra = json.dumps(extra_dict)
+ self.db_hook.local_infile = True
+ self.db_hook.get_conn()
+ assert mock_connect.call_count == 1
+ args, kwargs = mock_connect.call_args
+ assert args == ()
+ assert kwargs["allow_local_infile"] == 1
+
+ @mock.patch("mysql.connector.connect")
+ def test_get_ssl_mode(self, mock_connect):
+ extra_dict = self.connection.extra_dejson
+ extra_dict.update(ssl_disabled=True)
+ self.connection.extra = json.dumps(extra_dict)
+ self.db_hook.get_conn()
+ assert mock_connect.call_count == 1
+ args, kwargs = mock_connect.call_args
+ assert args == ()
+ assert kwargs["ssl_disabled"] == 1