mirror of
https://github.com/ammaraskar/pyCraft.git
synced 2024-11-22 10:15:55 +01:00
More datatype abstraction:
* NumberDatatype * StringDatatype _raise_serialization_value_error_data is now a classmethod instead of a classmethod. Tests for MIN and MAX number values of NumberDatatypes have been made more abstract. TEST_DATA_* has been removed from variable names. Sometimes it can get a little too rediculous with these var-names, don't you think?
This commit is contained in:
parent
73b990cc81
commit
a31f1c543f
@ -6,7 +6,7 @@ These datatypes are used by the packet definitions.
|
||||
"""
|
||||
|
||||
__all__ = ["ENDIANNESS",
|
||||
"Datatype",
|
||||
"Datatype", "NumberDatatype", "StringDatatype",
|
||||
"Boolean",
|
||||
"Byte", "UnsignedByte",
|
||||
"Short", "UnsignedShort",
|
||||
@ -92,6 +92,7 @@ class Datatype(object):
|
||||
"""
|
||||
error_message = "'data's type ('{}') is not an allowed type."
|
||||
error_message = error_message.format(type(data).__name__)
|
||||
|
||||
if (cls.ALLOWED_SERIALIZATION_TYPES and
|
||||
not any([isinstance(data, type_) for type_
|
||||
in cls.ALLOWED_SERIALIZATION_TYPES])):
|
||||
@ -106,8 +107,8 @@ class Datatype(object):
|
||||
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def _raise_serialization_value_error_data(data):
|
||||
@classmethod
|
||||
def _raise_serialization_value_error_data(cls, data):
|
||||
"""
|
||||
Raises a ValueError if ``data`` is not valid.
|
||||
|
||||
@ -137,13 +138,51 @@ class Datatype(object):
|
||||
|
||||
if cls.SIZE != len(data):
|
||||
err = "'data' must have a length of {}, not {}"
|
||||
err = err.format(str(cls.SIZE), len(data))
|
||||
err = err.format(str(cls.SIZE), str(len(data)))
|
||||
|
||||
raise TypeError(err)
|
||||
raise ValueError(err)
|
||||
|
||||
return None
|
||||
|
||||
|
||||
class NumberDatatype(Datatype):
|
||||
"""
|
||||
Base abstract class for all number-like minecraft networking datatypes.
|
||||
|
||||
.. note::
|
||||
Numbers to be serialized must be between this classes
|
||||
``MIN_NUMBER_VALUE`` and ``MAX_NUMBER_VALUE``, or a ``ValueError`` will
|
||||
be raised.
|
||||
|
||||
If ``MIN_NUMBER_VALUE`` or ``MAX_NUMBER_VALUE`` are ``None``
|
||||
(as in the case of float), checking is left to the ``struct`` module.
|
||||
"""
|
||||
|
||||
MIN_NUMBER_VALUE = None
|
||||
MAX_NUMBER_VALUE = None
|
||||
|
||||
ALLOWED_SERIALIZATION_TYPES = (int,)
|
||||
DISALLOWED_SERIALIZATION_TYPES = (bool,)
|
||||
|
||||
@classmethod
|
||||
def _raise_serialization_value_error_data(cls, data):
|
||||
if (cls.MIN_NUMBER_VALUE is not None
|
||||
and cls.MAX_NUMBER_VALUE is not None):
|
||||
|
||||
if not cls.MIN_NUMBER_VALUE <= data <= cls.MAX_NUMBER_VALUE:
|
||||
err = "'data' must be an integer with value between {} and {}."
|
||||
err = err.format(str(cls.MIN_NUMBER_VALUE),
|
||||
str(cls.MAX_NUMBER_VALUE))
|
||||
|
||||
raise ValueError(err)
|
||||
|
||||
return None
|
||||
|
||||
|
||||
class StringDatatype(Datatype):
|
||||
pass
|
||||
|
||||
|
||||
class Boolean(Datatype):
|
||||
FORMAT = "?"
|
||||
SIZE = 1
|
||||
@ -152,76 +191,97 @@ class Boolean(Datatype):
|
||||
ALLOWED_DESERIALIZATION_TYPES = (collections.Sequence,)
|
||||
|
||||
|
||||
class Byte(Datatype):
|
||||
class Byte(NumberDatatype):
|
||||
FORMAT = "b"
|
||||
SIZE = 1
|
||||
|
||||
ALLOWED_SERIALIZATION_TYPES = (int,)
|
||||
DISALLOWED_SERIALIZATION_TYPES = (bool,)
|
||||
|
||||
@staticmethod
|
||||
def _raise_serialization_value_error_data(data):
|
||||
if not -128 <= data <= 127:
|
||||
e = "'data' must be an integer with value between -128 and 127."
|
||||
raise ValueError(e)
|
||||
MIN_NUMBER_VALUE = -128
|
||||
MAX_NUMBER_VALUE = 127
|
||||
|
||||
|
||||
class UnsignedByte(Datatype):
|
||||
class UnsignedByte(NumberDatatype):
|
||||
FORMAT = "B"
|
||||
SIZE = 1
|
||||
|
||||
MIN_NUMBER_VALUE = 0
|
||||
MAX_NUMBER_VALUE = 255
|
||||
|
||||
class Short(Datatype):
|
||||
|
||||
class Short(NumberDatatype):
|
||||
FORMAT = "h"
|
||||
SIZE = 2
|
||||
|
||||
MIN_NUMBER_VALUE = -32768
|
||||
MAX_NUMBER_VALUE = 32767
|
||||
|
||||
class UnsignedShort(Datatype):
|
||||
|
||||
class UnsignedShort(NumberDatatype):
|
||||
FORMAT = "H"
|
||||
SIZE = 2
|
||||
|
||||
MIN_NUMBER_VALUE = 0
|
||||
MAX_NUMBER_VALUE = 65535
|
||||
|
||||
class Integer(Datatype):
|
||||
|
||||
class Integer(NumberDatatype):
|
||||
FORMAT = "i"
|
||||
SIZE = 4
|
||||
|
||||
MIN_NUMBER_VALUE = -2147483648
|
||||
MAX_NUMBER_VALUE = 2147483647
|
||||
|
||||
class UnsignedInteger(Datatype):
|
||||
|
||||
class UnsignedInteger(NumberDatatype):
|
||||
FORMAT = "I"
|
||||
SIZE = 4
|
||||
|
||||
MIN_NUMBER_VALUE = 0
|
||||
MAX_NUMBER_VALUE = 4294967295
|
||||
|
||||
class Long(Datatype):
|
||||
|
||||
class Long(NumberDatatype):
|
||||
FORMAT = "l"
|
||||
SIZE = 4
|
||||
|
||||
MIN_NUMBER_VALUE = -2147483648
|
||||
MAX_NUMBER_VALUE = 2147483647
|
||||
|
||||
class UnsignedLong(Datatype):
|
||||
|
||||
class UnsignedLong(NumberDatatype):
|
||||
FORMAT = "L"
|
||||
SIZE = 4
|
||||
|
||||
MIN_NUMBER_VALUE = 0
|
||||
MAX_NUMBER_VALUE = 4294967295
|
||||
|
||||
class LongLong(Datatype):
|
||||
|
||||
class LongLong(NumberDatatype):
|
||||
FORMAT = "q"
|
||||
SIZE = 8
|
||||
|
||||
MIN_NUMBER_VALUE = -9223372036854775808
|
||||
MAX_NUMBER_VALUE = 9223372036854775807
|
||||
|
||||
class UnsignedLongLong(Datatype):
|
||||
|
||||
class UnsignedLongLong(NumberDatatype):
|
||||
FORMAT = "Q"
|
||||
SIZE = 8
|
||||
|
||||
MIN_NUMBER_VALUE = 0
|
||||
MAX_NUMBER_VALUE = 18446744073709551615
|
||||
|
||||
class Float(Datatype):
|
||||
|
||||
class Float(NumberDatatype):
|
||||
FORMAT = "f"
|
||||
SIZE = 4
|
||||
|
||||
|
||||
class Double(Datatype):
|
||||
class Double(NumberDatatype):
|
||||
FORMAT = "d"
|
||||
SIZE = 8
|
||||
|
||||
|
||||
class VarInt(Datatype):
|
||||
class VarInt(NumberDatatype):
|
||||
# See: https://developers.google.com/protocol-buffers/docs/encoding#varints
|
||||
# See: https://github.com/ammaraskar/pyCraft/blob/7e8df473520d57ca22fb57888681f51705128cdc/network/types.py#l123 # noqa
|
||||
# See: https://github.com/google/protobuf/blob/0c59f2e6fc0a2cb8e8e3b4c7327f650e8586880a/python/google/protobuf/internal/decoder.py#l107 # noqa
|
||||
|
@ -27,21 +27,21 @@ import unittest
|
||||
class BaseDatatypeTester(unittest.TestCase):
|
||||
DATATYPE_CLS = Datatype # We use Datatype as a an example here.
|
||||
|
||||
# TEST_DATA_VALID_VALUES should have the following format:
|
||||
# VALID_VALUES should have the following format:
|
||||
# [(DESERIALIZED_VALUE, SERIALIZED_VALUE), ...]
|
||||
#
|
||||
# So that DESERIALIZED_VALUE is SERIALIZED_VALUE when serialized
|
||||
# and vice versa.
|
||||
|
||||
TEST_DATA_VALID_VALUES = []
|
||||
VALID_VALUES = []
|
||||
|
||||
# TEST_DATA_INVALID_SERIALIZATION_VALUES should be a list of tuples
|
||||
# INVALID_SERIALIZATION_VALUES should be a list of tuples
|
||||
# containing the value and the expected exception.
|
||||
TEST_DATA_INVALID_SERIALIZATION_VALUES = []
|
||||
INVALID_SERIALIZATION_VALUES = []
|
||||
|
||||
# TEST_DATA_INVALID_DESERIALIZATION_VALUES should be a list of tuples
|
||||
# INVALID_DESERIALIZATION_VALUES should be a list of tuples
|
||||
# containing the value and the expected exception.
|
||||
TEST_DATA_INVALID_DESERIALIZATION_VALUES = []
|
||||
INVALID_DESERIALIZATION_VALUES = []
|
||||
|
||||
def test_init(self):
|
||||
d = self.DATATYPE_CLS() # noqa
|
||||
@ -52,40 +52,84 @@ class BaseDatatypeTester(unittest.TestCase):
|
||||
d = self.DATATYPE_CLS("This is a positional argument...") # noqa
|
||||
|
||||
def test_valid_data_serialization_values(self):
|
||||
for deserialized_val, serialized_val in self.TEST_DATA_VALID_VALUES:
|
||||
for deserialized_val, serialized_val in self.VALID_VALUES:
|
||||
self.assertEqual(self.DATATYPE_CLS.serialize(deserialized_val),
|
||||
serialized_val)
|
||||
|
||||
def test_valid_data_deserialization_values(self):
|
||||
for deserialized_val, serialized_val in self.TEST_DATA_VALID_VALUES:
|
||||
for deserialized_val, serialized_val in self.VALID_VALUES:
|
||||
self.assertEqual(self.DATATYPE_CLS.deserialize(serialized_val),
|
||||
deserialized_val)
|
||||
|
||||
def test_invalid_data_serialization_values(self):
|
||||
for value, exception in self.TEST_DATA_INVALID_SERIALIZATION_VALUES:
|
||||
for value, exception in self.INVALID_SERIALIZATION_VALUES:
|
||||
with self.assertRaises(exception):
|
||||
print(value)
|
||||
self.DATATYPE_CLS.serialize(value)
|
||||
|
||||
def test_invalid_data_deserialization_values(self):
|
||||
for value, exception in self.TEST_DATA_INVALID_DESERIALIZATION_VALUES:
|
||||
for value, exception in self.INVALID_DESERIALIZATION_VALUES:
|
||||
with self.assertRaises(exception):
|
||||
self.DATATYPE_CLS.deserialize(value)
|
||||
|
||||
|
||||
class BaseNumberDatatypeTester(BaseDatatypeTester):
|
||||
BASE_NUMBER_INVALID_SERIALIZATION_VALUES = [
|
||||
("", TypeError),
|
||||
("Test", TypeError),
|
||||
(b"\x00", TypeError),
|
||||
(b"\x80", TypeError),
|
||||
(True, TypeError),
|
||||
(False, TypeError)
|
||||
]
|
||||
|
||||
def base_number_invalid_data_serialization_values(self):
|
||||
values_to_test = BASE_INVALID_SERIALIZATION_VALUES
|
||||
values_to_test.extend([
|
||||
(self.DATATYPE_CLS.MIN_NUMBER_VALUE - 1, ValueError),
|
||||
(self.DATATYPE_CLS.MAX_NUMBER_VALUE + 1, ValueError)
|
||||
])
|
||||
|
||||
for value, exception in values_to_test:
|
||||
with self.assertRaises(exception):
|
||||
self.DATATYPE_CLS.serialize(value)
|
||||
|
||||
|
||||
class BaseStringDatatypeTester(BaseDatatypeTester):
|
||||
pass
|
||||
|
||||
|
||||
BASE_INVALID_DESERIALIZATION_VALUES = [
|
||||
(-1, TypeError),
|
||||
(0, TypeError),
|
||||
(1, TypeError),
|
||||
("", ValueError),
|
||||
("Test", ValueError),
|
||||
(True, TypeError),
|
||||
(False, TypeError)
|
||||
]
|
||||
|
||||
|
||||
class DatatypeTest(BaseDatatypeTester):
|
||||
DATATYPE_CLS = Datatype
|
||||
|
||||
|
||||
class NumberDatatypeTest(BaseNumberDatatypeTester):
|
||||
DATATYPE_CLS = NumberDatatype
|
||||
|
||||
|
||||
class StringDatatypeTest(BaseStringDatatypeTester):
|
||||
DATATYPE_CLS = StringDatatype
|
||||
|
||||
|
||||
class BooleanTest(BaseDatatypeTester):
|
||||
DATATYPE_CLS = Boolean
|
||||
|
||||
TEST_DATA_VALID_VALUES = [
|
||||
VALID_VALUES = [
|
||||
(True, b"\x01"),
|
||||
(False, b"\x00")
|
||||
]
|
||||
|
||||
TEST_DATA_INVALID_SERIALIZATION_VALUES = [
|
||||
INVALID_SERIALIZATION_VALUES = [
|
||||
("\x00", TypeError),
|
||||
("\x01", TypeError),
|
||||
("\x02", TypeError),
|
||||
@ -96,21 +140,19 @@ class BooleanTest(BaseDatatypeTester):
|
||||
("Test", TypeError)
|
||||
]
|
||||
|
||||
TEST_DATA_INVALID_DESERIALIZATION_VALUES = [
|
||||
(-1, TypeError),
|
||||
(0, TypeError),
|
||||
(1, TypeError),
|
||||
("", TypeError),
|
||||
("Test", TypeError),
|
||||
(True, TypeError),
|
||||
(False, TypeError)
|
||||
]
|
||||
# Use list(BASE_INVALID_DESERIALIZATION_VALUES) instead of
|
||||
# just = BASE_INVALID_DESERIALIZATION_VALUES, cause we want a COPY
|
||||
# of the list, NOT a reference (that we'll later extend!)
|
||||
INVALID_DESERIALIZATION_VALUES = list(BASE_INVALID_DESERIALIZATION_VALUES)
|
||||
INVALID_DESERIALIZATION_VALUES.extend([
|
||||
(b"\x00\x01", ValueError)
|
||||
])
|
||||
|
||||
|
||||
class ByteTest(BaseDatatypeTester):
|
||||
class ByteTest(BaseNumberDatatypeTester):
|
||||
DATATYPE_CLS = Byte
|
||||
|
||||
TEST_DATA_VALID_VALUES = [
|
||||
VALID_VALUES = [
|
||||
(-128, b"\x80"),
|
||||
(-22, b"\xea"),
|
||||
(0, b"\x00"),
|
||||
@ -118,22 +160,54 @@ class ByteTest(BaseDatatypeTester):
|
||||
(127, b"\x7f")
|
||||
]
|
||||
|
||||
TEST_DATA_INVALID_SERIALIZATION_VALUES = [
|
||||
(-500, ValueError),
|
||||
(128, ValueError),
|
||||
(1024, ValueError),
|
||||
("", TypeError),
|
||||
("Test", TypeError),
|
||||
(b"\x00", TypeError),
|
||||
(b"\x80", TypeError),
|
||||
(True, TypeError),
|
||||
(False, TypeError),
|
||||
INVALID_DESERIALIZATION_VALUES = list(BASE_INVALID_DESERIALIZATION_VALUES)
|
||||
INVALID_DESERIALIZATION_VALUES.extend([
|
||||
(b"\x01\x20", ValueError),
|
||||
])
|
||||
|
||||
|
||||
class UnsignedByteTest(BaseNumberDatatypeTester):
|
||||
DATATYPE_CLS = UnsignedByte
|
||||
|
||||
VALID_VALUES = [
|
||||
(0, b"\x00"),
|
||||
(127, b"\x7f"),
|
||||
(255, b"\xff")
|
||||
]
|
||||
|
||||
TEST_DATA_INVALID_DESERIALIZATION_VALUES = [
|
||||
INVALID_DESERIALIZATION_VALUES = ByteTest.INVALID_DESERIALIZATION_VALUES
|
||||
|
||||
|
||||
class ShortTest(BaseNumberDatatypeTester):
|
||||
DATATYPE_CLS = Short
|
||||
|
||||
VALID_VALUES = [
|
||||
(-32768, b"\x80\x00"),
|
||||
(-10000, b"\xd8\xf0"),
|
||||
(0, b"\x00\x00"),
|
||||
(5000, b"\x13\x88"),
|
||||
(32767, b"\x7f\xff")
|
||||
]
|
||||
|
||||
INVALID_DESERIALIZATION_VALUES = list(BASE_INVALID_DESERIALIZATION_VALUES)
|
||||
INVALID_DESERIALIZATION_VALUES.extend([
|
||||
(b"\xff", ValueError),
|
||||
(b"\xff\x01\x6e", ValueError)
|
||||
])
|
||||
|
||||
|
||||
class UnsignedShortTest(BaseNumberDatatypeTester):
|
||||
DATATYPE_CLS = UnsignedShort
|
||||
|
||||
VALID_VALUES = [
|
||||
(0, b"\x00\x00"),
|
||||
(10000, b"'\x10"),
|
||||
(32767, b"\x7f\xff"),
|
||||
(65535, b"\xff\xff")
|
||||
]
|
||||
|
||||
INVALID_DESERIALIZATION_VALUES = ShortTest.INVALID_DESERIALIZATION_VALUES
|
||||
|
||||
# def _bin(binstr):
|
||||
# """
|
||||
# Accepts a pretty looking string of binary numbers and
|
||||
|
Loading…
Reference in New Issue
Block a user