Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .github/workflows/python-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ jobs:
fail-fast: false
matrix:
python-version:
- '3.9'
- '3.10'
- '3.11'
- '3.12'
Expand Down
8 changes: 6 additions & 2 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ repos:
rev: 97be693bf18bc2f050667dd282d243e2824b81e2 # frozen: 1.0.6
- hooks:
- args:
- --py39-plus
- --py310-plus
id: pyupgrade
repo: https://github.com/asottile/pyupgrade
rev: f90119b1b8bd9e46949d9592972b2c4c27d62a97 # frozen: v3.21.0
Expand Down Expand Up @@ -85,9 +85,13 @@ repos:
- python
repo: local
- repo: https://github.com/andreoliwa/nitpick
rev: "a5532d554f2c9035bb05ec14ee1bf31469e0a563" # frozen: v0.37.0
rev: "a1373f03f5a9394c75cc03e5da1b0cb6ff615dbb" # frozen: v0.38.0
hooks:
- id: nitpick
- repo: https://github.com/PyCQA/autoflake
rev: '0544741e2b4a22b472d9d93e37d4ea9153820bb1' # frozen: v2.3.1
hooks:
- id: autoflake
minimum_pre_commit_version: 2.18.0
default_install_hook_types:
- pre-commit
Expand Down
71 changes: 26 additions & 45 deletions asyncirc/protocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,10 @@
import time
from asyncio import Protocol, Task
from collections import defaultdict
from collections.abc import Coroutine, Sequence
from collections.abc import Callable, Coroutine, Sequence
from enum import IntEnum, auto, unique
from itertools import cycle
from typing import (
TYPE_CHECKING,
Callable,
Dict,
Final,
List,
Optional,
Tuple,
Union,
cast,
)
from typing import TYPE_CHECKING, Final, Optional, cast

from irclib.parser import Cap, CapList, Message

Expand Down Expand Up @@ -53,13 +43,13 @@ async def _internal_ping(conn: "IrcProtocol", message: "Message") -> None:
conn.send(f"PONG {message.parameters}")


def _handle_cap_list(conn: "IrcProtocol", caplist: "List[Cap]") -> None:
def _handle_cap_list(conn: "IrcProtocol", caplist: "list[Cap]") -> None:
if conn.logger:
conn.logger.info("Current Capabilities: %s", caplist)


