apache-airflow-providers-snowflake 6.2.2__py3-none-any.whl → 6.3.0rc1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of apache-airflow-providers-snowflake might be problematic. Click here for more details.

@@ -29,11 +29,11 @@ from airflow import __version__ as airflow_version
29
29
 
30
30
  __all__ = ["__version__"]
31
31
 
32
- __version__ = "6.2.2"
32
+ __version__ = "6.3.0"
33
33
 
34
34
  if packaging.version.parse(packaging.version.parse(airflow_version).base_version) < packaging.version.parse(
35
- "2.9.0"
35
+ "2.10.0"
36
36
  ):
37
37
  raise RuntimeError(
38
- f"The package `apache-airflow-providers-snowflake:{__version__}` needs Apache Airflow 2.9.0+"
38
+ f"The package `apache-airflow-providers-snowflake:{__version__}` needs Apache Airflow 2.10.0+"
39
39
  )
@@ -17,6 +17,7 @@
17
17
  # under the License.
18
18
  from __future__ import annotations
19
19
 
20
+ import base64
20
21
  import os
21
22
  from collections.abc import Iterable, Mapping
22
23
  from contextlib import closing, contextmanager
@@ -26,15 +27,18 @@ from pathlib import Path
26
27
  from typing import TYPE_CHECKING, Any, Callable, TypeVar, overload
27
28
  from urllib.parse import urlparse
28
29
 
30
+ import requests
29
31
  from cryptography.hazmat.backends import default_backend
30
32
  from cryptography.hazmat.primitives import serialization
33
+ from requests.auth import HTTPBasicAuth
31
34
  from snowflake import connector
32
35
  from snowflake.connector import DictCursor, SnowflakeConnection, util_text
33
36
  from snowflake.sqlalchemy import URL
34
37
  from sqlalchemy import create_engine
35
38
 
36
39
  from airflow.exceptions import AirflowException
37
- from airflow.providers.common.sql.hooks.sql import DbApiHook, return_single_query_results
40
+ from airflow.providers.common.sql.hooks.handlers import return_single_query_results
41
+ from airflow.providers.common.sql.hooks.sql import DbApiHook
38
42
  from airflow.providers.snowflake.utils.openlineage import fix_snowflake_sqlalchemy_uri
39
43
  from airflow.utils.strings import to_boolean
40
44
 
@@ -185,6 +189,45 @@ class SnowflakeHook(DbApiHook):
185
189
  return extra_dict[field_name] or None
186
190
  return extra_dict.get(backcompat_key) or None
187
191
 
192
+ @property
193
+ def account_identifier(self) -> str:
194
+ """Returns snowflake account identifier."""
195
+ conn_config = self._get_conn_params
196
+ account_identifier = f"https://{conn_config['account']}"
197
+
198
+ if conn_config["region"]:
199
+ account_identifier += f".{conn_config['region']}"
200
+
201
+ return account_identifier
202
+
203
+ def get_oauth_token(self, conn_config: dict | None = None) -> str:
204
+ """Generate temporary OAuth access token using refresh token in connection details."""
205
+ if conn_config is None:
206
+ conn_config = self._get_conn_params
207
+
208
+ url = f"{self.account_identifier}.snowflakecomputing.com/oauth/token-request"
209
+
210
+ data = {
211
+ "grant_type": "refresh_token",
212
+ "refresh_token": conn_config["refresh_token"],
213
+ "redirect_uri": conn_config.get("redirect_uri", "https://localhost.com"),
214
+ }
215
+ response = requests.post(
216
+ url,
217
+ data=data,
218
+ headers={
219
+ "Content-Type": "application/x-www-form-urlencoded",
220
+ },
221
+ auth=HTTPBasicAuth(conn_config["client_id"], conn_config["client_secret"]), # type: ignore[arg-type]
222
+ )
223
+
224
+ try:
225
+ response.raise_for_status()
226
+ except requests.exceptions.HTTPError as e: # pragma: no cover
227
+ msg = f"Response: {e.response.content.decode()} Status Code: {e.response.status_code}"
228
+ raise AirflowException(msg)
229
+ return response.json()["access_token"]
230
+
188
231
  @cached_property
189
232
  def _get_conn_params(self) -> dict[str, str | None]:
