Skip to content

Commit 09e71cb

Browse files
Merge branch 'main' into feat/type_hint
2 parents 1e3e55d + 8498605 commit 09e71cb

File tree

9 files changed

+57
-27
lines changed

9 files changed

+57
-27
lines changed

.github/workflows/tests.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ jobs:
1515
fail-fast: false
1616
matrix:
1717
python-version:
18-
- "3.8"
1918
- "3.9"
2019
- "3.10"
2120
- "3.11"

CHANGELOG.txt

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,23 @@
1+
4.3.0 (2025-07-22)
2+
------------------
3+
4+
* Added support for Python 3.13.
5+
6+
* Dropped support for EOL Python 3.8.
7+
8+
* Updated Channels dependency to at least v4.2.2.
9+
10+
* Updated asgiref dependency to at least v3.9.1.
11+
12+
* Fixed compatibility with latest versions of pytest-asyncio.
13+
14+
* Renamed internal methods in core channel layer for compatibility with
15+
Channels v4.2.1+
16+
17+
* Dropped testing against legacy Channels v3 branch.
18+
19+
* Updated testing against latest redis-py versions.
20+
121
4.2.1 (2024-11-15)
222
------------------
323

README.rst

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,11 @@ There are two available implementations:
1919

2020
Both layers support a single-server and sharded configurations.
2121

22-
`channels_redis` is tested against Python 3.8 to 3.13, `redis-py` versions 4.6,
23-
5.0, and the development branch, and Channels versions 3, 4 and the development
24-
branch there.
22+
`channels_redis` is tested against Python 3.9 to 3.13. We test with the latest
23+
`redis-py` version, as well as the older 5.x branch and main development branch,
24+
using the latest stable Python and Channels versions. We test the Channels
25+
development branch, using the latest stable Python and the latest `redis-py`
26+
release.
2527

2628
Installation
2729
------------

channels_redis/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = "4.2.1"
1+
__version__ = "4.3.0"

