Skip to content

Commit

Permalink
Make use of the validator decorator for data validation of single var…
Browse files Browse the repository at this point in the history
…iables
  • Loading branch information
LiteApplication committed Jun 29, 2024
1 parent 27e2422 commit ef348a4
Show file tree
Hide file tree
Showing 5 changed files with 34 additions and 27 deletions.
9 changes: 7 additions & 2 deletions mcproto/packets/status/status.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import json
from typing import Any, ClassVar, final

from attrs import define, field
from attrs import Attribute, define, field
from typing_extensions import Self, override

from mcproto.buffer import Buffer
Expand Down Expand Up @@ -43,7 +43,12 @@ class StatusResponse(ClientBoundPacket):
PACKET_ID: ClassVar[int] = 0x00
GAME_STATE: ClassVar[GameState] = GameState.STATUS

data: dict[str, Any] = field(validator=lambda self, _, value: json.dumps(value))
data: dict[str, Any] = field()

@data.validator # pyright: ignore
def _validate_data(self, _: Attribute[dict[str, Any]], value: dict[str, Any]) -> None:
"""Dump the data as json to check if it's valid."""
json.dumps(value)

@override
def serialize_to(self, buf: Buffer) -> None:
Expand Down
28 changes: 14 additions & 14 deletions mcproto/types/bitset.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,18 +25,18 @@ class FixedBitset(MCType):

__BIT_COUNT: ClassVar[int] = -1

@staticmethod
def data_length_check(_self: FixedBitset, attribute: Attribute[bytearray], value: bytearray) -> None:
data: bytearray = field()

@data.validator # pyright: ignore
def _data_length_check(self: FixedBitset, attribute: Attribute[bytearray], value: bytearray) -> None:
"""Check that the data length matches the bitset size.
:raises ValueError: If the data length doesn't match the bitset size.
"""
if _self.__BIT_COUNT == -1:
if self.__BIT_COUNT == -1:
raise ValueError("Bitset size is not defined.")
if len(value) != math.ceil(_self.__BIT_COUNT / 8):
raise ValueError(f"Bitset size is {_self.__BIT_COUNT}, but data length is {len(value)}.")

data: bytearray = field(validator=data_length_check.__get__(object))
if len(value) != math.ceil(self.__BIT_COUNT / 8):
raise ValueError(f"Bitset size is {self.__BIT_COUNT}, but data length is {len(value)}.")

@override
def serialize_to(self, buf: Buffer) -> None:
Expand Down Expand Up @@ -127,17 +127,17 @@ class Bitset(MCType):
:param data: The bits of the bitset.
"""

@staticmethod
def data_length_check(_self: Bitset, attribute: Attribute[list[int]], value: list[int]) -> None:
size: int = field(validator=validators.gt(0))
data: list[int] = field()

@data.validator # pyright: ignore
def _data_length_check(self: Bitset, attribute: Attribute[list[int]], value: list[int]) -> None:
"""Check that the data length matches the bitset size.
:raises ValueError: If the data length doesn't match the bitset size.
"""
if len(value) != _self.size:
raise ValueError(f"Bitset size is {_self.size}, but data length is {len(value)}.")

size: int = field(validator=validators.gt(0))
data: list[int] = field(validator=data_length_check.__get__(object))
if len(value) != self.size:
raise ValueError(f"Bitset size is {self.size}, but data length is {len(value)}.")

@override
def serialize_to(self, buf: Buffer) -> None:
Expand Down
12 changes: 6 additions & 6 deletions mcproto/types/block_entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,19 +28,19 @@ class BlockEntity(MCType):
.. note:: This class is used in the :class:`~mcproto.packets.play.ChunkData` packet.
"""

@staticmethod
def check_position(_self: BlockEntity, attribute: Attribute[Position], value: Position) -> None:
position: Position = field()
block_type: int = field()
nbt: CompoundNBT = field()

@position.validator # pyright: ignore
def _check_position(self: BlockEntity, attribute: Attribute[Position], value: Position) -> None:
"""Check that the position is within the chunk.
:raises ValueError: If the position is not within the chunk.
"""
if not (0 <= value.x < 16 and 0 <= value.z < 16):
raise ValueError("Position must be within the chunk")

position: Position = field(validator=check_position.__get__(object))
block_type: int = field()
nbt: CompoundNBT = field()

@override
def serialize_to(self, buf: Buffer) -> None:
x, y, z = int(self.position.x), int(self.position.y), int(self.position.z)
Expand Down
2 changes: 2 additions & 0 deletions mcproto/types/chat.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ def check_raw(_self: JSONTextComponent, _: Attribute[RawTextComponent], value: R
:raises AttributeError: If the `raw` attribute is not a valid JSON chat message (missing fields).
:raises TypeError: If the `raw` attribute is not a valid JSON chat message (wrong type).
.. note:: This is a separate method to be able to use the isinstance check for the type.
"""
if isinstance(value, dict): # We want to keep it this way for readability
if "text" not in value and "extra" not in value:
Expand Down
10 changes: 5 additions & 5 deletions mcproto/types/recipe.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,15 @@ class Ingredient(MCType):
.. note:: Each item in the list has to have a count of 1.
"""

@staticmethod
def check_quantity(instance: Ingredient, attribute: Attribute[set[Slot]], value: set[Slot]) -> None:
count: int = field(validator=validators.ge(1))
items: set[Slot] = field()

@items.validator # pyright: ignore
def _check_quantity(self, attribute: Attribute[set[Slot]], value: set[Slot]) -> None:
"""Check that each ingredient is valid."""
if any(item.data is None or item.data.num != 1 for item in value):
raise ValueError("Each item in the list has to have a count of 1.")

count: int = field(validator=validators.ge(1))
items: set[Slot] = field(validator=check_quantity.__get__(object))

@override
def serialize_to(self, buf: Buffer) -> None:
buf.write_varint(self.count)
Expand Down

0 comments on commit ef348a4

Please sign in to comment.