Skip to content

Commit 524e19f

Browse files
committed
SNOW-1572306: error experience in asyncio (#2082)
1 parent b637a22 commit 524e19f

File tree

7 files changed

+62
-31
lines changed

7 files changed

+62
-31
lines changed

src/snowflake/connector/aio/_connection.py

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -653,16 +653,30 @@ def auth_class(self, value: AuthByPlugin) -> None:
653653

654654
@property
655655
def client_prefetch_threads(self) -> int:
656-
# TODO: use client_prefetch_threads as numbers for coroutines? how to communicate to users
657-
logger.warning("asyncio does not support client_prefetch_threads")
658656
return self._client_prefetch_threads
659657

660658
@client_prefetch_threads.setter
661659
def client_prefetch_threads(self, value) -> None:
662-
# TODO: use client_prefetch_threads as numbers for coroutines? how to communicate to users
663-
logger.warning("asyncio does not support client_prefetch_threads")
664660
self._client_prefetch_threads = value
665661

662+
@property
663+
def errorhandler(self) -> None:
664+
# check SNOW-1763103
665+
raise NotImplementedError(
666+
"Async Snowflake Python Connector does not support errorhandler. "
667+
"Please open a feature request issue in github if your want this feature: "
668+
"https://github.com/snowflakedb/snowflake-connector-python/issues/new/choose."
669+
)
670+
671+
@errorhandler.setter
672+
def errorhandler(self, value) -> None:
673+
# check SNOW-1763103
674+
raise NotImplementedError(
675+
"Async Snowflake Python Connector does not support errorhandler. "
676+
"Please open a feature request issue in github if your want this feature: "
677+
"https://github.com/snowflakedb/snowflake-connector-python/issues/new/choose."
678+
)
679+
666680
@property
667681
def rest(self) -> SnowflakeRestful | None:
668682
return self._rest

src/snowflake/connector/aio/_cursor.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -826,6 +826,24 @@ async def execute_async(self, *args: Any, **kwargs: Any) -> dict[str, Any]:
826826
kwargs["_exec_async"] = True
827827
return await self.execute(*args, **kwargs)
828828

829+
@property
830+
def errorhandler(self):
831+
# check SNOW-1763103
832+
raise NotImplementedError(
833+
"Async Snowflake Python Connector does not support errorhandler. "
834+
"Please open a feature request issue in github if your want this feature: "
835+
"https://github.com/snowflakedb/snowflake-connector-python/issues/new/choose."
836+
)
837+
838+
@errorhandler.setter
839+
def errorhandler(self, value):
840+
# check SNOW-1763103
841+
raise NotImplementedError(
842+
"Async Snowflake Python Connector does not support errorhandler. "
843+
"Please open a feature request issue in github if your want this feature: "
844+
"https://github.com/snowflakedb/snowflake-connector-python/issues/new/choose."
845+
)
846+
829847
async def describe(self, *args: Any, **kwargs: Any) -> list[ResultMetadata]:
830848
"""Obtain the schema of the result without executing the query.
831849

src/snowflake/connector/aio/_network.py

Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,7 @@
1818
import OpenSSL.SSL
1919
from urllib3.util.url import parse_url
2020

21-
from ..compat import (
22-
FORBIDDEN,
23-
OK,
24-
UNAUTHORIZED,
25-
BadStatusLine,
26-
IncompleteRead,
27-
urlencode,
28-
urlparse,
29-
)
21+
from ..compat import FORBIDDEN, OK, UNAUTHORIZED, urlencode, urlparse
3022
from ..constants import (
3123
_CONNECTIVITY_ERR_MSG,
3224
HTTP_HEADER_ACCEPT,
@@ -798,7 +790,7 @@ async def _request_exec(
798790
return None # required for tests
799791
finally:
800792
raw_ret.close() # ensure response is closed
801-
except aiohttp.ClientSSLError as se:
793+
except (aiohttp.ClientSSLError, aiohttp.ClientConnectorSSLError) as se:
802794
msg = f"Hit non-retryable SSL error, {str(se)}.\n{_CONNECTIVITY_ERR_MSG}"
803795
logger.debug(msg)
804796
# the following code is for backward compatibility with old versions of python connector which calls
@@ -812,15 +804,11 @@ async def _request_exec(
812804
"errno": ER_FAILED_TO_REQUEST,
813805
},
814806
)
815-
# TODO: sync feature parity, aiohttp network error handling
816807
except (
817-
BadStatusLine,
818-
ConnectionError,
819808
aiohttp.ClientConnectionError,
820-
aiohttp.ClientPayloadError,
821-
aiohttp.ClientResponseError,
809+
aiohttp.ClientConnectorError,
810+
aiohttp.ConnectionTimeoutError,
822811
asyncio.TimeoutError,
823-
IncompleteRead,
824812
OpenSSL.SSL.SysCallError,
825813
KeyError, # SNOW-39175: asn1crypto.keys.PublicKeyInfo
826814
ValueError,
@@ -845,7 +833,15 @@ async def _request_exec(
845833
)
846834
raise RetryRequest(err)
847835
except Exception as err:
848-
raise err
836+
if isinstance(err, (Error, RetryRequest, ReauthenticationRequest)):
837+
raise err
838+
raise OperationalError(
839+
msg=f"Unexpected error occurred during request execution: {err}"
840+
"Please check the stack trace for more information and retry the operation."
841+
"If you think this is a bug, please collect the error information and open a bug report in github: "
842+
"https://github.com/snowflakedb/snowflake-connector-python/issues/new/choose.",
843+
errno=ER_FAILED_TO_REQUEST,
844+
) from err
849845

850846
def make_requests_session(self) -> aiohttp.ClientSession:
851847
s = aiohttp.ClientSession(

src/snowflake/connector/errors.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -336,10 +336,18 @@ def hand_to_other_handler(
336336
connection.messages.append((error_class, error_value))
337337
if cursor is not None:
338338
cursor.messages.append((error_class, error_value))
339-
cursor.errorhandler(connection, cursor, error_class, error_value)
339+
try:
340+
cursor.errorhandler(connection, cursor, error_class, error_value)
341+
except NotImplementedError:
342+
# for async compatibility, check SNOW-1763096 and SNOW-1763103
343+
cursor._errorhandler(connection, cursor, error_class, error_value)
340344
return True
341345
elif connection is not None:
342-
connection.errorhandler(connection, cursor, error_class, error_value)
346+
try:
347+
connection.errorhandler(connection, cursor, error_class, error_value)
348+
except NotImplementedError:
349+
# for async compatibility, check SNOW-1763096 and SNOW-1763103
350+
connection._errorhandler(connection, cursor, error_class, error_value)
343351
return True
344352
return False
345353

test/integ/aio/test_connection_async.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -998,6 +998,7 @@ async def test_region_deprecation(conn_cnx):
998998
assert "Region has been deprecated" in str(w[0].message)
999999

10001000

1001+
@pytest.mark.skip("SNOW-1763103")
10011002
async def test_invalid_errorhander_error(conn_cnx):
10021003
"""Tests if no errorhandler cannot be set."""
10031004
async with conn_cnx() as conn:

test/integ/aio/test_cursor_async.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -984,12 +984,14 @@ async def test_real_decimal(conn, db_parameters):
984984
assert rec["RATIO"] == decimal.Decimal("23.4"), "the decimal value"
985985

986986

987+
@pytest.mark.skip("SNOW-1763103")
987988
async def test_none_errorhandler(conn_testaccount):
988989
c = conn_testaccount.cursor()
989990
with pytest.raises(errors.ProgrammingError):
990991
c.errorhandler = None
991992

992993

994+
@pytest.mark.skip("SNOW-1763103")
993995
async def test_nope_errorhandler(conn_testaccount):
994996
def user_errorhandler(connection, cursor, errorclass, errorvalue):
995997
pass

test/unit/aio/test_retry_network_async.py

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,6 @@
3030
OK,
3131
SERVICE_UNAVAILABLE,
3232
UNAUTHORIZED,
33-
BadStatusLine,
34-
IncompleteRead,
3533
)
3634
from snowflake.connector.errors import (
3735
DatabaseError,
@@ -232,16 +230,11 @@ async def test_request_exec():
232230
with pytest.raises(ForbiddenError):
233231
await rest._request_exec(session=session, **login_parameters)
234232

235-
class IncompleteReadMock(IncompleteRead):
236-
def __init__(self):
237-
IncompleteRead.__init__(self, "")
238-
239233
# handle retryable exception
240234
for exc in [
241235
aiohttp.ConnectionTimeoutError,
242236
aiohttp.ClientConnectorError(MagicMock(), OSError(1)),
243237
asyncio.TimeoutError,
244-
IncompleteReadMock,
245238
AttributeError,
246239
]:
247240
session = AsyncMock()
@@ -264,7 +257,6 @@ def __init__(self):
264257
OpenSSL.SSL.SysCallError(errno.ETIMEDOUT),
265258
OpenSSL.SSL.SysCallError(errno.EPIPE),
266259
OpenSSL.SSL.SysCallError(-1), # unknown
267-
BadStatusLine("fake"),
268260
]:
269261
session = AsyncMock()
270262
session.request = Mock(side_effect=exc)

0 commit comments

Comments
 (0)