Skip to content

Commit

Permalink
Merge pull request #3 from Bhargavasomu/ssz_bool
Browse files Browse the repository at this point in the history
Add basic API infrastructure with Boolean Support
  • Loading branch information
hwwhww committed Dec 2, 2018
2 parents 66ee42b + 8c2060f commit ebbc50b
Show file tree
Hide file tree
Showing 7 changed files with 239 additions and 0 deletions.
12 changes: 12 additions & 0 deletions ssz/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from .codec import ( # noqa: F401
encode,
decode
)

from .exceptions import ( # noqa: F401
SSZException,
EncodingError,
DecodingError,
SerializationError,
DeserializationError,
)
29 changes: 29 additions & 0 deletions ssz/codec.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
from eth_utils import (
is_bytes,
)

from ssz.exceptions import (
DecodingError,
)
from ssz.utils import (
infer_sedes,
)


def encode(obj):
"""
Encode object in SSZ format.
"""
serialized_obj = infer_sedes(obj).serialize(obj)
return serialized_obj


def decode(ssz, sedes):
"""
Decode a SSZ encoded object.
"""
if not is_bytes(ssz):
raise DecodingError('Can only decode SSZ bytes, got type %s' % type(ssz).__name__, ssz)

obj = sedes.deserialize(ssz)
return obj
45 changes: 45 additions & 0 deletions ssz/exceptions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
class SSZException(Exception):
"""
Base class for exceptions raised by this package.
"""
pass


class EncodingError(SSZException):
"""
Exception raised if encoding fails.
"""

def __init__(self, message, obj):
super(EncodingError, self).__init__(message)
self.obj = obj


class DecodingError(SSZException):
"""
Exception raised if decoding fails.
"""

def __init__(self, message, ssz):
super(DecodingError, self).__init__(message)
self.ssz = ssz


class SerializationError(SSZException):
"""
Exception raised if serialization fails.
"""

def __init__(self, message, obj):
super(SerializationError, self).__init__(message)
self.obj = obj


class DeserializationError(SSZException):
"""
Exception raised if deserialization fails.
"""

def __init__(self, message, serial):
super(DeserializationError, self).__init__(message)
self.serial = serial
4 changes: 4 additions & 0 deletions ssz/sedes/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from .boolean import ( # noqa: F401
boolean,
Boolean,
)
34 changes: 34 additions & 0 deletions ssz/sedes/boolean.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
from ssz.exceptions import (
DeserializationError,
SerializationError,
)


class Boolean:
"""
A sedes for booleans.
"""
def serialize(self, obj):
if not isinstance(obj, bool):
raise SerializationError('Can only serialize boolean values', obj)

if obj is False:
return b'\x00'
elif obj is True:
return b'\x01'
else:
raise Exception("Invariant: no other options for boolean values")

def deserialize(self, serialized_obj):
if serialized_obj == b'\x00':
return False
elif serialized_obj == b'\x01':
return True
else:
raise DeserializationError(
'Invalid serialized boolean. Must be either 0x01 or 0x00',
serialized_obj
)


boolean = Boolean()
23 changes: 23 additions & 0 deletions ssz/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
from ssz.sedes import (
boolean,
)


def is_sedes(obj):
"""
Check if `obj` is a sedes object.
A sedes object is characterized by having the methods
`serialize(obj)` and `deserialize(serial)`.
"""
return hasattr(obj, 'serialize') and hasattr(obj, 'deserialize')


def infer_sedes(obj):
"""
Try to find a sedes objects suitable for a given Python object.
"""
if isinstance(obj, bool):
return boolean

msg = 'Did not find sedes handling type {}'.format(type(obj).__name__)
raise TypeError(msg)
92 changes: 92 additions & 0 deletions tests/core/test_boolean_serializer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import pytest

from ssz import (
DeserializationError,
SerializationError,
decode,
encode,
)
from ssz.sedes import (
Boolean,
)


@pytest.mark.parametrize(
'value,expected',
(
(True, b'\x01'),
(False, b'\x00'),
),
)
def test_boolean_serialize_values(value, expected):
sedes = Boolean()
assert sedes.serialize(value) == expected


@pytest.mark.parametrize(
'value',
(
None,
1,
0,
'True',
b'True',
),
)
def test_boolean_serialize_bad_values(value):
sedes = Boolean()
with pytest.raises(SerializationError):
sedes.serialize(value)


@pytest.mark.parametrize(
'value,expected',
(
(b'\x01', True),
(b'\x00', False),
),
)
def test_boolean_deserialization(value, expected):
sedes = Boolean()
assert sedes.deserialize(value) == expected


@pytest.mark.parametrize(
'value',
(
b' ',
b'\x02',
b'\x00\x00',
b'\x01\x00',
b'\x00\x01',
b'\x01\x01',
),
)
def test_boolean_deserialization_bad_value(value):
sedes = Boolean()
with pytest.raises(DeserializationError):
sedes.deserialize(value)


@pytest.mark.parametrize(
'value,expected',
(
(True, True),
(False, False),
),
)
def test_boolean_round_trip(value, expected):
sedes = Boolean()
assert sedes.deserialize(sedes.serialize(value)) == expected


@pytest.mark.parametrize(
'value,expected',
(
(True, True),
(False, False),
),
)
def test_boolean_round_trip_codec(value, expected):
sedes = Boolean()
assert decode(encode(value), sedes) == expected

0 comments on commit ebbc50b

Please sign in to comment.