Skip to content

Commit

Permalink
Merge pull request #3 from devinaconley/type_0.0.3
Browse files Browse the repository at this point in the history
Support for nested field types
  • Loading branch information
devinaconley authored Dec 4, 2019
2 parents 2b5fa49 + 38d8ded commit f4158af
Show file tree
Hide file tree
Showing 5 changed files with 142 additions and 8 deletions.
2 changes: 1 addition & 1 deletion objectfactory/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@
from .factory import Factory
from .field import Nested, List

__version__ = '0.0.2'
__version__ = '0.0.3'
33 changes: 29 additions & 4 deletions objectfactory/field.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,20 @@ def deserialize_field( self, instance, value ):
"""
if value is None:
return
obj = Factory.create_object( value )

if '_type' in value:
obj = Factory.create_object( value )
if self._field_type and not isinstance( obj, self._field_type ):
raise ValueError(
'{} is not an instance of type: {}'.format(
type( obj ).__name__, self._field_type.__name__ )
)
elif self._field_type:
obj = self._field_type()
obj.deserialize( value )
else:
raise ValueError( 'Cannot infer type information' )

setattr( instance, self._key, obj )


Expand All @@ -50,10 +63,10 @@ class List( Field ):
field type for list of serializable objects
"""

def __init__( self, default=None ):
def __init__( self, default=None, name=None, field_type=None ):
if default is None:
default = []
super().__init__( default )
super().__init__( default=default, name=name, field_type=field_type )

def serialize_field( self, instance, deserializable=True ):
"""
Expand Down Expand Up @@ -82,5 +95,17 @@ def deserialize_field( self, instance, value ):
"""
lst = []
for body in value:
lst.append( Factory.create_object( body ) )
if '_type' in body:
obj = Factory.create_object( body )
if self._field_type and not isinstance( obj, self._field_type ):
raise ValueError(
'{} is not an instance of type: {}'.format(
type( obj ).__name__, self._field_type.__name__ )
)
elif self._field_type:
obj = self._field_type()
obj.deserialize( body )
else:
raise ValueError( 'Cannot infer type information' )
lst.append( obj )
setattr( instance, self._key, lst )
3 changes: 2 additions & 1 deletion objectfactory/serializable.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,11 @@ class Field( object ):
serializable objects
"""

def __init__( self, default=None, name=None ):
def __init__( self, default=None, name=None, field_type=None ):
self._name = name
self._key = None # note: this will be set from parent metaclass __new__
self._default = default
self._field_type = field_type

def __get__( self, instance, owner ):
return getattr( instance, self._key, self._default )
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

setuptools.setup(
name='objectfactory',
version='0.0.2',
version='0.0.3',
author='Devin A. Conley',
author_email='devinaconley@gmail.com',
description='A python package for the serializable model / factory pattern',
Expand Down
110 changes: 109 additions & 1 deletion test/test_fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
module for testing functionality of serializable fields
"""

# lib
import pytest

# src
from objectfactory import Factory, Serializable, Field, Nested, List
from .test_serializable import MyBasicClass
Expand All @@ -21,7 +24,16 @@ class MyComplexClass( Serializable ):
"""
complex class to test hierarchical serialization
"""
nested = Nested( MyBasicClass )
nested = Nested()
prop = Field()


@Factory.register_class
class MyTypedComplexClass( Serializable ):
"""
complex class to test hierarchical serialization
"""
nested = Nested( field_type=MyBasicClass )
prop = Field()


Expand All @@ -34,6 +46,15 @@ class MyOtherComplexClass( Serializable ):
nested_list_prop = List()


@Factory.register_class
class MyOtherTypedComplexClass( Serializable ):
"""
complex class to test list serialization
"""
str_prop = Field()
nested_list_prop = List( field_type=MyBasicClass )


@Factory.register_class
class MyClassWithFieldOptionals( Serializable ):
"""
Expand Down Expand Up @@ -98,6 +119,56 @@ def test_deserialize( self ):
assert obj.nested.str_prop == 'random string'
assert obj.nested.int_prop == 4321

def test_deserialize_typed( self ):
"""
test deserialization without _type field
expect nested json to be deserialized into a MyBasicClass object that is
a member of MyTypedComplexClass, even without nested _type field
:return:
"""
body = {
'_type': 'MyComplexClass',
'prop': 'really cool property',
'nested': {
'str_prop': 'random string',
'int_prop': 4321
}
}

obj = MyTypedComplexClass()
obj.deserialize( body )

assert isinstance( obj, MyTypedComplexClass )
assert obj.prop == 'really cool property'
assert isinstance( obj.nested, MyBasicClass )
assert obj.nested.str_prop == 'random string'
assert obj.nested.int_prop == 4321

def test_deserialize_enforce( self ):
"""
test deserialization enforcing field type
expect an error to be thrown on deserialization because the nested
field is of the incorrect type
:return:
"""
body = {
'_type': 'MyComplexClass',
'prop': 'really cool property',
'nested': {
'_type': 'MyBasicClassWithLists',
'str_prop': 'random string',
'int_prop': 4321
}
}

obj = MyTypedComplexClass()
with pytest.raises( ValueError ):
obj.deserialize( body )


class TestPrimitiveList( object ):
"""
Expand Down Expand Up @@ -267,6 +338,43 @@ def test_deserialize( self ):
assert nested_obj.str_prop == nested_strings[i]
assert nested_obj.int_prop == nested_ints[i]

def test_deserialize_typed( self ):
"""
test deserialization without _type field
expect list of nested json objects to be deserialized into a list
of MyBasicClass objects that is a member of MyComplexClass, even without
_type field specified
:return:
"""
body = {
'_type': 'MyOtherTypedComplexClass',
'str_prop': 'really great string property',
'nested_list_prop': []
}
nested_strings = ['some string', 'another string', 'one more string']
nested_ints = [101, 102, 103]

for s, n in zip( nested_strings, nested_ints ):
body['nested_list_prop'].append(
{
'str_prop': s,
'int_prop': n
}
)

obj = MyOtherTypedComplexClass()
obj.deserialize( body )

assert isinstance( obj, MyOtherTypedComplexClass )
assert obj.str_prop == 'really great string property'
assert len( obj.nested_list_prop ) == 3
for i, nested_obj in enumerate( obj.nested_list_prop ):
assert isinstance( nested_obj, MyBasicClass )
assert nested_obj.str_prop == nested_strings[i]
assert nested_obj.int_prop == nested_ints[i]


class TestFieldOptionals( object ):
"""
Expand Down

0 comments on commit f4158af

Please # to comment.