190
233
  """
@@ -262,7 +305,7 @@ class SnowflakeHook(DbApiHook):
262
305
  raise ValueError("The private_key_file size is too big. Please keep it less than 4 KB.")
263
306
  private_key_pem = Path(private_key_file_path).read_bytes()
264
307
  elif private_key_content:
265
- private_key_pem = private_key_content.encode()
308
+ private_key_pem = base64.b64decode(private_key_content)
266
309
 
267
310
  if private_key_pem:
268
311
  passphrase = None
@@ -289,8 +332,11 @@ class SnowflakeHook(DbApiHook):
289
332
  conn_config["client_id"] = conn.login
290
333
  conn_config["client_secret"] = conn.password
291
334
  conn_config.pop("login", None)
335
+ conn_config.pop("user", None)
292
336
  conn_config.pop("password", None)
293
337
 
338
+ conn_config["token"] = self.get_oauth_token(conn_config=conn_config)
339
+
294
340
  # configure custom target hostname and port, if specified
295
341
  snowflake_host = extra_dict.get("host")
296
342
  snowflake_port = extra_dict.get("port")
@@ -472,6 +518,7 @@ class SnowflakeHook(DbApiHook):
472
518
  with self._get_cursor(conn, return_dictionaries) as cur:
473
519
  results = []
474
520
  for sql_statement in sql_list:
521
+ self.log.info("Running statement: %s, parameters: %s", sql_statement, parameters)
475
522
  self._run_command(cur, sql_statement, parameters) # type: ignore[attr-defined]
476
523
 
477
524
  if handler is not None:
@@ -16,7 +16,9 @@
16
16
  # under the License.
17
17
  from __future__ import annotations
18
18
 
19
+ import base64
19
20
  import uuid
21
+ import warnings
20
22
  from datetime import timedelta
21
23
  from pathlib import Path
22
24
  from typing import Any
@@ -25,9 +27,8 @@ import aiohttp
25
27
  import requests
26
28
  from cryptography.hazmat.backends import default_backend
27
29
  from cryptography.hazmat.primitives import serialization
28
- from requests.auth import HTTPBasicAuth
29
30
 
30
- from airflow.exceptions import AirflowException
31
+ from airflow.exceptions import AirflowException, AirflowProviderDeprecationWarning
31
32
  from airflow.providers.snowflake.hooks.snowflake import SnowflakeHook
32
33
  from airflow.providers.snowflake.utils.sql_api_generate_jwt import JWTGenerator
33
34
 
@@ -83,17 +84,6 @@ class SnowflakeSqlApiHook(SnowflakeHook):
83
84
  super().__init__(snowflake_conn_id, *args, **kwargs)
84
85
  self.private_key: Any = None
85
86
 
86
- @property
87
- def account_identifier(self) -> str:
88
- """Returns snowflake account identifier."""
89
- conn_config = self._get_conn_params
90
- account_identifier = f"https://{conn_config['account']}"
91
-
92
- if conn_config["region"]:
93
- account_identifier += f".{conn_config['region']}"
94
-
95
- return account_identifier
96
-
97
87
  def get_private_key(self) -> None:
98
88
  """Get the private key from snowflake connection."""
99
89
  conn = self.get_connection(self.snowflake_conn_id)
@@ -120,7 +110,7 @@ class SnowflakeSqlApiHook(SnowflakeHook):
120
110
  if private_key_file:
121
111
  private_key_pem = Path(private_key_file).read_bytes()
122
112
  elif private_key_content:
123
- private_key_pem = private_key_content.encode()
113
+ private_key_pem = base64.b64decode(private_key_content)
124
114
 
125
115
  if private_key_pem:
126
116
  passphrase = None
@@ -201,7 +191,7 @@ class SnowflakeSqlApiHook(SnowflakeHook):
201
191
  if all(
202
192
  [conn_config.get("refresh_token"), conn_config.get("client_id"), conn_config.get("client_secret")]
203
193
  ):
204
- oauth_token = self.get_oauth_token()
194
+ oauth_token = self.get_oauth_token(conn_config=conn_config)
205
195
  headers = {
206
196
  "Content-Type": "application/json",
207
197
  "Authorization": f"Bearer {oauth_token}",
@@ -232,30 +222,14 @@ class SnowflakeSqlApiHook(SnowflakeHook):
232
222
  }
233
223
  return headers
234
224
 
235
- def get_oauth_token(self) -> str:
225
+ def get_oauth_token(self, conn_config: dict[str, Any] | None = None) -> str:
236
226
  """Generate temporary OAuth access token using refresh token in connection details."""
237
- conn_config = self._get_conn_params
238
- url = f"{self.account_identifier}.snowflakecomputing.com/oauth/token-request"
239
- data = {
240
- "grant_type": "refresh_token",
241
- "refresh_token": conn_config["refresh_token"],
242
- "redirect_uri": conn_config.get("redirect_uri", "https://localhost.com"),
243
- }
244
- response = requests.post(
245
- url,
246
- data=data,
247
- headers={
248
- "Content-Type": "application/x-www-form-urlencoded",
249
- },
250
- auth=HTTPBasicAuth(conn_config["client_id"], conn_config["client_secret"]), # type: ignore[arg-type]
227
+ warnings.warn(
228
+ "This method is deprecated. Please use `get_oauth_token` method from `SnowflakeHook` instead. ",
229
+ AirflowProviderDeprecationWarning,
230
+ stacklevel=2,
251
231
  )
252
-
253
- try:
254
- response.raise_for_status()
255
- except requests.exceptions.HTTPError as e: # pragma: no cover
256
- msg = f"Response: {e.response.content.decode()} Status Code: {e.response.status_code}"
257
- raise AirflowException(msg)
258
- return response.json()["access_token"]
232
+ return super().get_oauth_token(conn_config=conn_config)
259
233
 
260
234
  def get_request_url_header_params(self, query_id: str) -> tuple[dict[str, Any], dict[str, Any], str]:
261
235
  """
