Repository: aio-libs/aiohttp

Issue:
String representation of ServerDisconnectedError should be more meaningful
## Long story short

When the server closes its side of the socket, a ```ServerDisconnectedError``` is raised in the client (happened to me while testing an application). Converting this exception to a string results in an empty string (or in ```None``` in earlier versions, IIRC), which is not very useful, for example when logging the exception.

## Expected behaviour

The string representation of ```ServerDisconnectedError``` should always be meaningful. If no further information is available, something like "Server disconnected" could be returned.

## Actual behaviour

Converting the exception to a string results in an empty string.

## Steps to reproduce

```
#!/usr/bin/env python3

import aiohttp
import asyncio
import io
import platform
import sys


async def server():
    # Server that shuts down its write side of the connection immediately.

    async def handler(reader, writer):
        print('closing write side')
        writer.write_eof()
        await asyncio.sleep(1)

    server = await asyncio.start_server(handler, '127.0.0.1', 8080, limit=20)
    await server.serve_forever()


async def main():
    asyncio.create_task(server())
    await asyncio.sleep(1)

    data = b'0' * 30
    file = io.BytesIO(data)

    try:
        async with aiohttp.ClientSession() as session:
            print('making request')
            await session.patch('http://127.0.0.1:8080', data=file)
    except Exception as e:
        print(f'type(e): "{type(e)}"')
        print(f'str(e): "{str(e)}"')


if __name__ == '__main__':
    print(f'platform: {platform.platform()}')
    print(f'python version: {sys.version.replace(chr(10), "")}')
    print(f'aiohttp version: {aiohttp.__version__}\n')

    asyncio.run(main())
```

```
$ ./test.py 
platform: Linux-5.2.18-200.fc30.x86_64-x86_64-with-fedora-30-Thirty
python version: 3.7.4 (default, Jul  9 2019, 16:32:37) [GCC 9.1.1 20190503 (Red Hat 9.1.1-1)]
aiohttp version: 3.6.2

making request
closing write side
type(e): "<class 'aiohttp.client_exceptions.ServerDisconnectedError'>"
str(e): ""
```

## Your environment

Please see the output of the script above.



Discussion:
The fix is welcome!

Resolving patch:
diff --git a/aiohttp/client_exceptions.py b/aiohttp/client_exceptions.py
--- a/aiohttp/client_exceptions.py
+++ b/aiohttp/client_exceptions.py
@@ -160,11 +160,11 @@ class ServerDisconnectedError(ServerConnectionError):
     """Server disconnected."""
 
     def __init__(self, message: Optional[str]=None) -> None:
-        self.message = message
         if message is None:
-            self.args = ()
-        else:
-            self.args = (message,)
+            message = 'Server disconnected'
+
+        self.args = (message,)
+        self.message = message
 
 
 class ServerTimeoutError(ServerConnectionError, asyncio.TimeoutError):


Test patch:
diff --git a/tests/test_client_exceptions.py b/tests/test_client_exceptions.py
--- a/tests/test_client_exceptions.py
+++ b/tests/test_client_exceptions.py
@@ -178,7 +178,7 @@ def test_str(self) -> None:
 class TestServerDisconnectedError:
     def test_ctor(self) -> None:
         err = client.ServerDisconnectedError()
-        assert err.message is None
+        assert err.message == 'Server disconnected'
 
         err = client.ServerDisconnectedError(message='No connection')
         assert err.message == 'No connection'
@@ -194,7 +194,12 @@ def test_pickle(self) -> None:
 
     def test_repr(self) -> None:
         err = client.ServerDisconnectedError()
-        assert repr(err) == "ServerDisconnectedError()"
+        if sys.version_info < (3, 7):
+            assert repr(err) == ("ServerDisconnectedError"
+                                 "('Server disconnected',)")
+        else:
+            assert repr(err) == ("ServerDisconnectedError"
+                                 "('Server disconnected')")
 
         err = client.ServerDisconnectedError(message='No connection')
         if sys.version_info < (3, 7):
@@ -204,7 +209,7 @@ def test_repr(self) -> None:
 
     def test_str(self) -> None:
         err = client.ServerDisconnectedError()
-        assert str(err) == ''
+        assert str(err) == 'Server disconnected'
 
         err = client.ServerDisconnectedError(message='No connection')
         assert str(err) == 'No connection'
diff --git a/tests/test_client_functional.py b/tests/test_client_functional.py
--- a/tests/test_client_functional.py
+++ b/tests/test_client_functional.py
@@ -2680,10 +2680,12 @@ async def handler(request):
     app.router.add_get('/', handler)
 
     client = await aiohttp_client(app)
-    with pytest.raises(aiohttp.ServerDisconnectedError):
+    with pytest.raises(aiohttp.ServerDisconnectedError) as excinfo:
         resp = await client.get('/')
         await resp.read()
 
+    assert str(excinfo.value) != ''
+
 
 async def test_dont_close_explicit_connector(aiohttp_client) -> None:
     async def handler(request):