Repository: aio-libs-abandoned/aioredis-py

Issue:
ConnectionResetError: [WinError 10054] 远程主机强迫关闭了一个现有的连接。
idk what happend, but it dosen't work suddenly.
i guarantee redis server is ok and i try other redis lib it works too,

i am use version:
aioredis==2.0.0
  - async-timeout [required: Any, installed: 3.0.1]
  - typing-extensions [required: Any, installed: 3.10.0.2]
 
on Microsoft Windows [Version 10.0.19043.1165]
python 3.9.6


Discussion:
the  error stack is :
```
Traceback (most recent call last):
  File "c:\users\ronaldinho\.pyenv\pyenv-win\versions\3.9.6\lib\asyncio\runners.py", line 44, in run
    return loop.run_until_complete(main)
  File "c:\users\ronaldinho\.pyenv\pyenv-win\versions\3.9.6\lib\asyncio\base_events.py", line 642, in run_until_complete
    return future.result()
  File "E:/xxx.py", line 14, in main
    for sessionid in await redis.keys("session*"):
  File "C:\Users\Ronaldinho\.virtualenvs\xxx-5Vbn2Ffj\lib\site-packages\aioredis\client.py", line 1064, in execute_command
    return await self.parse_response(conn, command_name, **options)
  File "C:\Users\Ronaldinho\.virtualenvs\xxx-5Vbn2Ffj\lib\site-packages\aioredis\client.py", line 1080, in parse_response
    response = await connection.read_response()
  File "C:\Users\Ronaldinho\.virtualenvs\xxx-5Vbn2Ffj\lib\site-packages\aioredis\connection.py", line 854, in read_response
    response = await self._parser.read_response()
  File "C:\Users\Ronaldinho\.virtualenvs\xxx-5Vbn2Ffj\lib\site-packages\aioredis\connection.py", line 367, in read_response
    raw = await self._buffer.readline()
  File "C:\Users\Ronaldinho\.virtualenvs\xxx-5Vbn2Ffj\lib\site-packages\aioredis\connection.py", line 301, in readline
    await self._read_from_socket()
  File "C:\Users\Ronaldinho\.virtualenvs\xxx-5Vbn2Ffj\lib\site-packages\aioredis\connection.py", line 247, in _read_from_socket
    data = await self._stream.read(self.socket_read_size)
  File "c:\users\ronaldinho\.pyenv\pyenv-win\versions\3.9.6\lib\asyncio\streams.py", line 684, in read
    await self._wait_for_data('read')
  File "c:\users\ronaldinho\.pyenv\pyenv-win\versions\3.9.6\lib\asyncio\streams.py", line 517, in _wait_for_data
    await self._waiter
  File "c:\users\ronaldinho\.pyenv\pyenv-win\versions\3.9.6\lib\asyncio\selector_events.py", line 856, in _read_ready__data_received
    data = self._sock.recv(self.max_size)
ConnectionResetError: [WinError 10054] 远程主机强迫关闭了一个现有的连接。
python-BaseException

Process finished with exit code 1
```
See #778.
Hi all, the problem lies with the `ConnectionError` type. `aioredis` implements its own `ConnectionError(builtins.ConnectionError, RedisError` which causes problems because `ConnectionResetError` is no longer a subclass of `ConnectionError`. If one looks at line 37 of `client.py`, it overwrites `ConnectionError` so that the exception can no longer be caught on line `1067`....
@eneloop2 please create a PR or gist to demonstrate the fix. Greatly appreciated investigation!
Hey @Andrew-Chen-Wang, I'm not sure how you guys want to fix it since I'm not sure about the motivation behind subclassing `builtins.ConnectionError and RedisError`. It would be better to just check if the exception is one of `builtins.ConnectionError, RedisError` instead. perhaps you can shed some light on that.
@eneloop2 this is just a port from redis-py, so the only original motivation behind it was that we copied code. Besides that, I guess it would make more sense to solely subclass RedisError as a specific exception to our library.
This is incorrect - with multiple inheritance, a class is a subclass of both parent classes.

```python

class MyException(Exception): ...

class SomeException(ConnectionError, MyException): ...


print(issubclass(SomeException, ConnectionError))
print(issubclass(SomeException, MyException))

try:
    raise SomeException("ack!")
except MyException as e:
    print(e)

try:
    raise SomeException("agh!")
except ConnectionError as e:
    print(e)

#> True
#> True
#> ack!
#> agh!
```

No, your example is wrong. Because that's not how `ConnectionError` is defined in the aioredis.
> This is incorrect - with multiple inheritance, a class is a subclass of both parent classes.
> 
> ```python
> class MyException(Exception): ...
> 
> class SomeException(ConnectionError, MyException): ...
> 
> 
> print(issubclass(SomeException, ConnectionError))
> print(issubclass(SomeException, MyException))
> 
> try:
>     raise SomeException("ack!")
> except MyException as e:
>     print(e)
> 
> try:
>     raise SomeException("agh!")
> except ConnectionError as e:
>     print(e)
> 
> #> True
> #> True
> #> ack!
> #> agh!
> ```

Look and see which `ConnectionError` is being handled in the aioredis code. Of course `SomeException` is an instance of `MyException`. But aioredis `ConnectionError` is `ConnectionError(builtins.ConnectionError, RedisError)`. Later, there's code that does `from exceptions import ConnectionError ...`. At that point it's been re-written. Run it through the code and see for yourself. It's quite obvious.
```
from aioredis.exceptions import ConnectionError

try:
  raise ConnectionResetError("bad!!")
except ConnectionError:
  print("not bad!")
```
What does this print? This is exactly what's going on in `aioredis/connection.py`, it does `from .exceptions import ConnectionError`.
@Andrew-Chen-Wang maybe you should look this issue and patch from redis-py project.

* https://github.com/andymccurdy/redis-py/issues/1202
* https://github.com/andymccurdy/redis-py/commit/f0516c9f7589557883a5eb23a44531066e275950 

and try this demo.

```python
from aioredis.connection import NONBLOCKING_EXCEPTIONS

try:
    raise ConnectionResetError
except NONBLOCKING_EXCEPTIONS:
    print('aioredis catched!')
except Exception as exc:
    print("aioredis not catched!")

from redis.connection import NONBLOCKING_EXCEPTIONS

try:
    raise ConnectionResetError
except NONBLOCKING_EXCEPTIONS:
    print('redis-py catched')
```

output:
> aioredis not catched!
> redis-py catched

add `socket.error` in `NONBLOCKING_EXCEPTIONS` is not only for Python 2/3 compatible, please fix it, thanks.


Resolving patch:
diff --git a/aioredis/connection.py b/aioredis/connection.py
--- a/aioredis/connection.py
+++ b/aioredis/connection.py
@@ -901,6 +901,11 @@ async def read_response(self):
         except asyncio.TimeoutError:
             await self.disconnect()
             raise TimeoutError(f"Timeout reading from {self.host}:{self.port}")
+        except OSError as e:
+            await self.disconnect()
+            raise ConnectionError(
+                f"Error while reading from {self.host}:{self.port} : {e.args}"
+            )
         except BaseException:
             await self.disconnect()
             raise
diff --git a/aioredis/exceptions.py b/aioredis/exceptions.py
--- a/aioredis/exceptions.py
+++ b/aioredis/exceptions.py
@@ -7,7 +7,7 @@ class RedisError(Exception):
     pass
 
 
-class ConnectionError(builtins.ConnectionError, RedisError):
+class ConnectionError(RedisError):
     pass
 
 


Test patch: