Commit 40e9d33e authored by Tres Seaver's avatar Tres Seaver

Merge r121847 from /ZODB/branches/jim-python-btrees.

Tests are now passing (via 'setup.py test').
parent 432e9e8e
......@@ -15,6 +15,7 @@
"""
from struct import pack
from struct import unpack
from BTrees.Interfaces import BTreesConflictError
import persistent
......@@ -25,13 +26,14 @@ _marker = object()
class _Base(persistent.Persistent):
_key_type = list
_to_key = lambda x: x
def __init__(self, items=None):
self.clear()
if items:
self.update(items)
class _BucketBase(_Base):
def clear(self):
self._keys = self._key_type()
self._next = None
......@@ -149,19 +151,23 @@ class _Base(persistent.Persistent):
buckets = []
for state in states:
bucket = self.__class__()
bucket.__setstate__(state)
if state:
bucket.__setstate__(state)
buckets.append(bucket)
if (buckets[1]._next != buckets[0]._next or
buckets[2]._next != buckets[0]._next):
raise BTreesConflictError(-1, -1, -1, 0)
i1 = _SetIteration(buckets[0])
i2 = _SetIteration(buckets[1])
i3 = _SetIteration(buckets[2])
if not (buckets[1] and buckets[2]):
raise BTreesConflictError(-1, -1, -1, 12)
i1 = _SetIteration(buckets[0], True)
i2 = _SetIteration(buckets[1], True)
i3 = _SetIteration(buckets[2], True)
def merge_error(reason):
return BTreesConflictError(i1.position, i2.position, i3.position,
reason)
return BTreesConflictError(
i1.position, i2.position, i3.position, reason)
result = self.__class__()
......@@ -228,7 +234,7 @@ class _Base(persistent.Persistent):
elif cmp13 > 0: # insert i3
merge_output(i3)
else:
merge_error(5) # both deleted same key
raise merge_error(5) # both deleted same key
while i2.active and i3.active: # new inserts
cmp23 = cmp(i2.key, i3.key)
......@@ -247,7 +253,7 @@ class _Base(persistent.Persistent):
i1.advance()
i2.advance()
else: # dueling deletes or delete and change
merge_error(7)
raise merge_error(7)
while i1.active and i3.active: # committed deletes rest of original
cmp13 = cmp(i1.key, i3.key)
......@@ -258,16 +264,16 @@ class _Base(persistent.Persistent):
i1.advance()
i3.advance()
else: # dueling deletes or delete and change
merge_error(8)
raise merge_error(8)
if i1.active: # dueling deletes
merge_error(9)
raise merge_error(9)
while i2.active:
merge_output(i2)
while i3.active:
merge_output(i2)
merge_output(i3)
if len(result._keys) == 0:
# If the output bucket is empty, conflict resolution doesn't have
......@@ -294,7 +300,7 @@ class _SetIteration:
self.useValues = useValues
self._next = itmeth().next
self.active
self.active = True
self.position = 0
self.advance()
......@@ -355,14 +361,14 @@ class _SetBase(_Base):
for i in items:
add(i)
class Bucket(_MappingBase):
class Bucket(_MappingBase, _BucketBase):
_value_type = list
_to_value = lambda x: x
VALUE_SAME_CHECK = False
def clear(self):
_Base.clear(self)
_BucketBase.clear(self)
self._values = self._value_type()
def get(self, key, default=None):
......@@ -473,7 +479,7 @@ class Bucket(_MappingBase):
keys.append(state[i])
values.append(state[i+1])
class Set(_SetBase):
class Set(_SetBase, _BucketBase):
def __getstate__(self):
data = tuple(self._keys)
......@@ -819,6 +825,32 @@ class _Tree(_MappingBase):
else:
assert_(False, "Incorrect child type")
def _p_resolveConflict(self, *states):
states = map(_get_simple_btree_bucket_state, states)
return ((self._bucket_type()._p_resolveConflict(*states), ), )
def _get_simple_btree_bucket_state(state):
if state is None:
return state
if not isinstance(state, tuple):
raise TypeError("_p_resolveConflict: expected tuple or None for state")
if len(state) == 2:
raise BTreesConflictError(-1, -1, -1, 11)
if len(state) != 1:
raise TypeError("_p_resolveConflict: expected 1- or 2-tuple for state")
state = state[0]
if not isinstance(state, tuple) or len(state) != 1:
raise TypeError("_p_resolveConflict: expected 1-tuple containing "
"bucket state");
state = state[0]
if not isinstance(state, tuple):
raise TypeError("_p_resolveConflict: expected tuple for bucket state")
return state
class _TreeItems(object):
def __init__(self, firstbucket, itertype, iterargs):
......@@ -863,14 +895,17 @@ class _TreeItems(object):
bucket = self.firstbucket
itertype = self.itertype
iterargs = self.iterargs
done = 0
# Note that we don't mind if the first bucket yields no
# results due to an idiosyncrasy in how range searches are done.
while bucket is not None:
done = 1
for k in getattr(bucket, itertype)(*iterargs):
yield k
done = 0
if done:
return
bucket = bucket._next
done = 1
# class _Slice:
......@@ -914,7 +949,7 @@ class _TreeItems(object):
# _, _, _, self._len = self._get_len(self.base, self.slice_)
# return self._len
class Tree(_Tree, _MappingBase):
class Tree(_Tree):
def get(self, key, default=None):
bucket = self._findbucket(key)
......@@ -991,6 +1026,9 @@ def _set_operation(s1, s2,
i.advance()
else:
r = s1._set_type()
def copy(i, w):
r._keys.append(i.key)
i.advance()
while i1.active and i2.active:
cmp_ = cmp(i1.key, i2.key)
......@@ -1017,26 +1055,35 @@ def _set_operation(s1, s2,
return r
def difference(o1, o2):
class setop(object):
def __init__(self, func, set_type):
self.func = func
self.set_type = set_type
def __call__(self, *a, **k):
return self.func(self.set_type, *a, **k)
def difference(set_type, o1, o2):
if o1 is None or o2 is None:
return o1
return _set_operation(o1, o2, 1, 0, 1, 0, 1, 0, 0)
def union(o1, o2):
def union(set_type, o1, o2):
if o1 is None:
return o2
if o2 is None:
return o1
return _set_operation(o1, o2, 0, 0, 1, 1, 1, 1, 1)
def intersection(o1, o2):
def intersection(set_type, o1, o2):
if o1 is None:
return o2
if o2 is None:
return o1
return _set_operation(o1, o2, 0, 0, 1, 1, 0, 1, 0)
def weightedUnion(o1, o2, w1=1, w2=1):
def weightedUnion(set_type, o1, o2, w1=1, w2=1):
if o1 is None:
if o2 is None:
return 0, o2
......@@ -1047,7 +1094,7 @@ def weightedUnion(o1, o2, w1=1, w2=1):
else:
return 1, _set_operation(o1, o2, 1, 1, w1, w2, 0, 1, 0)
def weightedIntersection(o1, o2, w1=1, w2=1):
def weightedIntersection(set_type, o1, o2, w1=1, w2=1):
if o1 is None:
if o2 is None:
return 0, o2
......@@ -1061,26 +1108,30 @@ def weightedIntersection(o1, o2, w1=1, w2=1):
_set_operation(o1, o2, 1, 1, w1, w2, 0, 1, 0),
)
def multiunion(seqs):
def multiunion(set_type, seqs):
# XXX simple/slow implementation. Goal is just to get tests to pass.
if not seqs:
return None
result = seqs[0]._set_type()
return set_type()
result = set_type()
for s in seqs:
try:
iter(s)
except TypeError:
s = set_type((s, ))
result.update(s)
return result
def to_ob(self, v):
return v
int_types = int, long
def to_int(self, v):
try:
pack("i", v)
if not unpack("i", pack("i", v))[0] == v:
raise TypeError('32-bit integer expected')
except struct.error:
raise TypeError('32-bit integer expected')
if isinstance(v, float):
raise TypeError('32-bit integer expected')
return int(v)
def to_float(self, v):
......@@ -1092,13 +1143,15 @@ def to_float(self, v):
def to_long(self, v):
try:
pack("q", v)
if not unpack("q", pack("q", v))[0] == v:
if isinstance(v, int_types):
raise ValueError("Value out of range", v)
raise TypeError('64-bit integer expected')
except struct.error:
if isinstance(v, int_types):
raise ValueError("Value out of range", v)
raise TypeError('64-bit integer expected')
if isinstance(v, float):
raise TypeError('32-bit integer expected')
return int(v)
def to_str(l):
......@@ -1135,12 +1188,19 @@ def _import(globals, prefix, bucket_size, tree_size,
Set = set,
BTree = tree,
TreeSet = treeset,
difference = difference,
union = union,
intersection = intersection,
weightedUnion = weightedUnion,
weightedIntersection = weightedIntersection,
multiunion = multiunion,
difference = setop(difference, set),
union = setop(union, set),
intersection = setop(intersection, set),
using64bits='L' in prefix,
)
if prefix[0] in 'IL':
globals.update(
multiunion = setop(multiunion, set),
)
if prefix[1] != 'O':
globals.update(
weightedUnion = setop(weightedUnion, set),
weightedIntersection = setop(weightedIntersection, set),
)
del globals['___BTree']
......@@ -2149,6 +2149,7 @@ class FamilyTest(unittest.TestCase):
s.insert(BTrees.family64.minint)
self.assert_(BTrees.family64.minint in s)
s = LOTreeSet()
# XXX why oh why do we expect ValueError here, but TypeError in test32?
self.assertRaises(ValueError, s.insert, BTrees.family64.maxint + 1)
self.assertRaises(ValueError, s.insert, BTrees.family64.minint - 1)
self.check_pickling(BTrees.family64)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment