Skip to content

Commit 24413b1

Browse files
committed
Merge branch 'master' into async-graph
2 parents 34c5177 + f9f9d06 commit 24413b1

27 files changed

+330
-154
lines changed

.github/workflows/integration.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ jobs:
3939
strategy:
4040
max-parallel: 15
4141
matrix:
42-
python-version: ['3.6', '3.7', '3.8', '3.9', '3.10', 'pypy-3.7', 'pypy-3.8']
42+
python-version: ['3.7', '3.8', '3.9', '3.10', 'pypy-3.7', 'pypy-3.8']
4343
test-type: ['standalone', 'cluster']
4444
connection-type: ['hiredis', 'plain']
4545
env:
@@ -84,7 +84,7 @@ jobs:
8484
runs-on: ubuntu-latest
8585
strategy:
8686
matrix:
87-
python-version: ['3.6', '3.7', '3.8', '3.9', '3.10', 'pypy-3.7']
87+
python-version: ['3.7', '3.8', '3.9', '3.10', 'pypy-3.7']
8888
steps:
8989
- uses: actions/checkout@v2
9090
- name: install python ${{ matrix.python-version }}

CONTRIBUTING.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ this, instead of using make test, you need to pass
126126
Our test suite uses `pytest`. You can run a specific test suite against
127127
a specific Python version like this:
128128

129-
`$ docker-compose run test tox -e py36 -- --redis-url=redis://master:6379/9 tests/test_commands.py`
129+
`$ docker-compose run test tox -e py37 -- --redis-url=redis://master:6379/9 tests/test_commands.py`
130130

131131
### Troubleshooting
132132

redis/asyncio/client.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -754,9 +754,15 @@ async def parse_response(self, block: bool = True, timeout: float = 0):
754754

755755
await self.check_health()
756756

757-
if not block and not await self._execute(conn, conn.can_read, timeout=timeout):
758-
return None
759-
response = await self._execute(conn, conn.read_response)
757+
async def try_read():
758+
if not block:
759+
if not await conn.can_read(timeout=timeout):
760+
return None
761+
else:
762+
await conn.connect()
763+
return await conn.read_response()
764+
765+
response = await self._execute(conn, try_read)
760766

761767
if conn.health_check_interval and response == self.health_check_response:
762768
# ignore the health check message as user might not expect it

redis/asyncio/cluster.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -377,8 +377,7 @@ def __del__(self) -> None:
377377
warnings.warn(f"{self._DEL_MESSAGE} {self!r}", ResourceWarning, source=self)
378378
try:
379379
context = {"client": self, "message": self._DEL_MESSAGE}
380-
# TODO: Change to get_running_loop() when dropping support for py3.6
381-
asyncio.get_event_loop().call_exception_handler(context)
380+
asyncio.get_running_loop().call_exception_handler(context)
382381
except RuntimeError:
383382
...
384383

@@ -834,8 +833,7 @@ def __del__(self) -> None:
834833
)
835834
try:
836835
context = {"client": self, "message": self._DEL_MESSAGE}
837-
# TODO: Change to get_running_loop() when dropping support for py3.6
838-
asyncio.get_event_loop().call_exception_handler(context)
836+
asyncio.get_running_loop().call_exception_handler(context)
839837
except RuntimeError:
840838
...
841839
break

redis/asyncio/connection.py

Lines changed: 8 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
import os
88
import socket
99
import ssl
10-
import sys
1110
import threading
1211
import weakref
1312
from itertools import chain
@@ -864,12 +863,10 @@ async def _ping_failed(self, error):
864863

865864
async def check_health(self):
866865
"""Check the health of the connection with a PING/PONG"""
867-
if sys.version_info[0:2] == (3, 6):
868-
func = asyncio.get_event_loop
869-
else:
870-
func = asyncio.get_running_loop
871-
872-
if self.health_check_interval and func().time() > self.next_health_check:
866+
if (
867+
self.health_check_interval
868+
and asyncio.get_running_loop().time() > self.next_health_check
869+
):
873870
await self.retry.call_with_retry(self._send_ping, self._ping_failed)
874871

875872
async def _send_packed_command(self, command: Iterable[bytes]) -> None:
@@ -957,11 +954,8 @@ async def read_response(self, disable_decoding: bool = False):
957954
raise
958955

959956
if self.health_check_interval:
960-
if sys.version_info[0:2] == (3, 6):
961-
func = asyncio.get_event_loop
962-
else:
963-
func = asyncio.get_running_loop
964-
self.next_health_check = func().time() + self.health_check_interval
957+
next_time = asyncio.get_running_loop().time() + self.health_check_interval
958+
self.next_health_check = next_time
965959

966960
if isinstance(response, ResponseError):
967961
raise response from None
@@ -992,11 +986,8 @@ async def read_response_without_lock(self, disable_decoding: bool = False):
992986
raise
993987

