Skip to content

Commit fa12a4f

Browse files
gerzseaiven-sal
andcommitted
Format connection errors in the same way everywhere (redis#3305)
Connection errors are formatted in four places, sync and async, network socket and unix socket. Each place has some small differences compared to the others, while they could be, and should be, formatted in an uniform way. Factor out the logic in a helper method and call that method in all four places. Arguably we lose some specificity, e.g. the words "unix socket" won't be there anymore, but it is more valuable to not have code duplication. Co-authored-by: Salvatore Mesoraca <[email protected]> Signed-off-by: Salvatore Mesoraca <[email protected]>
1 parent 9f0758a commit fa12a4f

File tree

5 files changed

+110
-81
lines changed

5 files changed

+110
-81
lines changed

tests/test_asyncio/test_connection.py

Lines changed: 43 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,11 @@
1414
parse_url,
1515
)
1616
from valkey.asyncio import ConnectionPool, Valkey
17-
from valkey.asyncio.connection import Connection, UnixDomainSocketConnection
17+
from valkey.asyncio.connection import (
18+
Connection,
19+
SSLConnection,
20+
UnixDomainSocketConnection,
21+
)
1822
from valkey.asyncio.retry import Retry
1923
from valkey.backoff import NoBackoff
2024
from valkey.exceptions import ConnectionError, InvalidResponse, TimeoutError
@@ -497,18 +501,50 @@ async def test_connection_garbage_collection(request):
497501

498502

499503
@pytest.mark.parametrize(
500-
"error, expected_message",
504+
"conn, error, expected_message",
501505
[
502-
(OSError(), "Error connecting to localhost:6379. Connection reset by peer"),
503-
(OSError(12), "Error connecting to localhost:6379. 12."),
506+
(SSLConnection(), OSError(), "Error connecting to localhost:6379."),
507+
(SSLConnection(), OSError(12), "Error 12 connecting to localhost:6379."),
504508
(
509+
SSLConnection(),
505510
OSError(12, "Some Error"),
506-
"Error 12 connecting to localhost:6379. [Errno 12] Some Error.",
511+
"Error 12 connecting to localhost:6379. Some Error.",
512+
),
513+
(
514+
UnixDomainSocketConnection(path="unix:///tmp/valkey.sock"),
515+
OSError(),
516+
"Error connecting to unix:///tmp/valkey.sock.",
517+
),
518+
(
519+
UnixDomainSocketConnection(path="unix:///tmp/valkey.sock"),
520+
OSError(12),
521+
"Error 12 connecting to unix:///tmp/valkey.sock.",
522+
),
523+
(
524+
UnixDomainSocketConnection(path="unix:///tmp/valkey.sock"),
525+
OSError(12, "Some Error"),
526+
"Error 12 connecting to unix:///tmp/valkey.sock. Some Error.",
507527
),
508528
],
509529
)
510-
async def test_connect_error_message(error, expected_message):
530+
async def test_format_error_message(conn, error, expected_message):
511531
"""Test that the _error_message function formats errors correctly"""
512-
conn = Connection()
513532
error_message = conn._error_message(error)
514533
assert error_message == expected_message
534+
535+
536+
async def test_network_connection_failure():
537+
with pytest.raises(ConnectionError) as e:
538+
valkey = Valkey(host="127.0.0.1", port=9999)
539+
await valkey.set("a", "b")
540+
assert str(e.value).startswith("Error 111 connecting to 127.0.0.1:9999. Connect")
541+
542+
543+
async def test_unix_socket_connection_failure():
544+
with pytest.raises(ConnectionError) as e:
545+
valkey = Valkey(unix_socket_path="unix:///tmp/a.sock")
546+
await valkey.set("a", "b")
547+
assert (
548+
str(e.value)
549+
== "Error 2 connecting to unix:///tmp/a.sock. No such file or directory."
550+
)

tests/test_connection.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,3 +291,53 @@ def mock_disconnect(_):
291291

292292
assert called == 1
293293
pool.disconnect()
294+
295+
296+
@pytest.mark.parametrize(
297+
"conn, error, expected_message",
298+
[
299+
(SSLConnection(), OSError(), "Error connecting to localhost:6379."),
300+
(SSLConnection(), OSError(12), "Error 12 connecting to localhost:6379."),
301+
(
302+
SSLConnection(),
303+
OSError(12, "Some Error"),
304+
"Error 12 connecting to localhost:6379. Some Error.",
305+
),
306+
(
307+
UnixDomainSocketConnection(path="unix:///tmp/valkey.sock"),
308+
OSError(),
309+
"Error connecting to unix:///tmp/valkey.sock.",
310+
),
311+
(
312+
UnixDomainSocketConnection(path="unix:///tmp/valkey.sock"),
313+
OSError(12),
314+
"Error 12 connecting to unix:///tmp/valkey.sock.",
315+
),
316+
(
317+
UnixDomainSocketConnection(path="unix:///tmp/valkey.sock"),
318+
OSError(12, "Some Error"),
319+
"Error 12 connecting to unix:///tmp/valkey.sock. Some Error.",
320+
),
321+
],
322+
)
323+
def test_format_error_message(conn, error, expected_message):
324+
"""Test that the _error_message function formats errors correctly"""
325+
error_message = conn._error_message(error)
326+
assert error_message == expected_message
327+
328+
329+
def test_network_connection_failure():
330+
with pytest.raises(ConnectionError) as e:
331+
valkey = Valkey(port=9999)
332+
valkey.set("a", "b")
333+
assert str(e.value) == "Error 111 connecting to localhost:9999. Connection refused."
334+
335+
336+
def test_unix_socket_connection_failure():
337+
with pytest.raises(ConnectionError) as e:
338+
valkey = Valkey(unix_socket_path="unix:///tmp/a.sock")
339+
valkey.set("a", "b")
340+
assert (
341+
str(e.value)
342+
== "Error 2 connecting to unix:///tmp/a.sock. No such file or directory."
343+
)

