Skip to content

Commit 6dc07d6

Browse files
General project maintenance. (#114)
* update linting tools, sync lint configs with starlette, improve typings * disable 3.13-dev as asyncpg cannot be build yet * remove .python-version * Update scripts/lint Co-authored-by: Zanie Blue <contact@zanie.dev> * update ruff --------- Co-authored-by: Zanie Blue <contact@zanie.dev>
1 parent eeb09de commit 6dc07d6

File tree

12 files changed

+53
-58
lines changed

12 files changed

+53
-58
lines changed

.github/workflows/test-suite.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ jobs:
1414

1515
strategy:
1616
matrix:
17-
python-version: ["3.8", "3.9", "3.10", "3.11-dev"]
17+
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]
1818

1919
services:
2020
zookeeper:

broadcaster/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
from ._base import Broadcast, Event
21
from ._backends.base import BroadcastBackend
2+
from ._base import Broadcast, Event
33

44
__version__ = "0.2.0"
55
__all__ = ["Broadcast", "Event", "BroadcastBackend"]

broadcaster/_backends/kafka.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from __future__ import annotations
2+
13
import typing
24
from urllib.parse import urlparse
35

@@ -10,7 +12,7 @@
1012
class KafkaBackend(BroadcastBackend):
1113
def __init__(self, url: str):
1214
self._servers = [urlparse(url).netloc]
13-
self._consumer_channels: typing.Set = set()
15+
self._consumer_channels: set[str] = set()
1416

1517
async def connect(self) -> None:
1618
self._producer = AIOKafkaProducer(bootstrap_servers=self._servers)

broadcaster/_backends/memory.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from __future__ import annotations
2+
13
import asyncio
24
import typing
35

@@ -7,10 +9,10 @@
79

810
class MemoryBackend(BroadcastBackend):
911
def __init__(self, url: str):
10-
self._subscribed: typing.Set = set()
12+
self._subscribed: set[str] = set()
1113

1214
async def connect(self) -> None:
13-
self._published: asyncio.Queue = asyncio.Queue()
15+
self._published: asyncio.Queue[Event] = asyncio.Queue()
1416

1517
async def disconnect(self) -> None:
1618
pass

broadcaster/_backends/postgres.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ def __init__(self, url: str):
1313

1414
async def connect(self) -> None:
1515
self._conn = await asyncpg.connect(self._url)
16-
self._listen_queue: asyncio.Queue = asyncio.Queue()
16+
self._listen_queue: asyncio.Queue[Event] = asyncio.Queue()
1717

1818
async def disconnect(self) -> None:
1919
await self._conn.close()

broadcaster/_base.py

+12-24
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,8 @@
1+
from __future__ import annotations
2+
13
import asyncio
24
from contextlib import asynccontextmanager
3-
from typing import (
4-
TYPE_CHECKING,
5-
Any,
6-
AsyncGenerator,
7-
AsyncIterator,
8-
Dict,
9-
Optional,
10-
cast,
11-
)
5+
from typing import TYPE_CHECKING, Any, AsyncGenerator, AsyncIterator, cast
126
from urllib.parse import urlparse
137

148
if TYPE_CHECKING: # pragma: no cover
@@ -21,11 +15,7 @@ def __init__(self, channel: str, message: str) -> None:
2115
self.message = message
2216

2317
def __eq__(self, other: object) -> bool:
24-
return (
25-
isinstance(other, Event)
26-
and self.channel == other.channel
27-
and self.message == other.message
28-
)
18+
return isinstance(other, Event) and self.channel == other.channel and self.message == other.message
2919

3020
def __repr__(self) -> str:
3121
return f"Event(channel={self.channel!r}, message={self.message!r})"
@@ -36,14 +26,12 @@ class Unsubscribed(Exception):
3626

3727

3828
class Broadcast:
39-
def __init__(
40-
self, url: Optional[str] = None, *, backend: Optional["BroadcastBackend"] = None
41-
) -> None:
29+
def __init__(self, url: str | None = None, *, backend: BroadcastBackend | None = None) -> None:
4230
assert url or backend, "Either `url` or `backend` must be provided."
4331
self._backend = backend or self._create_backend(cast(str, url))
44-
self._subscribers: Dict[str, Any] = {}
32+
self._subscribers: dict[str, set[asyncio.Queue[Event | None]]] = {}
4533

46-
def _create_backend(self, url: str) -> "BroadcastBackend":
34+
def _create_backend(self, url: str) -> BroadcastBackend:
4735
parsed_url = urlparse(url)
4836
if parsed_url.scheme in ("redis", "rediss"):
4937
from broadcaster._backends.redis import RedisBackend
@@ -66,7 +54,7 @@ def _create_backend(self, url: str) -> "BroadcastBackend":
6654
return MemoryBackend(url)
6755
raise ValueError(f"Unsupported backend: {parsed_url.scheme}")
6856

69-
async def __aenter__(self) -> "Broadcast":
57+
async def __aenter__(self) -> Broadcast:
7058
await self.connect()
7159
return self
7260

@@ -94,8 +82,8 @@ async def publish(self, channel: str, message: Any) -> None:
9482
await self._backend.publish(channel, message)
9583

9684
@asynccontextmanager
97-
async def subscribe(self, channel: str) -> AsyncIterator["Subscriber"]:
98-
queue: asyncio.Queue = asyncio.Queue()
85+
async def subscribe(self, channel: str) -> AsyncIterator[Subscriber]:
86+
queue: asyncio.Queue[Event | None] = asyncio.Queue()
9987