994988
if self.health_check_interval:
995-
if sys.version_info[0:2] == (3, 6):
996-
func = asyncio.get_event_loop
997-
else:
998-
func = asyncio.get_running_loop
999-
self.next_health_check = func().time() + self.health_check_interval
989+
next_time = asyncio.get_running_loop().time() + self.health_check_interval
990+
self.next_health_check = next_time
1000991

1001992
if isinstance(response, ResponseError):
1002993
raise response from None

redis/asyncio/lock.py

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import asyncio
2-
import sys
32
import threading
43
import uuid
54
from types import SimpleNamespace
@@ -186,11 +185,6 @@ async def acquire(
186185
object with the default encoding. If a token isn't specified, a UUID
187186
will be generated.
188187
"""
189-
if sys.version_info[0:2] != (3, 6):
190-
loop = asyncio.get_running_loop()
191-
else:
192-
loop = asyncio.get_event_loop()
193-
194188
sleep = self.sleep
195189
if token is None:
196190
token = uuid.uuid1().hex.encode()
@@ -203,14 +197,14 @@ async def acquire(
203197
blocking_timeout = self.blocking_timeout
204198
stop_trying_at = None
205199
if blocking_timeout is not None:
206-
stop_trying_at = loop.time() + blocking_timeout
200+
stop_trying_at = asyncio.get_event_loop().time() + blocking_timeout
207201
while True:
208202
if await self.do_acquire(token):
209203
self.local.token = token
210204
return True
211205
if not blocking:
212206
return False
213-
next_try_at = loop.time() + sleep
207+
next_try_at = asyncio.get_event_loop().time() + sleep
214208
if stop_trying_at is not None and next_try_at > stop_trying_at:
215209
return False
216210
await asyncio.sleep(sleep)

redis/client.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1497,9 +1497,15 @@ def parse_response(self, block=True, timeout=0):
14971497

14981498
self.check_health()
14991499

1500-
if not block and not self._execute(conn, conn.can_read, timeout=timeout):
1501-
return None
1502-
response = self._execute(conn, conn.read_response)
1500+
def try_read():
1501+
if not block:
1502+
if not conn.can_read(timeout=timeout):
1503+
return None
1504+
else:
1505+
conn.connect()
1506+
return conn.read_response()
1507+
1508+
response = self._execute(conn, try_read)
15031509

15041510
if self.is_health_check_response(response):
15051511
# ignore the health check message as user might not expect it

setup.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
},
3131
author="Redis Inc.",
3232
author_email="[email protected]",
33-
python_requires=">=3.6",
33+
python_requires=">=3.7",
3434
install_requires=[
3535
"deprecated>=1.2.3",
3636
"packaging>=20.4",
@@ -47,7 +47,6 @@
4747
"Programming Language :: Python",
4848
"Programming Language :: Python :: 3",
4949
"Programming Language :: Python :: 3 :: Only",
50-
"Programming Language :: Python :: 3.6",
5150
"Programming Language :: Python :: 3.7",
5251
"Programming Language :: Python :: 3.8",
5352
"Programming Language :: Python :: 3.9",

tests/conftest.py

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -130,15 +130,25 @@ def _get_info(redis_url):
130130

131131

132132
def pytest_sessionstart(session):
133+
# during test discovery, e.g. with VS Code, we may not
134+
# have a server running.
133135
redis_url = session.config.getoption("--redis-url")
134-
info = _get_info(redis_url)
135-
version = info["redis_version"]
136-
arch_bits = info["arch_bits"]
137-
cluster_enabled = info["cluster_enabled"]
136+
try:
137+
info = _get_info(redis_url)
138+
version = info["redis_version"]
139+
arch_bits = info["arch_bits"]
140+
cluster_enabled = info["cluster_enabled"]
141+
enterprise = info["enterprise"]
142+
except redis.ConnectionError:
143+
# provide optimistic defaults
144+
version = "10.0.0"
145+
arch_bits = 64
146+
cluster_enabled = False
147+
enterprise = False
138148
REDIS_INFO["version"] = version
139149
REDIS_INFO["arch_bits"] = arch_bits
140150
REDIS_INFO["cluster_enabled"] = cluster_enabled
141-
REDIS_INFO["enterprise"] = info["enterprise"]
151+
REDIS_INFO["enterprise"] = enterprise
142152
# store REDIS_INFO in config so that it is available from "condition strings"
143153
session.config.REDIS_INFO = REDIS_INFO
144154

tests/test_asyncio/compat.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,15 @@
1+
import asyncio
2+
import sys
13
from unittest import mock
24

35
try:
46
mock.AsyncMock
57
except AttributeError:
68
import mock
9+
10+
11+
def create_task(coroutine):
12+
if sys.version_info[:2] >= (3, 7):
13+
return asyncio.create_task(coroutine)
14+
else:
15+
return asyncio.ensure_future(coroutine)

tests/test_asyncio/conftest.py

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
import functools
21
import random
3-
import sys
2+
from contextlib import asynccontextmanager as _asynccontextmanager
43
from typing import Union
54
from urllib.parse import urlparse
65

76
import pytest
7+
import pytest_asyncio
88
from packaging.version import Version
99

1010
import redis.asyncio as redis
@@ -268,17 +268,5 @@ async def __aexit__(self, exc_type, exc_inst, tb):
268268
raise RuntimeError("More pickles")
269269

270270

271-
if sys.version_info[0:2] == (3, 6):
272-
273-
def asynccontextmanager(func):
274-
@functools.wraps(func)
275-
def wrapper(*args, **kwargs):
276-
return AsyncContextManager(func(*args, **kwargs))
277-
278-
return wrapper
279-
280-
else:
281-
from contextlib import asynccontextmanager as _asynccontextmanager
282-
283-
def asynccontextmanager(func):
284-
return _asynccontextmanager(func)
271+
def asynccontextmanager(func):
272+
return _asynccontextmanager(func)

tests/test_asyncio/test_bloom.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,6 @@
66
from redis.exceptions import ModuleError, RedisError
77
from redis.utils import HIREDIS_AVAILABLE
88

9-
if sys.version_info[0:2] == (3, 6):
10-
pytestmark = pytest.mark.asyncio
11-
129

1310
def intlist(obj):
1411
return [int(v) for v in obj]

tests/test_asyncio/test_cluster.py

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,12 @@
22
import binascii
33
import datetime
44
import os
5-
import sys
65
import warnings
76
from typing import Any, Awaitable, Callable, Dict, List, Optional, Type, Union
87
from urllib.parse import urlparse
98

109
import pytest
11-
12-
from .compat import mock
13-
14-
if sys.version_info[0:2] == (3, 6):
15-
import pytest as pytest_asyncio
16-
17-
pytestmark = pytest.mark.asyncio
18-
else:
19-
import pytest_asyncio
20-
10+
import pytest_asyncio
2111
from _pytest.fixtures import FixtureRequest
2212

2313
from redis.asyncio.cluster import ClusterNode, NodesManager, RedisCluster
@@ -44,6 +34,8 @@
4434
skip_unless_arch_bits,
4535
)
4636

37+
from .compat import mock
38+
4739
pytestmark = pytest.mark.onlycluster
4840

4941

tests/test_asyncio/test_commands.py

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,11 @@
44
import binascii
55
import datetime
66
import re
7-
import sys
87
import time
98
from string import ascii_letters
109

1110
import pytest
12-
13-
if sys.version_info[0:2] == (3, 6):
14-
import pytest as pytest_asyncio
15-
16-
pytestmark = pytest.mark.asyncio
17-
else:
18-
import pytest_asyncio
11+
import pytest_asyncio
1912

2013
import redis
2114
from redis import exceptions

tests/test_asyncio/test_connection.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import asyncio
22
import socket
3-
import sys
43
import types
54
from unittest.mock import patch
65

@@ -19,9 +18,6 @@
1918

2019
from .compat import mock
2120

22-
if sys.version_info[0:2] == (3, 6):
23-
pytestmark = pytest.mark.asyncio
24-
2521

2622
@pytest.mark.onlynoncluster
2723
@pytest.mark.skipif(HIREDIS_AVAILABLE, reason="PythonParser only")

tests/test_asyncio/test_connection_pool.py

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,9 @@
11
import asyncio
22
import os
33
import re
4-
import sys
54

65
import pytest
7-
8-
if sys.version_info[0:2] == (3, 6):
9-
import pytest as pytest_asyncio
10-
11-
pytestmark = pytest.mark.asyncio
12-
else:
13-
import pytest_asyncio
6+
import pytest_asyncio
147

158
import redis.asyncio as redis
169
from redis.asyncio.connection import Connection, to_bool

tests/test_asyncio/test_encoding.py

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,5 @@
1-
import sys
2-
31
import pytest
4-
5-
if sys.version_info[0:2] == (3, 6):
6-
import pytest as pytest_asyncio
7-
8-
pytestmark = pytest.mark.asyncio
9-
else:
10-
import pytest_asyncio
2+
import pytest_asyncio
113

124
import redis.asyncio as redis
135
from redis.exceptions import DataError

tests/test_asyncio/test_lock.py

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,7 @@
11
import asyncio
2-
import sys
32

43
import pytest
5-
6-
if sys.version_info[0:2] == (3, 6):
7-
import pytest as pytest_asyncio
8-
9-
pytestmark = pytest.mark.asyncio
10-
else:
11-
import pytest_asyncio
4+
import pytest_asyncio
125

136
from redis.asyncio.lock import Lock
147
from redis.exceptions import LockError, LockNotOwnedError

0 commit comments

Comments
 (0)