mirror of
https://github.com/ammaraskar/pyCraft.git
synced 2024-11-22 02:08:56 +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",
|
"VarInt", "VarLong",
|
||||||
"String"]
|
"String"]
|
||||||
|
|
||||||
from minecraft.exceptions import DeserializationError, SerializationError
|
from minecraft.exceptions import DeserializationError
|
||||||
from minecraft.compat import long
|
from minecraft.compat import long
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
import struct
|
import struct
|
||||||
import collections
|
import collections
|
||||||
|
import numbers
|
||||||
|
|
||||||
ENDIANNESS = "!" # Network, big-endian
|
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):
|
class Datatype(object):
|
||||||
"""
|
"""
|
||||||
Base object for all `pyminecraft` networking datatypes.
|
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::
|
.. note::
|
||||||
If ``ALLOWED_SERIALIZATION_TYPES`` is not empty, only the types found
|
If ``ALLOWED_SERIALIZATION_TYPES`` is not empty, only the types found
|
||||||
@ -65,9 +98,8 @@ class Datatype(object):
|
|||||||
return cls.deserialize(bin_data)
|
return cls.deserialize(bin_data)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@raise_deserialization_data
|
||||||
def deserialize(cls, data):
|
def deserialize(cls, data):
|
||||||
cls.raise_deserialization_data(data)
|
|
||||||
|
|
||||||
deserialized_data = struct.unpack(ENDIANNESS + cls.FORMAT, data)[0]
|
deserialized_data = struct.unpack(ENDIANNESS + cls.FORMAT, data)[0]
|
||||||
return deserialized_data
|
return deserialized_data
|
||||||
|
|
||||||
@ -76,9 +108,8 @@ class Datatype(object):
|
|||||||
return fileobject.write(cls.serialize(data))
|
return fileobject.write(cls.serialize(data))
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@raise_serialization_data
|
||||||
def serialize(cls, data):
|
def serialize(cls, data):
|
||||||
cls.raise_serialization_data(data)
|
|
||||||
|
|
||||||
serialized_data = struct.pack(ENDIANNESS + cls.FORMAT, data)
|
serialized_data = struct.pack(ENDIANNESS + cls.FORMAT, data)
|
||||||
return serialized_data
|
return serialized_data
|
||||||
|
|
||||||
@ -137,11 +168,23 @@ class Datatype(object):
|
|||||||
|
|
||||||
raise TypeError(err)
|
raise TypeError(err)
|
||||||
|
|
||||||
if cls.SIZE != len(data):
|
if isinstance(cls.SIZE, numbers.Number):
|
||||||
err = "'data' must have a length of {}, not {}"
|
if cls.SIZE != len(data):
|
||||||
err = err.format(str(cls.SIZE), str(len(data)))
|
err = "'data' must have a length of {}, not {}"
|
||||||
|
err = err.format(str(cls.SIZE), str(len(data)))
|
||||||
|
|
||||||
raise ValueError(err)
|
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
|
return None
|
||||||
|
|
||||||
@ -304,6 +347,8 @@ class VarInt(NumberDatatype):
|
|||||||
# Largest element in SIZE_TABLE, assuming largest element is last.
|
# Largest element in SIZE_TABLE, assuming largest element is last.
|
||||||
MAX_SIZE = list(SIZE_TABLE.items())[-1][-1]
|
MAX_SIZE = list(SIZE_TABLE.items())[-1][-1]
|
||||||
|
|
||||||
|
SIZE = (1, MAX_SIZE)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def read(cls, fileobject):
|
def read(cls, fileobject):
|
||||||
number = 0 # The decoded number
|
number = 0 # The decoded number
|
||||||
@ -329,16 +374,18 @@ class VarInt(NumberDatatype):
|
|||||||
return number
|
return number
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@raise_deserialization_data
|
||||||
def deserialize(cls, data):
|
def deserialize(cls, data):
|
||||||
data_fileobject = BytesIO(bytes(data))
|
data_fileobject = BytesIO(bytes(data))
|
||||||
return cls.read(data_fileobject)
|
return cls.read(data_fileobject)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@raise_serialization_data
|
||||||
def serialize(cls, data):
|
def serialize(cls, data):
|
||||||
if data > cls.SIZE_TABLE[-1][0]:
|
if data > cls.SIZE_TABLE[-1][0]:
|
||||||
name_of_self = str(type(cls))
|
name_of_self = str(type(cls))
|
||||||
e = "Number too big to serialize as {}".format(name_of_self)
|
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
|
result = bytes() # Where we store the serialized number
|
||||||
|
|
||||||
@ -370,6 +417,8 @@ class VarLong(VarInt):
|
|||||||
|
|
||||||
MAX_SIZE = list(SIZE_TABLE.items())[-1][-1]
|
MAX_SIZE = list(SIZE_TABLE.items())[-1][-1]
|
||||||
|
|
||||||
|
SIZE = (1, MAX_SIZE)
|
||||||
|
|
||||||
|
|
||||||
class String(Datatype):
|
class String(Datatype):
|
||||||
FORMAT = "utf-8"
|
FORMAT = "utf-8"
|
||||||
|
@ -308,6 +308,12 @@ class DoubleTest(FloatTest):
|
|||||||
(5324342541.72123, b"A\xf3\xd5\xb0P\xdb\x8a(")
|
(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):
|
# def _bin(binstr):
|
||||||
# """
|
# """
|
||||||
# Accepts a pretty looking string of binary numbers and
|
# Accepts a pretty looking string of binary numbers and
|
||||||
|
Loading…
Reference in New Issue
Block a user