channels_redis/core.py

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ async def send(self, channel: str, message: typing.Any) -> None:
177177
"""
178178
# Typecheck
179179
assert isinstance(message, dict), "message is not a dict"
180-
assert self.valid_channel_name(channel), "Channel name not valid"
180+
assert self.require_valid_channel_name(channel), "Channel name not valid"
181181
# Make sure the message does not contain reserved keys
182182
assert "__asgi_channel__" not in message
183183
# If it's a process-local channel, strip off local part and stick full name in message
@@ -266,7 +266,7 @@ async def receive(self, channel: str) -> typing.Any:
266266
"""
267267
# Make sure the channel name is valid then get the non-local part
268268
# and thus its index
269-
assert self.valid_channel_name(channel)
269+
assert self.require_valid_channel_name(channel)
270270
if "!" in channel:
271271
real_channel = self.non_local_name(channel)
272272
assert real_channel.endswith(
@@ -389,7 +389,9 @@ async def receive_single(
389389
Receives a single message off of the channel and returns it.
390390
"""
391391
# Check channel name
392-
assert self.valid_channel_name(channel, receive=True), "Channel name invalid"
392+
assert self.require_valid_channel_name(
393+
channel, receive=True
394+
), "Channel name invalid"
393395
# Work out the connection to use
394396
if "!" in channel:
395397
assert channel.endswith("!")
@@ -494,8 +496,8 @@ async def group_add(self, group: str, channel: str) -> None:
494496
Adds the channel name to a group.
495497
"""
496498
# Check the inputs
497-
assert self.valid_group_name(group), "Group name not valid"
498-
assert self.valid_channel_name(channel), "Channel name not valid"
499+
assert self.require_valid_group_name(group), "Group name not valid"
500+
assert self.require_valid_channel_name(channel), "Channel name not valid"
499501
# Get a connection to the right shard
500502
group_key = self._group_key(group)
501503
connection = self.connection(self.consistent_hash(group))
@@ -510,8 +512,8 @@ async def group_discard(self, group: str, channel: str) -> None:
510512
Removes the channel from the named group if it is in the group;
511513
does nothing otherwise (does not error)
512514
"""
513-
assert self.valid_group_name(group), "Group name not valid"
514-
assert self.valid_channel_name(channel), "Channel name not valid"
515+
assert self.require_valid_group_name(group), "Group name not valid"
516+
assert self.require_valid_channel_name(channel), "Channel name not valid"
515517
key = self._group_key(group)
516518
connection = self.connection(self.consistent_hash(group))
517519
await connection.zrem(key, channel)
@@ -520,7 +522,7 @@ async def group_send(self, group: str, message: typing.Any) -> None:
520522
"""
521523
Sends a message to the entire group.
522524
"""
523-
assert self.valid_group_name(group), "Group name not valid"
525+
assert self.require_valid_group_name(group), "Group name not valid"
524526
# Retrieve list of all channel names
525527
key = self._group_key(group)
526528
connection = self.connection(self.consistent_hash(group))

setup.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,12 @@
2929
zip_safe=False,
3030
packages=find_packages(exclude=["tests"]),
3131
include_package_data=True,
32-
python_requires=">=3.8",
32+
python_requires=">=3.9",
3333
install_requires=[
3434
"redis>=4.6",
3535
"msgpack~=1.0",
36-
"asgiref>=3.2.10,<4",
37-
"channels",
36+
"asgiref>=3.9.1,<4",
37+
"channels>=4.2.2",
3838
],
3939
extras_require={"cryptography": crypto_requires, "tests": test_requires},
4040
)

tests/test_pubsub.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,8 @@ async def test_send_receive(channel_layer):
6161
assert message["text"] == "Ahoy-hoy!"
6262

6363

64-
def test_send_receive_sync(channel_layer, event_loop):
64+
def test_send_receive_sync(channel_layer):
65+
event_loop = asyncio.new_event_loop()
6566
_await = event_loop.run_until_complete
6667
channel = _await(channel_layer.new_channel())
6768
async_to_sync(channel_layer.send, force_new_loop=True)(
@@ -70,6 +71,7 @@ def test_send_receive_sync(channel_layer, event_loop):
7071
message = _await(channel_layer.receive(channel))
7172
assert message["type"] == "test.message"
7273
assert message["text"] == "Ahoy-hoy!"
74+
event_loop.close()
7375

7476

7577
@pytest.mark.asyncio
@@ -86,7 +88,8 @@ async def test_multi_send_receive(channel_layer):
8688
assert (await channel_layer.receive(channel))["type"] == "message.3"
8789

8890

89-
def test_multi_send_receive_sync(channel_layer, event_loop):
91+
def test_multi_send_receive_sync(channel_layer):
92+
event_loop = asyncio.new_event_loop()
9093
_await = event_loop.run_until_complete
9194
channel = _await(channel_layer.new_channel())
9295
send = async_to_sync(channel_layer.send)
@@ -96,6 +99,7 @@ def test_multi_send_receive_sync(channel_layer, event_loop):
9699
assert _await(channel_layer.receive(channel))["type"] == "message.1"
97100
assert _await(channel_layer.receive(channel))["type"] == "message.2"
98101
assert _await(channel_layer.receive(channel))["type"] == "message.3"
102+
event_loop.close()
99103

100104

101105
@pytest.mark.asyncio

tests/test_pubsub_sentinel.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,8 @@ async def test_send_receive(channel_layer):
4242
assert message["text"] == "Ahoy-hoy!"
4343

4444

45-
def test_send_receive_sync(channel_layer, event_loop):
45+
def test_send_receive_sync(channel_layer):
46+
event_loop = asyncio.new_event_loop()
4647
_await = event_loop.run_until_complete
4748
channel = _await(channel_layer.new_channel())
4849
async_to_sync(channel_layer.send, force_new_loop=True)(
@@ -51,6 +52,7 @@ def test_send_receive_sync(channel_layer, event_loop):
5152
message = _await(channel_layer.receive(channel))
5253
assert message["type"] == "test.message"
5354
assert message["text"] == "Ahoy-hoy!"
55+
event_loop.close()
5456

5557

5658
@pytest.mark.asyncio
@@ -67,7 +69,8 @@ async def test_multi_send_receive(channel_layer):
6769
assert (await channel_layer.receive(channel))["type"] == "message.3"
6870

6971

70-
def test_multi_send_receive_sync(channel_layer, event_loop):
72+
def test_multi_send_receive_sync(channel_layer):
73+
event_loop = asyncio.new_event_loop()
7174
_await = event_loop.run_until_complete
7275
channel = _await(channel_layer.new_channel())
7376
send = async_to_sync(channel_layer.send)
@@ -77,6 +80,7 @@ def test_multi_send_receive_sync(channel_layer, event_loop):
7780
assert _await(channel_layer.receive(channel))["type"] == "message.1"
7881
assert _await(channel_layer.receive(channel))["type"] == "message.2"
7982
assert _await(channel_layer.receive(channel))["type"] == "message.3"
83+
event_loop.close()
8084

8185

8286
@pytest.mark.asyncio

tox.ini

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
[tox]
22
envlist =
3-
py{38,39,310,311,312,313}-ch{30,40,main}-redis50
4-
py311-chmain-redis{45,46,50,main}
3+
py{39,310,311,312,313}-ch4-redis6
4+
py313-chmain-redis6
5+
py313-ch4-redis{5,main}
56
qa
67

78
[testenv]
@@ -11,12 +12,10 @@ commands =
1112
pytest -v {posargs}
1213
mypy --config-file=tox.ini channels_redis
1314
deps =
14-
mypy>=1.9.0
15-
ch30: channels>=3.0,<3.1
16-
ch40: channels>=4.0,<4.1
15+
ch4: channels>=4.0,<5
1716
chmain: https://github.com/django/channels/archive/main.tar.gz
18-
redis46: redis>=4.6,<4.7
19-
redis50: redis>=5.0,<5.1
17+
redis5: redis>=5.0,<6
18+
redis6: redis>=6.0,<7
2019
redismain: https://github.com/redis/redis-py/archive/master.tar.gz
2120

2221
[testenv:qa]

0 commit comments

Comments
 (0)