valkey/asyncio/connection.py

Lines changed: 3 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
Union,
2525
)
2626

27+
from ..utils import format_error_message
28+
2729
# the functionality is available in 3.11.x but has a major issue before
2830
# 3.11.3. See https://github.com/redis/redis-py/issues/2633
2931
if sys.version_info >= (3, 11, 3):
@@ -342,9 +344,8 @@ async def _connect(self):
342344
def _host_error(self) -> str:
343345
pass
344346

345-
@abstractmethod
346347
def _error_message(self, exception: BaseException) -> str:
347-
pass
348+
return format_error_message(self._host_error(), exception)
348349

349350
async def on_connect(self) -> None:
350351
"""Initialize the connection, authenticate and select a database"""
@@ -796,27 +797,6 @@ async def _connect(self):
796797
def _host_error(self) -> str:
797798
return f"{self.host}:{self.port}"
798799

799-
def _error_message(self, exception: BaseException) -> str:
800-
# args for socket.error can either be (errno, "message")
801-
# or just "message"
802-
803-
host_error = self._host_error()
804-
805-
if not exception.args:
806-
# asyncio has a bug where on Connection reset by peer, the
807-
# exception is not instanciated, so args is empty. This is the
808-
# workaround.
809-
# See: https://github.com/redis/redis-py/issues/2237
810-
# See: https://github.com/python/cpython/issues/94061
811-
return f"Error connecting to {host_error}. Connection reset by peer"
812-
elif len(exception.args) == 1:
813-
return f"Error connecting to {host_error}. {exception.args[0]}."
814-
else:
815-
return (
816-
f"Error {exception.args[0]} connecting to {host_error}. "
817-
f"{exception}."
818-
)
819-
820800

821801
class SSLConnection(Connection):
822802
"""Manages SSL connections to and from the Valkey server(s).
@@ -968,20 +948,6 @@ async def _connect(self):
968948
def _host_error(self) -> str:
969949
return self.path
970950

971-
def _error_message(self, exception: BaseException) -> str:
972-
# args for socket.error can either be (errno, "message")
973-
# or just "message"
974-
host_error = self._host_error()
975-
if len(exception.args) == 1:
976-
return (
977-
f"Error connecting to unix socket: {host_error}. {exception.args[0]}."
978-
)
979-
else:
980-
return (
981-
f"Error {exception.args[0]} connecting to unix socket: "
982-
f"{host_error}. {exception.args[1]}."
983-
)
984-
985951

986952
class ConnectKwargs(TypedDict, total=False):
987953
username: str

valkey/connection.py

Lines changed: 2 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
CRYPTOGRAPHY_AVAILABLE,
3838
LIBVALKEY_AVAILABLE,
3939
SSL_AVAILABLE,
40+
format_error_message,
4041
get_lib_version,
4142
str_if_bytes,
4243
)
@@ -336,9 +337,8 @@ def _connect(self):
336337
def _host_error(self):
337338
pass
338339

339-
@abstractmethod
340340
def _error_message(self, exception):
341-
pass
341+
return format_error_message(self._host_error(), exception)
342342

343343
def on_connect(self):
344344
"Initialize the connection, authenticate and select a database"
@@ -731,27 +731,6 @@ def _connect(self):
731731
def _host_error(self):
732732
return f"{self.host}:{self.port}"
733733

734-
def _error_message(self, exception):
735-
# args for socket.error can either be (errno, "message")
736-
# or just "message"
737-
738-
host_error = self._host_error()
739-
740-
if len(exception.args) == 1:
741-
try:
742-
return f"Error connecting to {host_error}. \
743-
{exception.args[0]}."
744-
except AttributeError:
745-
return f"Connection Error: {exception.args[0]}"
746-
else:
747-
try:
748-
return (
749-
f"Error {exception.args[0]} connecting to "
750-
f"{host_error}. {exception.args[1]}."
751-
)
752-
except AttributeError:
753-
return f"Connection Error: {exception.args[0]}"
754-
755734

756735
class SSLConnection(Connection):
757736
"""Manages SSL connections to and from the Valkey server(s).
@@ -928,20 +907,6 @@ def _connect(self):
928907
def _host_error(self):
929908
return self.path
930909

931-
def _error_message(self, exception):
932-
# args for socket.error can either be (errno, "message")
933-
# or just "message"
934-
host_error = self._host_error()
935-
if len(exception.args) == 1:
936-
return (
937-
f"Error connecting to unix socket: {host_error}. {exception.args[0]}."
938-
)
939-
else:
940-
return (
941-
f"Error {exception.args[0]} connecting to unix socket: "
942-
f"{host_error}. {exception.args[1]}."
943-
)
944-
945910

946911
FALSE_STRINGS = ("0", "F", "FALSE", "N", "NO")
947912

valkey/utils.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,3 +139,15 @@ def get_lib_version():
139139
except metadata.PackageNotFoundError:
140140
libver = "99.99.99"
141141
return libver
142+
143+
144+
def format_error_message(host_error: str, exception: BaseException) -> str:
145+
if not exception.args:
146+
return f"Error connecting to {host_error}."
147+
elif len(exception.args) == 1:
148+
return f"Error {exception.args[0]} connecting to {host_error}."
149+
else:
150+
return (
151+
f"Error {exception.args[0]} connecting to {host_error}. "
152+
f"{exception.args[1]}."
153+
)

0 commit comments

Comments
 (0)