@@ -23,9 +23,8 @@ from typing import TYPE_CHECKING
23
23
  from urllib.parse import quote, urlparse, urlunparse
24
24
 
25
25
  from airflow.providers.common.compat.openlineage.check import require_openlineage_version
26
- from airflow.providers.snowflake.version_compat import AIRFLOW_V_2_10_PLUS, AIRFLOW_V_3_0_PLUS
26
+ from airflow.providers.snowflake.version_compat import AIRFLOW_V_3_0_PLUS
27
27
  from airflow.utils import timezone
28
- from airflow.utils.state import TaskInstanceState
29
28
 
30
29
  if TYPE_CHECKING:
31
30
  from openlineage.client.event_v2 import RunEvent
@@ -122,21 +121,12 @@ def _get_ol_run_id(task_instance) -> str:
122
121
 
123
122
  return date
124
123
 
125
- def _get_try_number_success():
126
- """We are running this in the _on_complete, so need to adjust for try_num changes."""
127
- # todo: remove when min airflow version >= 2.10.0
128
- if AIRFLOW_V_2_10_PLUS:
129
- return task_instance.try_number
130
- if task_instance.state == TaskInstanceState.SUCCESS:
131
- return task_instance.try_number - 1
132
- return task_instance.try_number
133
-
134
124
  # Generate same OL run id as is generated for current task instance
135
125
  return OpenLineageAdapter.build_task_instance_run_id(
136
126
  dag_id=task_instance.dag_id,
137
127
  task_id=task_instance.task_id,
138
128
  logical_date=_get_logical_date(),
139
- try_number=_get_try_number_success(),
129
+ try_number=task_instance.try_number,
140
130
  map_index=task_instance.map_index,
141
131
  )
142
132
 
@@ -32,5 +32,4 @@ def get_base_airflow_version_tuple() -> tuple[int, int, int]:
32
32
  return airflow_version.major, airflow_version.minor, airflow_version.micro
33
33
 
34
34
 
35
- AIRFLOW_V_2_10_PLUS = get_base_airflow_version_tuple() >= (2, 10, 0)
36
35
  AIRFLOW_V_3_0_PLUS = get_base_airflow_version_tuple() >= (3, 0, 0)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: apache-airflow-providers-snowflake
3
- Version: 6.2.2
3
+ Version: 6.3.0rc1
4
4
  Summary: Provider package apache-airflow-providers-snowflake for Apache Airflow
5
5
  Keywords: airflow-provider,snowflake,airflow,integration
6
6
  Author-email: Apache Software Foundation <dev@airflow.apache.org>
@@ -20,9 +20,9 @@ Classifier: Programming Language :: Python :: 3.10
20
20
  Classifier: Programming Language :: Python :: 3.11
21
21
  Classifier: Programming Language :: Python :: 3.12
22
22
  Classifier: Topic :: System :: Monitoring
23
- Requires-Dist: apache-airflow>=2.9.0
24
- Requires-Dist: apache-airflow-providers-common-compat>=1.6.0
25
- Requires-Dist: apache-airflow-providers-common-sql>=1.20.0
23
+ Requires-Dist: apache-airflow>=2.10.0rc1
24
+ Requires-Dist: apache-airflow-providers-common-compat>=1.6.0rc1
25
+ Requires-Dist: apache-airflow-providers-common-sql>=1.21.0rc1
26
26
  Requires-Dist: pandas>=2.1.2,<2.2
