Repository: redis/redis-py
File: redis/asyncio/connection.py (Python)

Code before:
                # causing an error. Do not leave the connection open.
                writer.close()
                raise

    def _host_error(self) -> str:
        return f"{self.host}:{self.port}"

    def _error_message(self, exception: BaseException) -> str:
        # args for socket.error can either be (errno, "message")
        # or just "message"

        host_error = self._host_error()

        if not exception.args:
            # asyncio has a bug where on Connection reset by peer, the
            # exception is not instanciated, so args is empty. This is the
            # workaround.
            # See: https://github.com/redis/redis-py/issues/2237
            # See: https://github.com/python/cpython/issues/94061
            return f"Error connecting to {host_error}. Connection reset by peer"
        elif len(exception.args) == 1:
            return f"Error connecting to {host_error}. {exception.args[0]}."
        else:
            return (
                f"Error {exception.args[0]} connecting to {host_error}. "
                f"{exception.args[0]}."
            )


class SSLConnection(Connection):
    """Manages SSL connections to and from the Redis server(s).
    This class extends the Connection class, adding SSL functionality, and making
    use of ssl.SSLContext (https://docs.python.org/3/library/ssl.html#ssl.SSLContext)
    """

    def __init__(
        self,
        ssl_keyfile: Optional[str] = None,
        ssl_certfile: Optional[str] = None,
        ssl_cert_reqs: str = "required",
        ssl_ca_certs: Optional[str] = None,
        ssl_ca_data: Optional[str] = None,
        ssl_check_hostname: bool = False,
        ssl_min_version: Optional[ssl.TLSVersion] = None,
        ssl_ciphers: Optional[str] = None,
        **kwargs,
    ):
        self.ssl_context: RedisSSLContext = RedisSSLContext(
            keyfile=ssl_keyfile,
            certfile=ssl_certfile,
            cert_reqs=ssl_cert_reqs,

Review comment (refactor):
Thanks for straightening this out.

This rendering of the error message is done in four places, sync and async, unix and network socket. All four slightly different from each other.

Would you care aligning all four places? Some ideas that come on the spot:
- pull out the handling at least into the two abstract connection classes, maybe even reuse a helper method between the two abstract classes;
- if there are no args, return a simple error message, don't even assume it's the "connection reset", there might be other cases too;
- if there is one arg, use that one;
- if there are more then one args, use the first one like it is today, and then put `exception.arg[1:]` at the end of the message, concatenated.

What do you think, does it make sense?

Code after:
                # causing an error. Do not leave the connection open.
                writer.close()
                raise

    def _host_error(self) -> str:
        return f"{self.host}:{self.port}"

    def _error_message(self, exception: BaseException) -> str:
        # args for socket.error can either be (errno, "message")
        # or just "message"

        host_error = self._host_error()

        if not exception.args:
            # asyncio has a bug where on Connection reset by peer, the
            # exception is not instanciated, so args is empty. This is the
            # workaround.
            # See: https://github.com/redis/redis-py/issues/2237
            # See: https://github.com/python/cpython/issues/94061
            return f"Error connecting to {host_error}. Connection reset by peer"
        elif len(exception.args) == 1:
            return f"Error connecting to {host_error}. {exception.args[0]}."
        else:
            return (
                f"Error {exception.args[0]} connecting to {host_error}. "
                f"{exception}."
            )


class SSLConnection(Connection):
    """Manages SSL connections to and from the Redis server(s).
    This class extends the Connection class, adding SSL functionality, and making
    use of ssl.SSLContext (https://docs.python.org/3/library/ssl.html#ssl.SSLContext)
    """

    def __init__(
        self,
        ssl_keyfile: Optional[str] = None,
        ssl_certfile: Optional[str] = None,
        ssl_cert_reqs: str = "required",
        ssl_ca_certs: Optional[str] = None,
        ssl_ca_data: Optional[str] = None,
        ssl_check_hostname: bool = False,
        ssl_min_version: Optional[ssl.TLSVersion] = None,
        ssl_ciphers: Optional[str] = None,
        **kwargs,
    ):
        self.ssl_context: RedisSSLContext = RedisSSLContext(
            keyfile=ssl_keyfile,
            certfile=ssl_certfile,
            cert_reqs=ssl_cert_reqs,