11"""
22pynamodb attributes tests
33"""
4+ import calendar
45import json
56
67from base64 import b64encode
1314
1415from pynamodb .attributes import (
1516 BinarySetAttribute , BinaryAttribute , DynamicMapAttribute , NumberSetAttribute , NumberAttribute ,
16- UnicodeAttribute , UnicodeSetAttribute , UTCDateTimeAttribute , BooleanAttribute , MapAttribute ,
17+ UnicodeAttribute , UnicodeSetAttribute , UTCDateTimeAttribute , BooleanAttribute , MapAttribute , NullAttribute ,
1718 ListAttribute , JSONAttribute , TTLAttribute , VersionAttribute )
1819from pynamodb .constants import (
19- DEFAULT_ENCODING , NUMBER , STRING , STRING_SET , NUMBER_SET , BINARY_SET ,
20+ DATETIME_FORMAT , DEFAULT_ENCODING , NUMBER , STRING , STRING_SET , NUMBER_SET , BINARY_SET ,
2021 BINARY , BOOLEAN ,
2122)
2223from pynamodb .models import Model
@@ -39,6 +40,7 @@ class Meta:
3940 json_attr = JSONAttribute ()
4041 map_attr = MapAttribute ()
4142 ttl_attr = TTLAttribute ()
43+ null_attr = NullAttribute (null = True )
4244
4345
4446class CustomAttrMap (MapAttribute ):
@@ -1062,3 +1064,119 @@ def test_deserialize(self):
10621064 assert attr .deserialize ('1' ) == 1
10631065 assert attr .deserialize ('3.141' ) == 3
10641066 assert attr .deserialize ('12345678909876543211234234324234' ) == 12345678909876543211234234324234
1067+
1068+
1069+ class TestAttributeContainer :
1070+ def test_to_json (self ):
1071+ now = datetime .now (tz = timezone .utc )
1072+ now_formatted = now .strftime (DATETIME_FORMAT )
1073+ now_unix_ts = calendar .timegm (now .utctimetuple ())
1074+ test_model = AttributeTestModel ()
1075+ test_model .binary_attr = b'foo'
1076+ test_model .binary_set_attr = {b'bar' }
1077+ test_model .number_attr = 1
1078+ test_model .number_set_attr = {0 , 0.5 , 1 }
1079+ test_model .unicode_attr = 'foo'
1080+ test_model .unicode_set_attr = {'baz' }
1081+ test_model .datetime_attr = now
1082+ test_model .bool_attr = True
1083+ test_model .json_attr = {'foo' : 'bar' }
1084+ test_model .map_attr = {'foo' : 'bar' }
1085+ test_model .ttl_attr = now
1086+ test_model .null_attr = True
1087+ assert test_model .to_json () == (
1088+ '{'
1089+ '"binary_attr": "Zm9v", '
1090+ '"binary_set_attr": ["YmFy"], '
1091+ '"bool_attr": true, '
1092+ '"datetime_attr": "' + now_formatted + '", '
1093+ '"json_attr": "{\\ "foo\\ ": \\ "bar\\ "}", '
1094+ '"map_attr": {"foo": "bar"}, '
1095+ '"null_attr": null, '
1096+ '"number_attr": 1, '
1097+ '"number_set_attr": [0, 0.5, 1], '
1098+ '"ttl_attr": ' + str (now_unix_ts ) + ', '
1099+ '"unicode_attr": "foo", '
1100+ '"unicode_set_attr": ["baz"]'
1101+ '}' )
1102+
1103+ def test_from_json (self ):
1104+ now = datetime .now (tz = timezone .utc )
1105+ now_formatted = now .strftime (DATETIME_FORMAT )
1106+ now_unix_ts = calendar .timegm (now .utctimetuple ())
1107+ json_string = (
1108+ '{'
1109+ '"binary_attr": "Zm9v", '
1110+ '"binary_set_attr": ["YmFy"], '
1111+ '"bool_attr": true, '
1112+ '"datetime_attr": "' + now_formatted + '", '
1113+ '"json_attr": "{\\ "foo\\ ": \\ "bar\\ "}", '
1114+ '"map_attr": {"foo": "bar"}, '
1115+ '"null_attr": null, '
1116+ '"number_attr": 1, '
1117+ '"number_set_attr": [0, 0.5, 1], '
1118+ '"ttl_attr": ' + str (now_unix_ts ) + ', '
1119+ '"unicode_attr": "foo", '
1120+ '"unicode_set_attr": ["baz"]'
1121+ '}' )
1122+ test_model = AttributeTestModel ()
1123+ test_model .from_json (json_string )
1124+ assert test_model .binary_attr == b'foo'
1125+ assert test_model .binary_set_attr == {b'bar' }
1126+ assert test_model .number_attr == 1
1127+ assert test_model .number_set_attr == {0 , 0.5 , 1 }
1128+ assert test_model .unicode_attr == 'foo'
1129+ assert test_model .unicode_set_attr == {'baz' }
1130+ assert test_model .datetime_attr == now
1131+ assert test_model .bool_attr is True
1132+ assert test_model .json_attr == {'foo' : 'bar' }
1133+ assert test_model .map_attr .foo == 'bar'
1134+ assert test_model .ttl_attr == now .replace (microsecond = 0 )
1135+ assert test_model .null_attr is None
1136+
1137+ def test_to_json_complex (self ):
1138+ class MyMap (MapAttribute ):
1139+ foo = UnicodeSetAttribute (attr_name = 'bar' )
1140+
1141+ class ListTestModel (Model ):
1142+ class Meta :
1143+ host = 'http://localhost:8000'
1144+ table_name = 'test'
1145+ unicode_attr = UnicodeAttribute (hash_key = True )
1146+ list_attr = ListAttribute (of = NumberSetAttribute )
1147+ list_map_attr = ListAttribute (of = MyMap )
1148+
1149+ list_test_model = ListTestModel ()
1150+ list_test_model .unicode_attr = 'foo'
1151+ list_test_model .list_attr = [{0 , 1 , 2 }]
1152+ list_test_model .list_map_attr = [MyMap (foo = {'baz' })]
1153+ assert list_test_model .to_json () == (
1154+ '{'
1155+ '"list_attr": [[0, 1, 2]], '
1156+ '"list_map_attr": [{"bar": ["baz"]}], '
1157+ '"unicode_attr": "foo"'
1158+ '}' )
1159+
1160+ def test_from_json_complex (self ):
1161+ class MyMap (MapAttribute ):
1162+ foo = UnicodeSetAttribute (attr_name = 'bar' )
1163+
1164+ class ListTestModel (Model ):
1165+ class Meta :
1166+ host = 'http://localhost:8000'
1167+ table_name = 'test'
1168+ unicode_attr = UnicodeAttribute (hash_key = True )
1169+ list_attr = ListAttribute (of = NumberSetAttribute )
1170+ list_map_attr = ListAttribute (of = MyMap )
1171+
1172+ json_string = (
1173+ '{'
1174+ '"list_attr": [[0, 1, 2]], '
1175+ '"list_map_attr": [{"bar": ["baz"]}], '
1176+ '"unicode_attr": "foo"'
1177+ '}' )
1178+ list_test_model = ListTestModel ()
1179+ list_test_model .from_json (json_string )
1180+ assert list_test_model .unicode_attr == 'foo'
1181+ assert list_test_model .list_attr == [{0 , 1 , 2 }]
1182+ assert list_test_model .list_map_attr [0 ].foo == {'baz' }
0 commit comments