10088
try:
10189
if not self._subscribers.get(channel):
@@ -114,10 +102,10 @@ async def subscribe(self, channel: str) -> AsyncIterator["Subscriber"]:
114102

115103

116104
class Subscriber:
117-
def __init__(self, queue: asyncio.Queue) -> None:
105+
def __init__(self, queue: asyncio.Queue[Event | None]) -> None:
118106
self._queue = queue
119107

120-
async def __aiter__(self) -> Optional[AsyncGenerator]:
108+
async def __aiter__(self) -> AsyncGenerator[Event | None, None] | None:
121109
try:
122110
while True:
123111
yield await self.get()

pyproject.toml

+7-3
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ classifiers = [
2424
"Programming Language :: Python :: 3.9",
2525
"Programming Language :: Python :: 3.10",
2626
"Programming Language :: Python :: 3.11",
27+
"Programming Language :: Python :: 3.12",
2728
]
2829
dependencies = [
2930
"anyio>=3.4.0,<5",
@@ -48,14 +49,17 @@ include = [
4849
]
4950

5051
[tool.ruff]
51-
ignore = []
5252
line-length = 120
53-
select = ["E","F","W"]
5453

55-
[tool.ruff.isort]
54+
[tool.ruff.lint]
55+
select = ["E", "F", "I", "FA", "UP"]
56+
57+
[tool.ruff.lint.isort]
5658
combine-as-imports = true
5759

5860
[tool.mypy]
61+
strict = true
62+
python_version = "3.8"
5963
disallow_untyped_defs = true
6064
ignore_missing_imports = true
6165

requirements.txt

+9-10
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,17 @@
11
-e .[redis,postgres,kafka]
22

33
# Documentation
4-
mkdocs==1.3.1
4+
mkdocs==1.5.3
5+
mkdocs-material==9.5.12
56
mkautodoc==0.2.0
6-
mkdocs-material==8.4.0
77

88
# Packaging
9-
build==0.10.0
10-
twine==4.0.1
9+
build==1.1.1
10+
twine==5.0.0
1111

1212
# Tests & Linting
13-
ruff==0.0.277
14-
black==22.6.0
15-
coverage==6.4.4
16-
mypy==0.971
17-
pytest==7.1.2
18-
pytest-asyncio==0.19.0
13+
ruff==0.3.5
14+
coverage==7.4.3
15+
mypy==1.8.0
16+
pytest==8.0.2
17+
pytest-asyncio==0.23.6

scripts/check

+2-2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,6 @@ export SOURCE_FILES="broadcaster tests"
88

99
set -x
1010

11-
${PREFIX}black --check --diff --target-version=py37 $SOURCE_FILES
12-
${PREFIX}ruff check $SOURCE_FILES
11+
${PREFIX}ruff format --check --diff $SOURCE_FILES
1312
${PREFIX}mypy $SOURCE_FILES
13+
${PREFIX}ruff check $SOURCE_FILES

scripts/lint

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
#!/bin/sh -e
22

33
export PREFIX=""
4-
if [ -d 'venv' ]; then
4+
if [ -d 'venv' ] ; then
55
export PREFIX="venv/bin/"
66
fi
77
export SOURCE_FILES="broadcaster tests"
88

99
set -x
1010

11-
${PREFIX}ruff --fix $SOURCE_FILES
12-
${PREFIX}black --target-version=py37 $SOURCE_FILES
11+
${PREFIX}ruff format $SOURCE_FILES
12+
${PREFIX}ruff check --fix $SOURCE_FILES

tests/test_broadcast.py

+9-10
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,19 @@
1-
import pytest
2-
import typing
1+
from __future__ import annotations
2+
33
import asyncio
4+
import typing
5+
6+
import pytest
47

58
from broadcaster import Broadcast, BroadcastBackend, Event
69

710

811
class CustomBackend(BroadcastBackend):
912
def __init__(self, url: str):
10-
self._subscribed: typing.Set = set()
13+
self._subscribed: set[str] = set()
1114

1215
async def connect(self) -> None:
13-
self._published: asyncio.Queue = asyncio.Queue()
16+
self._published: asyncio.Queue[Event] = asyncio.Queue()
1417

1518
async def disconnect(self) -> None:
1619
pass
@@ -54,9 +57,7 @@ async def test_redis():
5457

5558
@pytest.mark.asyncio
5659
async def test_postgres():
57-
async with Broadcast(
58-
"postgres://postgres:postgres@localhost:5432/broadcaster"
59-
) as broadcast:
60+
async with Broadcast("postgres://postgres:postgres@localhost:5432/broadcaster") as broadcast:
6061
async with broadcast.subscribe("chatroom") as subscriber:
6162
await broadcast.publish("chatroom", "hello")
6263
event = await subscriber.get()
@@ -95,7 +96,5 @@ async def test_unknown_backend():
9596

9697
@pytest.mark.asyncio
9798
async def test_needs_url_or_backend():
98-
with pytest.raises(
99-
AssertionError, match="Either `url` or `backend` must be provided."
100-
):
99+
with pytest.raises(AssertionError, match="Either `url` or `backend` must be provided."):
101100
Broadcast()

tests/test_unsubscribe.py

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import pytest
2+
23
from broadcaster import Broadcast
34

45

0 commit comments

Comments
 (0)