def _handle_cap_del(
conn: "IrcProtocol", caplist: "List[Cap]", server: "ConnectedServer"
conn: "IrcProtocol", caplist: "list[Cap]", server: "ConnectedServer"
) -> None:
if conn.logger:
conn.logger.info("Capabilities removed: %s", caplist)
Expand All @@ -72,7 +62,7 @@ def _handle_cap_del(
def _handle_cap_new(
conn: "IrcProtocol",
message: "Message",
caplist: "List[Cap]",
caplist: "list[Cap]",
server: "ConnectedServer",
) -> None:
if conn.logger:
Expand All @@ -90,7 +80,7 @@ def _handle_cap_new(
async def _handle_cap_reply(
conn: "IrcProtocol",
message: "Message",
caplist: "List[Cap]",
caplist: "list[Cap]",
server: "ConnectedServer",
) -> None:
enabled = message.parameters[1] == "ACK"
Expand All @@ -109,7 +99,7 @@ async def _handle_cap_reply(
def _handle_cap_ls(
conn: "IrcProtocol",
message: "Message",
caplist: "List[Cap]",
caplist: "list[Cap]",
server: "ConnectedServer",
) -> None:
for cap in caplist:
Expand Down Expand Up @@ -170,7 +160,7 @@ async def _do_sasl(conn: "IrcProtocol", cap: Cap) -> None:
return

if cap.value is not None:
supported_mechs: Optional[list[str]] = cap.value.split(",")
supported_mechs: list[str] | None = cap.value.split(",")
else:
supported_mechs = None

Expand Down Expand Up @@ -244,11 +234,11 @@ def __init__(
self,
servers: Sequence["BaseServer"],
nick: str,
user: Optional[str] = None,
realname: Optional[str] = None,
certpath: Optional[str] = None,
sasl_auth: Optional[tuple[str, str]] = None,
sasl_mech: Optional[SASLMechanism] = None,
user: str | None = None,
realname: str | None = None,
certpath: str | None = None,
sasl_auth: tuple[str, str] | None = None,
sasl_mech: SASLMechanism | None = None,
logger: Optional["Logger"] = None,
loop: Optional["AbstractEventLoop"] = None,
*,
Expand Down Expand Up @@ -290,19 +280,14 @@ def __init__(
int,
tuple[
str,
Callable[
["IrcProtocol", "Message"], Coroutine[None, None, None]
],
Callable[[IrcProtocol, Message], Coroutine[None, None, None]],
],
] = {}
self.cap_handlers: dict[
str,
list[
Optional[
Callable[
["IrcProtocol", "Cap"], Coroutine[None, None, None]
]
]
None
| (Callable[[IrcProtocol, Cap], Coroutine[None, None, None]])
],
] = defaultdict(list)

Expand All @@ -316,9 +301,7 @@ def __init__(
self.register("005", _isupport_handler)
self.register_cap("sasl", _do_sasl)

self._pinger: Optional[Task[None]] = self.loop.create_task(
self.pinger()
)
self._pinger: Task[None] | None = self.loop.create_task(self.pinger())

def __del__(self) -> None:
"""Automatically close connection on garbage collection."""
Expand Down Expand Up @@ -417,9 +400,9 @@ def unregister(self, hook_id: int) -> None:
def register_cap(
self,
cap: str,
handler: Optional[
handler: None | (
Callable[["IrcProtocol", "Cap"], Coroutine[None, None, None]]
] = None,
) = None,
) -> None:
"""Register a CAP handler.

Expand All @@ -430,16 +413,16 @@ def register_cap(
self.cap_handlers[cap].append(handler)

async def wait_for(
self, *cmds: str, timeout: Optional[int] = None
) -> Optional[Message]:
self, *cmds: str, timeout: int | None = None
) -> Message | None:
"""Wait for a matching command from the server.

Wait for a specific command from the server, optionally returning after [timeout] seconds.
"""
if not cmds:
return None

fut: "asyncio.Future[Message]" = self.loop.create_future()
fut: asyncio.Future[Message] = self.loop.create_future()

async def _wait(_conn: "IrcProtocol", message: "Message") -> None:
if not fut.done():
Expand All @@ -457,14 +440,12 @@ async def _wait(_conn: "IrcProtocol", message: "Message") -> None:

return result

async def _send(self, text: Union[str, bytes]) -> None:
async def _send(self, text: str | bytes) -> None:
if not self.connected:
await self._connected_future

if isinstance(text, str):
text = text.encode()
elif isinstance(text, memoryview):
text = text.tobytes()

if self.logger:
self.logger.info(">> %s", text.decode())
Expand All @@ -475,15 +456,15 @@ async def _send(self, text: Union[str, bytes]) -> None:

self._transport.write(text + b"\r\n")

def send(self, text: Union[str, bytes]) -> None:
def send(self, text: str | bytes) -> None:
"""Send a raw line to the server."""
asyncio.run_coroutine_threadsafe(self._send(text), self.loop)

def send_command(self, msg: Message) -> None:
"""Send an irclib Message object to the server."""
return self.send(str(msg))

def quit(self, reason: Optional[str] = None) -> None:
def quit(self, reason: str | None = None) -> None:
"""Quit the IRC connection with an optional reason."""
if not self._quitting:
self._quitting = True
Expand All @@ -509,7 +490,7 @@ def connection_made(self, transport: "asyncio.BaseTransport") -> None:
self.send(f"NICK {self.nick}")
self.send(f"USER {self.user} 0 * :{self.realname}")

def connection_lost(self, exc: Optional[Exception]) -> None:
def connection_lost(self, exc: Exception | None) -> None:
"""Connection to the IRC server has been lost."""
self._transport = None
self._connected = False
Expand Down
45 changes: 23 additions & 22 deletions asyncirc/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
import asyncio
import ssl
import warnings
from typing import TYPE_CHECKING, Any, Callable, Dict, Optional, Tuple, TypeVar
from collections.abc import Callable
from typing import TYPE_CHECKING, Any, TypeVar

if TYPE_CHECKING:
from irclib.parser import Cap
Expand All @@ -25,10 +26,10 @@ class BaseServer:
def __init__(
self,
*,
password: Optional[str] = None,
password: str | None = None,
is_ssl: bool = False,
ssl_ctx: Optional[ssl.SSLContext] = None,
certpath: Optional[str] = None,
ssl_ctx: ssl.SSLContext | None = None,
certpath: str | None = None,
) -> None:
"""Set server connection options.

Expand All @@ -45,7 +46,7 @@ def __init__(
if certpath:
ssl_ctx.load_cert_chain(certpath)

self.ssl_ctx: Optional[ssl.SSLContext] = ssl_ctx
self.ssl_ctx: ssl.SSLContext | None = ssl_ctx
else:
self.ssl_ctx = None

Expand All @@ -60,8 +61,8 @@ async def connect(
self,
protocol_factory: Callable[[], _ProtoT],
*,
loop: Optional[asyncio.AbstractEventLoop] = None,
ssl: Optional[ssl.SSLContext] = None,
loop: asyncio.AbstractEventLoop | None = None,
ssl: ssl.SSLContext | None = None,
) -> tuple[asyncio.Transport, _ProtoT]:
"""Internal connect implementation.

Expand All @@ -82,7 +83,7 @@ async def do_connect(
self,
protocol_factory: Callable[[], _ProtoT],
*,
loop: Optional[asyncio.AbstractEventLoop] = None,
loop: asyncio.AbstractEventLoop | None = None,
) -> tuple[asyncio.Transport, _ProtoT]:
"""Wrapper for internal connect implementation.

Expand All @@ -109,9 +110,9 @@ def __init__(
host: str,
port: int,
is_ssl: bool = False,
password: Optional[str] = None,
ssl_ctx: Optional[ssl.SSLContext] = None,
certpath: Optional[str] = None,
password: str | None = None,
ssl_ctx: ssl.SSLContext | None = None,
certpath: str | None = None,
) -> None:
"""Create TCP server configuration.

Expand All @@ -134,8 +135,8 @@ async def connect(
self,
protocol_factory: Callable[[], _ProtoT],
*,
loop: Optional[asyncio.AbstractEventLoop] = None,
ssl: Optional[ssl.SSLContext] = None,
loop: asyncio.AbstractEventLoop | None = None,
ssl: ssl.SSLContext | None = None,
) -> tuple[asyncio.Transport, _ProtoT]:
"""TCP server connection implementation.

Expand Down Expand Up @@ -170,9 +171,9 @@ def __init__(
*,
path: str,
is_ssl: bool = False,
password: Optional[str] = None,
ssl_ctx: Optional[ssl.SSLContext] = None,
certpath: Optional[str] = None,
password: str | None = None,
ssl_ctx: ssl.SSLContext | None = None,
certpath: str | None = None,
) -> None:
"""Configure UNIX socket based server connection.

Expand All @@ -193,8 +194,8 @@ async def connect(
self,
protocol_factory: Callable[[], _ProtoT],
*,
loop: Optional[asyncio.AbstractEventLoop] = None,
ssl: Optional[ssl.SSLContext] = None,
loop: asyncio.AbstractEventLoop | None = None,
ssl: ssl.SSLContext | None = None,
) -> tuple[asyncio.Transport, _ProtoT]:
"""Connect to UNIX socket.

Expand Down Expand Up @@ -229,7 +230,7 @@ def __init__(
host: str,
port: int,
is_ssl: bool = False,
password: Optional[str] = None,
password: str | None = None,
) -> None:
"""Create basic server configuration.

Expand Down Expand Up @@ -268,7 +269,7 @@ def __init__(self, server: "BaseServer") -> None:
self.connection = server
self.is_ssl = server.is_ssl
self.password = server.password
self.isupport_tokens: dict[str, Optional[str]] = {}
self.caps: dict[str, tuple[Cap, Optional[bool]]] = {}
self.server_name: Optional[str] = None
self.isupport_tokens: dict[str, str | None] = {}
self.caps: dict[str, tuple[Cap, bool | None]] = {}
self.server_name: str | None = None
self.data: dict[str, Any] = {}
2 changes: 1 addition & 1 deletion asyncirc/util/backoff.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import asyncio
import random
from typing import Callable
from collections.abc import Callable

__all__ = ("AsyncDelayer",)

Expand Down
Loading