Permalink
Browse files

Fixed Bug #2 (double definition of TypedList)

  • Loading branch information...
1 parent 4c5e8a8 commit 473acb83c4e136b8a666418ffa697d780486651a @ceelian committed Jan 13, 2012
Showing with 111 additions and 37 deletions.
  1. +86 −34 src/flatty/flatty.py
  2. +23 −2 src/flatty/tests/test_actions.py
  3. +2 −1 src/flatty/tests/test_utils.py
View
@@ -13,12 +13,52 @@
import types
+class MetaBaseFlattyType(type):
+ def __eq__(self, other):
+ """ We need to overwrite this since the dynamically generated classes
+ with ftype don't return equal when compared unlass compared only by names
+ """
+ if hasattr(self, '__name__') and hasattr(other, '__name__') and \
+ self.__name__ == other.__name__:
+ return True
+ else:
+ return False
+
+ def __instancecheck__(self, inst):
+ """ We also need to overwrite this because of the failing comparrission
+ due to dynamic class generation
+ """
+ candidates = [type(inst), inst.__class__]
+ issubclasslist = []
+ for c in candidates:
+ issubclasslist.append(self.__subclasscheck__(c))
+ return any(issubclasslist)
+
+ def __subclasscheck__(cls, sub):
+ """Implement issubclass(sub, cls)."""
+ candidates = cls.__dict__.get("__subclass__", []) or [cls]
+ str_candidates = []
+ for c in candidates:
+ str_candidates.append(str(c))
+
+ str_c = []
+ for c in sub.mro():
+ str_c.append(str(c))
+
+ intersect = set(str_c).intersection( set(str_candidates) )
+ if len(intersect) > 0: return True
+
+
+
class BaseFlattyType(object):
"""
This class is the base Class for all special flatty schema types.
These are :class:`TypedList` and :class:`TypedDict`
"""
-
+ ftype=None
+ __metaclass__ = MetaBaseFlattyType
+
+
@classmethod
def set_type(cls, ftype):
@@ -30,8 +70,11 @@ def set_type(cls, ftype):
Returns:
a class object with the class variable `ftype` set, used to
determine the instance type during unflattening"""
- cls.ftype = ftype
- return cls
+
+ # class must be generated dynamically otherwise ftype is set on
+ # all classes which caused Bug #2
+ new_cls = type(cls.__name__, cls.__bases__, dict(ftype=ftype, set_type=cls.set_type))
+ return new_cls
class TypedList(BaseFlattyType, list):
"""
@@ -166,7 +209,7 @@ def check_type(cls, attr_type, attr_value):
@classmethod
- def to_flat(cls, obj_type,obj):
+ def to_flat(cls, obj_type, obj):
"""need to be implemented to convert a python object to a primitive
Args:
@@ -194,7 +237,7 @@ class DateConverter(Converter):
"""
@classmethod
- def to_flat(cls,obj_type, obj):
+ def to_flat(cls, obj_type, obj):
if obj == None:
return None
return obj.isoformat()
@@ -211,7 +254,7 @@ class DateTimeConverter(Converter):
"""
@classmethod
- def to_flat(cls,obj_type, obj):
+ def to_flat(cls, obj_type, obj):
if obj == None:
return None
return obj.isoformat()
@@ -228,7 +271,7 @@ class TimeConverter(Converter):
"""
@classmethod
- def to_flat(cls,obj_type, obj):
+ def to_flat(cls, obj_type, obj):
if obj == None:
return None
return obj.strftime("%H:%M:%S.%f")
@@ -246,7 +289,7 @@ class SchemaConverter(Converter):
@classmethod
def check_type(cls, attr_type, attr_value):
if not issubclass(type(attr_value), attr_type):
- raise TypeError(repr(type(attr_value)) +'!=' + repr(attr_type))
+ raise TypeError(repr(type(attr_value)) + '!=' + repr(attr_type))
@classmethod
def to_flat(cls, obj_type, obj):
@@ -260,7 +303,7 @@ def to_flat(cls, obj_type, obj):
# and these are types and not objects
if attr_value == attr_type and inspect.isclass(attr_value):
attr_value = None
-
+
#get the type of default instances in schema definitions
if inspect.isclass(attr_type) == False:
attr_type = type(attr_type)
@@ -272,7 +315,7 @@ def to_flat(cls, obj_type, obj):
return flat_dict
@classmethod
- def to_obj(cls, val_type, val):
+ def to_obj(cls, val_type, val):
#instantiate new object
cls_obj = val_type()
#iterate all attributes
@@ -289,7 +332,7 @@ def to_obj(cls, val_type, val):
conv_attr_value = unflatit(attr_value, flat_val)
check_type(attr_value, conv_attr_value)
- setattr(cls_obj,attr_name,conv_attr_value)
+ setattr(cls_obj, attr_name, conv_attr_value)
return cls_obj
class TypedListConverter(Converter):
@@ -303,21 +346,21 @@ def check_type(cls, attr_type, attr_value):
if not(issubclass(type(attr_value), attr_type) \
or issubclass(type(attr_value), list) \
or type(attr_value) == types.NoneType):
- raise TypeError(repr(type(attr_value)) +'!=' + repr(attr_type))
+ raise TypeError(repr(type(attr_value)) + '!=' + repr(attr_type))
@classmethod
- def to_flat(cls,obj_type, obj):
+ def to_flat(cls, obj_type, obj):
#TODO: CHECK TYPE but we might need also an additional val_type here as
# method argument. Especially for the SchemaConverter because
# there you need to check if the obj is a subclass of type defined in
# the schema
- check_type(obj_type,obj)
+ check_type(obj_type, obj)
if obj == None:
return None
flat_list = []
for item in obj:
- check_type(obj_type.ftype,item)
+ check_type(obj_type.ftype, item)
flat_list.append(flatit(item))
return flat_list
@@ -342,17 +385,17 @@ def check_type(cls, attr_type, attr_value):
if not(issubclass(type(attr_value), attr_type) \
or issubclass(type(attr_value), dict) \
or type(attr_value) == types.NoneType):
- raise TypeError(repr(type(attr_value)) +'!=' + repr(attr_type))
+ raise TypeError(repr(type(attr_value)) + '!=' + repr(attr_type))
@classmethod
- def to_flat(cls, obj_type,obj):
- check_type(obj_type,obj)
+ def to_flat(cls, obj_type, obj):
+ check_type(obj_type, obj)
if obj == None:
return None
flat_dict = {}
- for k,v in obj.items():
- check_type(obj_type.ftype,v)
+ for k, v in obj.items():
+ check_type(obj_type.ftype, v)
flat_dict[k] = flatit(v)
return flat_dict
@@ -361,7 +404,7 @@ def to_obj(cls, val_type, val):
if val == None:
return None
obj = val_type()
- for k,v in val.items():
+ for k, v in val.items():
obj[k] = unflatit(val_type.ftype, v)
return obj
@@ -393,12 +436,15 @@ def to_flat(cls, val_type, obj):
Returns:
a converted primitive object"""
for type in cls._convert_dict:
- if val_type == type:
+ #String comparisson is okay here since we compare schema against
+ #object types which can differ in the ftype class variable therefore
+ #string compare is correct and direct type compare fails
+ if str(val_type) == str(type):
return cls._convert_dict[type]['conv'].to_flat(val_type, obj)
for type in cls._convert_dict:
- if cls._convert_dict[type]['exact']==False and issubclass(val_type, type):
- return cls._convert_dict[type]['conv'].to_flat(val_type,obj)
+ if cls._convert_dict[type]['exact'] == False and issubclass(val_type, type):
+ return cls._convert_dict[type]['conv'].to_flat(val_type, obj)
return obj
@@ -416,12 +462,15 @@ def to_obj(cls, val_type, val):
a converted high level schema object"""
for type in cls._convert_dict:
- if val_type == type:
- return cls._convert_dict[type]['conv'].to_obj(val_type,val)
+ #String comparisson is okay here since we compare schema against
+ #object types which can differ in the ftype class variable therefore
+ #string compare is correct and direct type compare fails
+ if str(val_type) == str(type):
+ return cls._convert_dict[type]['conv'].to_obj(val_type, val)
for type in cls._convert_dict:
- if cls._convert_dict[type]['exact']==False and issubclass(val_type, type):
- return cls._convert_dict[type]['conv'].to_obj(val_type,val)
+ if cls._convert_dict[type]['exact'] == False and issubclass(val_type, type):
+ return cls._convert_dict[type]['conv'].to_obj(val_type, val)
return val
@@ -437,13 +486,16 @@ def check_type(cls, attr_type, attr_value):
Returns:
None if everything is ok, otherwise raise TypeError"""
for type in cls._convert_dict:
- if attr_type == type:
- cls._convert_dict[type]['conv'].check_type(attr_type,attr_value)
+ #String comparisson is okay here since we compare schema against
+ #object types which can differ in the ftype class variable therefore
+ #string compare is correct and direct type compare fails
+ if str(attr_type) == str(type):
+ cls._convert_dict[type]['conv'].check_type(attr_type, attr_value)
return
for type in cls._convert_dict:
- if cls._convert_dict[type]['exact']==False and issubclass(attr_type, type):
- cls._convert_dict[type]['conv'].check_type(attr_type,attr_value)
+ if cls._convert_dict[type]['exact'] == False and issubclass(attr_type, type):
+ cls._convert_dict[type]['conv'].check_type(attr_type, attr_value)
return
_check_type(attr_value, attr_type)
@@ -490,7 +542,7 @@ def check_type(attr_type, attr_value):
attr_value, raise TypeError"""
ConvertManager.check_type(attr_type, attr_value)
-def flatit(obj, obj_type= None):
+def flatit(obj, obj_type=None):
"""one way to flatten the `obj`
Args:
@@ -514,4 +566,4 @@ def unflatit(cls, flat_dict):
Returns:
an instance of type `cls`"""
return ConvertManager.to_obj(cls, flat_dict)
-
+
@@ -52,7 +52,7 @@ class Foo(flatty.Schema):
l2.first_name = "karl"
l2.last_name = "hirsch"
- t1.b = [] #Foo.b()
+ t1.b = []
t1.b.append(l1)
t1.b.append(l2)
@@ -62,7 +62,7 @@ class Foo(flatty.Schema):
self.assertEqual(t1.a, n1.a)
self.assertEqual(len(t1.b), len(n1.b))
self.assertTrue(isinstance(n1.b, list))
- self.assertTrue(isinstance(n1.b, flatty.TypedList))
+ self.assertTrue(isinstance(n1.b,flatty.TypedList))
for i in range(len(t1.b)):
self.assertTrue(isinstance(n1.b[i], Name))
@@ -72,6 +72,26 @@ class Foo(flatty.Schema):
#reflattening and compare if results doesn't change
marsh2 = flatty.flatit(n1)
self.assertEqual(marsh, marsh2)
+
+ def test_typed_list_constructor(self):
+ """ Test for Bug #2
+ """
+
+ class X(flatty.Schema):
+ x = int
+
+ class Y(flatty.Schema):
+ y = int
+
+ class Top(flatty.Schema):
+ t1 = flatty.TypedList.set_type(X)
+ t2 = flatty.TypedList.set_type(Y)
+
+ a = X(x=1)
+ b = Y(y=2)
+ t = Top(t1=[a], t2=[b])
+
+ t.flatit()
def test_types_in_typed_list(self):
class Name(flatty.Schema):
@@ -175,6 +195,7 @@ class Foo(flatty.Schema):
self.assertEqual(t1.a, n1.a)
self.assertEqual(len(t1.b), len(n1.b))
self.assertTrue(isinstance(n1.b, dict))
+ self.assertTrue(str(type(n1.b)) == str(flatty.TypedDict))
self.assertTrue(isinstance(n1.b, flatty.TypedDict))
for k,v in t1.b.items():
@@ -8,7 +8,8 @@
types.StringType,
types.UnicodeType,
types.ListType,
- types.DictType)
+ types.DictType,
+ types.NoneType)
def is_plain_dict(obj):
if isinstance(obj, allowed_types):

0 comments on commit 473acb8

Please sign in to comment.