27
27
  Requires-Dist: pyarrow>=14.0.1
28
28
  Requires-Dist: snowflake-connector-python>=3.7.1
@@ -30,8 +30,8 @@ Requires-Dist: snowflake-sqlalchemy>=1.4.0
30
30
  Requires-Dist: snowflake-snowpark-python>=1.17.0;python_version<'3.12'
31
31
  Requires-Dist: apache-airflow-providers-openlineage ; extra == "openlineage"
32
32
  Project-URL: Bug Tracker, https://github.com/apache/airflow/issues
33
- Project-URL: Changelog, https://airflow.apache.org/docs/apache-airflow-providers-snowflake/6.2.2/changelog.html
34
- Project-URL: Documentation, https://airflow.apache.org/docs/apache-airflow-providers-snowflake/6.2.2
33
+ Project-URL: Changelog, https://airflow.apache.org/docs/apache-airflow-providers-snowflake/6.3.0/changelog.html
34
+ Project-URL: Documentation, https://airflow.apache.org/docs/apache-airflow-providers-snowflake/6.3.0
35
35
  Project-URL: Mastodon, https://fosstodon.org/@airflow
36
36
  Project-URL: Slack Chat, https://s.apache.org/airflow-slack
37
37
  Project-URL: Source Code, https://github.com/apache/airflow
@@ -63,7 +63,7 @@ Provides-Extra: openlineage
63
63
 
64
64
  Package ``apache-airflow-providers-snowflake``
65
65
 
66
- Release: ``6.2.2``
66
+ Release: ``6.3.0``
67
67
 
68
68
 
69
69
  `Snowflake <https://www.snowflake.com/>`__
@@ -76,7 +76,7 @@ This is a provider package for ``snowflake`` provider. All classes for this prov
76
76
  are in ``airflow.providers.snowflake`` python package.
77
77
 
78
78
  You can find package information and changelog for the provider
79
- in the `documentation <https://airflow.apache.org/docs/apache-airflow-providers-snowflake/6.2.2/>`_.
79
+ in the `documentation <https://airflow.apache.org/docs/apache-airflow-providers-snowflake/6.3.0/>`_.
80
80
 
81
81
  Installation
82
82
  ------------
@@ -93,9 +93,9 @@ Requirements
93
93
  ========================================== =====================================
94
94
  PIP package Version required
95
95
  ========================================== =====================================
96
- ``apache-airflow`` ``>=2.9.0``
96
+ ``apache-airflow`` ``>=2.10.0``
97
97
  ``apache-airflow-providers-common-compat`` ``>=1.6.0``
98
- ``apache-airflow-providers-common-sql`` ``>=1.20.0``
98
+ ``apache-airflow-providers-common-sql`` ``>=1.21.0``
99
99
  ``pandas`` ``>=2.1.2,<2.2``
100
100
  ``pyarrow`` ``>=14.0.1``
101
101
  ``snowflake-connector-python`` ``>=3.7.1``
@@ -125,5 +125,5 @@ Dependent package
125
125
  ================================================================================================================== =================
126
126
 
127
127
  The changelog for the provider package can be found in the
128
- `changelog <https://airflow.apache.org/docs/apache-airflow-providers-snowflake/6.2.2/changelog.html>`_.
128
+ `changelog <https://airflow.apache.org/docs/apache-airflow-providers-snowflake/6.3.0/changelog.html>`_.
129
129
 
@@ -1,12 +1,12 @@
1
1
  airflow/providers/snowflake/LICENSE,sha256=gXPVwptPlW1TJ4HSuG5OMPg-a3h43OGMkZRR1rpwfJA,10850
2
- airflow/providers/snowflake/__init__.py,sha256=yIZrxDjowZmilRDQWDlAnWoCcRPNNBNAeqKWxexc7pw,1496
2
+ airflow/providers/snowflake/__init__.py,sha256=bV-absjYLO_SPfvPyRwm7orXAY2ssUaMnwtRxzd8frk,1498
3
3
  airflow/providers/snowflake/get_provider_info.py,sha256=NdNRMfulBbpD-I4yFRr8U533m9djD18ijEMvuxOp4_g,3875
4
- airflow/providers/snowflake/version_compat.py,sha256=aHg90_DtgoSnQvILFICexMyNlHlALBdaeWqkX3dFDug,1605
4
+ airflow/providers/snowflake/version_compat.py,sha256=j5PCtXvZ71aBjixu-EFTNtVDPsngzzs7os0ZQDgFVDk,1536
5
5
  airflow/providers/snowflake/decorators/__init__.py,sha256=9hdXHABrVpkbpjZgUft39kOFL2xSGeG4GEua0Hmelus,785
