Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/databricks/sql/auth/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ def get_auth_provider(cfg: ClientContext, http_client):
cfg.auth_type,
)
elif cfg.access_token is not None:
if not cfg.access_token:
# An explicitly empty token is a missing credential; never fall
# back to an interactive browser login for it.
raise RuntimeError("No valid authentication settings! access_token is empty")
base_provider = AccessTokenAuthProvider(cfg.access_token)
elif cfg.use_cert_as_auth and cfg.tls_client_cert_file:
# no op authenticator. authentication is performed using ssl certificate outside of headers
Expand Down
6 changes: 5 additions & 1 deletion src/databricks/sql/backend/thrift_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -459,7 +459,11 @@ def attempt_request(attempt):
f"GetOperationStatus failed with HTTP error and will be retried: {str(err)}"
)
else:
raise err
# Not retried here (urllib3 already applied the retry policy),
# but surfaced as a DB-API RequestError rather than a raw
# urllib3 exception.
error = err
error_message = str(err)
except OSError as err:
error = err
error_message = str(err)
Expand Down
2 changes: 1 addition & 1 deletion src/databricks/sql/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -372,7 +372,7 @@ def read(self) -> Optional[OAuthToken]:
http_path,
)

if access_token:
if access_token is not None:
access_token_kv = {"access_token": access_token}
kwargs = {**kwargs, **access_token_kv}

Expand Down
7 changes: 7 additions & 0 deletions tests/unit/test_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,13 @@ def test_get_python_sql_connector_auth_provider_access_token(self):
auth_provider.add_headers(headers)
self.assertEqual(headers["Authorization"], "Bearer dpi123")

def test_get_python_sql_connector_auth_provider_empty_access_token(self):
"""An explicitly empty token must not start an interactive OAuth login."""
with self.assertRaisesRegex(RuntimeError, "No valid authentication settings!"):
get_python_sql_connector_auth_provider(
"moderakh-test.cloud.databricks.com", MagicMock(), access_token=""
)

def test_get_python_sql_connector_auth_provider_external(self):
class MyProvider(CredentialsProvider):
def auth_type(self) -> str:
Expand Down
32 changes: 31 additions & 1 deletion tests/unit/test_thrift_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -1927,7 +1927,7 @@ def test_make_request_will_retry_GetOperationStatus(

import thrift, errno
from databricks.sql.thrift_api.TCLIService.TCLIService import Client
from databricks.sql.exc import RequestError
from databricks.sql.exc import RequestError, RequestError
from databricks.sql.utils import NoRetryReason

this_gos_name = "GetOperationStatus"
Expand Down Expand Up @@ -2047,6 +2047,36 @@ def test_make_request_will_retry_GetOperationStatus_for_http_error(
f"{EXPECTED_RETRIES}/{EXPECTED_RETRIES}", cm.exception.context["attempt"]
)

@patch("thrift.transport.THttpClient.THttpClient")
def test_make_request_wraps_urllib3_http_error_as_request_error(
self, t_transport_class
):
import urllib3

t_transport_instance = t_transport_class.return_value
t_transport_instance.code = None
t_transport_instance.headers = {}
mock_method = Mock()
mock_method.__name__ = "OpenSession"
mock_method.side_effect = urllib3.exceptions.MaxRetryError(
None, "/", "Tunnel connection failed: 503"
)

thrift_backend = ThriftDatabricksClient(
"foobar",
443,
"path",
[],
auth_provider=AuthProvider(),
ssl_options=SSLOptions(),
http_client=MagicMock(),
)

with self.assertRaises(RequestError) as cm:
thrift_backend.make_request(mock_method, Mock())

self.assertIn("Tunnel connection failed", str(cm.exception.message_with_context()))

@patch("thrift.transport.THttpClient.THttpClient")
def test_make_request_wont_retry_if_error_code_not_429_or_503(
self, t_transport_class
Expand Down