mirror of
https://github.com/ammaraskar/pyCraft.git
synced 2024-11-21 17:56:30 +01:00
Tests for VarInt
Removed unused import (SerializationError) We now check if Datatype.SIZE is a number using ABC. Added 2 decorators for raising serialized and deserialized data exceptions. Datatype.SIZE can now be either a number or a sequence. If sequence, first value is MIN_SIZE and second value is MAX_SIZE VarInt.serialize now raises ValueError instead of SerializationError when number is too big to serialize.
This commit is contained in:
parent
e1f8f0254a
commit
55ff270f16
@ -18,19 +18,52 @@ __all__ = ["ENDIANNESS",
|
||||
"VarInt", "VarLong",
|
||||
"String"]
|
||||
|
||||
from minecraft.exceptions import DeserializationError, SerializationError
|
||||
from minecraft.exceptions import DeserializationError
|
||||
from minecraft.compat import long
|
||||
from io import BytesIO
|
||||
import struct
|
||||
import collections
|
||||
import numbers
|
||||
|
||||
ENDIANNESS = "!" # Network, big-endian
|
||||
|
||||
|
||||
def raise_serialization_data(func):
|
||||
"""
|
||||
A decorator to be used on a ``Datatype``.serialize definition.
|
||||
|
||||
Must be placed before a classmethod decorator.
|
||||
"""
|
||||
def wrapped(cls, data):
|
||||
cls.raise_serialization_data(data)
|
||||
|
||||
return func(cls, data)
|
||||
|
||||
return wrapped
|
||||
|
||||
|
||||
def raise_deserialization_data(func):
|
||||
"""
|
||||
A decorator to be used on a ``Datatype``.serialize definition.
|
||||
|
||||
Must be placed before a classmethod decorator.
|
||||
"""
|
||||
def wrapped(cls, data):
|
||||
cls.raise_deserialization_data(data)
|
||||
|
||||
return func(cls, data)
|
||||
|
||||
return wrapped
|
||||
|
||||
|
||||
class Datatype(object):
|
||||
"""
|
||||
Base object for all `pyminecraft` networking datatypes.
|
||||
|
||||
``Datatype``.SIZE can be either a number, specifying an exact required size
|
||||
of data to be deserialized, or it can be a tuple like this:
|
||||
``(MIN_SIZE, MAX_SIZE)``
|
||||
|
||||
|
||||
.. note::
|
||||
If ``ALLOWED_SERIALIZATION_TYPES`` is not empty, only the types found
|
||||
@ -65,9 +98,8 @@ class Datatype(object):
|
||||
return cls.deserialize(bin_data)
|
||||
|
||||
@classmethod
|
||||
@raise_deserialization_data
|
||||
def deserialize(cls, data):
|
||||
cls.raise_deserialization_data(data)
|
||||
|
||||
deserialized_data = struct.unpack(ENDIANNESS + cls.FORMAT, data)[0]
|
||||
return deserialized_data
|
||||
|
||||
@ -76,9 +108,8 @@ class Datatype(object):
|
||||
return fileobject.write(cls.serialize(data))
|
||||
|
||||
@classmethod
|
||||
@raise_serialization_data
|
||||
def serialize(cls, data):
|
||||
cls.raise_serialization_data(data)
|
||||
|
||||
serialized_data = struct.pack(ENDIANNESS + cls.FORMAT, data)
|
||||
return serialized_data
|
||||
|
||||
@ -137,12 +168,24 @@ class Datatype(object):
|
||||
|
||||
raise TypeError(err)
|
||||
|
||||
if isinstance(cls.SIZE, numbers.Number):
|
||||
if cls.SIZE != len(data):
|
||||
err = "'data' must have a length of {}, not {}"
|
||||
err = err.format(str(cls.SIZE), str(len(data)))
|
||||
|
||||
raise ValueError(err)
|
||||
|
||||
elif isinstance(cls.SIZE, collections.Sequence):
|
||||
if not cls.SIZE[0] <= len(data) <= cls.SIZE[1]:
|
||||
err = "'data' must have a length between {} and {}, not {}"
|
||||
err = err.format(str(cls.SIZE[0]), str(cls.SIZE[1]),
|
||||
str(len(data)))
|
||||
|
||||
raise ValueError(err)
|
||||
|
||||
else:
|
||||
raise TypeError("'cls.SIZE' must be a number or a sequence.")
|
||||
|
||||
return None
|
||||
|
||||
|
||||
@ -304,6 +347,8 @@ class VarInt(NumberDatatype):
|
||||
# Largest element in SIZE_TABLE, assuming largest element is last.
|
||||
MAX_SIZE = list(SIZE_TABLE.items())[-1][-1]
|
||||
|
||||
SIZE = (1, MAX_SIZE)
|
||||
|
||||
@classmethod
|
||||
def read(cls, fileobject):
|
||||
number = 0 # The decoded number
|
||||
@ -329,16 +374,18 @@ class VarInt(NumberDatatype):
|
||||
return number
|
||||
|
||||
@classmethod
|
||||
@raise_deserialization_data
|
||||
def deserialize(cls, data):
|
||||
data_fileobject = BytesIO(bytes(data))
|
||||
return cls.read(data_fileobject)
|
||||
|
||||
@classmethod
|
||||
@raise_serialization_data
|
||||
def serialize(cls, data):
|
||||
if data > cls.SIZE_TABLE[-1][0]:
|
||||
name_of_self = str(type(cls))
|
||||
e = "Number too big to serialize as {}".format(name_of_self)
|
||||
raise SerializationError(e)
|
||||
raise ValueError(e)
|
||||
|
||||
result = bytes() # Where we store the serialized number
|
||||
|
||||
@ -370,6 +417,8 @@ class VarLong(VarInt):
|
||||
|
||||
MAX_SIZE = list(SIZE_TABLE.items())[-1][-1]
|
||||
|
||||
SIZE = (1, MAX_SIZE)
|
||||
|
||||
|
||||
class String(Datatype):
|
||||
FORMAT = "utf-8"
|
||||
|
@ -308,6 +308,12 @@ class DoubleTest(FloatTest):
|
||||
(5324342541.72123, b"A\xf3\xd5\xb0P\xdb\x8a(")
|
||||
]
|
||||
|
||||
|
||||
class VarIntTest(BaseNumberDatatypeTester):
|
||||
DATATYPE_CLS = VarInt
|
||||
|
||||
INVALID_DESERIALIZATION_VALUES = BASE_INVALID_DESERIALIZATION_VALUES
|
||||
|
||||
# def _bin(binstr):
|
||||
# """
|
||||
# Accepts a pretty looking string of binary numbers and
|
||||
|
Loading…
Reference in New Issue
Block a user