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 2bac27fd66 Remove deprecations in Connection for Airflow 3 (#41762)
2bac27fd66 is described below

commit 2bac27fd669d3863d41b613b690bd74558ccc9d9
Author: Jens Scheffler <[email protected]>
AuthorDate: Tue Aug 27 15:49:18 2024 +0200

    Remove deprecations in Connection for Airflow 3 (#41762)
    
    * Remove deprecations in Connection for Airflow 3
    
    * Fix pytests
---
 airflow/models/connection.py        |  75 ++------------------
 newsfragments/41762.significant.rst |   7 ++
 tests/always/test_connection.py     | 133 +-----------------------------------
 3 files changed, 16 insertions(+), 199 deletions(-)

diff --git a/airflow/models/connection.py b/airflow/models/connection.py
index 65db6d76df..01df162665 100644
--- a/airflow/models/connection.py
+++ b/airflow/models/connection.py
@@ -19,7 +19,6 @@ from __future__ import annotations
 
 import json
 import logging
-import warnings
 from contextlib import suppress
 from json import JSONDecodeError
 from typing import Any
@@ -30,7 +29,7 @@ from sqlalchemy import Boolean, Column, Integer, String, Text
 from sqlalchemy.orm import declared_attr, reconstructor, synonym
 
 from airflow.configuration import ensure_secrets_loaded
-from airflow.exceptions import AirflowException, AirflowNotFoundException, 
RemovedInAirflow3Warning
+from airflow.exceptions import AirflowException, AirflowNotFoundException
 from airflow.models.base import ID_LEN, Base
 from airflow.models.crypto import get_fernet
 from airflow.secrets.cache import SecretCache
@@ -49,12 +48,6 @@ RE_SANITIZE_CONN_ID = 
re2.compile(r"^[\w\#\!\(\)\-\.\:\/\\]{1,}$")
 CONN_ID_MAX_LEN: int = 250
 
 
-def parse_netloc_to_hostname(*args, **kwargs):
-    """Do not use, this method is deprecated."""
-    warnings.warn("This method is deprecated.", RemovedInAirflow3Warning, 
stacklevel=2)
-    return _parse_netloc_to_hostname(*args, **kwargs)
-
-
 def sanitize_conn_id(conn_id: str | None, max_length=CONN_ID_MAX_LEN) -> str | 
None:
     r"""
     Sanitizes the connection id and allows only specific characters to be 
within.
@@ -184,30 +177,19 @@ class Connection(Base, LoggingMixin):
 
     @staticmethod
     def _validate_extra(extra, conn_id) -> None:
-        """
-        Verify that ``extra`` is a JSON-encoded Python dict.
-
-        From Airflow 3.0, we should no longer suppress these errors but raise 
instead.
-        """
+        """Verify that ``extra`` is a JSON-encoded Python dict."""
         if extra is None:
             return None
         try:
             extra_parsed = json.loads(extra)
             if not isinstance(extra_parsed, dict):
-                warnings.warn(
+                raise ValueError(
                     "Encountered JSON value in `extra` which does not parse as 
a dictionary in "
-                    f"connection {conn_id!r}. From Airflow 3.0, the `extra` 
field must contain a JSON "
-                    "representation of a Python dict.",
-                    RemovedInAirflow3Warning,
-                    stacklevel=3,
+                    f"connection {conn_id!r}. The `extra` field must contain a 
JSON "
+                    "representation of a Python dict."
                 )
         except json.JSONDecodeError:
-            warnings.warn(
-                f"Encountered non-JSON in `extra` field for connection 
{conn_id!r}. Support for "
-                "non-JSON `extra` will be removed in Airflow 3.0",
-                RemovedInAirflow3Warning,
-                stacklevel=2,
-            )
+            raise ValueError(f"Encountered non-JSON in `extra` field for 
connection {conn_id!r}.")
         return None
 
     @reconstructor
@@ -216,15 +198,6 @@ class Connection(Base, LoggingMixin):
             mask_secret(self.password)
             mask_secret(quote(self.password))
 
-    def parse_from_uri(self, **uri):
-        """Use uri parameter in constructor, this method is deprecated."""
-        warnings.warn(
-            "This method is deprecated. Please use uri parameter in 
constructor.",
-            RemovedInAirflow3Warning,
-            stacklevel=2,
-        )
-        self._parse_from_uri(**uri)
-
     @staticmethod
     def _normalize_conn_type(conn_type):
         if conn_type == "postgresql":
@@ -421,42 +394,6 @@ class Connection(Base, LoggingMixin):
     def __repr__(self):
         return self.conn_id or ""
 
-    def log_info(self):
-        """
-        Read each field individually or use the default representation 
(`__repr__`).
-
-        This method is deprecated.
-        """
-        warnings.warn(
-            "This method is deprecated. You can read each field individually 
or "
-            "use the default representation (__repr__).",
-            RemovedInAirflow3Warning,
-            stacklevel=2,
-        )
-        return (
-            f"id: {self.conn_id}. Host: {self.host}, Port: {self.port}, 
Schema: {self.schema}, "
-            f"Login: {self.login}, Password: {'XXXXXXXX' if self.password else 
None}, "
-            f"extra: {'XXXXXXXX' if self.extra_dejson else None}"
-        )
-
-    def debug_info(self):
-        """
-        Read each field individually or use the default representation 
(`__repr__`).
-
-        This method is deprecated.
-        """
-        warnings.warn(
-            "This method is deprecated. You can read each field individually 
or "
-            "use the default representation (__repr__).",
-            RemovedInAirflow3Warning,
-            stacklevel=2,
-        )
-        return (
-            f"id: {self.conn_id}. Host: {self.host}, Port: {self.port}, 
Schema: {self.schema}, "
-            f"Login: {self.login}, Password: {'XXXXXXXX' if self.password else 
None}, "
-            f"extra: {self.extra_dejson}"
-        )
-
     def test_connection(self):
         """Calls out get_hook method and executes test_connection method on 
that."""
         status, message = False, ""
diff --git a/newsfragments/41762.significant.rst 
b/newsfragments/41762.significant.rst
new file mode 100644
index 0000000000..7c8f3c8945
--- /dev/null
+++ b/newsfragments/41762.significant.rst
@@ -0,0 +1,7 @@
+Removed a set of deprecations in ``Connection`` from ``airflow.models``.
+
+- Validation of extra fields is now enforcing that JSON values are provided. 
If a non-JSON value is provided
+  a ValueError will be raised.
+- Removed utility method ``parse_netloc_to_hostname()``
+- Removed utility method ``parse_from_uri()``.
+- Removed utility method ``log_info()`` and ``log_debug``.
diff --git a/tests/always/test_connection.py b/tests/always/test_connection.py
index 9deef826f6..5e41ddda79 100644
--- a/tests/always/test_connection.py
+++ b/tests/always/test_connection.py
@@ -28,7 +28,7 @@ import pytest
 import sqlalchemy
 from cryptography.fernet import Fernet
 
-from airflow.exceptions import AirflowException, RemovedInAirflow3Warning
+from airflow.exceptions import AirflowException
 from airflow.hooks.base import BaseHook
 from airflow.models import Connection, crypto
 from airflow.providers.sqlite.hooks.sqlite import SqliteHook
@@ -341,49 +341,6 @@ class TestConnection:
             description="login only",
         ),
     ]
-    test_from_uri_params_deprecated = [
-        UriTestCaseConfig(
-            
test_conn_uri="scheme://user:password@host%2Flocation:1234/schema?__extra__=single+value",
-            test_conn_attributes=dict(
-                conn_type="scheme",
-                host="host/location",
-                schema="schema",
-                login="user",
-                password="password",
-                port=1234,
-                extra="single value",
-            ),
-            description="with extras single value",
-        ),
-        UriTestCaseConfig(
-            test_conn_uri="scheme://user:password@host%2Flocation:1234/schema?"
-            "__extra__=arbitrary+string+%2A%29%2A%24",
-            test_conn_attributes=dict(
-                conn_type="scheme",
-                host="host/location",
-                schema="schema",
-                login="user",
-                password="password",
-                port=1234,
-                extra="arbitrary string *)*$",
-            ),
-            description="with extra non-json",
-        ),
-        UriTestCaseConfig(
-            test_conn_uri="scheme://user:password@host%2Flocation:1234/schema?"
-            "__extra__=%5B%22list%22%2C+%22of%22%2C+%22values%22%5D",
-            test_conn_attributes=dict(
-                conn_type="scheme",
-                host="host/location",
-                schema="schema",
-                login="user",
-                password="password",
-                port=1234,
-                extra_dejson=["list", "of", "values"],
-            ),
-            description="with extras list",
-        ),
-    ]
 
     @pytest.mark.parametrize("test_config", test_from_uri_params)
     def test_connection_from_uri(self, test_config: UriTestCaseConfig):
@@ -407,34 +364,6 @@ class TestConnection:
 
         self.mask_secret.assert_has_calls(expected_calls)
 
-    @pytest.mark.parametrize("test_config", test_from_uri_params_deprecated)
-    def test_connection_from_uri_deprecated_extra_type(self, test_config: 
UriTestCaseConfig):
-        with pytest.warns(RemovedInAirflow3Warning):
-            connection = Connection(uri=test_config.test_uri)
-        for conn_attr, expected_val in 
test_config.test_conn_attributes.items():
-            if conn_attr in ("extra", "extra_dejson"):
-                with pytest.warns(RemovedInAirflow3Warning):
-                    actual_val = getattr(connection, conn_attr)
-            else:
-                actual_val = getattr(connection, conn_attr)
-
-            if expected_val is None:
-                assert expected_val is None
-            if isinstance(expected_val, dict):
-                assert expected_val == actual_val
-            else:
-                assert expected_val == actual_val
-
-        expected_calls = []
-        if test_config.test_conn_attributes.get("password"):
-            
expected_calls.append(mock.call(test_config.test_conn_attributes["password"]))
-            
expected_calls.append(mock.call(quote(test_config.test_conn_attributes["password"])))
-
-        if test_config.test_conn_attributes.get("extra_dejson"):
-            
expected_calls.append(mock.call(test_config.test_conn_attributes["extra_dejson"]))
-
-        self.mask_secret.assert_has_calls(expected_calls)
-
     @pytest.mark.parametrize("test_config", test_from_uri_params)
     def test_connection_get_uri_from_uri(self, test_config: UriTestCaseConfig):
         """
@@ -456,23 +385,6 @@ class TestConnection:
         assert connection.schema == new_conn.schema
         assert connection.extra_dejson == new_conn.extra_dejson
 
-    @pytest.mark.parametrize("test_config", test_from_uri_params_deprecated)
-    def test_connection_get_uri_from_uri_deprecated_extra_type(self, 
test_config: UriTestCaseConfig):
-        with pytest.warns(RemovedInAirflow3Warning):
-            connection = Connection(uri=test_config.test_uri)
-        with pytest.warns(RemovedInAirflow3Warning):
-            generated_uri = connection.get_uri()
-        with pytest.warns(RemovedInAirflow3Warning):
-            new_conn = Connection(uri=generated_uri)
-        assert connection.conn_type == new_conn.conn_type
-        assert connection.login == new_conn.login
-        assert connection.password == new_conn.password
-        assert connection.host == new_conn.host
-        assert connection.port == new_conn.port
-        assert connection.schema == new_conn.schema
-        with pytest.warns(RemovedInAirflow3Warning):
-            assert connection.extra_dejson == new_conn.extra_dejson
-
     @pytest.mark.parametrize("test_config", test_from_uri_params)
     def test_connection_get_uri_from_conn(self, test_config: 
UriTestCaseConfig):
         """
@@ -501,41 +413,6 @@ class TestConnection:
             else:
                 assert actual_val == expected_val
 
-    @pytest.mark.parametrize("test_config", test_from_uri_params_deprecated)
-    def test_connection_get_uri_from_conn_deprecated_extra_type(self, 
test_config: UriTestCaseConfig):
-        """
-        This test verifies that if we create conn_1 from attributes (rather 
than from URI), and we generate a
-        URI, that when we create conn_2 from this URI, we get an equivalent 
conn.
-        1. Build conn init params using `test_conn_attributes` and store in 
`conn_kwargs`
-        2. Instantiate conn `connection` from `conn_kwargs`.
-        3. Generate uri `get_uri` from this conn.
-        4. Create conn `new_conn` from this uri.
-        5. Verify `new_conn` has same attributes as `connection`.
-        """
-        conn_kwargs = {}
-        for k, v in test_config.test_conn_attributes.items():
-            if k == "extra_dejson":
-                conn_kwargs.update({"extra": json.dumps(v)})
-            else:
-                conn_kwargs.update({k: v})
-
-        with pytest.warns(RemovedInAirflow3Warning):
-            connection = Connection(conn_id="test_conn", **conn_kwargs)  # 
type: ignore
-        with pytest.warns(RemovedInAirflow3Warning):
-            gen_uri = connection.get_uri()
-        with pytest.warns(RemovedInAirflow3Warning):
-            new_conn = Connection(conn_id="test_conn", uri=gen_uri)
-        for conn_attr, expected_val in 
test_config.test_conn_attributes.items():
-            if conn_attr in ("extra", "extra_dejson"):
-                with pytest.warns(RemovedInAirflow3Warning):
-                    actual_val = getattr(new_conn, conn_attr)
-            else:
-                actual_val = getattr(new_conn, conn_attr)
-            if expected_val is None:
-                assert actual_val is None
-            else:
-                assert actual_val == expected_val
-
     @pytest.mark.parametrize(
         "uri,uri_parts",
         [
@@ -670,10 +547,6 @@ class TestConnection:
         """json serialization should support extra stored as object _or_ as 
object string representation"""
         assert Connection.from_json(extra).extra == expected
 
-    def test_from_json_extra_string(self):
-        with pytest.warns(RemovedInAirflow3Warning, match="Support for 
non-JSON `extra` will be removed"):
-            assert Connection.from_json('{"extra": "hi"}').extra == "hi"
-
     @pytest.mark.parametrize(
         "val,expected",
         [
@@ -899,11 +772,11 @@ class TestConnection:
         assert res[1] == "Hook GrpcHook doesn't implement or inherit 
test_connection method"
 
     def test_extra_warnings_non_json(self):
-        with pytest.warns(DeprecationWarning, match="non-JSON"):
+        with pytest.raises(ValueError, match="non-JSON"):
             Connection(conn_id="test_extra", conn_type="none", extra="hi")
 
     def test_extra_warnings_non_dict_json(self):
-        with pytest.warns(DeprecationWarning, match="not parse as a 
dictionary"):
+        with pytest.raises(ValueError, match="not parse as a dictionary"):
             Connection(conn_id="test_extra", conn_type="none", extra='"hi"')
 
     def test_get_uri_no_conn_type(self):

Reply via email to