6
6
  airflow/providers/snowflake/decorators/snowpark.py,sha256=tKXOjP8m8SEIu0jx2KSrd0n3jGMaIKDOwG2lMkvk3cI,5523
7
7
  airflow/providers/snowflake/hooks/__init__.py,sha256=9hdXHABrVpkbpjZgUft39kOFL2xSGeG4GEua0Hmelus,785
8
- airflow/providers/snowflake/hooks/snowflake.py,sha256=ZUoReavDSsgphmcVAotln824W6jPfaOldrGedDNxIdU,26148
9
- airflow/providers/snowflake/hooks/snowflake_sql_api.py,sha256=sbv4jrYUykP3huKIHYx-cpJi7xsFnhh6WqXtF0loZvw,15462
8
+ airflow/providers/snowflake/hooks/snowflake.py,sha256=2dlbnxu-0h1BrlNgdHyXQ69PVeutxijeBUBPNwF8dEg,28022
9
+ airflow/providers/snowflake/hooks/snowflake_sql_api.py,sha256=-J0mPcdDc9wbB7DcnZfnXJN7H62nbR_NK5WQJxeKZjE,14532
10
10
  airflow/providers/snowflake/operators/__init__.py,sha256=9hdXHABrVpkbpjZgUft39kOFL2xSGeG4GEua0Hmelus,785
11
11
  airflow/providers/snowflake/operators/snowflake.py,sha256=5MisB-bKqUFM9t5Ky913UqewoHlq3k3mCv4bnc-VY7g,22657
12
12
  airflow/providers/snowflake/operators/snowpark.py,sha256=Wt3wzcsja0ed4q2KE9WyL74XH6mUVSPNZvcCHWEHQtc,5815
@@ -16,10 +16,10 @@ airflow/providers/snowflake/triggers/__init__.py,sha256=9hdXHABrVpkbpjZgUft39kOF
16
16
  airflow/providers/snowflake/triggers/snowflake_trigger.py,sha256=38tkByMyjbVbSt-69YL8EzRBQT4rhwuOKHgbwHfULL0,4250
17
17
  airflow/providers/snowflake/utils/__init__.py,sha256=9hdXHABrVpkbpjZgUft39kOFL2xSGeG4GEua0Hmelus,785
18
18
  airflow/providers/snowflake/utils/common.py,sha256=DG-KLy2KpZWAqZqm_XIECm8lmdoUlzwkXv9onmkQThc,1644
19
- airflow/providers/snowflake/utils/openlineage.py,sha256=SKmePIebxQ23nhm6lshguelIA1TXApjRCSf1fTQqpn0,13995
19
+ airflow/providers/snowflake/utils/openlineage.py,sha256=joZ0ENJXF6I-BQ5EnVeaAYvz2C1Jtp_4pkPvYeJZheI,13510
20
20
  airflow/providers/snowflake/utils/snowpark.py,sha256=9kzWRkdgoNQ8f3Wnr92LdZylMpcpRasxefpOXrM30Cw,1602
21
21
  airflow/providers/snowflake/utils/sql_api_generate_jwt.py,sha256=9mR-vHIquv60tfAni87f6FAjKsiRHUDDrsVhzw4M9vM,6762
22
- apache_airflow_providers_snowflake-6.2.2.dist-info/entry_points.txt,sha256=bCrl5J1PXUMzbgnrKYho61rkbL2gHRT4I6f_1jlxAX4,105
23
- apache_airflow_providers_snowflake-6.2.2.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82
24
- apache_airflow_providers_snowflake-6.2.2.dist-info/METADATA,sha256=SL57jeZfS9REg_Gc5lxHDJ_RN8WRAgFtek89M7MVjGA,6204
25
- apache_airflow_providers_snowflake-6.2.2.dist-info/RECORD,,
22
+ apache_airflow_providers_snowflake-6.3.0rc1.dist-info/entry_points.txt,sha256=bCrl5J1PXUMzbgnrKYho61rkbL2gHRT4I6f_1jlxAX4,105
23
+ apache_airflow_providers_snowflake-6.3.0rc1.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82
24
+ apache_airflow_providers_snowflake-6.3.0rc1.dist-info/METADATA,sha256=7KYZ4V22kQjTg3Mtds9fRGXEq9J-SCPzyg_HjJRYUzs,6218
25
+ apache_airflow_providers_snowflake-6.3.0rc1.dist-info/RECORD,,