Skip to content

Commit 7d80273

Browse files
authored
Merge pull request #20 from axellpadilla/fix/optional-azure-dep
Fix/optional azure dep
2 parents cc0af18 + 5641792 commit 7d80273

2 files changed

Lines changed: 65 additions & 8 deletions

File tree

dbt/adapters/sqlserver/sqlserver_connections.py

Lines changed: 43 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,41 @@
22
import struct
33
import time
44
from contextlib import contextmanager
5+
from dataclasses import dataclass
56
from itertools import chain, repeat
67
from typing import Any, Callable, Dict, Mapping, Optional, Tuple, Type, Union
78

89
import agate
910
import dbt_common.exceptions
1011
import pyodbc
11-
from azure.core.credentials import AccessToken
12-
from azure.identity import (
13-
AzureCliCredential,
14-
ClientSecretCredential,
15-
DefaultAzureCredential,
16-
EnvironmentCredential,
17-
ManagedIdentityCredential,
18-
)
12+
13+
try:
14+
from azure.core.credentials import AccessToken
15+
except ModuleNotFoundError:
16+
@dataclass
17+
class AccessToken:
18+
token: str
19+
expires_on: int
20+
21+
22+
try:
23+
from azure.identity import (
24+
AzureCliCredential,
25+
ClientSecretCredential,
26+
DefaultAzureCredential,
27+
EnvironmentCredential,
28+
ManagedIdentityCredential,
29+
)
30+
31+
_AZURE_IDENTITY_IMPORT_ERROR = None
32+
except ModuleNotFoundError as exc:
33+
AzureCliCredential = None
34+
ClientSecretCredential = None
35+
DefaultAzureCredential = None
36+
EnvironmentCredential = None
37+
ManagedIdentityCredential = None
38+
_AZURE_IDENTITY_IMPORT_ERROR = exc
39+
1940
from dbt.adapters.contracts.connection import AdapterResponse, Connection, ConnectionState
2041
from dbt.adapters.events.logging import AdapterLogger
2142
from dbt.adapters.events.types import AdapterEventDebug, ConnectionUsed, SQLQuery, SQLQueryStatus
@@ -51,6 +72,15 @@
5172
}
5273

5374

75+
def _require_azure_identity(authentication: str) -> None:
76+
if _AZURE_IDENTITY_IMPORT_ERROR is not None:
77+
raise dbt_common.exceptions.DbtRuntimeError(
78+
"Azure authentication '{}' requires the optional dependency 'azure-identity'. "
79+
"Install it with `pip install azure-identity` or use a non-Azure authentication mode."
80+
.format(authentication)
81+
) from _AZURE_IDENTITY_IMPORT_ERROR
82+
83+
5484
def convert_bytes_to_mswindows_byte_string(value: bytes) -> bytes:
5585
"""
5686
Convert bytes to a Microsoft windows byte string.
@@ -110,6 +140,7 @@ def get_cli_access_token(
110140
Access token.
111141
"""
112142
_ = credentials
143+
_require_azure_identity("cli")
113144
token = AzureCliCredential().get_token(
114145
scope, timeout=getattr(credentials, "login_timeout", None)
115146
)
@@ -132,6 +163,7 @@ def get_auto_access_token(
132163
out : AccessToken
133164
The access token.
134165
"""
166+
_require_azure_identity("auto")
135167
token = DefaultAzureCredential().get_token(
136168
scope, timeout=getattr(credentials, "login_timeout", None)
137169
)
@@ -154,6 +186,7 @@ def get_environment_access_token(
154186
out : AccessToken
155187
The access token.
156188
"""
189+
_require_azure_identity("environment")
157190
token = EnvironmentCredential().get_token(
158191
scope, timeout=getattr(credentials, "login_timeout", None)
159192
)
@@ -177,6 +210,7 @@ def get_msi_access_token(
177210
The access token.
178211
"""
179212
_ = credentials
213+
_require_azure_identity("msi")
180214
token = ManagedIdentityCredential().get_token(scope)
181215
return token
182216

@@ -198,6 +232,7 @@ def get_sp_access_token(
198232
The access token.
199233
"""
200234
_ = scope
235+
_require_azure_identity("serviceprincipal")
201236
token = ClientSecretCredential(
202237
str(credentials.tenant_id),
203238
str(credentials.client_id),

tests/unit/adapters/mssql/test_sqlserver_connection_manager.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import pytest
22
from azure.identity import AzureCliCredential
3+
from dbt_common.exceptions import DbtRuntimeError
34

5+
from dbt.adapters.sqlserver import sqlserver_connections
46
from dbt.adapters.sqlserver.sqlserver_connections import ( # byte_array_to_datetime,
57
bool_to_connection_string_arg,
68
get_pyodbc_attrs_before_credentials,
@@ -33,6 +35,26 @@ def test_get_pyodbc_attrs_before_empty_dict_when_service_principal(
3335
assert attrs_before == {}
3436

3537

38+
def test_get_pyodbc_attrs_before_sql_auth_without_azure_identity(
39+
credentials: SQLServerCredentials, monkeypatch: pytest.MonkeyPatch
40+
) -> None:
41+
monkeypatch.setattr(sqlserver_connections, "_AZURE_IDENTITY_IMPORT_ERROR", ModuleNotFoundError())
42+
43+
attrs_before = get_pyodbc_attrs_before_credentials(credentials)
44+
45+
assert attrs_before == {}
46+
47+
48+
def test_get_pyodbc_attrs_before_cli_auth_requires_azure_identity(
49+
credentials: SQLServerCredentials, monkeypatch: pytest.MonkeyPatch
50+
) -> None:
51+
credentials.authentication = "cli"
52+
monkeypatch.setattr(sqlserver_connections, "_AZURE_IDENTITY_IMPORT_ERROR", ModuleNotFoundError())
53+
54+
with pytest.raises(DbtRuntimeError, match="requires the optional dependency 'azure-identity'"):
55+
get_pyodbc_attrs_before_credentials(credentials)
56+
57+
3658
@pytest.mark.parametrize(
3759
"key, value, expected",
3860
[("somekey", False, "somekey=No"), ("somekey", True, "somekey=Yes")],

0 commit comments

Comments
 (0)