diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 4c627fed..f221e2c1 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -11,12 +11,6 @@ repos: - id: mixed-line-ending args: [--fix=lf] - - repo: https://github.com/pre-commit/pygrep-hooks - rev: v1.9.0 - hooks: - - id: python-check-blanket-noqa # Enforce noqa annotations (noqa: F401,W203) - - id: python-use-type-annotations # Enforce type annotations instead of type comments - - repo: local hooks: - id: black diff --git a/changes/154.internal.md b/changes/154.internal.md new file mode 100644 index 00000000..90b049ed --- /dev/null +++ b/changes/154.internal.md @@ -0,0 +1,11 @@ +Enforce various new ruff linter rules: + +- **PGH:** pygrep-hooks (replaces pre-commit version) +- **PL:** pylint (bunch of typing related linter rules) +- **UP:** pyupgrade (forces use of the newest possible standards, depending on target version) +- **RET:** flake8-return (various linter rules related to function returns) +- **Q:** flake8-quotes (always use double quotes) +- **ASYNC:** flake8-async (report blocking operations in async functions) +- **INT:** flake-gettext (gettext related linting rules) +- **PTH:** flake8-use-pathlib (always prefer pathlib alternatives to the os ones) +- **RUF:** ruff custom rules (various additional rules created by the ruff linter team) diff --git a/docs/conf.py b/docs/conf.py index 03244664..d34f8da5 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -23,7 +23,7 @@ # -- Basic project information ----------------------------------------------- -with open("../pyproject.toml", "rb") as f: +with Path("../pyproject.toml").open("rb") as f: pkg_meta: dict[str, str] = toml_parse(f)["tool"]["poetry"] project = str(pkg_meta["name"]) diff --git a/docs/extensions/attributetable.py b/docs/extensions/attributetable.py index 8ca1d035..7d4601da 100644 --- a/docs/extensions/attributetable.py +++ b/docs/extensions/attributetable.py @@ -93,7 +93,7 @@ class PyAttributeTable(SphinxDirective): required_arguments = 1 optional_arguments = 0 final_argument_whitespace = False - option_spec: OptionSpec = {} + option_spec: OptionSpec = {} # noqa: RUF012 # (from original impl) def parse_name(self, content: str) -> tuple[str, str]: match = _name_parser_regex.match(content) diff --git a/mcproto/connection.py b/mcproto/connection.py index 5a949704..e24f65bd 100644 --- a/mcproto/connection.py +++ b/mcproto/connection.py @@ -4,7 +4,7 @@ import errno import socket from abc import ABC, abstractmethod -from typing import Generic, Optional, TypeVar +from typing import Generic, TypeVar import asyncio_dgram from typing_extensions import ParamSpec, Self @@ -262,7 +262,7 @@ def make_client(cls, address: tuple[str, int], timeout: float) -> Self: sock.settimeout(timeout) return cls(sock, address) - def read(self, length: Optional[int] = None) -> bytearray: + def read(self, length: int | None = None) -> bytearray: """Receive data sent through the connection. :param length: @@ -311,7 +311,7 @@ async def make_client(cls, address: tuple[str, int], timeout: float) -> Self: stream = await asyncio.wait_for(conn, timeout=timeout) return cls(stream, timeout) - async def read(self, length: Optional[int] = None) -> bytearray: + async def read(self, length: int | None = None) -> bytearray: """Receive data sent through the connection. :param length: diff --git a/mcproto/packets/handshaking/handshake.py b/mcproto/packets/handshaking/handshake.py index fd64058a..d9db1021 100644 --- a/mcproto/packets/handshaking/handshake.py +++ b/mcproto/packets/handshaking/handshake.py @@ -1,7 +1,7 @@ from __future__ import annotations from enum import IntEnum -from typing import ClassVar, Union, final +from typing import ClassVar, final from typing_extensions import Self @@ -35,7 +35,7 @@ def __init__( protocol_version: int, server_address: str, server_port: int, - next_state: Union[NextState, int], + next_state: NextState | int, ): """ :param protocol_version: Protocol version number to be used. diff --git a/mcproto/packets/interactions.py b/mcproto/packets/interactions.py index d0ab6900..33013ab1 100644 --- a/mcproto/packets/interactions.py +++ b/mcproto/packets/interactions.py @@ -43,8 +43,7 @@ def _serialize_packet(packet: Packet, *, compressed: bool = False) -> Buffer: data_buf.write_varint(data_length) data_buf.write(packet_buf) return data_buf - else: - return packet_buf + return packet_buf def _deserialize_packet( diff --git a/mcproto/packets/login/login.py b/mcproto/packets/login/login.py index 9d8bdc06..7e317d1b 100644 --- a/mcproto/packets/login/login.py +++ b/mcproto/packets/login/login.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import ClassVar, Optional, final +from typing import ClassVar, final from typing_extensions import Self @@ -215,7 +215,7 @@ class LoginPluginResponse(ServerBoundPacket): PACKET_ID: ClassVar[int] = 0x02 GAME_STATE: ClassVar[GameState] = GameState.LOGIN - def __init__(self, message_id: int, data: Optional[bytes]): + def __init__(self, message_id: int, data: bytes | None): """ :param message_id: Message id, generated by the server, should be unique to the connection. :param data: Optional response data, present if client understood request. diff --git a/mcproto/packets/packet_map.py b/mcproto/packets/packet_map.py index ada5083a..f691220a 100644 --- a/mcproto/packets/packet_map.py +++ b/mcproto/packets/packet_map.py @@ -94,7 +94,7 @@ def generate_packet_map( ... -@lru_cache() +@lru_cache def generate_packet_map(direction: PacketDirection, state: GameState) -> Mapping[int, type[Packet]]: """Dynamically generated a packet map for given ``direction`` and ``state``. diff --git a/mcproto/protocol/base_io.py b/mcproto/protocol/base_io.py index 10ebc427..f89d1390 100644 --- a/mcproto/protocol/base_io.py +++ b/mcproto/protocol/base_io.py @@ -5,7 +5,7 @@ from collections.abc import Awaitable, Callable from enum import Enum from itertools import count -from typing import Literal, Optional, TypeVar, Union, overload +from typing import Literal, TypeVar, Union, overload from typing_extensions import TypeAlias @@ -105,7 +105,7 @@ async def write_value(self, fmt: StructFormat, value: object, /) -> None: """Write a given ``value`` as given struct format (``fmt``) in big-endian mode.""" await self.write(struct.pack(">" + fmt.value, value)) - async def _write_varuint(self, value: int, /, *, max_bits: Optional[int] = None) -> None: + async def _write_varuint(self, value: int, /, *, max_bits: int | None = None) -> None: """Write an arbitrarily big unsigned integer in a variable length format. This is a standard way of transmitting ints, and it allows smaller numbers to take less bytes. @@ -179,7 +179,7 @@ async def write_utf(self, value: str, /) -> None: await self.write_varint(len(data)) await self.write(data) - async def write_optional(self, value: Optional[T], /, writer: Callable[[T], Awaitable[R]]) -> Optional[R]: + async def write_optional(self, value: T | None, /, writer: Callable[[T], Awaitable[R]]) -> R | None: """Writes a bool showing if a ``value`` is present, if so, also writes this value with ``writer`` function. * When ``value`` is ``None``, a bool of ``False`` will be written, and ``None`` is returned. @@ -223,7 +223,7 @@ def write_value(self, fmt: StructFormat, value: object, /) -> None: """Write a given ``value`` as given struct format (``fmt``) in big-endian mode.""" self.write(struct.pack(">" + fmt.value, value)) - def _write_varuint(self, value: int, /, *, max_bits: Optional[int] = None) -> None: + def _write_varuint(self, value: int, /, *, max_bits: int | None = None) -> None: """Write an arbitrarily big unsigned integer in a variable length format. This is a standard way of transmitting ints, and it allows smaller numbers to take less bytes. @@ -297,7 +297,7 @@ def write_utf(self, value: str, /) -> None: self.write_varint(len(data)) self.write(data) - def write_optional(self, value: Optional[T], /, writer: Callable[[T], R]) -> Optional[R]: + def write_optional(self, value: T | None, /, writer: Callable[[T], R]) -> R | None: """Writes a bool showing if a ``value`` is present, if so, also writes this value with ``writer`` function. * When ``value`` is ``None``, a bool of ``False`` will be written, and ``None`` is returned. @@ -351,7 +351,7 @@ async def read_value(self, fmt: StructFormat, /) -> object: unpacked = struct.unpack(">" + fmt.value, data) return unpacked[0] - async def _read_varuint(self, *, max_bits: Optional[int] = None) -> int: + async def _read_varuint(self, *, max_bits: int | None = None) -> int: """Read an arbitrarily big unsigned integer in a variable length format. This is a standard way of transmitting ints, and it allows smaller numbers to take less bytes. @@ -391,8 +391,7 @@ async def read_varint(self) -> int: For more information about variable length format check :meth:`._read_varuint`. """ unsigned_num = await self._read_varuint(max_bits=32) - val = from_twos_complement(unsigned_num, bits=32) - return val + return from_twos_complement(unsigned_num, bits=32) async def read_varlong(self) -> int: """Read a 64-bit signed integer in a variable length format. @@ -400,8 +399,7 @@ async def read_varlong(self) -> int: For more information about variable length format check :meth:`._read_varuint`. """ unsigned_num = await self._read_varuint(max_bits=64) - val = from_twos_complement(unsigned_num, bits=64) - return val + return from_twos_complement(unsigned_num, bits=64) async def read_bytearray(self, /) -> bytearray: """Read an arbitrary sequence of bytes, prefixed with a varint of it's size.""" @@ -446,7 +444,7 @@ async def read_utf(self) -> str: return chars - async def read_optional(self, reader: Callable[[], Awaitable[R]]) -> Optional[R]: + async def read_optional(self, reader: Callable[[], Awaitable[R]]) -> R | None: """Reads a bool showing if a value is present, if so, also reads this value with ``reader`` function. * When ``False`` is read, the function will not read anything and ``None`` is returned. @@ -493,7 +491,7 @@ def read_value(self, fmt: StructFormat, /) -> object: unpacked = struct.unpack(">" + fmt.value, data) return unpacked[0] - def _read_varuint(self, *, max_bits: Optional[int] = None) -> int: + def _read_varuint(self, *, max_bits: int | None = None) -> int: """Read an arbitrarily big unsigned integer in a variable length format. This is a standard way of transmitting ints, and it allows smaller numbers to take less bytes. @@ -533,8 +531,7 @@ def read_varint(self) -> int: For more information about variable length format check :meth:`._read_varuint`. """ unsigned_num = self._read_varuint(max_bits=32) - val = from_twos_complement(unsigned_num, bits=32) - return val + return from_twos_complement(unsigned_num, bits=32) def read_varlong(self) -> int: """Read a 64-bit signed integer in a variable length format. @@ -542,8 +539,7 @@ def read_varlong(self) -> int: For more information about variable length format check :meth:`._read_varuint`. """ unsigned_num = self._read_varuint(max_bits=64) - val = from_twos_complement(unsigned_num, bits=64) - return val + return from_twos_complement(unsigned_num, bits=64) def read_bytearray(self) -> bytearray: """Read an arbitrary sequence of bytes, prefixed with a varint of it's size.""" @@ -588,7 +584,7 @@ def read_utf(self) -> str: return chars - def read_optional(self, reader: Callable[[], R]) -> Optional[R]: + def read_optional(self, reader: Callable[[], R]) -> R | None: """Reads a bool showing if a value is present, if so, also reads this value with ``reader`` function. * When ``False`` is read, the function will not read anything and ``None`` is returned. diff --git a/mcproto/types/chat.py b/mcproto/types/chat.py index 7c7db134..354a2341 100644 --- a/mcproto/types/chat.py +++ b/mcproto/types/chat.py @@ -42,12 +42,12 @@ def as_dict(self) -> RawChatMessageDict: """Convert received ``raw`` into a stadard :class:`dict` form.""" if isinstance(self.raw, list): return RawChatMessageDict(extra=self.raw) - elif isinstance(self.raw, str): + if isinstance(self.raw, str): return RawChatMessageDict(text=self.raw) - elif isinstance(self.raw, dict): # pyright: ignore[reportUnnecessaryIsInstance] + if isinstance(self.raw, dict): # pyright: ignore[reportUnnecessaryIsInstance] return self.raw - else: # pragma: no cover - raise TypeError(f"Found unexpected type ({self.raw.__class__!r}) ({self.raw!r}) in `raw` attribute") + # pragma: no cover + raise TypeError(f"Found unexpected type ({self.raw.__class__!r}) ({self.raw!r}) in `raw` attribute") def __eq__(self, other: Self) -> bool: """Check equality between two chat messages. diff --git a/mcproto/utils/deprecation.py b/mcproto/utils/deprecation.py index 56694f03..7bf4032d 100644 --- a/mcproto/utils/deprecation.py +++ b/mcproto/utils/deprecation.py @@ -4,7 +4,7 @@ import warnings from collections.abc import Callable from functools import wraps -from typing import Optional, TypeVar, Union +from typing import TypeVar from semantic_version import Version from typing_extensions import ParamSpec, Protocol @@ -18,9 +18,9 @@ def deprecation_warn( *, obj_name: str, - removal_version: Union[str, Version], - replacement: Optional[str] = None, - extra_msg: Optional[str] = None, + removal_version: str | Version, + replacement: str | None = None, + extra_msg: str | None = None, stack_level: int = 3, ) -> None: """Produce an appropriate deprecation warning given the parameters. @@ -76,10 +76,10 @@ def __call__(self, __x: Callable[P, R]) -> Callable[P, R]: def deprecated( - removal_version: Union[str, Version], - display_name: Optional[str] = None, - replacement: Optional[str] = None, - extra_msg: Optional[str] = None, + removal_version: str | Version, + display_name: str | None = None, + replacement: str | None = None, + extra_msg: str | None = None, ) -> DecoratorFunction: """Mark an object as deprecated. diff --git a/pyproject.toml b/pyproject.toml index 4e487d3b..89962557 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -99,33 +99,43 @@ target-version = "py38" line-length = 119 select = [ - "F", # Pyflakes - "W", # Pycodestyle (warnigns) - "E", # Pycodestyle (errors) - "N", # pep8-naming - "YTT", # flake8-2020 - "ANN", # flake8-annotations - "S", # flake8-bandit - "B", # flake8-bugbear - "A", # flake8-builtins - "C4", # flake8-comprehensions - "FA", # flake8-future-annotations - "T20", # flake8-print - "PT", # flake8-pytest-style - "RSE", # flake8-raise - "SIM", # flake8-simplify - "TID", # flake8-tidy-imports - "RUF100", # flake8-noqa - "UP006", # flake8-pep585 + "F", # Pyflakes + "W", # Pycodestyle (warnigns) + "E", # Pycodestyle (errors) + "N", # pep8-naming + "YTT", # flake8-2020 + "ANN", # flake8-annotations + "ASYNC", # flake8-async + "S", # flake8-bandit + "B", # flake8-bugbear + "A", # flake8-builtins + "C4", # flake8-comprehensions + "FA", # flake8-future-annotations + "T20", # flake8-print + "PT", # flake8-pytest-style + "Q", # flake8-quotes + "RSE", # flake8-raise + "RET", # flake8-return + "SIM", # flake8-simplify + "TID", # flake8-tidy-imports + "INT", # flake8-gettext + "PTH", # flake8-use-pathlib + "PGH", # pygrep-hooks + "PL", # pylint + "RUF", # ruff-specific rules + "UP", # pyupgrade ] ignore = [ - "ANN002", # Missing type annotation for *args - "ANN003", # Missing type annotation for **kwargs - "ANN101", # Missing type annotation for self in method - "ANN102", # Missing type annotation for cls in classmethod - "ANN204", # Missing return type annotation for special method - "PT011", # pytest.raises without match parameter is too broad + "ANN002", # Missing type annotation for *args + "ANN003", # Missing type annotation for **kwargs + "ANN101", # Missing type annotation for self in method + "ANN102", # Missing type annotation for cls in classmethod + "ANN204", # Missing return type annotation for special method + "PT011", # pytest.raises without match parameter is too broad # TODO: Unignore this + "UP024", # Using errors that alias OSError + "PLR2004", # Using unnamed numerical constants + "PGH003", # Using specific rule codes in type ignores ] [tool.ruff.extend-per-file-ignores] diff --git a/tests/mcproto/protocol/helpers.py b/tests/mcproto/protocol/helpers.py index a6aee118..d3381c46 100644 --- a/tests/mcproto/protocol/helpers.py +++ b/tests/mcproto/protocol/helpers.py @@ -1,6 +1,5 @@ from __future__ import annotations -from typing import Optional from unittest.mock import AsyncMock, Mock @@ -33,7 +32,7 @@ class WriteFunctionAsyncMock(WriteFunctionMock, AsyncMock): class ReadFunctionMock(Mock): - def __init__(self, *a, combined_data: Optional[bytearray] = None, **kw): + def __init__(self, *a, combined_data: bytearray | None = None, **kw): super().__init__(*a, **kw) if combined_data is None: combined_data = bytearray() diff --git a/tests/mcproto/protocol/test_base_io.py b/tests/mcproto/protocol/test_base_io.py index 52cd2bac..5f37825d 100644 --- a/tests/mcproto/protocol/test_base_io.py +++ b/tests/mcproto/protocol/test_base_io.py @@ -3,7 +3,7 @@ import platform import struct from abc import ABC, abstractmethod -from typing import Any, Union +from typing import Any from unittest.mock import AsyncMock, Mock import pytest @@ -159,7 +159,7 @@ def __init__(self): class WriterTests(ABC): """This class holds tests for both sync and async versions of writer.""" - writer: Union[BaseSyncWriter, BaseAsyncWriter] + writer: BaseSyncWriter | BaseAsyncWriter @classmethod @abstractmethod @@ -168,7 +168,7 @@ def setup_class(cls): ... @pytest.fixture() - def method_mock(self) -> Union[Mock, AsyncMock]: + def method_mock(self) -> Mock | AsyncMock: """Returns the appropriate type of mock, supporting both sync and async modes.""" if isinstance(self.writer, BaseSyncWriter): return Mock @@ -188,7 +188,7 @@ def autopatch(self, monkeypatch: pytest.MonkeyPatch): patch_path = "mcproto.protocol.base_io.BaseAsyncWriter" mock_type = AsyncMock - def autopatch(function_name: str) -> Union[Mock, AsyncMock]: + def autopatch(function_name: str) -> Mock | AsyncMock: mock_f = mock_type() monkeypatch.setattr(f"{patch_path}.{function_name}", mock_f) return mock_f @@ -316,8 +316,8 @@ def test_write_bytearray(self, data: bytes, expected_bytes: list[int], write_moc @pytest.mark.parametrize( ("string", "expected_bytes"), [ - ("test", list(map(ord, "test")) + [0]), - ("a" * 100, list(map(ord, "a" * 100)) + [0]), + ("test", [*list(map(ord, "test")), 0]), + ("a" * 100, [*list(map(ord, "a" * 100)), 0]), ("", [0]), ], ) @@ -329,10 +329,10 @@ def test_write_ascii(self, string: str, expected_bytes: list[int], write_mock: W @pytest.mark.parametrize( ("string", "expected_bytes"), [ - ("test", [len("test")] + list(map(ord, "test"))), - ("a" * 100, [len("a" * 100)] + list(map(ord, "a" * 100))), + ("test", [len("test"), *list(map(ord, "test"))]), + ("a" * 100, [len("a" * 100), *list(map(ord, "a" * 100))]), ("", [0]), - ("नमस्ते", [18] + [int(x) for x in "नमस्ते".encode("utf-8")]), + ("नमस्ते", [18] + [int(x) for x in "नमस्ते".encode()]), ], ) def test_write_utf(self, string: str, expected_bytes: list[int], write_mock: WriteFunctionMock): @@ -346,7 +346,7 @@ def test_write_utf_limit(self, write_mock: WriteFunctionMock): with pytest.raises(ValueError, match="Maximum character limit for writing strings is 32767 characters."): self.writer.write_utf("a" * (32768)) - def test_write_optional_true(self, method_mock: Union[Mock, AsyncMock], write_mock: WriteFunctionMock): + def test_write_optional_true(self, method_mock: Mock | AsyncMock, write_mock: WriteFunctionMock): """Writing non-None value should write True and run the writer function.""" mock_v = Mock() mock_f = method_mock() @@ -354,7 +354,7 @@ def test_write_optional_true(self, method_mock: Union[Mock, AsyncMock], write_mo mock_f.assert_called_once_with(mock_v) write_mock.assert_has_data(bytearray([1])) - def test_write_optional_false(self, method_mock: Union[Mock, AsyncMock], write_mock: WriteFunctionMock): + def test_write_optional_false(self, method_mock: Mock | AsyncMock, write_mock: WriteFunctionMock): """Writing None value should write False and skip running the writer function.""" mock_f = method_mock() self.writer.write_optional(None, mock_f) @@ -365,7 +365,7 @@ def test_write_optional_false(self, method_mock: Union[Mock, AsyncMock], write_m class ReaderTests(ABC): """This class holds tests for both sync and async versions of reader.""" - reader: Union[BaseSyncReader, BaseAsyncReader] + reader: BaseSyncReader | BaseAsyncReader @classmethod @abstractmethod @@ -374,7 +374,7 @@ def setup_class(cls): ... @pytest.fixture() - def method_mock(self) -> Union[Mock, AsyncMock]: + def method_mock(self) -> Mock | AsyncMock: """Returns the appropriate type of mock, supporting both sync and async modes.""" if isinstance(self.reader, BaseSyncReader): return Mock @@ -394,7 +394,7 @@ def autopatch(self, monkeypatch: pytest.MonkeyPatch): patch_path = "mcproto.protocol.base_io.BaseAsyncReader" mock_type = AsyncMock - def autopatch(function_name: str) -> Union[Mock, AsyncMock]: + def autopatch(function_name: str) -> Mock | AsyncMock: mock_f = mock_type() monkeypatch.setattr(f"{patch_path}.{function_name}", mock_f) return mock_f @@ -507,8 +507,8 @@ def test_read_bytearray(self, read_bytes: list[int], expected_bytes: bytes, read @pytest.mark.parametrize( ("read_bytes", "expected_string"), [ - (list(map(ord, "test")) + [0], "test"), - (list(map(ord, "a" * 100)) + [0], "a" * 100), + ([*list(map(ord, "test")), 0], "test"), + ([*list(map(ord, "a" * 100)), 0], "a" * 100), ([0], ""), ], ) @@ -520,10 +520,10 @@ def test_read_ascii(self, read_bytes: list[int], expected_string: str, read_mock @pytest.mark.parametrize( ("read_bytes", "expected_string"), [ - ([len("test")] + list(map(ord, "test")), "test"), - ([len("a" * 100)] + list(map(ord, "a" * 100)), "a" * 100), + ([len("test"), *list(map(ord, "test"))], "test"), + ([len("a" * 100), *list(map(ord, "a" * 100))], "a" * 100), ([0], ""), - ([18] + [int(x) for x in "नमस्ते".encode("utf-8")], "नमस्ते"), + ([18] + [int(x) for x in "नमस्ते".encode()], "नमस्ते"), ], ) def test_read_utf(self, read_bytes: list[int], expected_string: str, read_mock: ReadFunctionMock): @@ -536,7 +536,7 @@ def test_read_utf(self, read_bytes: list[int], expected_string: str, read_mock: ("read_bytes"), [ [253, 255, 7], - [128, 128, 2] + list(map(ord, "a" * (32768))), + [128, 128, 2, *list(map(ord, "a" * 32768))], ], # Temporary workaround. # https://github.com/pytest-dev/pytest/issues/6881#issuecomment-596381626 @@ -548,14 +548,14 @@ def test_read_utf_limit(self, read_bytes: list[int], read_mock: ReadFunctionMock with pytest.raises(IOError): self.reader.read_utf() - def test_read_optional_true(self, method_mock: Union[Mock, AsyncMock], read_mock: ReadFunctionMock): + def test_read_optional_true(self, method_mock: Mock | AsyncMock, read_mock: ReadFunctionMock): """Reading optional should run reader function when first bool is True.""" mock_f = method_mock() read_mock.combined_data = bytearray([1]) self.reader.read_optional(mock_f) mock_f.assert_called_once_with() - def test_read_optional_false(self, method_mock: Union[Mock, AsyncMock], read_mock: ReadFunctionMock): + def test_read_optional_false(self, method_mock: Mock | AsyncMock, read_mock: ReadFunctionMock): """Reading optional should not run reader function when first bool is False.""" mock_f = method_mock() read_mock.combined_data = bytearray([0]) diff --git a/tests/mcproto/test_connection.py b/tests/mcproto/test_connection.py index 2409cbeb..8056865d 100644 --- a/tests/mcproto/test_connection.py +++ b/tests/mcproto/test_connection.py @@ -3,7 +3,6 @@ import asyncio import errno import socket -from typing import Optional from unittest.mock import MagicMock import pytest @@ -16,7 +15,7 @@ class MockSocket(CustomMockMixin, MagicMock): spec_set = socket.socket - def __init__(self, *args, read_data: Optional[bytearray] = None, **kwargs) -> None: + def __init__(self, *args, read_data: bytearray | None = None, **kwargs) -> None: super().__init__(*args, **kwargs) self.mock_add_spec(["_recv", "_send", "_closed"]) self._recv = ReadFunctionMock(combined_data=read_data) @@ -61,7 +60,7 @@ def close(self) -> None: class MockStreamReader(CustomMockMixin, MagicMock): spec_set = asyncio.StreamReader - def __init__(self, *args, read_data: Optional[bytearray] = None, **kwargs) -> None: + def __init__(self, *args, read_data: bytearray | None = None, **kwargs) -> None: super().__init__(*args, **kwargs) self.mock_add_spec(["_read"]) self._read = ReadFunctionAsyncMock(combined_data=read_data) @@ -71,7 +70,7 @@ def read(self, length: int) -> bytearray: class TestTCPSyncConnection: - def make_connection(self, read_data: Optional[bytearray] = None) -> TCPSyncConnection[MockSocket]: + def make_connection(self, read_data: bytearray | None = None) -> TCPSyncConnection[MockSocket]: if read_data is not None: read_data = read_data.copy() @@ -125,7 +124,7 @@ def test_socket_close_contextmanager(self): class TestTCPAsyncConnection: def make_connection( self, - read_data: Optional[bytearray] = None, + read_data: bytearray | None = None, ) -> TCPAsyncConnection[MockStreamReader, MockStreamWriter]: if read_data is not None: read_data = read_data.copy()