From 1d22a05523b0efac45d2826da2e921c95ba983fe Mon Sep 17 00:00:00 2001 From: CoolCat467 <52022020+CoolCat467@users.noreply.github.com> Date: Sun, 28 May 2023 03:19:23 -0500 Subject: [PATCH 1/8] Implement reading and writing boolean values --- mcstatus/protocol/connection.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/mcstatus/protocol/connection.py b/mcstatus/protocol/connection.py index 0fe94d2d..b47d353e 100644 --- a/mcstatus/protocol/connection.py +++ b/mcstatus/protocol/connection.py @@ -122,6 +122,10 @@ def write_ulong(self, value: int) -> None: """Write 8 bytes for value ``0 - 18446744073709551613 (2 ** 64 - 1)``.""" self.write(self._pack("Q", value)) + def write_bool(self, value: bool) -> None: + """Write 1 byte for boolean `True` or `False`""" + self.write(self._pack("?", value)) + def write_buffer(self, buffer: "Connection") -> None: """Flush buffer, then write a varint of the length of the buffer's data, then write buffer data.""" data = buffer.flush() @@ -214,6 +218,10 @@ async def write_ulong(self, value: int) -> None: """Write 8 bytes for value ``0 - 18446744073709551613 (2 ** 64 - 1)``.""" await self.write(self._pack("Q", value)) + async def write_bool(self, value: bool) -> None: + """Write 1 byte for boolean `True` or `False`""" + await self.write(self._pack("?", value)) + async def write_buffer(self, buffer: "Connection") -> None: """Flush buffer, then write a varint of the length of the buffer's data, then write buffer data.""" data = buffer.flush() @@ -302,6 +310,10 @@ def read_ulong(self) -> int: """Return ``0 - 18446744073709551613 (2 ** 64 - 1)``. Read 8 bytes.""" return self._unpack("Q", self.read(8)) + def read_bool(self) -> bool: + """Return `True` or `False`. Read 1 byte.""" + return self._unpack("?", self.read(1)) == 1 + def read_buffer(self) -> "Connection": """Read a varint for length, then return a new connection from length read bytes.""" length = self.read_varint() @@ -391,6 +403,10 @@ async def read_ulong(self) -> int: """Return ``0 - 18446744073709551613 (2 ** 64 - 1)``. Read 8 bytes.""" return self._unpack("Q", await self.read(8)) + async def read_bool(self) -> bool: + """Return `True` or `False`. Read 1 byte.""" + return self._unpack("?", await self.read(1)) == 1 + async def read_buffer(self) -> Connection: """Read a varint for length, then return a new connection from length read bytes.""" length = await self.read_varint() From 02fb13c0de4e9fc637a3ea67f62aa157ec6aeb6d Mon Sep 17 00:00:00 2001 From: CoolCat467 <52022020+CoolCat467@users.noreply.github.com> Date: Mon, 29 May 2023 13:38:12 -0500 Subject: [PATCH 2/8] Fix type annotations --- mcstatus/protocol/connection.py | 44 ++++++++++++++++++++++++++------- 1 file changed, 35 insertions(+), 9 deletions(-) diff --git a/mcstatus/protocol/connection.py b/mcstatus/protocol/connection.py index b47d353e..323e9e40 100644 --- a/mcstatus/protocol/connection.py +++ b/mcstatus/protocol/connection.py @@ -11,14 +11,16 @@ from ctypes import c_uint32 as unsigned_int32 from ctypes import c_uint64 as unsigned_int64 from ipaddress import ip_address -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, cast import asyncio_dgram from mcstatus.address import Address if TYPE_CHECKING: - from typing_extensions import Self, SupportsIndex, TypeAlias + from types import TracebackType + + from typing_extensions import Literal, Self, SupportsIndex, TypeAlias BytesConvertable: TypeAlias = "SupportsIndex | Iterable[SupportsIndex]" @@ -244,7 +246,7 @@ def __repr__(self) -> str: @staticmethod def _unpack(format_: str, data: bytes) -> int: """Unpack data as bytes with format in big-endian.""" - return struct.unpack(">" + format_, bytes(data))[0] + return cast(int, struct.unpack(">" + format_, bytes(data))[0]) def read_varint(self) -> int: """Read varint from ``self`` and return it. @@ -312,7 +314,7 @@ def read_ulong(self) -> int: def read_bool(self) -> bool: """Return `True` or `False`. Read 1 byte.""" - return self._unpack("?", self.read(1)) == 1 + return cast(bool, self._unpack("?", self.read(1))) def read_buffer(self) -> "Connection": """Read a varint for length, then return a new connection from length read bytes.""" @@ -337,7 +339,7 @@ def __repr__(self) -> str: @staticmethod def _unpack(format_: str, data: bytes) -> int: """Unpack data as bytes with format in big-endian.""" - return struct.unpack(">" + format_, bytes(data))[0] + return cast(int, struct.unpack(">" + format_, bytes(data))[0]) async def read_varint(self) -> int: """Read varint from ``self`` and return it. @@ -405,7 +407,7 @@ async def read_ulong(self) -> int: async def read_bool(self) -> bool: """Return `True` or `False`. Read 1 byte.""" - return self._unpack("?", await self.read(1)) == 1 + return cast(bool, self._unpack("?", await self.read(1))) async def read_buffer(self) -> Connection: """Read a varint for length, then return a new connection from length read bytes.""" @@ -526,8 +528,16 @@ def close(self) -> None: def __enter__(self) -> Self: return self - def __exit__(self, *_) -> None: + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> Literal[False]: self.close() + # Return false, we don't want to suppress + # exceptions raised in the context + return False class TCPSocketConnection(SocketConnection): @@ -637,8 +647,16 @@ async def __aenter__(self) -> Self: await self.connect() return self - async def __aexit__(self, *_) -> None: + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> Literal[False]: self.close() + # Return false, we don't want to suppress + # exceptions raised in the context + return False class UDPAsyncSocketConnection(BaseAsyncConnection): @@ -683,5 +701,13 @@ async def __aenter__(self) -> Self: await self.connect() return self - async def __aexit__(self, *_) -> None: + async def __aexit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: TracebackType | None, + ) -> Literal[False]: self.close() + # Return false, we don't want to suppress + # exceptions raised in the context + return False From 2c30c9950da1b3ba0ea6127e8d033c1a2c329d2d Mon Sep 17 00:00:00 2001 From: CoolCat467 <52022020+CoolCat467@users.noreply.github.com> Date: Mon, 29 May 2023 13:49:07 -0500 Subject: [PATCH 3/8] Add tests for booleans --- tests/protocol/test_connection.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/tests/protocol/test_connection.py b/tests/protocol/test_connection.py index dd3bf2ca..0949178a 100644 --- a/tests/protocol/test_connection.py +++ b/tests/protocol/test_connection.py @@ -190,6 +190,26 @@ def test_write_ulong_positive(self): assert self.connection.flush() == bytearray.fromhex("8000000000000000") + def test_read_bool_true(self): + self.connection.receive(bytearray.fromhex("01")) + + assert self.connection.read_bool() == True + + def test_write_bool_true(self): + self.connection.write_bool(True) + + assert self.connection.flush() == bytearray.fromhex("01") + + def test_read_bool_false(self): + self.connection.receive(bytearray.fromhex("00")) + + assert self.connection.read_bool() == False + + def test_write_bool_false(self): + self.connection.write_bool(False) + + assert self.connection.flush() == bytearray.fromhex("00") + def test_read_buffer(self): self.connection.receive(bytearray.fromhex("027FAA")) buffer = self.connection.read_buffer() From a4e73b4899fcd3287aac79ff3bdc86e54b136660 Mon Sep 17 00:00:00 2001 From: CoolCat467 <52022020+CoolCat467@users.noreply.github.com> Date: Mon, 29 May 2023 13:52:31 -0500 Subject: [PATCH 4/8] Use `is` instead of `==` --- tests/protocol/test_connection.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/protocol/test_connection.py b/tests/protocol/test_connection.py index 0949178a..4030876a 100644 --- a/tests/protocol/test_connection.py +++ b/tests/protocol/test_connection.py @@ -193,7 +193,7 @@ def test_write_ulong_positive(self): def test_read_bool_true(self): self.connection.receive(bytearray.fromhex("01")) - assert self.connection.read_bool() == True + assert self.connection.read_bool() is True def test_write_bool_true(self): self.connection.write_bool(True) @@ -203,7 +203,7 @@ def test_write_bool_true(self): def test_read_bool_false(self): self.connection.receive(bytearray.fromhex("00")) - assert self.connection.read_bool() == False + assert self.connection.read_bool() is False def test_write_bool_false(self): self.connection.write_bool(False) From 909e36c8a46f0f163e5309ad9fac62bb890b5282 Mon Sep 17 00:00:00 2001 From: CoolCat467 <52022020+CoolCat467@users.noreply.github.com> Date: Mon, 29 May 2023 17:21:18 -0500 Subject: [PATCH 5/8] Simplify boolean tests Co-authored-by: Perchun Pak --- tests/protocol/test_connection.py | 25 ++++++++----------------- 1 file changed, 8 insertions(+), 17 deletions(-) diff --git a/tests/protocol/test_connection.py b/tests/protocol/test_connection.py index 4030876a..781aad60 100644 --- a/tests/protocol/test_connection.py +++ b/tests/protocol/test_connection.py @@ -190,26 +190,17 @@ def test_write_ulong_positive(self): assert self.connection.flush() == bytearray.fromhex("8000000000000000") - def test_read_bool_true(self): - self.connection.receive(bytearray.fromhex("01")) + @pytest.mark.parametrize("as_bytes,as_bool", [("01", True), ("00", False)]) + def test_read_bool(self, as_bytes: str, as_bool: bool) -> None: + self.connection.receive(bytearray.fromhex(as_bytes)) - assert self.connection.read_bool() is True + assert self.connection.read_bool() is as_bool - def test_write_bool_true(self): - self.connection.write_bool(True) - - assert self.connection.flush() == bytearray.fromhex("01") - - def test_read_bool_false(self): - self.connection.receive(bytearray.fromhex("00")) - - assert self.connection.read_bool() is False - - def test_write_bool_false(self): - self.connection.write_bool(False) - - assert self.connection.flush() == bytearray.fromhex("00") + @pytest.mark.parametrize("as_bytes,as_bool", [("01", True), ("00", False)]) + def test_write_bool(self, as_bytes: str, as_bool: bool) -> None: + self.connection.write_bool(as_bool) + assert self.connection.flush() == bytearray.fromhex(as_bytes) def test_read_buffer(self): self.connection.receive(bytearray.fromhex("027FAA")) buffer = self.connection.read_buffer() From 64f7e0f49a5a95e1f346665510bdb18ca3f50d70 Mon Sep 17 00:00:00 2001 From: CoolCat467 <52022020+CoolCat467@users.noreply.github.com> Date: Mon, 29 May 2023 17:27:01 -0500 Subject: [PATCH 6/8] Format, remove cast, and remove False literal return --- mcstatus/protocol/connection.py | 21 ++++++--------------- tests/protocol/test_connection.py | 1 + 2 files changed, 7 insertions(+), 15 deletions(-) diff --git a/mcstatus/protocol/connection.py b/mcstatus/protocol/connection.py index 323e9e40..41372459 100644 --- a/mcstatus/protocol/connection.py +++ b/mcstatus/protocol/connection.py @@ -20,7 +20,7 @@ if TYPE_CHECKING: from types import TracebackType - from typing_extensions import Literal, Self, SupportsIndex, TypeAlias + from typing_extensions import Self, SupportsIndex, TypeAlias BytesConvertable: TypeAlias = "SupportsIndex | Iterable[SupportsIndex]" @@ -246,7 +246,7 @@ def __repr__(self) -> str: @staticmethod def _unpack(format_: str, data: bytes) -> int: """Unpack data as bytes with format in big-endian.""" - return cast(int, struct.unpack(">" + format_, bytes(data))[0]) + return struct.unpack(">" + format_, bytes(data))[0] def read_varint(self) -> int: """Read varint from ``self`` and return it. @@ -339,7 +339,7 @@ def __repr__(self) -> str: @staticmethod def _unpack(format_: str, data: bytes) -> int: """Unpack data as bytes with format in big-endian.""" - return cast(int, struct.unpack(">" + format_, bytes(data))[0]) + return struct.unpack(">" + format_, bytes(data))[0] async def read_varint(self) -> int: """Read varint from ``self`` and return it. @@ -533,11 +533,8 @@ def __exit__( exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None, - ) -> Literal[False]: + ) -> None: self.close() - # Return false, we don't want to suppress - # exceptions raised in the context - return False class TCPSocketConnection(SocketConnection): @@ -652,11 +649,8 @@ async def __aexit__( exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None, - ) -> Literal[False]: + ) -> None: self.close() - # Return false, we don't want to suppress - # exceptions raised in the context - return False class UDPAsyncSocketConnection(BaseAsyncConnection): @@ -706,8 +700,5 @@ async def __aexit__( exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None, - ) -> Literal[False]: + ) -> None: self.close() - # Return false, we don't want to suppress - # exceptions raised in the context - return False diff --git a/tests/protocol/test_connection.py b/tests/protocol/test_connection.py index 781aad60..d4e183e9 100644 --- a/tests/protocol/test_connection.py +++ b/tests/protocol/test_connection.py @@ -201,6 +201,7 @@ def test_write_bool(self, as_bytes: str, as_bool: bool) -> None: self.connection.write_bool(as_bool) assert self.connection.flush() == bytearray.fromhex(as_bytes) + def test_read_buffer(self): self.connection.receive(bytearray.fromhex("027FAA")) buffer = self.connection.read_buffer() From 9676b71d2328661844857748664754015e283ee5 Mon Sep 17 00:00:00 2001 From: CoolCat467 <52022020+CoolCat467@users.noreply.github.com> Date: Sat, 24 Jun 2023 10:47:31 -0500 Subject: [PATCH 7/8] Remove type annotations from context manager exit functions --- mcstatus/protocol/connection.py | 21 +++------------------ 1 file changed, 3 insertions(+), 18 deletions(-) diff --git a/mcstatus/protocol/connection.py b/mcstatus/protocol/connection.py index 41372459..190fd654 100644 --- a/mcstatus/protocol/connection.py +++ b/mcstatus/protocol/connection.py @@ -528,12 +528,7 @@ def close(self) -> None: def __enter__(self) -> Self: return self - def __exit__( - self, - exc_type: type[BaseException] | None, - exc_val: BaseException | None, - exc_tb: TracebackType | None, - ) -> None: + def __exit__(self, *_) -> None: self.close() @@ -644,12 +639,7 @@ async def __aenter__(self) -> Self: await self.connect() return self - async def __aexit__( - self, - exc_type: type[BaseException] | None, - exc_val: BaseException | None, - exc_tb: TracebackType | None, - ) -> None: + async def __aexit__(self, *_) -> None: self.close() @@ -695,10 +685,5 @@ async def __aenter__(self) -> Self: await self.connect() return self - async def __aexit__( - self, - exc_type: type[BaseException] | None, - exc_val: BaseException | None, - exc_tb: TracebackType | None, - ) -> None: + async def __aexit__(self, *_) -> None: self.close() From 7f92695b727e08c4121e20cb0b0f3d0519e8f919 Mon Sep 17 00:00:00 2001 From: CoolCat467 <52022020+CoolCat467@users.noreply.github.com> Date: Sat, 24 Jun 2023 10:52:46 -0500 Subject: [PATCH 8/8] Remove unused traceback type import --- mcstatus/protocol/connection.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/mcstatus/protocol/connection.py b/mcstatus/protocol/connection.py index 190fd654..fd025848 100644 --- a/mcstatus/protocol/connection.py +++ b/mcstatus/protocol/connection.py @@ -18,8 +18,6 @@ from mcstatus.address import Address if TYPE_CHECKING: - from types import TracebackType - from typing_extensions import Self, SupportsIndex, TypeAlias BytesConvertable: TypeAlias = "SupportsIndex | Iterable[SupportsIndex]"