Skip to content

Commit 00f322a

Browse files
committed
New OrderedMap type in support of nested collections
PYTHON-186 Updated existing tests, still need to add tests for nested collections.
1 parent 6b1cb95 commit 00f322a

4 files changed

Lines changed: 81 additions & 11 deletions

File tree

cassandra/cqltypes.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@
4747
int32_pack, int32_unpack, int64_pack, int64_unpack,
4848
float_pack, float_unpack, double_pack, double_unpack,
4949
varint_pack, varint_unpack)
50-
from cassandra.util import OrderedDict, sortedset
50+
from cassandra.util import OrderedMap, sortedset
5151

5252
apache_cassandra_type_prefix = 'org.apache.cassandra.db.marshal.'
5353

@@ -741,7 +741,7 @@ def deserialize_safe(cls, byts, protocol_version):
741741
length = 2
742742
numelements = unpack(byts[:length])
743743
p = length
744-
themap = OrderedDict()
744+
themap = OrderedMap()
745745
for _ in range(numelements):
746746
key_len = unpack(byts[p:p + length])
747747
p += length
@@ -753,7 +753,7 @@ def deserialize_safe(cls, byts, protocol_version):
753753
p += val_len
754754
key = subkeytype.from_binary(keybytes, protocol_version)
755755
val = subvaltype.from_binary(valbytes, protocol_version)
756-
themap[key] = val
756+
themap._insert(key, val)
757757
return themap
758758

759759
@classmethod

cassandra/encoder.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
from uuid import UUID
2929
import six
3030

31-
from cassandra.util import OrderedDict, sortedset
31+
from cassandra.util import OrderedDict, OrderedMap, sortedset
3232

3333
if six.PY3:
3434
long = int
@@ -76,6 +76,7 @@ def __init__(self):
7676
datetime.date: self.cql_encode_date,
7777
dict: self.cql_encode_map_collection,
7878
OrderedDict: self.cql_encode_map_collection,
79+
OrderedMap: self.cql_encode_map_collection,
7980
list: self.cql_encode_list_collection,
8081
tuple: self.cql_encode_list_collection,
8182
set: self.cql_encode_set_collection,

cassandra/util.py

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -555,3 +555,73 @@ def _intersect(self, other):
555555
if item in other:
556556
isect.add(item)
557557
return isect
558+
559+
from collections import Mapping
560+
import six
561+
from six.moves import cPickle
562+
563+
class OrderedMap(Mapping):
564+
'''
565+
An ordered map that accepts non-hashable types for keys.
566+
567+
Implemented in support of Cassandra nested collections.
568+
569+
'''
570+
def __init__(self, *args, **kwargs):
571+
if len(args) > 1:
572+
raise TypeError('expected at most 1 arguments, got %d' % len(args))
573+
574+
self._items = []
575+
self._index = {}
576+
if args:
577+
e = args[0]
578+
if callable(getattr(e, 'keys', None)):
579+
for k in e.keys():
580+
self._items.append((k, e[k]))
581+
else:
582+
for k, v in e:
583+
self._insert(k, v)
584+
585+
for k, v in six.iteritems(kwargs):
586+
self._insert(k, v)
587+
588+
def _insert(self, key, value):
589+
flat_key = self._serialize_key(key)
590+
i = self._index.get(flat_key, -1)
591+
if i >= 0:
592+
self._items[i] = (key, value)
593+
else:
594+
self._items.append((key, value))
595+
self._index[flat_key] = len(self._items) - 1
596+
597+
def __getitem__(self, key):
598+
index = self._index[self._serialize_key(key)]
599+
return self._items[index][1]
600+
601+
def __iter__(self):
602+
for i in self._items:
603+
yield i[0]
604+
605+
def __len__(self):
606+
return len(self._items)
607+
608+
def __eq__(self, other):
609+
if isinstance(other, OrderedMap):
610+
return self._items == other._items
611+
try:
612+
d = dict(other)
613+
return len(d) == len(self._items) and all(i[1] == d[i[0]] for i in self._items)
614+
except KeyError:
615+
return False
616+
except TypeError:
617+
pass
618+
return NotImplemented
619+
620+
def __repr__(self):
621+
return '%s[%s]' % (
622+
self.__class__.__name__,
623+
', '.join("(%r, %r)" % (k, v) for k, v in self._items))
624+
625+
@staticmethod
626+
def _serialize_key(key):
627+
return cPickle.dumps(key)

tests/unit/test_marshalling.py

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
from uuid import UUID
2525

2626
from cassandra.cqltypes import lookup_casstype
27-
from cassandra.util import OrderedDict, sortedset
27+
from cassandra.util import OrderedMap, sortedset
2828

2929
marshalled_value_pairs = (
3030
# binary form, type, python native type
@@ -75,21 +75,20 @@
7575
(b'', 'MapType(AsciiType, BooleanType)', None),
7676
(b'', 'ListType(FloatType)', None),
7777
(b'', 'SetType(LongType)', None),
78-
(b'\x00\x00', 'MapType(DecimalType, BooleanType)', OrderedDict()),
78+
(b'\x00\x00', 'MapType(DecimalType, BooleanType)', OrderedMap()),
7979
(b'\x00\x00', 'ListType(FloatType)', []),
8080
(b'\x00\x00', 'SetType(IntegerType)', sortedset()),
8181
(b'\x00\x01\x00\x10\xafYC\xa3\xea<\x11\xe1\xabc\xc4,\x03"y\xf0', 'ListType(TimeUUIDType)', [UUID(bytes=b'\xafYC\xa3\xea<\x11\xe1\xabc\xc4,\x03"y\xf0')]),
8282
)
8383

84-
ordered_dict_value = OrderedDict()
85-
ordered_dict_value[u'\u307fbob'] = 199
86-
ordered_dict_value[u''] = -1
87-
ordered_dict_value[u'\\'] = 0
84+
ordered_map_value = OrderedMap([(u'\u307fbob', 199),
85+
(u'', -1),
86+
(u'\\', 0)])
8887

8988
# these following entries work for me right now, but they're dependent on
9089
# vagaries of internal python ordering for unordered types
9190
marshalled_value_pairs_unsafe = (
92-
(b'\x00\x03\x00\x06\xe3\x81\xbfbob\x00\x04\x00\x00\x00\xc7\x00\x00\x00\x04\xff\xff\xff\xff\x00\x01\\\x00\x04\x00\x00\x00\x00', 'MapType(UTF8Type, Int32Type)', ordered_dict_value),
91+
(b'\x00\x03\x00\x06\xe3\x81\xbfbob\x00\x04\x00\x00\x00\xc7\x00\x00\x00\x04\xff\xff\xff\xff\x00\x01\\\x00\x04\x00\x00\x00\x00', 'MapType(UTF8Type, Int32Type)', ordered_map_value),
9392
(b'\x00\x02\x00\x08@\x01\x99\x99\x99\x99\x99\x9a\x00\x08@\x14\x00\x00\x00\x00\x00\x00', 'SetType(DoubleType)', sortedset([2.2, 5.0])),
9493
(b'\x00', 'IntegerType', 0),
9594
)

0 commit comments

Comments
 (0)