tested django-newsletter

This commit is contained in:
Esther Kleinhenz
2018-10-26 15:21:17 +02:00
parent 7ed667d043
commit 811b7c5453
2352 changed files with 448169 additions and 1 deletions
@@ -0,0 +1,790 @@
/*****************************************************************************
Copyright (c) 2001, 2002 Zope Foundation and Contributors.
All Rights Reserved.
This software is subject to the provisions of the Zope Public License,
Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
FOR A PARTICULAR PURPOSE
****************************************************************************/
#define BTREEITEMSTEMPLATE_C "$Id$\n"
/* A BTreeItems struct is returned from calling .items(), .keys() or
* .values() on a BTree-based data structure, and is also the result of
* taking slices of those. It represents a contiguous slice of a BTree.
*
* The start of the slice is in firstbucket, at offset first. The end of
* the slice is in lastbucket, at offset last. Both endpoints are inclusive.
* It must possible to get from firstbucket to lastbucket via following
* bucket 'next' pointers zero or more times. firstbucket, first, lastbucket,
* and last are readonly after initialization. An empty slice is represented
* by firstbucket == lastbucket == currentbucket == NULL.
*
* 'kind' determines whether this slice represents 'k'eys alone, 'v'alues
* alone, or 'i'items (key+value pairs). 'kind' is also readonly after
* initialization.
*
* The combination of currentbucket, currentoffset and pseudoindex acts as
* a search finger. Offset currentoffset in bucket currentbucket is at index
* pseudoindex, where pseudoindex==0 corresponds to offset first in bucket
* firstbucket, and pseudoindex==-1 corresponds to offset last in bucket
* lastbucket. The function BTreeItems_seek() can be used to set this combo
* correctly for any in-bounds index, and uses this combo on input to avoid
* needing to search from the start (or end) on each call. Calling
* BTreeItems_seek() with consecutive larger positions is very efficent.
* Calling it with consecutive smaller positions is more efficient than if
* a search finger weren't being used at all, but is still quadratic time
* in the number of buckets in the slice.
*/
typedef struct
{
PyObject_HEAD
Bucket *firstbucket; /* First bucket */
Bucket *currentbucket; /* Current bucket (search finger) */
Bucket *lastbucket; /* Last bucket */
int currentoffset; /* Offset in currentbucket */
int pseudoindex; /* search finger index */
int first; /* Start offset in firstbucket */
int last; /* End offset in lastbucket */
char kind; /* 'k', 'v', 'i' */
} BTreeItems;
#define ITEMS(O)((BTreeItems*)(O))
static PyObject *
newBTreeItems(char kind,
Bucket *lowbucket, int lowoffset,
Bucket *highbucket, int highoffset);
static void
BTreeItems_dealloc(BTreeItems *self)
{
Py_XDECREF(self->firstbucket);
Py_XDECREF(self->lastbucket);
Py_XDECREF(self->currentbucket);
PyObject_DEL(self);
}
static Py_ssize_t
BTreeItems_length_or_nonzero(BTreeItems *self, int nonzero)
{
Py_ssize_t r;
Bucket *b, *next;
b = self->firstbucket;
if (b == NULL)
return 0;
r = self->last + 1 - self->first;
if (nonzero && r > 0)
/* Short-circuit if all we care about is nonempty */
return 1;
if (b == self->lastbucket)
return r;
Py_INCREF(b);
PER_USE_OR_RETURN(b, -1);
while ((next = b->next))
{
r += b->len;
if (nonzero && r > 0)
/* Short-circuit if all we care about is nonempty */
break;
if (next == self->lastbucket)
break; /* we already counted the last bucket */
Py_INCREF(next);
PER_UNUSE(b);
Py_DECREF(b);
b = next;
PER_USE_OR_RETURN(b, -1);
}
PER_UNUSE(b);
Py_DECREF(b);
return r >= 0 ? r : 0;
}
static Py_ssize_t
BTreeItems_length(BTreeItems *self)
{
return BTreeItems_length_or_nonzero(self, 0);
}
/*
** BTreeItems_seek
**
** Find the ith position in the BTreeItems.
**
** Arguments: self The BTree
** i the index to seek to, in 0 .. len(self)-1, or in
** -len(self) .. -1, as for indexing a Python sequence.
**
**
** Returns 0 if successful, -1 on failure to seek (like out-of-bounds).
** Upon successful return, index i is at offset self->currentoffset in bucket
** self->currentbucket.
*/
static int
BTreeItems_seek(BTreeItems *self, Py_ssize_t i)
{
int delta, pseudoindex, currentoffset;
Bucket *b, *currentbucket;
int error;
pseudoindex = self->pseudoindex;
currentoffset = self->currentoffset;
currentbucket = self->currentbucket;
if (currentbucket == NULL)
goto no_match;
delta = i - pseudoindex;
while (delta > 0) /* move right */
{
int max;
/* Want to move right delta positions; the most we can move right in
* this bucket is currentbucket->len - currentoffset - 1 positions.
*/
PER_USE_OR_RETURN(currentbucket, -1);
max = currentbucket->len - currentoffset - 1;
b = currentbucket->next;
PER_UNUSE(currentbucket);
if (delta <= max)
{
currentoffset += delta;
pseudoindex += delta;
if (currentbucket == self->lastbucket
&& currentoffset > self->last)
goto no_match;
break;
}
/* Move to start of next bucket. */
if (currentbucket == self->lastbucket || b == NULL)
goto no_match;
currentbucket = b;
pseudoindex += max + 1;
delta -= max + 1;
currentoffset = 0;
}
while (delta < 0) /* move left */
{
int status;
/* Want to move left -delta positions; the most we can move left in
* this bucket is currentoffset positions.
*/
if ((-delta) <= currentoffset)
{
currentoffset += delta;
pseudoindex += delta;
if (currentbucket == self->firstbucket
&& currentoffset < self->first)
goto no_match;
break;
}
/* Move to end of previous bucket. */
if (currentbucket == self->firstbucket)
goto no_match;
status = PreviousBucket(&currentbucket, self->firstbucket);
if (status == 0)
goto no_match;
else if (status < 0)
return -1;
pseudoindex -= currentoffset + 1;
delta += currentoffset + 1;
PER_USE_OR_RETURN(currentbucket, -1);
currentoffset = currentbucket->len - 1;
PER_UNUSE(currentbucket);
}
assert(pseudoindex == i);
/* Alas, the user may have mutated the bucket since the last time we
* were called, and if they deleted stuff, we may be pointing into
* trash memory now.
*/
PER_USE_OR_RETURN(currentbucket, -1);
error = currentoffset < 0 || currentoffset >= currentbucket->len;
PER_UNUSE(currentbucket);
if (error)
{
PyErr_SetString(PyExc_RuntimeError,
"the bucket being iterated changed size");
return -1;
}
Py_INCREF(currentbucket);
Py_DECREF(self->currentbucket);
self->currentbucket = currentbucket;
self->currentoffset = currentoffset;
self->pseudoindex = pseudoindex;
return 0;
no_match:
IndexError(i);
return -1;
}
/* Return the right kind ('k','v','i') of entry from bucket b at offset i.
* b must be activated. Returns NULL on error.
*/
static PyObject *
getBucketEntry(Bucket *b, int i, char kind)
{
PyObject *result = NULL;
assert(b);
assert(0 <= i && i < b->len);
switch (kind)
{
case 'k':
COPY_KEY_TO_OBJECT(result, b->keys[i]);
break;
case 'v':
COPY_VALUE_TO_OBJECT(result, b->values[i]);
break;
case 'i':
{
PyObject *key;
PyObject *value;;
COPY_KEY_TO_OBJECT(key, b->keys[i]);
if (!key)
break;
COPY_VALUE_TO_OBJECT(value, b->values[i]);
if (!value)
{
Py_DECREF(key);
break;
}
result = PyTuple_New(2);
if (result)
{
PyTuple_SET_ITEM(result, 0, key);
PyTuple_SET_ITEM(result, 1, value);
}
else
{
Py_DECREF(key);
Py_DECREF(value);
}
break;
}
default:
PyErr_SetString(PyExc_AssertionError,
"getBucketEntry: unknown kind");
break;
}
return result;
}
/*
** BTreeItems_item
**
** Arguments: self a BTreeItems structure
** i Which item to inspect
**
** Returns: the BTreeItems_item_BTree of self->kind, i
** (ie pulls the ith item out)
*/
static PyObject *
BTreeItems_item(BTreeItems *self, Py_ssize_t i)
{
PyObject *result;
if (BTreeItems_seek(self, i) < 0)
return NULL;
PER_USE_OR_RETURN(self->currentbucket, NULL);
result = getBucketEntry(self->currentbucket, self->currentoffset,
self->kind);
PER_UNUSE(self->currentbucket);
return result;
}
/*
** BTreeItems_slice
**
** Creates a new BTreeItems structure representing the slice
** between the low and high range
**
** Arguments: self The old BTreeItems structure
** ilow The start index
** ihigh The end index
**
** Returns: BTreeItems item
*/
static PyObject *
BTreeItems_slice(BTreeItems *self, Py_ssize_t ilow, Py_ssize_t ihigh)
{
Bucket *lowbucket;
Bucket *highbucket;
int lowoffset;
int highoffset;
Py_ssize_t length = -1; /* len(self), but computed only if needed */
/* Complications:
* A Python slice never raises IndexError, but BTreeItems_seek does.
* Python did only part of index normalization before calling this:
* ilow may be < 0 now, and ihigh may be arbitrarily large. It's
* our responsibility to clip them.
* A Python slice is exclusive of the high index, but a BTreeItems
* struct is inclusive on both ends.
*/
/* First adjust ilow and ihigh to be legit endpoints in the Python
* sense (ilow inclusive, ihigh exclusive). This block duplicates the
* logic from Python's list_slice function (slicing for builtin lists).
*/
if (ilow < 0)
ilow = 0;
else
{
if (length < 0)
length = BTreeItems_length(self);
if (ilow > length)
ilow = length;
}
if (ihigh < ilow)
ihigh = ilow;
else
{
if (length < 0)
length = BTreeItems_length(self);
if (ihigh > length)
ihigh = length;
}
assert(0 <= ilow && ilow <= ihigh);
assert(length < 0 || ihigh <= length);
/* Now adjust for that our struct is inclusive on both ends. This is
* easy *except* when the slice is empty: there's no good way to spell
* that in an inclusive-on-both-ends scheme. For example, if the
* slice is btree.items([:0]), ilow == ihigh == 0 at this point, and if
* we were to subtract 1 from ihigh that would get interpreted by
* BTreeItems_seek as meaning the *entire* set of items. Setting ilow==1
* and ihigh==0 doesn't work either, as BTreeItems_seek raises IndexError
* if we attempt to seek to ilow==1 when the underlying sequence is empty.
* It seems simplest to deal with empty slices as a special case here.
*/
if (ilow == ihigh) /* empty slice */
{
lowbucket = highbucket = NULL;
lowoffset = 1;
highoffset = 0;
}
else
{
assert(ilow < ihigh);
--ihigh; /* exclusive -> inclusive */
if (BTreeItems_seek(self, ilow) < 0)
return NULL;
lowbucket = self->currentbucket;
lowoffset = self->currentoffset;
if (BTreeItems_seek(self, ihigh) < 0)
return NULL;
highbucket = self->currentbucket;
highoffset = self->currentoffset;
}
return newBTreeItems(self->kind,
lowbucket, lowoffset, highbucket, highoffset);
}
static PyObject *
BTreeItems_subscript(BTreeItems *self, PyObject* subscript)
{
Py_ssize_t len = BTreeItems_length_or_nonzero(self, 0);
if (PyIndex_Check(subscript))
{
Py_ssize_t i = PyNumber_AsSsize_t(subscript, PyExc_IndexError);
if (i == -1 && PyErr_Occurred())
return NULL;
if (i < 0)
i += len;
return BTreeItems_item(self, i);
}
if (PySlice_Check(subscript))
{
Py_ssize_t start, stop, step, slicelength;
#ifdef PY3K
#define SLICEOBJ(x) (x)
#else
#define SLICEOBJ(x) (PySliceObject*)(x)
#endif
if (PySlice_GetIndicesEx(SLICEOBJ(subscript), len,
&start, &stop, &step, &slicelength) < 0)
{
return NULL;
}
if (step != 1)
{
PyErr_SetString(PyExc_RuntimeError,
"slices must have step size of 1");
return NULL;
}
return BTreeItems_slice(self, start, stop);
}
PyErr_SetString(PyExc_RuntimeError,
"Unknown index type: must be int or slice");
return NULL;
}
/* Py3K doesn't honor sequence slicing, so implement via mapping */
static PyMappingMethods BTreeItems_as_mapping = {
(lenfunc)BTreeItems_length, /* mp_length */
(binaryfunc)BTreeItems_subscript, /* mp_subscript */
};
static PySequenceMethods BTreeItems_as_sequence =
{
(lenfunc) BTreeItems_length, /* sq_length */
(binaryfunc)0, /* sq_concat */
(ssizeargfunc)0, /* sq_repeat */
(ssizeargfunc) BTreeItems_item, /* sq_item */
#ifndef PY3K
/* Py3K doesn't honor this slot */
(ssizessizeargfunc) BTreeItems_slice, /* sq_slice */
#endif
};
/* Number Method items (just for nb_nonzero!) */
static int
BTreeItems_nonzero(BTreeItems *self)
{
return BTreeItems_length_or_nonzero(self, 1);
}
static PyNumberMethods BTreeItems_as_number_for_nonzero = {
0, /* nb_add */
0, /* nb_subtract */
0, /* nb_multiply */
#ifndef PY3K
0, /* nb_divide */
#endif
0, /* nb_remainder */
0, /* nb_divmod */
0, /* nb_power */
0, /* nb_negative */
0, /* nb_positive */
0, /* nb_absolute */
(inquiry)BTreeItems_nonzero /* nb_nonzero */
};
static PyTypeObject BTreeItemsType = {
PyVarObject_HEAD_INIT(NULL, 0)
MOD_NAME_PREFIX "BTreeItems", /* tp_name */
sizeof(BTreeItems), /* tp_basicsize */
0, /* tp_itemsize */
/* methods */
(destructor) BTreeItems_dealloc, /* tp_dealloc */
0, /* tp_print */
0, /* obsolete tp_getattr */
0, /* obsolete tp_setattr */
0, /* tp_compare */
0, /* tp_repr */
&BTreeItems_as_number_for_nonzero, /* tp_as_number */
&BTreeItems_as_sequence, /* tp_as_sequence */
&BTreeItems_as_mapping, /* tp_as_mapping */
(hashfunc)0, /* tp_hash */
(ternaryfunc)0, /* tp_call */
(reprfunc)0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
/* Space for future expansion */
0L,0L,
"Sequence type used to iterate over BTree items." /* Documentation string */
};
/* Returns a new BTreeItems object representing the contiguous slice from
* offset lowoffset in bucket lowbucket through offset highoffset in bucket
* highbucket, inclusive. Pass lowbucket == NULL for an empty slice.
* The currentbucket is set to lowbucket, currentoffset ot lowoffset, and
* pseudoindex to 0. kind is 'k', 'v' or 'i' (see BTreeItems struct docs).
*/
static PyObject *
newBTreeItems(char kind,
Bucket *lowbucket, int lowoffset,
Bucket *highbucket, int highoffset)
{
BTreeItems *self;
UNLESS (self = PyObject_NEW(BTreeItems, &BTreeItemsType))
return NULL;
self->kind=kind;
self->first=lowoffset;
self->last=highoffset;
if (! lowbucket || ! highbucket
|| (lowbucket == highbucket && lowoffset > highoffset))
{
self->firstbucket = 0;
self->lastbucket = 0;
self->currentbucket = 0;
}
else
{
Py_INCREF(lowbucket);
self->firstbucket = lowbucket;
Py_INCREF(highbucket);
self->lastbucket = highbucket;
Py_INCREF(lowbucket);
self->currentbucket = lowbucket;
}
self->currentoffset = lowoffset;
self->pseudoindex = 0;
return OBJECT(self);
}
static int
nextBTreeItems(SetIteration *i)
{
if (i->position >= 0)
{
if (i->position)
{
DECREF_KEY(i->key);
DECREF_VALUE(i->value);
}
if (BTreeItems_seek(ITEMS(i->set), i->position) >= 0)
{
Bucket *currentbucket;
currentbucket = BUCKET(ITEMS(i->set)->currentbucket);
UNLESS(PER_USE(currentbucket))
{
/* Mark iteration terminated, so that finiSetIteration doesn't
* try to redundantly decref the key and value
*/
i->position = -1;
return -1;
}
COPY_KEY(i->key, currentbucket->keys[ITEMS(i->set)->currentoffset]);
INCREF_KEY(i->key);
COPY_VALUE(i->value,
currentbucket->values[ITEMS(i->set)->currentoffset]);
INCREF_VALUE(i->value);
i->position ++;
PER_UNUSE(currentbucket);
}
else
{
i->position = -1;
PyErr_Clear();
}
}
return 0;
}
static int
nextTreeSetItems(SetIteration *i)
{
if (i->position >= 0)
{
if (i->position)
{
DECREF_KEY(i->key);
}
if (BTreeItems_seek(ITEMS(i->set), i->position) >= 0)
{
Bucket *currentbucket;
currentbucket = BUCKET(ITEMS(i->set)->currentbucket);
UNLESS(PER_USE(currentbucket))
{
/* Mark iteration terminated, so that finiSetIteration doesn't
* try to redundantly decref the key and value
*/
i->position = -1;
return -1;
}
COPY_KEY(i->key, currentbucket->keys[ITEMS(i->set)->currentoffset]);
INCREF_KEY(i->key);
i->position ++;
PER_UNUSE(currentbucket);
}
else
{
i->position = -1;
PyErr_Clear();
}
}
return 0;
}
/* Support for the iteration protocol new in Python 2.2. */
static PyTypeObject BTreeIter_Type;
/* The type of iterator objects, returned by e.g. iter(IIBTree()). */
typedef struct
{
PyObject_HEAD
/* We use a BTreeItems object because it's convenient and flexible.
* We abuse it two ways:
* 1. We set currentbucket to NULL when the iteration is finished.
* 2. We don't bother keeping pseudoindex in synch.
*/
BTreeItems *pitems;
} BTreeIter;
/* Return a new iterator object, to traverse the keys and/or values
* represented by pitems. pitems must not be NULL. Returns NULL if error.
*/
static BTreeIter *
BTreeIter_new(BTreeItems *pitems)
{
BTreeIter *result;
assert(pitems != NULL);
result = PyObject_New(BTreeIter, &BTreeIter_Type);
if (result)
{
Py_INCREF(pitems);
result->pitems = pitems;
}
return result;
}
/* The iterator's tp_dealloc slot. */
static void
BTreeIter_dealloc(BTreeIter *bi)
{
Py_DECREF(bi->pitems);
PyObject_Del(bi);
}
/* The implementation of the iterator's tp_iternext slot. Returns "the next"
* item; returns NULL if error; returns NULL without setting an error if the
* iteration is exhausted (that's the way to terminate the iteration protocol).
*/
static PyObject *
BTreeIter_next(BTreeIter *bi, PyObject *args)
{
PyObject *result = NULL; /* until proven innocent */
BTreeItems *items = bi->pitems;
int i = items->currentoffset;
Bucket *bucket = items->currentbucket;
if (bucket == NULL) /* iteration termination is sticky */
return NULL;
PER_USE_OR_RETURN(bucket, NULL);
if (i >= bucket->len)
{
/* We never leave this routine normally with i >= len: somebody
* else mutated the current bucket.
*/
PyErr_SetString(PyExc_RuntimeError,
"the bucket being iterated changed size");
/* Arrange for that this error is sticky too. */
items->currentoffset = INT_MAX;
goto Done;
}
/* Build the result object, from bucket at offset i. */
result = getBucketEntry(bucket, i, items->kind);
/* Advance position for next call. */
if (bucket == items->lastbucket && i >= items->last)
{
/* Next call should terminate the iteration. */
Py_DECREF(items->currentbucket);
items->currentbucket = NULL;
}
else
{
++i;
if (i >= bucket->len)
{
Py_XINCREF(bucket->next);
items->currentbucket = bucket->next;
Py_DECREF(bucket);
i = 0;
}
items->currentoffset = i;
}
Done:
PER_UNUSE(bucket);
return result;
}
static PyObject *
BTreeIter_getiter(PyObject *it)
{
Py_INCREF(it);
return it;
}
static PyTypeObject BTreeIter_Type = {
PyVarObject_HEAD_INIT(NULL, 0)
MODULE_NAME MOD_NAME_PREFIX "TreeIterator", /* tp_name */
sizeof(BTreeIter), /* tp_basicsize */
0, /* tp_itemsize */
/* methods */
(destructor)BTreeIter_dealloc, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_compare */
0, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
0, /*PyObject_GenericGetAttr,*/ /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT, /* tp_flags */
0, /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
(getiterfunc)BTreeIter_getiter, /* tp_iter */
(iternextfunc)BTreeIter_next, /* tp_iternext */
0, /* tp_methods */
0, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
};
@@ -0,0 +1,671 @@
/*****************************************************************************
Copyright (c) 2001, 2002 Zope Foundation and Contributors.
All Rights Reserved.
This software is subject to the provisions of the Zope Public License,
Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
FOR A PARTICULAR PURPOSE
****************************************************************************/
#include "Python.h"
/* include structmember.h for offsetof */
#include "structmember.h"
#include "bytesobject.h"
#ifdef PERSISTENT
#include "persistent/cPersistence.h"
#else
#define PER_USE_OR_RETURN(self, NULL)
#define PER_ALLOW_DEACTIVATION(self)
#define PER_PREVENT_DEACTIVATION(self)
#define PER_DEL(self)
#define PER_USE(O) 1
#define PER_ACCESSED(O) 1
#endif
#include "_compat.h"
/* So sue me. This pair gets used all over the place, so much so that it
* interferes with understanding non-persistence parts of algorithms.
* PER_UNUSE can be used after a successul PER_USE or PER_USE_OR_RETURN.
* It allows the object to become ghostified, and tells the persistence
* machinery that the object's fields were used recently.
*/
#define PER_UNUSE(OBJ) do { \
PER_ALLOW_DEACTIVATION(OBJ); \
PER_ACCESSED(OBJ); \
} while (0)
/* The tp_name slots of the various BTree types contain the fully
* qualified names of the types, e.g. zodb.btrees.OOBTree.OOBTree.
* The full name is usd to support pickling and because it is not
* possible to modify the __module__ slot of a type dynamically. (This
* may be a bug in Python 2.2).
*
* The MODULE_NAME here used to be "BTrees._". We actually want the module
* name to point to the Python module rather than the C, so the underline
* is now removed.
*/
#define MODULE_NAME "BTrees." MOD_NAME_PREFIX "BTree."
static PyObject *sort_str, *reverse_str, *__setstate___str;
static PyObject *_bucket_type_str, *max_internal_size_str, *max_leaf_size_str;
static PyObject *ConflictError = NULL;
static void PyVar_Assign(PyObject **v, PyObject *e) { Py_XDECREF(*v); *v=e;}
#define ASSIGN(V,E) PyVar_Assign(&(V),(E))
#define UNLESS(E) if (!(E))
#define OBJECT(O) ((PyObject*)(O))
#define MIN_BUCKET_ALLOC 16
#define SameType_Check(O1, O2) (Py_TYPE((O1))==Py_TYPE((O2)))
#define ASSERT(C, S, R) if (! (C)) { \
PyErr_SetString(PyExc_AssertionError, (S)); return (R); }
#ifdef NEED_LONG_LONG_SUPPORT
/* Helper code used to support long long instead of int. */
#ifndef PY_LONG_LONG
#error "PY_LONG_LONG required but not defined"
#endif
#ifdef NEED_LONG_LONG_KEYS
static int
longlong_check(PyObject *ob)
{
if (INT_CHECK(ob))
return 1;
if (PyLong_Check(ob)) {
int overflow;
(void)PyLong_AsLongLongAndOverflow(ob, &overflow);
if (overflow)
goto overflow;
return 1;
}
return 0;
overflow:
PyErr_SetString(PyExc_ValueError,
"longlong_check: long integer out of range");
return 0;
}
#endif
static PyObject *
longlong_as_object(PY_LONG_LONG val)
{
if ((val > LONG_MAX) || (val < LONG_MIN))
return PyLong_FromLongLong(val);
return INT_FROM_LONG((long)val);
}
static int
longlong_convert(PyObject *ob, PY_LONG_LONG *value)
{
#ifndef PY3K
if (PyInt_Check(ob))
{
(*value) = (PY_LONG_LONG)PyInt_AS_LONG(ob);
return 1;
}
#endif
if (!PyLong_Check(ob))
{
PyErr_SetString(PyExc_TypeError, "expected integer key");
return 0;
}
else
{
PY_LONG_LONG val;
int overflow;
val = PyLong_AsLongLongAndOverflow(ob, &overflow);
if (overflow)
goto overflow;
(*value) = val;
return 1;
}
overflow:
PyErr_SetString(PyExc_ValueError, "long integer out of range");
return 0;
}
#endif /* NEED_LONG_LONG_SUPPORT */
/* Various kinds of BTree and Bucket structs are instances of
* "sized containers", and have a common initial layout:
* The stuff needed for all Python objects, or all Persistent objects.
* int size: The maximum number of things that could be contained
* without growing the container.
* int len: The number of things currently contained.
*
* Invariant: 0 <= len <= size.
*
* A sized container typically goes on to declare one or more pointers
* to contiguous arrays with 'size' elements each, the initial 'len' of
* which are currently in use.
*/
#ifdef PERSISTENT
#define sizedcontainer_HEAD \
cPersistent_HEAD \
int size; \
int len;
#else
#define sizedcontainer_HEAD \
PyObject_HEAD \
int size; \
int len;
#endif
/* Nothing is actually of type Sized, but (pointers to) BTree nodes and
* Buckets can be cast to Sized* in contexts that only need to examine
* the members common to all sized containers.
*/
typedef struct Sized_s {
sizedcontainer_HEAD
} Sized;
#define SIZED(O) ((Sized*)(O))
/* A Bucket wraps contiguous vectors of keys and values. Keys are unique,
* and stored in sorted order. The 'values' pointer may be NULL if the
* Bucket is used to implement a set. Buckets serving as leafs of BTrees
* are chained together via 'next', so that the entire BTree contents
* can be traversed in sorted order quickly and easily.
*/
typedef struct Bucket_s {
sizedcontainer_HEAD
struct Bucket_s *next; /* the bucket with the next-larger keys */
KEY_TYPE *keys; /* 'len' keys, in increasing order */
VALUE_TYPE *values; /* 'len' corresponding values; NULL if a set */
} Bucket;
#define BUCKET(O) ((Bucket*)(O))
/* A BTree is complicated. See Maintainer.txt.
*/
typedef struct BTreeItem_s {
KEY_TYPE key;
Sized *child; /* points to another BTree, or to a Bucket of some sort */
} BTreeItem;
typedef struct BTree_s {
sizedcontainer_HEAD
/* firstbucket points to the bucket containing the smallest key in
* the BTree. This is found by traversing leftmost child pointers
* (data[0].child) until reaching a Bucket.
*/
Bucket *firstbucket;
/* The BTree points to 'len' children, via the "child" fields of the data
* array. There are len-1 keys in the 'key' fields, stored in increasing
* order. data[0].key is unused. For i in 0 .. len-1, all keys reachable
* from data[i].child are >= data[i].key and < data[i+1].key, at the
* endpoints pretending that data[0].key is minus infinity and
* data[len].key is positive infinity.
*/
BTreeItem *data;
long max_internal_size;
long max_leaf_size;
} BTree;
static PyTypeObject BTreeType;
static PyTypeObject BucketType;
#define BTREE(O) ((BTree*)(O))
/* Use BTREE_SEARCH to find which child pointer to follow.
* RESULT An int lvalue to hold the index i such that SELF->data[i].child
* is the correct node to search next.
* SELF A pointer to a BTree node.
* KEY The key you're looking for, of type KEY_TYPE.
* ONERROR What to do if key comparison raises an exception; for example,
* perhaps 'return NULL'.
*
* See Maintainer.txt for discussion: this is optimized in subtle ways.
* It's recommended that you call this at the start of a routine, waiting
* to check for self->len == 0 after.
*/
#define BTREE_SEARCH(RESULT, SELF, KEY, ONERROR) { \
int _lo = 0; \
int _hi = (SELF)->len; \
int _i, _cmp; \
for (_i = _hi >> 1; _i > _lo; _i = (_lo + _hi) >> 1) { \
TEST_KEY_SET_OR(_cmp, (SELF)->data[_i].key, (KEY)) \
ONERROR; \
if (_cmp < 0) _lo = _i; \
else if (_cmp > 0) _hi = _i; \
else /* equal */ break; \
} \
(RESULT) = _i; \
}
/* SetIteration structs are used in the internal set iteration protocol.
* When you want to iterate over a set or bucket or BTree (even an
* individual key!),
* 1. Declare a new iterator:
* SetIteration si = {0,0,0};
* Using "{0,0,0}" or "{0,0}" appear most common. Only one {0} is
* necssary. At least one must be given so that finiSetIteration() works
* correctly even if you don't get around to calling initSetIteration().
* 2. Initialize it via
* initSetIteration(&si, PyObject *s, useValues)
* It's an error if that returns an int < 0. In case of error on the
* init call, calling finiSetIteration(&si) is optional. But if the
* init call succeeds, you must eventually call finiSetIteration(),
* and whether or not subsequent calls to si.next() fail.
* 3. Get the first element:
* if (si.next(&si) < 0) { there was an error }
* If the set isn't empty, this sets si.position to an int >= 0,
* si.key to the element's key (of type KEY_TYPE), and maybe si.value to
* the element's value (of type VALUE_TYPE). si.value is defined
* iff si.usesValue is true.
* 4. Process all the elements:
* while (si.position >= 0) {
* do something with si.key and/or si.value;
* if (si.next(&si) < 0) { there was an error; }
* }
* 5. Finalize the SetIterator:
* finiSetIteration(&si);
* This is mandatory! si may contain references to iterator objects,
* keys and values, and they must be cleaned up else they'll leak. If
* this were C++ we'd hide that in the destructor, but in C you have to
* do it by hand.
*/
typedef struct SetIteration_s
{
PyObject *set; /* the set, bucket, BTree, ..., being iterated */
int position; /* initialized to 0; set to -1 by next() when done */
int usesValue; /* true iff 'set' has values & we iterate them */
KEY_TYPE key; /* next() sets to next key */
VALUE_TYPE value; /* next() may set to next value */
int (*next)(struct SetIteration_s*); /* function to get next key+value */
} SetIteration;
/* Finish the set iteration protocol. This MUST be called by everyone
* who starts a set iteration, unless the initial call to initSetIteration
* failed; in that case, and only that case, calling finiSetIteration is
* optional.
*/
static void
finiSetIteration(SetIteration *i)
{
assert(i != NULL);
if (i->set == NULL)
return;
Py_DECREF(i->set);
i->set = NULL; /* so it doesn't hurt to call this again */
if (i->position > 0) {
/* next() was called at least once, but didn't finish iterating
* (else position would be negative). So the cached key and
* value need to be cleaned up.
*/
DECREF_KEY(i->key);
if (i->usesValue) {
DECREF_VALUE(i->value);
}
}
i->position = -1; /* stop any stray next calls from doing harm */
}
static PyObject *
IndexError(int i)
{
PyObject *v;
v = INT_FROM_LONG(i);
if (!v) {
v = Py_None;
Py_INCREF(v);
}
PyErr_SetObject(PyExc_IndexError, v);
Py_DECREF(v);
return NULL;
}
/* Search for the bucket immediately preceding *current, in the bucket chain
* starting at first. current, *current and first must not be NULL.
*
* Return:
* 1 *current holds the correct bucket; this is a borrowed reference
* 0 no such bucket exists; *current unaltered
* -1 error; *current unaltered
*/
static int
PreviousBucket(Bucket **current, Bucket *first)
{
Bucket *trailing = NULL; /* first travels; trailing follows it */
int result = 0;
assert(current && *current && first);
if (first == *current)
return 0;
do {
trailing = first;
PER_USE_OR_RETURN(first, -1);
first = first->next;
((trailing)->state==cPersistent_STICKY_STATE
&&
((trailing)->state=cPersistent_UPTODATE_STATE));
PER_ACCESSED(trailing);
if (first == *current) {
*current = trailing;
result = 1;
break;
}
} while (first);
return result;
}
static void *
BTree_Malloc(size_t sz)
{
void *r;
ASSERT(sz > 0, "non-positive size malloc", NULL);
r = malloc(sz);
if (r)
return r;
PyErr_NoMemory();
return NULL;
}
static void *
BTree_Realloc(void *p, size_t sz)
{
void *r;
ASSERT(sz > 0, "non-positive size realloc", NULL);
if (p)
r = realloc(p, sz);
else
r = malloc(sz);
UNLESS (r)
PyErr_NoMemory();
return r;
}
/* Shared keyword-argument list for BTree/Bucket
* (iter)?(keys|values|items)
*/
static char *search_keywords[] = {"min", "max",
"excludemin", "excludemax",
0};
#include "BTreeItemsTemplate.c"
#include "BucketTemplate.c"
#include "SetTemplate.c"
#include "BTreeTemplate.c"
#include "TreeSetTemplate.c"
#include "SetOpTemplate.c"
#include "MergeTemplate.c"
static struct PyMethodDef module_methods[] = {
{"difference", (PyCFunction) difference_m, METH_VARARGS,
"difference(o1, o2) -- "
"compute the difference between o1 and o2"
},
{"union", (PyCFunction) union_m, METH_VARARGS,
"union(o1, o2) -- compute the union of o1 and o2\n"
},
{"intersection", (PyCFunction) intersection_m, METH_VARARGS,
"intersection(o1, o2) -- "
"compute the intersection of o1 and o2"
},
#ifdef MERGE
{"weightedUnion", (PyCFunction) wunion_m, METH_VARARGS,
"weightedUnion(o1, o2 [, w1, w2]) -- compute the union of o1 and o2\n"
"\nw1 and w2 are weights."
},
{"weightedIntersection", (PyCFunction) wintersection_m, METH_VARARGS,
"weightedIntersection(o1, o2 [, w1, w2]) -- "
"compute the intersection of o1 and o2\n"
"\nw1 and w2 are weights."
},
#endif
#ifdef MULTI_INT_UNION
{"multiunion", (PyCFunction) multiunion_m, METH_VARARGS,
"multiunion(seq) -- compute union of a sequence of integer sets.\n"
"\n"
"Each element of seq must be an integer set, or convertible to one\n"
"via the set iteration protocol. The union returned is an IISet."
},
#endif
{NULL, NULL} /* sentinel */
};
static char BTree_module_documentation[] =
"\n"
MASTER_ID
BTREEITEMSTEMPLATE_C
"$Id$\n"
BTREETEMPLATE_C
BUCKETTEMPLATE_C
KEYMACROS_H
MERGETEMPLATE_C
SETOPTEMPLATE_C
SETTEMPLATE_C
TREESETTEMPLATE_C
VALUEMACROS_H
BTREEITEMSTEMPLATE_C
;
int
init_persist_type(PyTypeObject *type)
{
#ifdef PY3K
((PyObject*)type)->ob_type = &PyType_Type;
#else
type->ob_type = &PyType_Type;
#endif
type->tp_base = cPersistenceCAPI->pertype;
if (PyType_Ready(type) < 0)
return 0;
return 1;
}
#ifdef PY3K
static struct PyModuleDef moduledef = {
PyModuleDef_HEAD_INIT,
"_" MOD_NAME_PREFIX "BTree", /* m_name */
BTree_module_documentation, /* m_doc */
-1, /* m_size */
module_methods, /* m_methods */
NULL, /* m_reload */
NULL, /* m_traverse */
NULL, /* m_clear */
NULL, /* m_free */
};
#endif
static PyObject*
module_init(void)
{
PyObject *module, *mod_dict, *interfaces, *conflicterr;
#ifdef KEY_TYPE_IS_PYOBJECT
object_ = PyTuple_GetItem(Py_TYPE(Py_None)->tp_bases, 0);
if (object_ == NULL)
return NULL;
#endif
sort_str = INTERN("sort");
if (!sort_str)
return NULL;
reverse_str = INTERN("reverse");
if (!reverse_str)
return NULL;
__setstate___str = INTERN("__setstate__");
if (!__setstate___str)
return NULL;
_bucket_type_str = INTERN("_bucket_type");
if (!_bucket_type_str)
return NULL;
max_internal_size_str = INTERN("max_internal_size");
if (! max_internal_size_str)
return NULL;
max_leaf_size_str = INTERN("max_leaf_size");
if (! max_leaf_size_str)
return NULL;
/* Grab the ConflictError class */
interfaces = PyImport_ImportModule("BTrees.Interfaces");
if (interfaces != NULL)
{
conflicterr = PyObject_GetAttrString(interfaces, "BTreesConflictError");
if (conflicterr != NULL)
ConflictError = conflicterr;
Py_DECREF(interfaces);
}
if (ConflictError == NULL)
{
Py_INCREF(PyExc_ValueError);
ConflictError=PyExc_ValueError;
}
/* Initialize the PyPersist_C_API and the type objects. */
#ifdef PY3K
cPersistenceCAPI = (cPersistenceCAPIstruct *)PyCapsule_Import(
"persistent.cPersistence.CAPI", 0);
#else
cPersistenceCAPI = (cPersistenceCAPIstruct *)PyCObject_Import(
"persistent.cPersistence", "CAPI");
#endif
if (cPersistenceCAPI == NULL) {
/* The Capsule API attempts to import 'persistent' and then
* walk down to the specified attribute using getattr. If the C
* extensions aren't available, this can result in an
* AttributeError being raised. Let that percolate up as an
* ImportError so it can be caught in the expected way.
*/
if (PyErr_Occurred() && !PyErr_ExceptionMatches(PyExc_ImportError)) {
PyErr_SetString(PyExc_ImportError, "persistent C extension unavailable");
}
return NULL;
}
#ifdef PY3K
#define _SET_TYPE(typ) ((PyObject*)(&typ))->ob_type = &PyType_Type
#else
#define _SET_TYPE(typ) (typ).ob_type = &PyType_Type
#endif
_SET_TYPE(BTreeItemsType);
_SET_TYPE(BTreeIter_Type);
BTreeIter_Type.tp_getattro = PyObject_GenericGetAttr;
BucketType.tp_new = PyType_GenericNew;
SetType.tp_new = PyType_GenericNew;
BTreeType.tp_new = PyType_GenericNew;
TreeSetType.tp_new = PyType_GenericNew;
if (!init_persist_type(&BucketType))
return NULL;
if (!init_persist_type(&BTreeType))
return NULL;
if (!init_persist_type(&SetType))
return NULL;
if (!init_persist_type(&TreeSetType))
return NULL;
if (PyDict_SetItem(BTreeType.tp_dict, _bucket_type_str,
(PyObject *)&BucketType) < 0)
{
fprintf(stderr, "btree failed\n");
return NULL;
}
if (PyDict_SetItem(TreeSetType.tp_dict, _bucket_type_str,
(PyObject *)&SetType) < 0)
{
fprintf(stderr, "bucket failed\n");
return NULL;
}
/* Create the module and add the functions */
#ifdef PY3K
module = PyModule_Create(&moduledef);
#else
module = Py_InitModule4("_" MOD_NAME_PREFIX "BTree",
module_methods, BTree_module_documentation,
(PyObject *)NULL, PYTHON_API_VERSION);
#endif
/* Add some symbolic constants to the module */
mod_dict = PyModule_GetDict(module);
if (PyDict_SetItemString(mod_dict, MOD_NAME_PREFIX "Bucket",
(PyObject *)&BucketType) < 0)
return NULL;
if (PyDict_SetItemString(mod_dict, MOD_NAME_PREFIX "BTree",
(PyObject *)&BTreeType) < 0)
return NULL;
if (PyDict_SetItemString(mod_dict, MOD_NAME_PREFIX "Set",
(PyObject *)&SetType) < 0)
return NULL;
if (PyDict_SetItemString(mod_dict, MOD_NAME_PREFIX "TreeSet",
(PyObject *)&TreeSetType) < 0)
return NULL;
if (PyDict_SetItemString(mod_dict, MOD_NAME_PREFIX "TreeIterator",
(PyObject *)&BTreeIter_Type) < 0)
return NULL;
/* We also want to be able to access these constants without the prefix
* so that code can more easily exchange modules (particularly the integer
* and long modules, but also others). The TreeIterator is only internal,
* so we don't bother to expose that.
*/
if (PyDict_SetItemString(mod_dict, "Bucket",
(PyObject *)&BucketType) < 0)
return NULL;
if (PyDict_SetItemString(mod_dict, "BTree",
(PyObject *)&BTreeType) < 0)
return NULL;
if (PyDict_SetItemString(mod_dict, "Set",
(PyObject *)&SetType) < 0)
return NULL;
if (PyDict_SetItemString(mod_dict, "TreeSet",
(PyObject *)&TreeSetType) < 0)
return NULL;
#if defined(ZODB_64BIT_INTS) && defined(NEED_LONG_LONG_SUPPORT)
if (PyDict_SetItemString(mod_dict, "using64bits", Py_True) < 0)
return NULL;
#else
if (PyDict_SetItemString(mod_dict, "using64bits", Py_False) < 0)
return NULL;
#endif
return module;
}
#ifdef PY3K
PyMODINIT_FUNC INITMODULE(void)
{
return module_init();
}
#else
PyMODINIT_FUNC INITMODULE(void)
{
module_init();
}
#endif
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,429 @@
=====================
Developer Information
=====================
This document provides information for developers who maintain or extend
`BTrees`.
Macros
======
`BTrees` are defined using a "template", roughly akin to a C++ template. To
create a new family of `BTrees`, create a source file that defines macros used
to handle differences in key and value types:
Configuration Macros
--------------------
``MASTER_ID``
A string to hold an RCS/CVS Id key to be included in compiled binaries.
``MOD_NAME_PREFIX``
A string (like "IO" or "OO") that provides the prefix used for the module.
This gets used to generate type names and the internal module name string.
``DEFAULT_MAX_BUCKET_SIZE``
An int giving the maximum bucket size (number of key/value pairs). When a
bucket gets larger than this due to an insertion *into a BTREE*, it
splits. Inserting into a bucket directly doesn't split, and functions
that produce a bucket output (e.g., ``union()``) also have no bound on how
large a bucket may get. Someday this will be tunable on `BTree`.
instances.
``DEFAULT_MAX_BTREE_SIZE``
An ``int`` giving the maximum size (number of children) of an internal
btree node. Someday this will be tunable on ``BTree`` instances.
Macros for Keys
---------------
``KEY_TYPE``
The C type declaration for keys (e.g., ``int`` or ``PyObject*``).
``KEY_TYPE_IS_PYOBJECT``
Define if ``KEY_TYPE`` is a ``PyObject*`, else ``undef``.
``KEY_CHECK(K)``
Tests whether the ``PyObject* K`` can be converted to the (``C``) key type
(``KEY_TYPE``). The macro should return a boolean (zero for false,
non-zero for true). When it returns false, its caller should probably set
a ``TypeError`` exception.
``KEY_CHECK_ON_SET(K)``
Like ``KEY_CHECK``, but only checked during ``__setitem__``.
``TEST_KEY_SET_OR(V, K, T)``
Like Python's ``cmp()``. Compares K(ey) to T(arget), where ``K``
and ``T`` are ``C`` values of type `KEY_TYPE`. ``V`` is assigned an `int`
value depending on the outcome::
< 0 if K < T
== 0 if K == T
> 0 if K > T
This macro acts like an ``if``, where the following statement is executed
only if a Python exception has been raised because the values could not be
compared.
``DECREF_KEY(K)``
``K`` is a value of ``KEY_TYPE``. If ``KEY_TYPE`` is a flavor of
``PyObject*``, write this to do ``Py_DECREF(K)``. Else (e.g.,
``KEY_TYPE`` is ``int``) make it a nop.
``INCREF_KEY(K)``
``K`` is a value of `KEY_TYPE`. If `KEY_TYPE` is a flavor of
``PyObject*``, write this to do ``Py_INCREF(K)``. Else (e.g., `KEY_TYPE`
is ``int``) make it a nop.
``COPY_KEY(K, E)``
Like ``K=E``. Copy a key from ``E`` to ``K``, both of ``KEY_TYPE``. Note
that this doesn't ``decref K`` or ``incref E`` when ``KEY_TYPE`` is a
``PyObject*``; the caller is responsible for keeping refcounts straight.
``COPY_KEY_TO_OBJECT(O, K)``
Roughly like ``O=K``. ``O`` is a ``PyObject*``, and the macro must build
a Python object form of ``K``, assign it to ``O``, and ensure that ``O``
owns the reference to its new value. It may do this by creating a new
Python object based on ``K`` (e.g., ``PyInt_FromLong(K)`` when
``KEY_TYPE`` is ``int``), or simply by doing ``Py_INCREF(K)`` if
``KEY_TYPE`` is a ``PyObject*``.
``COPY_KEY_FROM_ARG(TARGET, ARG, STATUS)``
Copy an argument to the target without creating a new reference to
``ARG``. ``ARG`` is a ``PyObject*``, and ``TARGET`` is of type
``KEY_TYPE``. If this can't be done (for example, ``KEY_CHECK(ARG)``
returns false), set a Python error and set status to ``0``. If there is
no error, leave status alone.
Macros for Values
-----------------
``VALUE_TYPE``
The C type declaration for values (e.g., ``int`` or ``PyObject*``).
``VALUE_TYPE_IS_PYOBJECT``
Define if ``VALUE_TYPE`` is a ``PyObject*``, else ``undef``.
``TEST_VALUE(X, Y)``
Like Python's ``cmp()``. Compares ``X`` to ``Y``, where ``X`` & ``Y`` are
``C`` values of type ``VALUE_TYPE``. The macro returns an ``int``, with
value::
< 0 if X < Y
== 0 if X == Y
> 0 if X > Y
Bug: There is no provision for determining whether the comparison attempt
failed (set a Python exception).
``DECREF_VALUE(K)``
Like ``DECREF_KEY``, except applied to values of ``VALUE_TYPE``.
``INCREF_VALUE(K)``
Like ``INCREF_KEY``, except applied to values of ``VALUE_TYPE``.
``COPY_VALUE(K, E)``
Like ``COPY_KEY``, except applied to values of ``VALUE_TYPE``.
``COPY_VALUE_TO_OBJECT(O, K)``
Like ``COPY_KEY_TO_OBJECT``, except applied to values of ``VALUE_TYPE``.
``COPY_VALUE_FROM_ARG(TARGET, ARG, STATUS)``
Like ``COPY_KEY_FROM_ARG``, except applied to values of ``VALUE_TYPE``.
``NORMALIZE_VALUE(V, MIN)``
Normalize the value, ``V``, using the parameter ``MIN``. This is almost
certainly a YAGNI. It is a no-op for most types. For integers, ``V`` is
replaced by ``V/MIN`` only if ``MIN > 0``.
Macros for Set Operations
-------------------------
``MERGE_DEFAULT``
A value of ``VALUE_TYPE`` specifying the value to associate with set
elements when sets are merged with mappings via weighed union or weighted
intersection.
``MERGE(O1, w1, O2, w2)``
Performs a weighted merge of two values, ``O1`` and ``O2``, using weights
``w1`` and ``w2``. The result must be of ``VALUE_TYPE``. Note that
weighted unions and weighted intersections are not enabled if this macro
is left undefined.
``MERGE_WEIGHT(O, w)``
Computes a weighted value for ``O``. The result must be of
``VALUE_TYPE``. This is used for "filling out" weighted unions, i.e. to
compute a weighted value for keys that appear in only one of the input
mappings. If left undefined, ``MERGE_WEIGHT`` defaults to::
#define MERGE_WEIGHT(O, w) (O)
``MULTI_INT_UNION``
The value doesn't matter. If defined, `SetOpTemplate.c` compiles code for
a ``multiunion()`` function (compute a union of many input sets at high
speed). This currently makes sense only for structures with integer keys.
BTree Clues
===========
More or less random bits of helpful info.
+ In papers and textbooks, this flavor of BTree is usually called a B+-Tree,
where "+" is a superscript.
+ All keys and all values live in the bucket leaf nodes. Keys in interior
(BTree) nodes merely serve to guide a search efficiently toward the correct
leaf.
+ When a key is deleted, it's physically removed from the bucket it's in, but
this doesn't propagate back up the tree: since keys in interior nodes only
serve to guide searches, it's OK-- and saves time --to leave "stale" keys in
interior nodes.
+ No attempt is made to rebalance the tree after a deletion, unless a bucket
thereby becomes entirely empty. "Classic BTrees" do rebalance, keeping all
buckets at least half full (provided there are enough keys in the entire
tree to fill half a bucket). The tradeoffs are murky. Pathological cases
in the presence of deletion do exist. Pathologies include trees tending
toward only one key per bucket, and buckets at differing depths (all buckets
are at the same depth in a classic BTree).
+ ``DEFAULT_MAX_BUCKET_SIZE`` and ``DEFAULT_MAX_BTREE_SIZE`` are chosen mostly
to "even out" pickle sizes in storage. That's why, e.g., an `IIBTree` has
larger values than an `OOBTree`: pickles store ints more efficiently than
they can store arbitrary Python objects.
+ In a non-empty BTree, every bucket node contains at least one key, and every
BTree node contains at least one child and a non-NULL firstbucket pointer.
However, a BTree node may not contain any keys.
+ An empty BTree consists solely of a BTree node with ``len==0`` and
``firstbucket==NULL``.
+ Although a BTree can become unbalanced under a mix of inserts and deletes
(meaning both that there's nothing stronger that can be said about buckets
than that they're not empty, and that buckets can appear at different
depths), a BTree node always has children of the same kind: they're all
buckets, or they're all BTree nodes.
The ``BTREE_SEARCH`` Macro
==========================
For notational ease, consider a fixed BTree node ``x``, and let
::
K(i) mean x->data.key[i]
C(i) mean all the keys reachable from x->data.child[i]
For each ``i`` in ``0`` to ``x->len-1`` inclusive,
::
K(i) <= C(i) < K(i+1)
is a BTree node invariant, where we pretend that ``K(0)`` holds a key smaller
than any possible key, and ``K(x->len)`` holds a key larger than any possible
key. (Note that ``K(x->len)`` doesn't actually exist, and ``K(0)`` is never
used although space for it exists in non-empty BTree nodes.)
When searching for a key ``k``, then, the child pointer we want to follow is
the one at index ``i`` such that ``K(i) <= k < K(i+1)``. There can be at most
one such ``i``, since the ``K(i)`` are strictly increasing. And there is at
least one such ``i`` provided the tree isn't empty (so that ``0 < len``). For
the moment, assume the tree isn't empty (we'll get back to that later).
The macro's chief loop invariant is
::
K(lo) < k < K(hi)
This holds trivially at the start, since ``lo`` is set to ``0``, and ``hi`` to
``x->len``, and we pretend ``K(0)`` is minus infinity and ``K(len)`` is plus
infinity. Inside the loop, if ``K(i) < k`` we set ``lo`` to ``i``, and if
``K(i) > k`` we set ``hi`` to ``i``. These obviously preserve the invariant.
If ``K(i) == k``, the loop breaks and sets the result to ``i``, and since
``K(i) == k`` in that case ``i`` is obviously the correct result.
Other cases depend on how ``i = floor((lo + hi)/2)`` works, exactly. Suppose
``lo + d = hi`` for some ``d >= 0``. Then ``i = floor((lo + lo + d)/2) =
floor(lo + d/2) = lo + floor(d/2)``. So:
a. ``[d == 0] (lo == i == hi)`` if and only if ``(lo == hi)``.
b. ``[d == 1] (lo == i < hi)`` if and only if ``(lo+1 == hi)``.
c. ``[d > 1] (lo < i < hi)`` if and only if ``(lo+1 < hi)``.
If the node is empty ``(x->len == 0)``, then ``lo==i==hi==0`` at the start,
and the loop exits immediately (the first ``i > lo`` test fails), without
entering the body.
Else ``lo < hi`` at the start, and the invariant ``K(lo) < k < K(hi)`` holds.
If ``lo+1 < hi``, we're in case (c): ``i`` is strictly between ``lo`` and
``hi``, so the loop body is entered, and regardless of whether the body sets
the new ``lo`` or the new ``hi`` to ``i``, the new ``lo`` is strictly less
than the new ``hi``, and the difference between the new ``lo`` and new ``hi``
is strictly less than the difference between the old ``lo`` and old ``hi``.
So long as the new ``lo + 1`` remains < the new ``hi``, we stay in this case.
We can't stay in this case forever, though: because ``hi-lo`` decreases on
each trip but remains > ``0``, ``lo+1 == hi`` must eventually become true.
(In fact, it becomes true quickly, in about ``log2(x->len)`` trips; the point
is more that ``lo`` doesn't equal ``hi`` when the loop ends, it has to end
with ``lo+1==hi`` and ``i==lo``).
Then we're in case (b): ``i==lo==hi-1`` then, and the loop exits. The
invariant still holds, with ``lo==i`` and ``hi==lo+1==i+1``::
K(i) < k < K(i+1)
so ``i`` is again the correct answer.
Optimization points:
--------------------
+ Division by 2 is done via shift rather via "/2". These are signed ints, and
almost all C compilers treat signed int division as truncating, and shifting
is not the same as truncation for signed int division. The compiler has no
way to know these values aren't negative, so has to generate longer-winded
code for "/2". But we know these values aren't negative, and exploit it.
+ The order of _cmp comparisons matters. We're in an interior BTree node, and
are looking at only a tiny fraction of all the keys that exist. So finding
the key exactly in this node is unlikely, and checking ``_cmp == 0`` is a
waste of time to the same extent. It doesn't matter whether we check for
``_cmp < 0`` or ``_cmp > 0`` first, so long as we do both before worrying
about equality.
+ At the start of a routine, it's better to run this macro even if ``x->len``
is ``0`` (check for that afterwards). We just called a function and so
probably drained the pipeline. If the first thing we do then is read up
``self->len`` and check it against ``0``, we just sit there waiting for the
data to get read up, and then another immediate test-and-branch, and for a
very unlikely case (BTree nodes are rarely empty). It's better to get into
the loop right away so the normal case makes progress ASAP.
The ``BUCKET_SEARCH`` Macro
===========================
This has a different job than ``BTREE_SEARCH``: the key ``0`` slot is
legitimate in a bucket, and we want to find the index at which the key
belongs. If the key is larger than the bucket's largest key, a new slot at
index len is where it belongs, else it belongs at the smallest ``i`` with
``keys[i]`` >= the key we're looking for. We also need to know whether or not
the key is present (``BTREE_SEARCH`` didn't care; it only wanted to find the
next node to search).
The mechanics of the search are quite similar, though. The primary
loop invariant changes to (say we're searching for key ``k``)::
K(lo-1) < k < K(hi)
where ``K(i)`` means ``keys[i]``, and we pretend ``K(-1)`` is minus infinity
and ``K(len)`` is plus infinity.
If the bucket is empty, ``lo=hi=i=0`` at the start, the loop body is never
entered, and the macro sets ``INDEX`` to 0 and ``ABSENT`` to true. That's why
``_cmp`` is initialized to 1 (``_cmp`` becomes ``ABSENT``).
Else the bucket is not empty, lo<hi at the start, and the loop body is
entered. The invariant is obviously satisfied then, as ``lo=0`` and
``hi=len``.
If ``K[i]<k``, ``lo`` is set to ``i+1``, preserving that ``K(lo-1) = K[i] <
k``.
If ``K[i]>k``, ``hi`` is set to ``i``, preserving that ``K[hi] = K[i] > k``.
If the loop exits after either of those, ``_cmp != 0``, so ``ABSENT`` becomes
true.
If ``K[i]=k``, the loop breaks, so that ``INDEX`` becomes ``i``, and
``ABSENT`` becomes false (``_cmp=0`` in this case).
The same case analysis for ``BTREE_SEARCH`` on ``lo`` and ``hi`` holds here:
a. ``(lo == i == hi)`` if and only if ``(lo == hi)``.
b. ``(lo == i < hi)`` if and only if ``(lo+1 == hi)``.
c. ``(lo < i < hi)`` if and only if ``(lo+1 < hi)``.
So long as ``lo+1 < hi``, we're in case (c), and either break with equality
(in which case the right results are obviously computed) or narrow the range.
If equality doesn't obtain, the range eventually narrows to cases (a) or (b).
To go from (c) to (a), we must have ``lo+2==hi`` at the start, and
``K[i]=K[lo+1]<k``. Then the new lo gets set to ``i+1 = lo+2 = hi``, and the
loop exits with ``lo=hi=i`` and ``_cmp<0``. This is correct, because we know
that ``k != K(i)`` (loop invariant! we actually know something stronger, that
``k < K(hi)``; since ``i=hi``, this implies ``k != K(i)``).
Else (c) eventually falls into case (b), ``lo+1==hi`` and ``i==lo``. The
invariant tells us ``K(lo-1) < k < K(hi) = K(lo+1)``, so if the key is present
it must be at ``K(lo)``. ``i==lo`` in this case, so we test ``K(lo)`` against
``k``. As always, if equality obtains we do the right thing, else case #b
becomes case (a).
When (b) becomes (a), the last comparison was non-equal, so ``_cmp`` is
non-zero, and the loop exits because ``lo==hi==i`` in case (a). The invariant
then tells us ``K(lo-1) < k < K(lo)``, so the key is in fact not present, it's
correct to exit with ``_cmp`` non-zero, and ``i==lo`` is again the index at
which ``k`` belongs.
Optimization points:
--------------------
+ As for ``BTREE_SEARCH``, shifting of signed ints is cheaper than division.
+ Unlike as for ``BTREE_SEARCH``, there's nothing special about searching an
empty bucket, and the macro computes thoroughly sensible results in that
case.
+ The order of ``_cmp`` comparisons differs from ``BTREE_SEARCH``. When
searching a bucket, it's much more likely (than when searching a BTree node)
that the key is present, so testing ``__cmp==0`` isn't a systematic waste of
cycles. At the extreme, if all searches are successful (key present), on
average this saves one comparison per search, against leaving the
determination of ``_cmp==0`` implicit (as ``BTREE_SEARCH`` does). But even
on successful searches, ``__cmp != 0`` is a more popular outcome than
``__cmp == 0`` across iterations (unless the bucket has only a few keys), so
it's important to check one of the inequality cases first. It turns out
it's better on average to check ``K(i) < key`` (than to check ``K(i) >
key``), because when it pays it narrows the range more (we get a little
boost from setting ``lo=i+1`` in this case; the other case sets ``hi=i``,
which isn't as much of a narrowing).
@@ -0,0 +1,112 @@
##############################################################################
#
# Copyright (c) 2001-2012 Zope Foundation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
__all__ = ('Bucket', 'Set', 'BTree', 'TreeSet',
'IFBucket', 'IFSet', 'IFBTree', 'IFTreeSet',
'union', 'intersection', 'difference',
'weightedUnion', 'weightedIntersection', 'multiunion',
)
from zope.interface import moduleProvides
from .Interfaces import IIntegerFloatBTreeModule
from ._base import Bucket
from ._base import MERGE
from ._base import MERGE_WEIGHT_numeric
from ._base import MERGE_DEFAULT_float
from ._base import Set
from ._base import Tree as BTree
from ._base import TreeSet
from ._base import _TreeIterator
from ._base import difference as _difference
from ._base import intersection as _intersection
from ._base import multiunion as _multiunion
from ._base import set_operation as _set_operation
from ._base import to_int as _to_key
from ._base import to_float as _to_value
from ._base import union as _union
from ._base import weightedIntersection as _weightedIntersection
from ._base import weightedUnion as _weightedUnion
from ._base import _fix_pickle
from ._compat import import_c_extension
_BUCKET_SIZE = 120
_TREE_SIZE = 500
using64bits = False
class IFBucketPy(Bucket):
_to_key = _to_key
_to_value = _to_value
MERGE = MERGE
MERGE_WEIGHT = MERGE_WEIGHT_numeric
MERGE_DEFAULT = MERGE_DEFAULT_float
class IFSetPy(Set):
_to_key = _to_key
MERGE = MERGE
MERGE_WEIGHT = MERGE_WEIGHT_numeric
MERGE_DEFAULT = MERGE_DEFAULT_float
class IFBTreePy(BTree):
max_leaf_size = _BUCKET_SIZE
max_internal_size = _TREE_SIZE
_to_key = _to_key
_to_value = _to_value
MERGE = MERGE
MERGE_WEIGHT = MERGE_WEIGHT_numeric
MERGE_DEFAULT = MERGE_DEFAULT_float
class IFTreeSetPy(TreeSet):
max_leaf_size = _BUCKET_SIZE
max_internal_size = _TREE_SIZE
_to_key = _to_key
MERGE = MERGE
MERGE_WEIGHT = MERGE_WEIGHT_numeric
MERGE_DEFAULT = MERGE_DEFAULT_float
class IFTreeIteratorPy(_TreeIterator):
pass
# Can't declare forward refs, so fix up afterwards:
IFBucketPy._mapping_type = IFBucketPy._bucket_type = IFBucketPy
IFBucketPy._set_type = IFSetPy
IFSetPy._mapping_type = IFBucketPy
IFSetPy._set_type = IFSetPy._bucket_type = IFSetPy
IFBTreePy._mapping_type = IFBTreePy._bucket_type = IFBucketPy
IFBTreePy._set_type = IFSetPy
IFTreeSetPy._mapping_type = IFBucketPy
IFTreeSetPy._set_type = IFTreeSetPy._bucket_type = IFSetPy
differencePy = _set_operation(_difference, IFSetPy)
unionPy = _set_operation(_union, IFSetPy)
intersectionPy = _set_operation(_intersection, IFSetPy)
multiunionPy = _set_operation(_multiunion, IFSetPy)
weightedUnionPy = _set_operation(_weightedUnion, IFSetPy)
weightedIntersectionPy = _set_operation(_weightedIntersection, IFSetPy)
import_c_extension(globals())
_fix_pickle(globals(), __name__)
moduleProvides(IIntegerFloatBTreeModule)
@@ -0,0 +1,113 @@
##############################################################################
#
# Copyright (c) 2001-2012 Zope Foundation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
__all__ = ('Bucket', 'Set', 'BTree', 'TreeSet',
'IIBucket', 'IISet', 'IIBTree', 'IITreeSet',
'union', 'intersection', 'difference',
'weightedUnion', 'weightedIntersection', 'multiunion',
)
from zope.interface import moduleProvides
from .Interfaces import IIntegerIntegerBTreeModule
from ._base import Bucket
from ._base import MERGE
from ._base import MERGE_WEIGHT_numeric
from ._base import MERGE_DEFAULT_int
from ._base import Set
from ._base import Tree as BTree
from ._base import TreeSet
from ._base import _TreeIterator
from ._base import difference as _difference
from ._base import intersection as _intersection
from ._base import multiunion as _multiunion
from ._base import set_operation as _set_operation
from ._base import to_int as _to_key
_to_value = _to_key
from ._base import union as _union
from ._base import weightedIntersection as _weightedIntersection
from ._base import weightedUnion as _weightedUnion
from ._base import _fix_pickle
from ._compat import import_c_extension
_BUCKET_SIZE = 120
_TREE_SIZE = 500
using64bits = False
class IIBucketPy(Bucket):
_to_key = _to_key
_to_value = _to_value
MERGE = MERGE
MERGE_WEIGHT = MERGE_WEIGHT_numeric
MERGE_DEFAULT = MERGE_DEFAULT_int
class IISetPy(Set):
_to_key = _to_key
MERGE = MERGE
MERGE_WEIGHT = MERGE_WEIGHT_numeric
MERGE_DEFAULT = MERGE_DEFAULT_int
class IIBTreePy(BTree):
max_leaf_size = _BUCKET_SIZE
max_internal_size = _TREE_SIZE
_to_key = _to_key
_to_value = _to_value
MERGE = MERGE
MERGE_WEIGHT = MERGE_WEIGHT_numeric
MERGE_DEFAULT = MERGE_DEFAULT_int
class IITreeSetPy(TreeSet):
max_leaf_size = _BUCKET_SIZE
max_internal_size = _TREE_SIZE
_to_key = _to_key
MERGE = MERGE
MERGE_WEIGHT = MERGE_WEIGHT_numeric
MERGE_DEFAULT = MERGE_DEFAULT_int
class IITreeIteratorPy(_TreeIterator):
pass
# Can't declare forward refs, so fix up afterwards:
IIBucketPy._mapping_type = IIBucketPy._bucket_type = IIBucketPy
IIBucketPy._set_type = IISetPy
IISetPy._mapping_type = IIBucketPy
IISetPy._set_type = IISetPy._bucket_type = IISetPy
IIBTreePy._mapping_type = IIBTreePy._bucket_type = IIBucketPy
IIBTreePy._set_type = IISetPy
IITreeSetPy._mapping_type = IIBucketPy
IITreeSetPy._set_type = IITreeSetPy._bucket_type = IISetPy
differencePy = _set_operation(_difference, IISetPy)
unionPy = _set_operation(_union, IISetPy)
intersectionPy = _set_operation(_intersection, IISetPy)
multiunionPy = _set_operation(_multiunion, IISetPy)
weightedUnionPy = _set_operation(_weightedUnion, IISetPy)
weightedIntersectionPy = _set_operation(_weightedIntersection, IISetPy)
import_c_extension(globals())
_fix_pickle(globals(), __name__)
moduleProvides(IIntegerIntegerBTreeModule)
@@ -0,0 +1,95 @@
##############################################################################
#
# Copyright (c) 2001-2012 Zope Foundation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
__all__ = ('Bucket', 'Set', 'BTree', 'TreeSet',
'IOBucket', 'IOSet', 'IOBTree', 'IOTreeSet',
'union', 'intersection', 'difference', 'multiunion',
)
from zope.interface import moduleProvides
from .Interfaces import IIntegerObjectBTreeModule
from ._base import Bucket
from ._base import MERGE_WEIGHT_default
from ._base import Set
from ._base import Tree as BTree
from ._base import TreeSet
from ._base import _TreeIterator
from ._base import difference as _difference
from ._base import intersection as _intersection
from ._base import multiunion as _multiunion
from ._base import set_operation as _set_operation
from ._base import to_int as _to_key
from ._base import to_ob as _to_value
from ._base import union as _union
from ._base import _fix_pickle
from ._compat import import_c_extension
_BUCKET_SIZE = 60
_TREE_SIZE = 500
using64bits = False
class IOBucketPy(Bucket):
_to_key = _to_key
_to_value = _to_value
MERGE_WEIGHT = MERGE_WEIGHT_default
class IOSetPy(Set):
_to_key = _to_key
class IOBTreePy(BTree):
max_leaf_size = _BUCKET_SIZE
max_internal_size = _TREE_SIZE
_to_key = _to_key
_to_value = _to_value
MERGE_WEIGHT = MERGE_WEIGHT_default
class IOTreeSetPy(TreeSet):
max_leaf_size = _BUCKET_SIZE
max_internal_size = _TREE_SIZE
_to_key = _to_key
class IOTreeIteratorPy(_TreeIterator):
pass
# Can't declare forward refs, so fix up afterwards:
IOBucketPy._mapping_type = IOBucketPy._bucket_type = IOBucketPy
IOBucketPy._set_type = IOSetPy
IOSetPy._mapping_type = IOBucketPy
IOSetPy._set_type = IOSetPy._bucket_type = IOSetPy
IOBTreePy._mapping_type = IOBTreePy._bucket_type = IOBucketPy
IOBTreePy._set_type = IOSetPy
IOTreeSetPy._mapping_type = IOBucketPy
IOTreeSetPy._set_type = IOTreeSetPy._bucket_type = IOSetPy
differencePy = _set_operation(_difference, IOSetPy)
unionPy = _set_operation(_union, IOSetPy)
intersectionPy = _set_operation(_intersection, IOSetPy)
multiunionPy = _set_operation(_multiunion, IOSetPy)
import_c_extension(globals())
_fix_pickle(globals(), __name__)
moduleProvides(IIntegerObjectBTreeModule)
@@ -0,0 +1,527 @@
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Foundation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
from zope.interface import Interface, Attribute
class ICollection(Interface):
def clear():
"""Remove all of the items from the collection."""
def __nonzero__():
"""Check if the collection is non-empty.
Return a true value if the collection is non-empty and a
false value otherwise.
"""
class IReadSequence(Interface):
def __getitem__(index):
"""Return the value at the given index.
An IndexError is raised if the index cannot be found.
"""
def __getslice__(index1, index2):
"""Return a subsequence from the original sequence.
The subsequence includes the items from index1 up to, but not
including, index2.
"""
class IKeyed(ICollection):
def has_key(key):
"""Check whether the object has an item with the given key.
Return a true value if the key is present, else a false value.
"""
def keys(min=None, max=None, excludemin=False, excludemax=False):
"""Return an IReadSequence containing the keys in the collection.
The type of the IReadSequence is not specified. It could be a list
or a tuple or some other type.
All arguments are optional, and may be specified as keyword
arguments, or by position.
If a min is specified, then output is constrained to keys greater
than or equal to the given min, and, if excludemin is specified and
true, is further constrained to keys strictly greater than min. A
min value of None is ignored. If min is None or not specified, and
excludemin is true, the smallest key is excluded.
If a max is specified, then output is constrained to keys less than
or equal to the given max, and, if excludemax is specified and
true, is further constrained to keys strictly less than max. A max
value of None is ignored. If max is None or not specified, and
excludemax is true, the largest key is excluded.
"""
def maxKey(key=None):
"""Return the maximum key.
If a key argument if provided and not None, return the largest key
that is less than or equal to the argument. Raise an exception if
no such key exists.
"""
def minKey(key=None):
"""Return the minimum key.
If a key argument if provided and not None, return the smallest key
that is greater than or equal to the argument. Raise an exception
if no such key exists.
"""
class ISetMutable(IKeyed):
def insert(key):
"""Add the key (value) to the set.
If the key was already in the set, return 0, otherwise return 1.
"""
def remove(key):
"""Remove the key from the set.
Raises KeyError if key is not in the set.
"""
def update(seq):
"""Add the items from the given sequence to the set."""
class ISized(Interface):
"""An object that supports __len__."""
def __len__():
"""Return the number of items in the container."""
class IKeySequence(IKeyed, ISized):
def __getitem__(index):
"""Return the key in the given index position.
This allows iteration with for loops and use in functions,
like map and list, that read sequences.
"""
class ISet(IKeySequence, ISetMutable):
pass
class ITreeSet(IKeyed, ISetMutable):
pass
class IMinimalDictionary(ISized, IKeyed):
def get(key, default):
"""Get the value associated with the given key.
Return the default if has_key(key) is false.
"""
def __getitem__(key):
"""Get the value associated with the given key.
Raise KeyError if has_key(key) is false.
"""
def __setitem__(key, value):
"""Set the value associated with the given key."""
def __delitem__(key):
"""Delete the value associated with the given key.
Raise KeyError if has_key(key) is false.
"""
def values(min=None, max=None, excludemin=False, excludemax=False):
"""Return an IReadSequence containing the values in the collection.
The type of the IReadSequence is not specified. It could be a list
or a tuple or some other type.
All arguments are optional, and may be specified as keyword
arguments, or by position.
If a min is specified, then output is constrained to values whose
keys are greater than or equal to the given min, and, if excludemin
is specified and true, is further constrained to values whose keys
are strictly greater than min. A min value of None is ignored. If
min is None or not specified, and excludemin is true, the value
corresponding to the smallest key is excluded.
If a max is specified, then output is constrained to values whose
keys are less than or equal to the given max, and, if excludemax is
specified and true, is further constrained to values whose keys are
strictly less than max. A max value of None is ignored. If max is
None or not specified, and excludemax is true, the value
corresponding to the largest key is excluded.
"""
def items(min=None, max=None, excludemin=False, excludemax=False):
"""Return an IReadSequence containing the items in the collection.
An item is a 2-tuple, a (key, value) pair.
The type of the IReadSequence is not specified. It could be a list
or a tuple or some other type.
All arguments are optional, and may be specified as keyword
arguments, or by position.
If a min is specified, then output is constrained to items whose
keys are greater than or equal to the given min, and, if excludemin
is specified and true, is further constrained to items whose keys
are strictly greater than min. A min value of None is ignored. If
min is None or not specified, and excludemin is true, the item with
the smallest key is excluded.
If a max is specified, then output is constrained to items whose
keys are less than or equal to the given max, and, if excludemax is
specified and true, is further constrained to items whose keys are
strictly less than max. A max value of None is ignored. If max is
None or not specified, and excludemax is true, the item with the
largest key is excluded.
"""
class IDictionaryIsh(IMinimalDictionary):
def update(collection):
"""Add the items from the given collection object to the collection.
The input collection must be a sequence of (key, value) 2-tuples,
or an object with an 'items' method that returns a sequence of
(key, value) pairs.
"""
def byValue(minValue):
"""Return a sequence of (value, key) pairs, sorted by value.
Values < minValue are omitted and other values are "normalized" by
the minimum value. This normalization may be a noop, but, for
integer values, the normalization is division.
"""
def setdefault(key, d):
"""D.setdefault(k, d) -> D.get(k, d), also set D[k]=d if k not in D.
Return the value like get() except that if key is missing, d is both
returned and inserted into the dictionary as the value of k.
Note that, unlike as for Python's dict.setdefault(), d is not
optional. Python defaults d to None, but that doesn't make sense
for mappings that can't have None as a value (for example, an
IIBTree can have only integers as values).
"""
def pop(key, d):
"""D.pop(k[, d]) -> v, remove key and return the corresponding value.
If key is not found, d is returned if given, otherwise KeyError is
raised.
"""
class IBTree(IDictionaryIsh):
def insert(key, value):
"""Insert a key and value into the collection.
If the key was already in the collection, then there is no
change and 0 is returned.
If the key was not already in the collection, then the item is
added and 1 is returned.
This method is here to allow one to generate random keys and
to insert and test whether the key was there in one operation.
A standard idiom for generating new keys will be::
key = generate_key()
while not t.insert(key, value):
key=generate_key()
"""
class IMerge(Interface):
"""Object with methods for merging sets, buckets, and trees.
These methods are supplied in modules that define collection
classes with particular key and value types. The operations apply
only to collections from the same module. For example, the
IIBTree.union can only be used with IIBTree.IIBTree,
IIBTree.IIBucket, IIBTree.IISet, and IIBTree.IITreeSet.
The implementing module has a value type. The IOBTree and OOBTree
modules have object value type. The IIBTree and OIBTree modules
have integer value types. Other modules may be defined in the
future that have other value types.
The individual types are classified into set (Set and TreeSet) and
mapping (Bucket and BTree) types.
"""
def difference(c1, c2):
"""Return the keys or items in c1 for which there is no key in c2.
If c1 is None, then None is returned. If c2 is None, then c1
is returned.
If neither c1 nor c2 is None, the output is a Set if c1 is a Set or
TreeSet, and is a Bucket if c1 is a Bucket or BTree.
"""
def union(c1, c2):
"""Compute the Union of c1 and c2.
If c1 is None, then c2 is returned, otherwise, if c2 is None,
then c1 is returned.
The output is a Set containing keys from the input
collections.
"""
def intersection(c1, c2):
"""Compute the intersection of c1 and c2.
If c1 is None, then c2 is returned, otherwise, if c2 is None,
then c1 is returned.
The output is a Set containing matching keys from the input
collections.
"""
class IBTreeModule(Interface):
"""These are available in all modules (IOBTree, OIBTree, OOBTree, IIBTree,
IFBTree, LFBTree, LOBTree, OLBTree, and LLBTree).
"""
BTree = Attribute(
"""The IBTree for this module.
Also available as [prefix]BTree, as in IOBTree.""")
Bucket = Attribute(
"""The leaf-node data buckets used by the BTree.
(IBucket is not currently defined in this file, but is essentially
IDictionaryIsh, with the exception of __nonzero__, as of this
writing.)
Also available as [prefix]Bucket, as in IOBucket.""")
TreeSet = Attribute(
"""The ITreeSet for this module.
Also available as [prefix]TreeSet, as in IOTreeSet.""")
Set = Attribute(
"""The ISet for this module: the leaf-node data buckets used by the
TreeSet.
Also available as [prefix]BTree, as in IOSet.""")
class IIMerge(IMerge):
"""Merge collections with integer value type.
A primary intent is to support operations with no or integer
values, which are used as "scores" to rate indiviual keys. That
is, in this context, a BTree or Bucket is viewed as a set with
scored keys, using integer scores.
"""
def weightedUnion(c1, c2, weight1=1, weight2=1):
"""Compute the weighted union of c1 and c2.
If c1 and c2 are None, the output is (0, None).
If c1 is None and c2 is not None, the output is (weight2, c2).
If c1 is not None and c2 is None, the output is (weight1, c1).
Else, and hereafter, c1 is not None and c2 is not None.
If c1 and c2 are both sets, the output is 1 and the (unweighted)
union of the sets.
Else the output is 1 and a Bucket whose keys are the union of c1 and
c2's keys, and whose values are::
v1*weight1 + v2*weight2
where:
v1 is 0 if the key is not in c1
1 if the key is in c1 and c1 is a set
c1[key] if the key is in c1 and c1 is a mapping
v2 is 0 if the key is not in c2
1 if the key is in c2 and c2 is a set
c2[key] if the key is in c2 and c2 is a mapping
Note that c1 and c2 must be collections.
"""
def weightedIntersection(c1, c2, weight1=1, weight2=1):
"""Compute the weighted intersection of c1 and c2.
If c1 and c2 are None, the output is (0, None).
If c1 is None and c2 is not None, the output is (weight2, c2).
If c1 is not None and c2 is None, the output is (weight1, c1).
Else, and hereafter, c1 is not None and c2 is not None.
If c1 and c2 are both sets, the output is the sum of the weights
and the (unweighted) intersection of the sets.
Else the output is 1 and a Bucket whose keys are the intersection of
c1 and c2's keys, and whose values are::
v1*weight1 + v2*weight2
where:
v1 is 1 if c1 is a set
c1[key] if c1 is a mapping
v2 is 1 if c2 is a set
c2[key] if c2 is a mapping
Note that c1 and c2 must be collections.
"""
class IMergeIntegerKey(IMerge):
"""IMerge-able objects with integer keys.
Concretely, this means the types in IOBTree and IIBTree.
"""
def multiunion(seq):
"""Return union of (zero or more) integer sets, as an integer set.
seq is a sequence of objects each convertible to an integer set.
These objects are convertible to an integer set:
+ An integer, which is added to the union.
+ A Set or TreeSet from the same module (for example, an
IIBTree.TreeSet for IIBTree.multiunion()). The elements of the
set are added to the union.
+ A Bucket or BTree from the same module (for example, an
IOBTree.IOBTree for IOBTree.multiunion()). The keys of the
mapping are added to the union.
The union is returned as a Set from the same module (for example,
IIBTree.multiunion() returns an IIBTree.IISet).
The point to this method is that it can run much faster than
doing a sequence of two-input union() calls. Under the covers,
all the integers in all the inputs are sorted via a single
linear-time radix sort, then duplicates are removed in a second
linear-time pass.
"""
class IBTreeFamily(Interface):
"""the 64-bit or 32-bit family"""
IO = Attribute('The IIntegerObjectBTreeModule for this family')
OI = Attribute('The IObjectIntegerBTreeModule for this family')
II = Attribute('The IIntegerIntegerBTreeModule for this family')
IF = Attribute('The IIntegerFloatBTreeModule for this family')
OO = Attribute('The IObjectObjectBTreeModule for this family')
maxint = Attribute('The maximum integer storable in this family')
minint = Attribute('The minimum integer storable in this family')
class IIntegerObjectBTreeModule(IBTreeModule, IMerge):
"""keys, or set values, are integers; values are objects.
describes IOBTree and LOBTree"""
family = Attribute('The IBTreeFamily of this module')
class IObjectIntegerBTreeModule(IBTreeModule, IIMerge):
"""keys, or set values, are objects; values are integers.
Object keys (and set values) must sort reliably (for instance, *not* on
object id)! Homogenous key types recommended.
describes OIBTree and LOBTree"""
family = Attribute('The IBTreeFamily of this module')
class IIntegerIntegerBTreeModule(IBTreeModule, IIMerge, IMergeIntegerKey):
"""keys, or set values, are integers; values are also integers.
describes IIBTree and LLBTree"""
family = Attribute('The IBTreeFamily of this module')
class IObjectObjectBTreeModule(IBTreeModule, IMerge):
"""keys, or set values, are objects; values are also objects.
Object keys (and set values) must sort reliably (for instance, *not* on
object id)! Homogenous key types recommended.
describes OOBTree"""
# Note that there's no ``family`` attribute; all families include
# the OO flavor of BTrees.
class IIntegerFloatBTreeModule(IBTreeModule, IMerge):
"""keys, or set values, are integers; values are floats.
describes IFBTree and LFBTree"""
family = Attribute('The IBTreeFamily of this module')
try:
from ZODB.POSException import BTreesConflictError
except ImportError:
class BTreesConflictError(ValueError):
@property
def reason(self):
return self.args[-1]
###############################################################
# IMPORTANT NOTE
#
# Getting the length of a BTree, TreeSet, or output of keys,
# values, or items of same is expensive. If you need to get the
# length, you need to maintain this separately.
#
# Eventually, I need to express this through the interfaces.
#
################################################################
@@ -0,0 +1,113 @@
##############################################################################
#
# Copyright (c) 2001-2012 Zope Foundation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
__all__ = ('Bucket', 'Set', 'BTree', 'TreeSet',
'LFBucket', 'LFSet', 'LFBTree', 'LFTreeSet',
'union', 'intersection', 'difference',
'weightedUnion', 'weightedIntersection', 'multiunion',
)
from zope.interface import moduleProvides
from .Interfaces import IIntegerFloatBTreeModule
from ._base import Bucket
from ._base import MERGE
from ._base import MERGE_WEIGHT_numeric
from ._base import MERGE_DEFAULT_float
from ._base import Set
from ._base import Tree as BTree
from ._base import TreeSet
from ._base import _TreeIterator
from ._base import difference as _difference
from ._base import intersection as _intersection
from ._base import multiunion as _multiunion
from ._base import set_operation as _set_operation
from ._base import to_long as _to_key
from ._base import to_float as _to_value
from ._base import union as _union
from ._base import weightedIntersection as _weightedIntersection
from ._base import weightedUnion as _weightedUnion
from ._base import _fix_pickle
from ._compat import import_c_extension
_BUCKET_SIZE = 120
_TREE_SIZE = 500
using64bits = True
class LFBucketPy(Bucket):
_to_key = _to_key
_to_value = _to_value
MERGE = MERGE
MERGE_WEIGHT = MERGE_WEIGHT_numeric
MERGE_DEFAULT = MERGE_DEFAULT_float
class LFSetPy(Set):
_to_key = _to_key
MERGE = MERGE
MERGE_WEIGHT = MERGE_WEIGHT_numeric
MERGE_DEFAULT = MERGE_DEFAULT_float
class LFBTreePy(BTree):
max_leaf_size = _BUCKET_SIZE
max_internal_size = _TREE_SIZE
_to_key = _to_key
_to_value = _to_value
MERGE = MERGE
MERGE_WEIGHT = MERGE_WEIGHT_numeric
MERGE_DEFAULT = MERGE_DEFAULT_float
class LFTreeSetPy(TreeSet):
max_leaf_size = _BUCKET_SIZE
max_internal_size = _TREE_SIZE
_to_key = _to_key
MERGE = MERGE
MERGE_WEIGHT = MERGE_WEIGHT_numeric
MERGE_DEFAULT = MERGE_DEFAULT_float
class LFTreeIteratorPy(_TreeIterator):
pass
# Can't declare forward refs, so fix up afterwards:
LFBucketPy._mapping_type = LFBucketPy._bucket_type = LFBucketPy
LFBucketPy._set_type = LFSetPy
LFSetPy._mapping_type = LFBucketPy
LFSetPy._set_type = LFSetPy._bucket_type = LFSetPy
LFBTreePy._mapping_type = LFBTreePy._bucket_type = LFBucketPy
LFBTreePy._set_type = LFSetPy
LFTreeSetPy._mapping_type = LFBucketPy
LFTreeSetPy._set_type = LFTreeSetPy._bucket_type = LFSetPy
differencePy = _set_operation(_difference, LFSetPy)
unionPy = _set_operation(_union, LFSetPy)
intersectionPy = _set_operation(_intersection, LFSetPy)
multiunionPy = _set_operation(_multiunion, LFSetPy)
weightedUnionPy = _set_operation(_weightedUnion, LFSetPy)
weightedIntersectionPy = _set_operation(_weightedIntersection, LFSetPy)
import_c_extension(globals())
_fix_pickle(globals(), __name__)
moduleProvides(IIntegerFloatBTreeModule)
@@ -0,0 +1,113 @@
##############################################################################
#
# Copyright (c) 2001-2012 Zope Foundation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
__all__ = ('Bucket', 'Set', 'BTree', 'TreeSet',
'LLBucket', 'LLSet', 'LLBTree', 'LLTreeSet',
'union', 'intersection', 'difference',
'weightedUnion', 'weightedIntersection', 'multiunion',
)
from zope.interface import moduleProvides
from .Interfaces import IIntegerIntegerBTreeModule
from ._base import Bucket
from ._base import MERGE
from ._base import MERGE_WEIGHT_numeric
from ._base import MERGE_DEFAULT_int
from ._base import Set
from ._base import Tree as BTree
from ._base import TreeSet
from ._base import _TreeIterator
from ._base import difference as _difference
from ._base import intersection as _intersection
from ._base import multiunion as _multiunion
from ._base import set_operation as _set_operation
from ._base import to_long as _to_key
from ._base import to_long as _to_value
from ._base import union as _union
from ._base import weightedIntersection as _weightedIntersection
from ._base import weightedUnion as _weightedUnion
from ._base import _fix_pickle
from ._compat import import_c_extension
_BUCKET_SIZE = 120
_TREE_SIZE = 500
using64bits = True
class LLBucketPy(Bucket):
_to_key = _to_key
_to_value = _to_value
MERGE = MERGE
MERGE_WEIGHT = MERGE_WEIGHT_numeric
MERGE_DEFAULT = MERGE_DEFAULT_int
class LLSetPy(Set):
_to_key = _to_key
MERGE = MERGE
MERGE_WEIGHT = MERGE_WEIGHT_numeric
MERGE_DEFAULT = MERGE_DEFAULT_int
class LLBTreePy(BTree):
max_leaf_size = _BUCKET_SIZE
max_internal_size = _TREE_SIZE
_to_key = _to_key
_to_value = _to_value
MERGE = MERGE
MERGE_WEIGHT = MERGE_WEIGHT_numeric
MERGE_DEFAULT = MERGE_DEFAULT_int
class LLTreeSetPy(TreeSet):
max_leaf_size = _BUCKET_SIZE
max_internal_size = _TREE_SIZE
_to_key = _to_key
MERGE = MERGE
MERGE_WEIGHT = MERGE_WEIGHT_numeric
MERGE_DEFAULT = MERGE_DEFAULT_int
class LLTreeIteratorPy(_TreeIterator):
pass
# Can't declare forward refs, so fix up afterwards:
LLBucketPy._mapping_type = LLBucketPy._bucket_type = LLBucketPy
LLBucketPy._set_type = LLSetPy
LLSetPy._mapping_type = LLBucketPy
LLSetPy._set_type = LLSetPy._bucket_type = LLSetPy
LLBTreePy._mapping_type = LLBTreePy._bucket_type = LLBucketPy
LLBTreePy._set_type = LLSetPy
LLTreeSetPy._mapping_type = LLBucketPy
LLTreeSetPy._set_type = LLTreeSetPy._bucket_type = LLSetPy
differencePy = _set_operation(_difference, LLSetPy)
unionPy = _set_operation(_union, LLSetPy)
intersectionPy = _set_operation(_intersection, LLSetPy)
multiunionPy = _set_operation(_multiunion, LLSetPy)
weightedUnionPy = _set_operation(_weightedUnion, LLSetPy)
weightedIntersectionPy = _set_operation(_weightedIntersection, LLSetPy)
import_c_extension(globals())
_fix_pickle(globals(), __name__)
moduleProvides(IIntegerIntegerBTreeModule)
@@ -0,0 +1,96 @@
##############################################################################
#
# Copyright (c) 2001-2012 Zope Foundation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
__all__ = ('Bucket', 'Set', 'BTree', 'TreeSet',
'LOBucket', 'LOSet', 'LOBTree', 'LOTreeSet',
'union', 'intersection', 'difference', 'multiunion',
)
from zope.interface import moduleProvides
from .Interfaces import IIntegerObjectBTreeModule
from ._base import Bucket
from ._base import MERGE_WEIGHT_default
from ._base import Set
from ._base import Tree as BTree
from ._base import TreeSet
from ._base import _TreeIterator
from ._base import difference as _difference
from ._base import intersection as _intersection
from ._base import multiunion as _multiunion
from ._base import set_operation as _set_operation
from ._base import to_long as _to_key
from ._base import to_ob as _to_value
from ._base import union as _union
from ._base import _fix_pickle
from ._compat import import_c_extension
_BUCKET_SIZE = 60
_TREE_SIZE = 500
using64bits = True
class LOBucketPy(Bucket):
_to_key = _to_key
_to_value = _to_value
MERGE_WEIGHT = MERGE_WEIGHT_default
class LOSetPy(Set):
_to_key = _to_key
class LOBTreePy(BTree):
max_leaf_size = _BUCKET_SIZE
max_internal_size = _TREE_SIZE
_to_key = _to_key
_to_value = _to_value
MERGE_WEIGHT = MERGE_WEIGHT_default
class LOTreeSetPy(TreeSet):
max_leaf_size = _BUCKET_SIZE
max_internal_size = _TREE_SIZE
_to_key = _to_key
class LOTreeIteratorPy(_TreeIterator):
pass
# Can't declare forward refs, so fix up afterwards:
LOBucketPy._mapping_type = LOBucketPy._bucket_type = LOBucketPy
LOBucketPy._set_type = LOSetPy
LOSetPy._mapping_type = LOBucketPy
LOSetPy._set_type = LOSetPy._bucket_type = LOSetPy
LOBTreePy._mapping_type = LOBTreePy._bucket_type = LOBucketPy
LOBTreePy._set_type = LOSetPy
LOTreeSetPy._mapping_type = LOBucketPy
LOTreeSetPy._set_type = LOTreeSetPy._bucket_type = LOSetPy
differencePy = _set_operation(_difference, LOSetPy)
unionPy = _set_operation(_union, LOSetPy)
intersectionPy = _set_operation(_intersection, LOSetPy)
multiunionPy = _set_operation(_multiunion, LOSetPy)
import_c_extension(globals())
_fix_pickle(globals(), __name__)
moduleProvides(IIntegerObjectBTreeModule)
@@ -0,0 +1,58 @@
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Foundation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
import persistent
class Length(persistent.Persistent):
"""BTree lengths are often too expensive to compute.
Objects that use BTrees need to keep track of lengths themselves.
This class provides an object for doing this.
As a bonus, the object support application-level conflict
resolution.
It is tempting to to assign length objects to __len__ attributes
to provide instance-specific __len__ methods. However, this no
longer works as expected, because new-style classes cache
class-defined slot methods (like __len__) in C type slots. Thus,
instance-defined slot fillers are ignored.
"""
# class-level default required to keep copy.deepcopy happy -- see
# https://bugs.launchpad.net/zodb/+bug/516653
value = 0
def __init__(self, v=0):
self.value = v
def __getstate__(self):
return self.value
def __setstate__(self, v):
self.value = v
def set(self, v):
"Set the length value to v."
self.value = v
def _p_resolveConflict(self, old, s1, s2):
return s1 + s2 - old
def change(self, delta):
"Add delta to the length value."
self.value += delta
def __call__(self, *args):
"Return the current length value."
return self.value
@@ -0,0 +1,349 @@
/*****************************************************************************
Copyright (c) 2001, 2002 Zope Foundation and Contributors.
All Rights Reserved.
This software is subject to the provisions of the Zope Public License,
Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
FOR A PARTICULAR PURPOSE
****************************************************************************/
#define MERGETEMPLATE_C "$Id$\n"
/****************************************************************************
Set operations
****************************************************************************/
static int
merge_output(Bucket *r, SetIteration *i, int mapping)
{
if (r->len >= r->size && Bucket_grow(r, -1, !mapping) < 0)
return -1;
COPY_KEY(r->keys[r->len], i->key);
INCREF_KEY(r->keys[r->len]);
if (mapping) {
COPY_VALUE(r->values[r->len], i->value);
INCREF_VALUE(r->values[r->len]);
}
r->len++;
return 0;
}
/* The "reason" argument is a little integer giving "a reason" for the
* error. In the Zope3 codebase, these are mapped to explanatory strings
* via zodb/btrees/interfaces.py.
*/
static PyObject *
merge_error(int p1, int p2, int p3, int reason)
{
PyObject *r;
UNLESS (r=Py_BuildValue("iiii", p1, p2, p3, reason)) r=Py_None;
if (ConflictError == NULL) {
ConflictError = PyExc_ValueError;
Py_INCREF(ConflictError);
}
PyErr_SetObject(ConflictError, r);
if (r != Py_None)
{
Py_DECREF(r);
}
return NULL;
}
/* It's hard to explain "the rules" for bucket_merge, in large part because
* any automatic conflict-resolution scheme is going to be incorrect for
* some endcases of *some* app. The scheme here is pretty conservative,
* and should be OK for most apps. It's easier to explain what the code
* allows than what it forbids:
*
* Leaving things alone: it's OK if both s2 and s3 leave a piece of s1
* alone (don't delete the key, and don't change the value).
*
* Key deletion: a transaction (s2 or s3) can delete a key (from s1), but
* only if the other transaction (of s2 and s3) doesn't delete the same key.
* However, it's not OK for s2 and s3 to, between them, end up deleting all
* the keys. This is a higher-level constraint, due to that the caller of
* bucket_merge() doesn't have enough info to unlink the resulting empty
* bucket from its BTree correctly. It's also not OK if s2 or s3 are empty,
* because the transaction that emptied the bucket unlinked the bucket from
* the tree, and nothing we do here can get it linked back in again.
*
* Key insertion: s2 or s3 can add a new key, provided the other transaction
* doesn't insert the same key. It's not OK even if they insert the same
* <key, value> pair.
*
* Mapping value modification: s2 or s3 can modify the value associated
* with a key in s1, provided the other transaction doesn't make a
* modification of the same key to a different value. It's OK if s2 and s3
* both give the same new value to the key while it's hard to be precise about
* why, this doesn't seem consistent with that it's *not* OK for both to add
* a new key mapping to the same value).
*/
static PyObject *
bucket_merge(Bucket *s1, Bucket *s2, Bucket *s3)
{
Bucket *r=0;
PyObject *s;
SetIteration i1 = {0,0,0}, i2 = {0,0,0}, i3 = {0,0,0};
int cmp12, cmp13, cmp23, mapping, set;
/* If either "after" bucket is empty, punt. */
if (s2->len == 0 || s3->len == 0)
{
merge_error(-1, -1, -1, 12);
goto err;
}
if (initSetIteration(&i1, OBJECT(s1), 1) < 0)
goto err;
if (initSetIteration(&i2, OBJECT(s2), 1) < 0)
goto err;
if (initSetIteration(&i3, OBJECT(s3), 1) < 0)
goto err;
mapping = i1.usesValue | i2.usesValue | i3.usesValue;
set = !mapping;
if (mapping)
r = (Bucket *)PyObject_CallObject((PyObject *)&BucketType, NULL);
else
r = (Bucket *)PyObject_CallObject((PyObject *)&SetType, NULL);
if (r == NULL)
goto err;
if (i1.next(&i1) < 0)
goto err;
if (i2.next(&i2) < 0)
goto err;
if (i3.next(&i3) < 0)
goto err;
/* Consult zodb/btrees/interfaces.py for the meaning of the last
* argument passed to merge_error().
*/
/* TODO: This isn't passing on errors raised by value comparisons. */
while (i1.position >= 0 && i2.position >= 0 && i3.position >= 0)
{
TEST_KEY_SET_OR(cmp12, i1.key, i2.key) goto err;
TEST_KEY_SET_OR(cmp13, i1.key, i3.key) goto err;
if (cmp12==0)
{
if (cmp13==0)
{
if (set || (TEST_VALUE(i1.value, i2.value) == 0))
{ /* change in i3 value or all same */
if (merge_output(r, &i3, mapping) < 0) goto err;
}
else if (set || (TEST_VALUE(i1.value, i3.value) == 0))
{ /* change in i2 value */
if (merge_output(r, &i2, mapping) < 0) goto err;
}
else
{ /* conflicting value changes in i2 and i3 */
merge_error(i1.position, i2.position, i3.position, 1);
goto err;
}
if (i1.next(&i1) < 0) goto err;
if (i2.next(&i2) < 0) goto err;
if (i3.next(&i3) < 0) goto err;
}
else if (cmp13 > 0)
{ /* insert i3 */
if (merge_output(r, &i3, mapping) < 0) goto err;
if (i3.next(&i3) < 0) goto err;
}
else if (set || (TEST_VALUE(i1.value, i2.value) == 0))
{ /* deleted in i3 */
if (i3.position == 1)
{
/* Deleted the first item. This will modify the
parent node, so we don't know if merging will be
safe
*/
merge_error(i1.position, i2.position, i3.position, 13);
goto err;
}
if (i1.next(&i1) < 0) goto err;
if (i2.next(&i2) < 0) goto err;
}
else
{ /* conflicting del in i3 and change in i2 */
merge_error(i1.position, i2.position, i3.position, 2);
goto err;
}
}
else if (cmp13 == 0)
{
if (cmp12 > 0)
{ /* insert i2 */
if (merge_output(r, &i2, mapping) < 0) goto err;
if (i2.next(&i2) < 0) goto err;
}
else if (set || (TEST_VALUE(i1.value, i3.value) == 0))
{ /* deleted in i2 */
if (i2.position == 1)
{
/* Deleted the first item. This will modify the
parent node, so we don't know if merging will be
safe
*/
merge_error(i1.position, i2.position, i3.position, 13);
goto err;
}
if (i1.next(&i1) < 0) goto err;
if (i3.next(&i3) < 0) goto err;
}
else
{ /* conflicting del in i2 and change in i3 */
merge_error(i1.position, i2.position, i3.position, 3);
goto err;
}
}
else
{ /* Both keys changed */
TEST_KEY_SET_OR(cmp23, i2.key, i3.key) goto err;
if (cmp23==0)
{ /* dueling inserts or deletes */
merge_error(i1.position, i2.position, i3.position, 4);
goto err;
}
if (cmp12 > 0)
{ /* insert i2 */
if (cmp23 > 0)
{ /* insert i3 first */
if (merge_output(r, &i3, mapping) < 0) goto err;
if (i3.next(&i3) < 0) goto err;
}
else
{ /* insert i2 first */
if (merge_output(r, &i2, mapping) < 0) goto err;
if (i2.next(&i2) < 0) goto err;
}
}
else if (cmp13 > 0)
{ /* Insert i3 */
if (merge_output(r, &i3, mapping) < 0) goto err;
if (i3.next(&i3) < 0) goto err;
}
else
{ /* 1<2 and 1<3: both deleted 1.key */
merge_error(i1.position, i2.position, i3.position, 5);
goto err;
}
}
}
while (i2.position >= 0 && i3.position >= 0)
{ /* New inserts */
TEST_KEY_SET_OR(cmp23, i2.key, i3.key) goto err;
if (cmp23==0)
{ /* dueling inserts */
merge_error(i1.position, i2.position, i3.position, 6);
goto err;
}
if (cmp23 > 0)
{ /* insert i3 */
if (merge_output(r, &i3, mapping) < 0) goto err;
if (i3.next(&i3) < 0) goto err;
}
else
{ /* insert i2 */
if (merge_output(r, &i2, mapping) < 0) goto err;
if (i2.next(&i2) < 0) goto err;
}
}
while (i1.position >= 0 && i2.position >= 0)
{ /* remainder of i1 deleted in i3 */
TEST_KEY_SET_OR(cmp12, i1.key, i2.key) goto err;
if (cmp12 > 0)
{ /* insert i2 */
if (merge_output(r, &i2, mapping) < 0) goto err;
if (i2.next(&i2) < 0) goto err;
}
else if (cmp12==0 && (set || (TEST_VALUE(i1.value, i2.value) == 0)))
{ /* delete i3 */
if (i1.next(&i1) < 0) goto err;
if (i2.next(&i2) < 0) goto err;
}
else
{ /* Dueling deletes or delete and change */
merge_error(i1.position, i2.position, i3.position, 7);
goto err;
}
}
while (i1.position >= 0 && i3.position >= 0)
{ /* remainder of i1 deleted in i2 */
TEST_KEY_SET_OR(cmp13, i1.key, i3.key) goto err;
if (cmp13 > 0)
{ /* insert i3 */
if (merge_output(r, &i3, mapping) < 0) goto err;
if (i3.next(&i3) < 0) goto err;
}
else if (cmp13==0 && (set || (TEST_VALUE(i1.value, i3.value) == 0)))
{ /* delete i2 */
if (i1.next(&i1) < 0) goto err;
if (i3.next(&i3) < 0) goto err;
}
else
{ /* Dueling deletes or delete and change */
merge_error(i1.position, i2.position, i3.position, 8);
goto err;
}
}
if (i1.position >= 0)
{ /* Dueling deletes */
merge_error(i1.position, i2.position, i3.position, 9);
goto err;
}
while (i2.position >= 0)
{ /* Inserting i2 at end */
if (merge_output(r, &i2, mapping) < 0) goto err;
if (i2.next(&i2) < 0) goto err;
}
while (i3.position >= 0)
{ /* Inserting i3 at end */
if (merge_output(r, &i3, mapping) < 0) goto err;
if (i3.next(&i3) < 0) goto err;
}
/* If the output bucket is empty, conflict resolution doesn't have
* enough info to unlink it from its containing BTree correctly.
*/
if (r->len == 0)
{
merge_error(-1, -1, -1, 10);
goto err;
}
finiSetIteration(&i1);
finiSetIteration(&i2);
finiSetIteration(&i3);
if (s1->next)
{
Py_INCREF(s1->next);
r->next = s1->next;
}
s = bucket_getstate(r);
Py_DECREF(r);
return s;
err:
finiSetIteration(&i1);
finiSetIteration(&i2);
finiSetIteration(&i3);
Py_XDECREF(r);
return NULL;
}
@@ -0,0 +1,110 @@
##############################################################################
#
# Copyright (c) 2001-2012 Zope Foundation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
__all__ = ('Bucket', 'Set', 'BTree', 'TreeSet',
'OIBucket', 'OISet', 'OIBTree', 'OITreeSet',
'union', 'intersection', 'difference',
'weightedUnion', 'weightedIntersection',
)
from zope.interface import moduleProvides
from .Interfaces import IObjectIntegerBTreeModule
from ._base import Bucket
from ._base import MERGE
from ._base import MERGE_WEIGHT_numeric
from ._base import MERGE_DEFAULT_float
from ._base import Set
from ._base import Tree as BTree
from ._base import TreeSet
from ._base import _TreeIterator
from ._base import difference as _difference
from ._base import intersection as _intersection
from ._base import set_operation as _set_operation
from ._base import to_ob as _to_key
from ._base import to_int as _to_value
from ._base import union as _union
from ._base import weightedIntersection as _weightedIntersection
from ._base import weightedUnion as _weightedUnion
from ._base import _fix_pickle
from ._compat import import_c_extension
_BUCKET_SIZE = 60
_TREE_SIZE = 250
using64bits = True
class OIBucketPy(Bucket):
_to_key = _to_key
_to_value = _to_value
MERGE = MERGE
MERGE_WEIGHT = MERGE_WEIGHT_numeric
MERGE_DEFAULT = MERGE_DEFAULT_float
class OISetPy(Set):
_to_key = _to_key
MERGE = MERGE
MERGE_WEIGHT = MERGE_WEIGHT_numeric
MERGE_DEFAULT = MERGE_DEFAULT_float
class OIBTreePy(BTree):
max_leaf_size = _BUCKET_SIZE
max_internal_size = _TREE_SIZE
_to_key = _to_key
_to_value = _to_value
MERGE = MERGE
MERGE_WEIGHT = MERGE_WEIGHT_numeric
MERGE_DEFAULT = MERGE_DEFAULT_float
class OITreeSetPy(TreeSet):
max_leaf_size = _BUCKET_SIZE
max_internal_size = _TREE_SIZE
_to_key = _to_key
MERGE = MERGE
MERGE_WEIGHT = MERGE_WEIGHT_numeric
MERGE_DEFAULT = MERGE_DEFAULT_float
class OITreeIteratorPy(_TreeIterator):
pass
# Can't declare forward refs, so fix up afterwards:
OIBucketPy._mapping_type = OIBucketPy._bucket_type = OIBucketPy
OIBucketPy._set_type = OISetPy
OISetPy._mapping_type = OIBucketPy
OISetPy._set_type = OISetPy._bucket_type = OISetPy
OIBTreePy._mapping_type = OIBTreePy._bucket_type = OIBucketPy
OIBTreePy._set_type = OISetPy
OITreeSetPy._mapping_type = OIBucketPy
OITreeSetPy._set_type = OITreeSetPy._bucket_type = OISetPy
differencePy = _set_operation(_difference, OISetPy)
unionPy = _set_operation(_union, OISetPy)
intersectionPy = _set_operation(_intersection, OISetPy)
weightedUnionPy = _set_operation(_weightedUnion, OISetPy)
weightedIntersectionPy = _set_operation(_weightedIntersection, OISetPy)
import_c_extension(globals())
_fix_pickle(globals(), __name__)
moduleProvides(IObjectIntegerBTreeModule)
@@ -0,0 +1,111 @@
##############################################################################
#
# Copyright (c) 2001-2012 Zope Foundation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
__all__ = ('Bucket', 'Set', 'BTree', 'TreeSet',
'OLBucket', 'OLSet', 'OLBTree', 'OLTreeSet',
'union', 'intersection', 'difference',
'weightedUnion', 'weightedIntersection',
)
from zope.interface import moduleProvides
from .Interfaces import IObjectIntegerBTreeModule
from ._base import Bucket
from ._base import MERGE
from ._base import MERGE_WEIGHT_numeric
from ._base import MERGE_DEFAULT_int
from ._base import Set
from ._base import Tree as BTree
from ._base import TreeSet
from ._base import _TreeIterator
from ._base import difference as _difference
from ._base import intersection as _intersection
from ._base import set_operation as _set_operation
from ._base import to_ob as _to_key
from ._base import to_long as _to_value
from ._base import union as _union
from ._base import weightedIntersection as _weightedIntersection
from ._base import weightedUnion as _weightedUnion
from ._base import _fix_pickle
from ._compat import import_c_extension
_BUCKET_SIZE = 60
_TREE_SIZE = 250
using64bits = True
class OLBucketPy(Bucket):
_to_key = _to_key
_to_value = _to_value
MERGE = MERGE
MERGE_WEIGHT = MERGE_WEIGHT_numeric
MERGE_DEFAULT = MERGE_DEFAULT_int
class OLSetPy(Set):
_to_key = _to_key
MERGE = MERGE
MERGE_WEIGHT = MERGE_WEIGHT_numeric
MERGE_DEFAULT = MERGE_DEFAULT_int
class OLBTreePy(BTree):
max_leaf_size = _BUCKET_SIZE
max_internal_size = _TREE_SIZE
_to_key = _to_key
_to_value = _to_value
MERGE = MERGE
MERGE_WEIGHT = MERGE_WEIGHT_numeric
MERGE_DEFAULT = MERGE_DEFAULT_int
class OLTreeSetPy(TreeSet):
max_leaf_size = _BUCKET_SIZE
max_internal_size = _TREE_SIZE
_to_key = _to_key
MERGE = MERGE
MERGE_WEIGHT = MERGE_WEIGHT_numeric
MERGE_DEFAULT = MERGE_DEFAULT_int
class OLTreeIteratorPy(_TreeIterator):
pass
# Can't declare forward refs, so fix up afterwards:
OLBucketPy._mapping_type = OLBucketPy._bucket_type = OLBucketPy
OLBucketPy._set_type = OLSetPy
OLSetPy._mapping_type = OLBucketPy
OLSetPy._set_type = OLSetPy._bucket_type = OLSetPy
OLBTreePy._mapping_type = OLBTreePy._bucket_type = OLBucketPy
OLBTreePy._set_type = OLSetPy
OLTreeSetPy._mapping_type = OLBucketPy
OLTreeSetPy._set_type = OLTreeSetPy._bucket_type = OLSetPy
differencePy = _set_operation(_difference, OLSetPy)
unionPy = _set_operation(_union, OLSetPy)
intersectionPy = _set_operation(_intersection, OLSetPy)
weightedUnionPy = _set_operation(_weightedUnion, OLSetPy)
weightedIntersectionPy = _set_operation(_weightedIntersection, OLSetPy)
import_c_extension(globals())
_fix_pickle(globals(), __name__)
moduleProvides(IObjectIntegerBTreeModule)
@@ -0,0 +1,91 @@
##############################################################################
#
# Copyright (c) 2001-2012 Zope Foundation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
__all__ = ('Bucket', 'Set', 'BTree', 'TreeSet',
'OOBucket', 'OOSet', 'OOBTree', 'OOTreeSet',
'union', 'intersection','difference',
)
from zope.interface import moduleProvides
from .Interfaces import IObjectObjectBTreeModule
from ._base import Bucket
from ._base import Set
from ._base import Tree as BTree
from ._base import TreeSet
from ._base import _TreeIterator
from ._base import difference as _difference
from ._base import intersection as _intersection
from ._base import set_operation as _set_operation
from ._base import to_ob as _to_key
_to_value = _to_key
from ._base import union as _union
from ._base import _fix_pickle
from ._compat import import_c_extension
_BUCKET_SIZE = 30
_TREE_SIZE = 250
using64bits = False
class OOBucketPy(Bucket):
_to_key = _to_key
_to_value = _to_value
class OOSetPy(Set):
_to_key = _to_key
class OOBTreePy(BTree):
max_leaf_size = _BUCKET_SIZE
max_internal_size = _TREE_SIZE
_to_key = _to_key
_to_value = _to_value
class OOTreeSetPy(TreeSet):
max_leaf_size = _BUCKET_SIZE
max_internal_size = _TREE_SIZE
_to_key = _to_key
class OOTreeIteratorPy(_TreeIterator):
pass
# Can't declare forward refs, so fix up afterwards:
OOBucketPy._mapping_type = OOBucketPy._bucket_type = OOBucketPy
OOBucketPy._set_type = OOSetPy
OOSetPy._mapping_type = OOBucketPy
OOSetPy._set_type = OOSetPy._bucket_type = OOSetPy
OOBTreePy._mapping_type = OOBTreePy._bucket_type = OOBucketPy
OOBTreePy._set_type = OOSetPy
OOTreeSetPy._mapping_type = OOBucketPy
OOTreeSetPy._set_type = OOTreeSetPy._bucket_type = OOSetPy
differencePy = _set_operation(_difference, OOSetPy)
unionPy = _set_operation(_union, OOSetPy)
intersectionPy = _set_operation(_intersection, OOSetPy)
import_c_extension(globals())
_fix_pickle(globals(), __name__)
moduleProvides(IObjectObjectBTreeModule)
@@ -0,0 +1,557 @@
/*****************************************************************************
Copyright (c) 2001, 2002 Zope Foundation and Contributors.
All Rights Reserved.
This software is subject to the provisions of the Zope Public License,
Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
FOR A PARTICULAR PURPOSE
****************************************************************************/
/****************************************************************************
Set operations
****************************************************************************/
#define SETOPTEMPLATE_C "$Id$\n"
#ifdef KEY_CHECK
static int
nextKeyAsSet(SetIteration *i)
{
if (i->position >= 0) {
if (i->position) {
DECREF_KEY(i->key);
i->position = -1;
}
else
i->position = 1;
}
return 0;
}
#endif
/* initSetIteration
*
* Start the set iteration protocol. See the comments at struct SetIteration.
*
* Arguments
* i The address of a SetIteration control struct.
* s The address of the set, bucket, BTree, ..., to be iterated.
* useValues Boolean; if true, and s has values (is a mapping), copy
* them into i->value each time i->next() is called; else
* ignore s's values even if s is a mapping.
*
* Return
* 0 on success; -1 and an exception set if error.
* i.usesValue is set to 1 (true) if s has values and useValues was
* true; else usesValue is set to 0 (false).
* i.set gets a new reference to s, or to some other object used to
* iterate over s.
* i.position is set to 0.
* i.next is set to an appropriate iteration function.
* i.key and i.value are left alone.
*
* Internal
* i.position < 0 means iteration terminated.
* i.position = 0 means iteration hasn't yet begun (next() hasn't
* been called yet).
* In all other cases, i.key, and possibly i.value, own references.
* These must be cleaned up, either by next() routines, or by
* finiSetIteration.
* next() routines must ensure the above. They should return without
* doing anything when i.position < 0.
* It's the responsibility of {init, fini}setIteration to clean up
* the reference in i.set, and to ensure that no stale references
* live in i.key or i.value if iteration terminates abnormally.
* A SetIteration struct has been cleaned up iff i.set is NULL.
*/
static int
initSetIteration(SetIteration *i, PyObject *s, int useValues)
{
i->set = NULL;
i->position = -1; /* set to 0 only on normal return */
i->usesValue = 0; /* assume it's a set or that values aren't iterated */
if (PyObject_IsInstance(s, (PyObject *)&BucketType))
{
i->set = s;
Py_INCREF(s);
if (useValues)
{
i->usesValue = 1;
i->next = nextBucket;
}
else
i->next = nextSet;
}
else if (PyObject_IsInstance(s, (PyObject *)&SetType))
{
i->set = s;
Py_INCREF(s);
i->next = nextSet;
}
else if (PyObject_IsInstance(s, (PyObject *)&BTreeType))
{
i->set = BTree_rangeSearch(BTREE(s), NULL, NULL, 'i');
UNLESS(i->set) return -1;
if (useValues)
{
i->usesValue = 1;
i->next = nextBTreeItems;
}
else
i->next = nextTreeSetItems;
}
else if (PyObject_IsInstance(s, (PyObject *)&TreeSetType))
{
i->set = BTree_rangeSearch(BTREE(s), NULL, NULL, 'k');
UNLESS(i->set) return -1;
i->next = nextTreeSetItems;
}
#ifdef KEY_CHECK
else if (KEY_CHECK(s))
{
int copied = 1;
COPY_KEY_FROM_ARG(i->key, s, copied);
UNLESS (copied) return -1;
INCREF_KEY(i->key);
i->set = s;
Py_INCREF(s);
i->next = nextKeyAsSet;
}
#endif
else
{
PyErr_SetString(PyExc_TypeError, "invalid argument");
return -1;
}
i->position = 0;
return 0;
}
#ifndef MERGE_WEIGHT
#define MERGE_WEIGHT(O, w) (O)
#endif
static int
copyRemaining(Bucket *r, SetIteration *i, int merge,
/* See comment # 42 */
#ifdef MERGE
VALUE_TYPE w)
#else
int w)
#endif
{
while (i->position >= 0)
{
if(r->len >= r->size && Bucket_grow(r, -1, ! merge) < 0) return -1;
COPY_KEY(r->keys[r->len], i->key);
INCREF_KEY(r->keys[r->len]);
if (merge)
{
COPY_VALUE(r->values[r->len], MERGE_WEIGHT(i->value, w));
INCREF_VALUE(r->values[r->len]);
}
r->len++;
if (i->next(i) < 0) return -1;
}
return 0;
}
/* This is the workhorse for all set merge operations: the weighted and
* unweighted flavors of union and intersection, and set difference. The
* algorithm is conceptually simple but the code is complicated due to all
* the options.
*
* s1, s2
* The input collections to be merged.
*
* usevalues1, usevalues2
* Booleans. In the output, should values from s1 (or s2) be used? This
* only makes sense when an operation intends to support mapping outputs;
* these should both be false for operations that want pure set outputs.
*
* w1, w2
* If usevalues1(2) are true, these are the weights to apply to the
* input values.
*
* c1
* Boolean. Should keys that appear in c1 but not c2 appear in the output?
* c12
* Boolean. Should keys that appear in both inputs appear in the output?
* c2
* Boolean. Should keys that appear in c2 but not c1 appear in the output?
*
* Returns NULL if error, else a Set or Bucket, depending on whether a set or
* mapping was requested.
*/
static PyObject *
set_operation(PyObject *s1, PyObject *s2,
int usevalues1, int usevalues2,
/* Comment # 42
The following ifdef works around a template/type problem
Weights are passed as integers. In particular, the weight passed by
difference is one. This works fine in the int value and float value
cases but makes no sense in the object value case. In the object
value case, we don't do merging, so we don't use the weights, so it
doesn't matter what they are.
*/
#ifdef MERGE
VALUE_TYPE w1, VALUE_TYPE w2,
#else
int w1, int w2,
#endif
int c1, int c12, int c2)
{
Bucket *r=0;
SetIteration i1 = {0,0,0}, i2 = {0,0,0};
int cmp, merge;
if (initSetIteration(&i1, s1, usevalues1) < 0) goto err;
if (initSetIteration(&i2, s2, usevalues2) < 0) goto err;
merge = i1.usesValue | i2.usesValue;
if (merge)
{
#ifndef MERGE
if (c12 && i1.usesValue && i2.usesValue) goto invalid_set_operation;
#endif
if (! i1.usesValue&& i2.usesValue)
{
SetIteration t;
int i;
/* See comment # 42 above */
#ifdef MERGE
VALUE_TYPE v;
#else
int v;
#endif
t=i1; i1=i2; i2=t;
i=c1; c1=c2; c2=i;
v=w1; w1=w2; w2=v;
}
#ifdef MERGE_DEFAULT
i1.value=MERGE_DEFAULT;
i2.value=MERGE_DEFAULT;
#else
if (i1.usesValue)
{
if (! i2.usesValue && c2) goto invalid_set_operation;
}
else
{
if (c1 || c12) goto invalid_set_operation;
}
#endif
UNLESS(r=BUCKET(PyObject_CallObject(OBJECT(&BucketType), NULL)))
goto err;
}
else
{
UNLESS(r=BUCKET(PyObject_CallObject(OBJECT(&SetType), NULL)))
goto err;
}
if (i1.next(&i1) < 0) goto err;
if (i2.next(&i2) < 0) goto err;
while (i1.position >= 0 && i2.position >= 0)
{
TEST_KEY_SET_OR(cmp, i1.key, i2.key) goto err;
if(cmp < 0)
{
if(c1)
{
if(r->len >= r->size && Bucket_grow(r, -1, ! merge) < 0) goto err;
COPY_KEY(r->keys[r->len], i1.key);
INCREF_KEY(r->keys[r->len]);
if (merge)
{
COPY_VALUE(r->values[r->len], MERGE_WEIGHT(i1.value, w1));
INCREF_VALUE(r->values[r->len]);
}
r->len++;
}
if (i1.next(&i1) < 0) goto err;
}
else if(cmp==0)
{
if(c12)
{
if(r->len >= r->size && Bucket_grow(r, -1, ! merge) < 0) goto err;
COPY_KEY(r->keys[r->len], i1.key);
INCREF_KEY(r->keys[r->len]);
if (merge)
{
#ifdef MERGE
r->values[r->len] = MERGE(i1.value, w1, i2.value, w2);
#else
COPY_VALUE(r->values[r->len], i1.value);
INCREF_VALUE(r->values[r->len]);
#endif
}
r->len++;
}
if (i1.next(&i1) < 0) goto err;
if (i2.next(&i2) < 0) goto err;
}
else
{
if(c2)
{
if(r->len >= r->size && Bucket_grow(r, -1, ! merge) < 0) goto err;
COPY_KEY(r->keys[r->len], i2.key);
INCREF_KEY(r->keys[r->len]);
if (merge)
{
COPY_VALUE(r->values[r->len], MERGE_WEIGHT(i2.value, w2));
INCREF_VALUE(r->values[r->len]);
}
r->len++;
}
if (i2.next(&i2) < 0) goto err;
}
}
if(c1 && copyRemaining(r, &i1, merge, w1) < 0) goto err;
if(c2 && copyRemaining(r, &i2, merge, w2) < 0) goto err;
finiSetIteration(&i1);
finiSetIteration(&i2);
return OBJECT(r);
#ifndef MERGE_DEFAULT
invalid_set_operation:
PyErr_SetString(PyExc_TypeError, "invalid set operation");
#endif
err:
finiSetIteration(&i1);
finiSetIteration(&i2);
Py_XDECREF(r);
return NULL;
}
static PyObject *
difference_m(PyObject *ignored, PyObject *args)
{
PyObject *o1, *o2;
UNLESS(PyArg_ParseTuple(args, "OO", &o1, &o2)) return NULL;
if (o1 == Py_None || o2 == Py_None)
{
/* difference(None, X) -> None; difference(X, None) -> X */
Py_INCREF(o1);
return o1;
}
return set_operation(o1, o2, 1, 0, /* preserve values from o1, ignore o2's */
1, 0, /* o1's values multiplied by 1 */
1, 0, 0); /* take only keys unique to o1 */
}
static PyObject *
union_m(PyObject *ignored, PyObject *args)
{
PyObject *o1, *o2;
UNLESS(PyArg_ParseTuple(args, "OO", &o1, &o2)) return NULL;
if (o1 == Py_None)
{
Py_INCREF(o2);
return o2;
}
else if (o2 == Py_None)
{
Py_INCREF(o1);
return o1;
}
return set_operation(o1, o2, 0, 0, /* ignore values in both */
1, 1, /* the weights are irrelevant */
1, 1, 1); /* take all keys */
}
static PyObject *
intersection_m(PyObject *ignored, PyObject *args)
{
PyObject *o1, *o2;
UNLESS(PyArg_ParseTuple(args, "OO", &o1, &o2)) return NULL;
if (o1 == Py_None)
{
Py_INCREF(o2);
return o2;
}
else if (o2 == Py_None)
{
Py_INCREF(o1);
return o1;
}
return set_operation(o1, o2, 0, 0, /* ignore values in both */
1, 1, /* the weights are irrelevant */
0, 1, 0); /* take only keys common to both */
}
#ifdef MERGE
static PyObject *
wunion_m(PyObject *ignored, PyObject *args)
{
PyObject *o1, *o2;
VALUE_TYPE w1 = 1, w2 = 1;
UNLESS(PyArg_ParseTuple(args, "OO|" VALUE_PARSE VALUE_PARSE,
&o1, &o2, &w1, &w2)
) return NULL;
if (o1 == Py_None)
return Py_BuildValue(VALUE_PARSE "O", (o2 == Py_None ? 0 : w2), o2);
else if (o2 == Py_None)
return Py_BuildValue(VALUE_PARSE "O", w1, o1);
o1 = set_operation(o1, o2, 1, 1, w1, w2, 1, 1, 1);
if (o1)
ASSIGN(o1, Py_BuildValue(VALUE_PARSE "O", (VALUE_TYPE)1, o1));
return o1;
}
static PyObject *
wintersection_m(PyObject *ignored, PyObject *args)
{
PyObject *o1, *o2;
VALUE_TYPE w1 = 1, w2 = 1;
UNLESS(PyArg_ParseTuple(args, "OO|" VALUE_PARSE VALUE_PARSE,
&o1, &o2, &w1, &w2)
) return NULL;
if (o1 == Py_None)
return Py_BuildValue(VALUE_PARSE "O", (o2 == Py_None ? 0 : w2), o2);
else if (o2 == Py_None)
return Py_BuildValue(VALUE_PARSE "O", w1, o1);
o1 = set_operation(o1, o2, 1, 1, w1, w2, 0, 1, 0);
if (o1)
ASSIGN(o1, Py_BuildValue(VALUE_PARSE "O",
((o1->ob_type == (PyTypeObject*)(&SetType)) ? w2+w1 : 1),
o1));
return o1;
}
#endif
#ifdef MULTI_INT_UNION
#include "sorters.c"
/* Input is a sequence of integer sets (or convertible to sets by the
set iteration protocol). Output is the union of the sets. The point
is to run much faster than doing pairs of unions.
*/
static PyObject *
multiunion_m(PyObject *ignored, PyObject *args)
{
PyObject *seq; /* input sequence */
int n; /* length of input sequence */
PyObject *set = NULL; /* an element of the input sequence */
Bucket *result; /* result set */
SetIteration setiter = {0};
int i;
UNLESS(PyArg_ParseTuple(args, "O", &seq))
return NULL;
n = PyObject_Length(seq);
if (n < 0)
return NULL;
/* Construct an empty result set. */
result = BUCKET(PyObject_CallObject(OBJECT(&SetType), NULL));
if (result == NULL)
return NULL;
/* For each set in the input sequence, append its elements to the result
set. At this point, we ignore the possibility of duplicates. */
for (i = 0; i < n; ++i) {
set = PySequence_GetItem(seq, i);
if (set == NULL)
goto Error;
/* If set is a bucket, do a straight resize + memcpy. */
if (set->ob_type == (PyTypeObject*)&SetType ||
set->ob_type == (PyTypeObject*)&BucketType)
{
Bucket *b = BUCKET(set);
int status = 0;
UNLESS (PER_USE(b)) goto Error;
if (b->len)
status = bucket_append(result, b, 0, b->len, 0, i < n-1);
PER_UNUSE(b);
if (status < 0) goto Error;
}
else {
/* No cheap way: iterate over set's elements one at a time. */
if (initSetIteration(&setiter, set, 0) < 0) goto Error;
if (setiter.next(&setiter) < 0) goto Error;
while (setiter.position >= 0) {
if (result->len >= result->size && Bucket_grow(result, -1, 1) < 0)
goto Error;
COPY_KEY(result->keys[result->len], setiter.key);
++result->len;
/* We know the key is an int, so no need to incref it. */
if (setiter.next(&setiter) < 0) goto Error;
}
finiSetIteration(&setiter);
}
Py_DECREF(set);
set = NULL;
}
/* Combine, sort, remove duplicates, and reset the result's len.
If the set shrinks (which happens if and only if there are
duplicates), no point to realloc'ing the set smaller, as we
expect the result set to be short-lived.
*/
if (result->len > 0) {
size_t newlen; /* number of elements in final result set */
newlen = sort_int_nodups(result->keys, (size_t)result->len);
result->len = (int)newlen;
}
return (PyObject *)result;
Error:
Py_DECREF(result);
Py_XDECREF(set);
finiSetIteration(&setiter);
return NULL;
}
#endif
@@ -0,0 +1,381 @@
/*****************************************************************************
Copyright (c) 2001, 2002 Zope Foundation and Contributors.
All Rights Reserved.
This software is subject to the provisions of the Zope Public License,
Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
FOR A PARTICULAR PURPOSE
****************************************************************************/
#include "_compat.h"
#define SETTEMPLATE_C "$Id$\n"
static PyObject *
Set_insert(Bucket *self, PyObject *args)
{
PyObject *key;
int i;
UNLESS (PyArg_ParseTuple(args, "O", &key))
return NULL;
if ( (i=_bucket_set(self, key, Py_None, 1, 1, 0)) < 0)
return NULL;
return INT_FROM_LONG(i);
}
/* _Set_update and _TreeSet_update are identical except for the
function they call to add the element to the set.
*/
static int
_Set_update(Bucket *self, PyObject *seq)
{
int n=0, ind=0;
PyObject *iter, *v;
iter = PyObject_GetIter(seq);
if (iter == NULL)
return -1;
while (1) {
v = PyIter_Next(iter);
if (v == NULL) {
if (PyErr_Occurred())
goto err;
else
break;
}
ind = _bucket_set(self, v, Py_None, 1, 1, 0);
Py_DECREF(v);
if (ind < 0)
goto err;
else
n += ind;
}
err:
Py_DECREF(iter);
if (ind < 0)
return -1;
return n;
}
static PyObject *
Set_update(Bucket *self, PyObject *args)
{
PyObject *seq = NULL;
int n = 0;
if (!PyArg_ParseTuple(args, "|O:update", &seq))
return NULL;
if (seq) {
n = _Set_update(self, seq);
if (n < 0)
return NULL;
}
return INT_FROM_LONG(n);
}
static PyObject *
Set_remove(Bucket *self, PyObject *args)
{
PyObject *key;
UNLESS (PyArg_ParseTuple(args, "O", &key))
return NULL;
if (_bucket_set(self, key, NULL, 0, 1, 0) < 0)
return NULL;
Py_INCREF(Py_None);
return Py_None;
}
static int
_set_setstate(Bucket *self, PyObject *args)
{
PyObject *k, *items;
Bucket *next=0;
int i, l, copied=1;
KEY_TYPE *keys;
UNLESS (PyArg_ParseTuple(args, "O|O", &items, &next))
return -1;
if (!PyTuple_Check(items)) {
PyErr_SetString(PyExc_TypeError,
"tuple required for first state element");
return -1;
}
if ((l=PyTuple_Size(items)) < 0)
return -1;
for (i=self->len; --i >= 0; )
{
DECREF_KEY(self->keys[i]);
}
self->len=0;
if (self->next)
{
Py_DECREF(self->next);
self->next=0;
}
if (l > self->size)
{
UNLESS (keys=BTree_Realloc(self->keys, sizeof(KEY_TYPE)*l))
return -1;
self->keys=keys;
self->size=l;
}
for (i=0; i<l; i++)
{
k=PyTuple_GET_ITEM(items, i);
COPY_KEY_FROM_ARG(self->keys[i], k, copied);
UNLESS (copied)
return -1;
INCREF_KEY(self->keys[i]);
}
self->len=l;
if (next)
{
self->next=next;
Py_INCREF(next);
}
return 0;
}
static PyObject *
set_setstate(Bucket *self, PyObject *args)
{
int r;
UNLESS (PyArg_ParseTuple(args, "O", &args))
return NULL;
PER_PREVENT_DEACTIVATION(self);
r=_set_setstate(self, args);
PER_UNUSE(self);
if (r < 0)
return NULL;
Py_INCREF(Py_None);
return Py_None;
}
static struct PyMethodDef Set_methods[] = {
{"__getstate__", (PyCFunction) bucket_getstate, METH_VARARGS,
"__getstate__() -- Return the picklable state of the object"},
{"__setstate__", (PyCFunction) set_setstate, METH_VARARGS,
"__setstate__() -- Set the state of the object"},
{"keys", (PyCFunction) bucket_keys, METH_VARARGS | METH_KEYWORDS,
"keys() -- Return the keys"},
{"has_key", (PyCFunction) bucket_has_key, METH_O,
"has_key(key) -- Test whether the bucket contains the given key"},
{"clear", (PyCFunction) bucket_clear, METH_VARARGS,
"clear() -- Remove all of the items from the bucket"},
{"maxKey", (PyCFunction) Bucket_maxKey, METH_VARARGS,
"maxKey([key]) -- Find the maximum key\n\n"
"If an argument is given, find the maximum <= the argument"},
{"minKey", (PyCFunction) Bucket_minKey, METH_VARARGS,
"minKey([key]) -- Find the minimum key\n\n"
"If an argument is given, find the minimum >= the argument"},
#ifdef PERSISTENT
{"_p_resolveConflict",
(PyCFunction) bucket__p_resolveConflict, METH_VARARGS,
"_p_resolveConflict() -- Reinitialize from a newly created copy"},
{"_p_deactivate",
(PyCFunction) bucket__p_deactivate, METH_VARARGS | METH_KEYWORDS,
"_p_deactivate() -- Reinitialize from a newly created copy"},
#endif
{"add", (PyCFunction)Set_insert, METH_VARARGS,
"add(id) -- Add a key to the set"},
{"insert", (PyCFunction)Set_insert, METH_VARARGS,
"insert(id) -- Add a key to the set"},
{"update", (PyCFunction)Set_update, METH_VARARGS,
"update(seq) -- Add the items from the given sequence to the set"},
{"remove", (PyCFunction)Set_remove, METH_VARARGS,
"remove(id) -- Remove an id from the set"},
{NULL, NULL} /* sentinel */
};
static int
Set_init(PyObject *self, PyObject *args, PyObject *kwds)
{
PyObject *v = NULL;
if (!PyArg_ParseTuple(args, "|O:" MOD_NAME_PREFIX "Set", &v))
return -1;
if (v)
return _Set_update((Bucket *)self, v);
else
return 0;
}
static PyObject *
set_repr(Bucket *self)
{
static PyObject *format;
PyObject *r, *t;
if (!format)
format = TEXT_FROM_STRING(MOD_NAME_PREFIX "Set(%s)");
UNLESS (t = PyTuple_New(1))
return NULL;
UNLESS (r = bucket_keys(self, NULL, NULL))
goto err;
PyTuple_SET_ITEM(t, 0, r);
r = t;
ASSIGN(r, TEXT_FORMAT(format, r));
return r;
err:
Py_DECREF(t);
return NULL;
}
static Py_ssize_t
set_length(Bucket *self)
{
int r;
PER_USE_OR_RETURN(self, -1);
r = self->len;
PER_UNUSE(self);
return r;
}
static PyObject *
set_item(Bucket *self, Py_ssize_t index)
{
PyObject *r=0;
PER_USE_OR_RETURN(self, NULL);
if (index >= 0 && index < self->len)
{
COPY_KEY_TO_OBJECT(r, self->keys[index]);
}
else
IndexError(index);
PER_UNUSE(self);
return r;
}
static PySequenceMethods set_as_sequence = {
(lenfunc)set_length, /* sq_length */
(binaryfunc)0, /* sq_concat */
(ssizeargfunc)0, /* sq_repeat */
(ssizeargfunc)set_item, /* sq_item */
(ssizessizeargfunc)0, /* sq_slice */
(ssizeobjargproc)0, /* sq_ass_item */
(ssizessizeobjargproc)0, /* sq_ass_slice */
(objobjproc)bucket_contains, /* sq_contains */
0, /* sq_inplace_concat */
0, /* sq_inplace_repeat */
};
static PyTypeObject SetType = {
PyVarObject_HEAD_INIT(NULL, 0) /* PyPersist_Type */
MODULE_NAME MOD_NAME_PREFIX "Set", /* tp_name */
sizeof(Bucket), /* tp_basicsize */
0, /* tp_itemsize */
(destructor)bucket_dealloc, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_compare */
(reprfunc)set_repr, /* tp_repr */
0, /* tp_as_number */
&set_as_sequence, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT |
Py_TPFLAGS_HAVE_GC |
Py_TPFLAGS_BASETYPE, /* tp_flags */
0, /* tp_doc */
(traverseproc)bucket_traverse, /* tp_traverse */
(inquiry)bucket_tp_clear, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
(getiterfunc)Bucket_getiter, /* tp_iter */
0, /* tp_iternext */
Set_methods, /* tp_methods */
Bucket_members, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
Set_init, /* tp_init */
0, /* tp_alloc */
0, /*PyType_GenericNew,*/ /* tp_new */
};
static int
nextSet(SetIteration *i)
{
if (i->position >= 0)
{
UNLESS(PER_USE(BUCKET(i->set)))
return -1;
if (i->position)
{
DECREF_KEY(i->key);
}
if (i->position < BUCKET(i->set)->len)
{
COPY_KEY(i->key, BUCKET(i->set)->keys[i->position]);
INCREF_KEY(i->key);
i->position ++;
}
else
{
i->position = -1;
PER_ACCESSED(BUCKET(i->set));
}
PER_ALLOW_DEACTIVATION(BUCKET(i->set));
}
return 0;
}
@@ -0,0 +1,254 @@
/*****************************************************************************
Copyright (c) 2001, 2002 Zope Foundation and Contributors.
All Rights Reserved.
This software is subject to the provisions of the Zope Public License,
Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
FOR A PARTICULAR PURPOSE
****************************************************************************/
#include "_compat.h"
#define TREESETTEMPLATE_C "$Id$\n"
static PyObject *
TreeSet_insert(BTree *self, PyObject *args)
{
PyObject *key;
int i;
if (!PyArg_ParseTuple(args, "O:insert", &key))
return NULL;
i = _BTree_set(self, key, Py_None, 1, 1);
if (i < 0)
return NULL;
return INT_FROM_LONG(i);
}
/* _Set_update and _TreeSet_update are identical except for the
function they call to add the element to the set.
*/
static int
_TreeSet_update(BTree *self, PyObject *seq)
{
int n=0, ind=0;
PyObject *iter, *v;
iter = PyObject_GetIter(seq);
if (iter == NULL)
return -1;
while (1)
{
v = PyIter_Next(iter);
if (v == NULL)
{
if (PyErr_Occurred())
goto err;
else
break;
}
ind = _BTree_set(self, v, Py_None, 1, 1);
Py_DECREF(v);
if (ind < 0)
goto err;
else
n += ind;
}
err:
Py_DECREF(iter);
if (ind < 0)
return -1;
return n;
}
static PyObject *
TreeSet_update(BTree *self, PyObject *args)
{
PyObject *seq = NULL;
int n = 0;
if (!PyArg_ParseTuple(args, "|O:update", &seq))
return NULL;
if (seq)
{
n = _TreeSet_update(self, seq);
if (n < 0)
return NULL;
}
return INT_FROM_LONG(n);
}
static PyObject *
TreeSet_remove(BTree *self, PyObject *args)
{
PyObject *key;
UNLESS (PyArg_ParseTuple(args, "O", &key))
return NULL;
if (_BTree_set(self, key, NULL, 0, 1) < 0)
return NULL;
Py_INCREF(Py_None);
return Py_None;
}
static PyObject *
TreeSet_setstate(BTree *self, PyObject *args)
{
int r;
if (!PyArg_ParseTuple(args,"O",&args))
return NULL;
PER_PREVENT_DEACTIVATION(self);
r=_BTree_setstate(self, args, 1);
PER_UNUSE(self);
if (r < 0)
return NULL;
Py_INCREF(Py_None);
return Py_None;
}
static struct PyMethodDef TreeSet_methods[] =
{
{"__getstate__", (PyCFunction) BTree_getstate, METH_NOARGS,
"__getstate__() -> state\n\n"
"Return the picklable state of the TreeSet."},
{"__setstate__", (PyCFunction) TreeSet_setstate, METH_VARARGS,
"__setstate__(state)\n\n"
"Set the state of the TreeSet."},
{"has_key", (PyCFunction) BTree_has_key, METH_O,
"has_key(key)\n\n"
"Return true if the TreeSet contains the given key."},
{"keys", (PyCFunction) BTree_keys, METH_VARARGS | METH_KEYWORDS,
"keys([min, max]) -> list of keys\n\n"
"Returns the keys of the TreeSet. If min and max are supplied, only\n"
"keys greater than min and less than max are returned."},
{"maxKey", (PyCFunction) BTree_maxKey, METH_VARARGS,
"maxKey([max]) -> key\n\n"
"Return the largest key in the BTree. If max is specified, return\n"
"the largest key <= max."},
{"minKey", (PyCFunction) BTree_minKey, METH_VARARGS,
"minKey([mi]) -> key\n\n"
"Return the smallest key in the BTree. If min is specified, return\n"
"the smallest key >= min."},
{"clear", (PyCFunction) BTree_clear, METH_NOARGS,
"clear()\n\nRemove all of the items from the BTree."},
{"add", (PyCFunction)TreeSet_insert, METH_VARARGS,
"add(id) -- Add an item to the set"},
{"insert", (PyCFunction)TreeSet_insert, METH_VARARGS,
"insert(id) -- Add an item to the set"},
{"update", (PyCFunction)TreeSet_update, METH_VARARGS,
"update(collection)\n\n Add the items from the given collection."},
{"remove", (PyCFunction)TreeSet_remove, METH_VARARGS,
"remove(id) -- Remove a key from the set"},
{"_check", (PyCFunction) BTree_check, METH_NOARGS,
"Perform sanity check on TreeSet, and raise exception if flawed."},
#ifdef PERSISTENT
{"_p_resolveConflict",
(PyCFunction) BTree__p_resolveConflict, METH_VARARGS,
"_p_resolveConflict() -- Reinitialize from a newly created copy"},
{"_p_deactivate",
(PyCFunction) BTree__p_deactivate, METH_VARARGS | METH_KEYWORDS,
"_p_deactivate()\n\nReinitialize from a newly created copy."},
#endif
{NULL, NULL} /* sentinel */
};
static PyMappingMethods TreeSet_as_mapping = {
(lenfunc)BTree_length, /*mp_length*/
};
static PySequenceMethods TreeSet_as_sequence = {
(lenfunc)0, /* sq_length */
(binaryfunc)0, /* sq_concat */
(ssizeargfunc)0, /* sq_repeat */
(ssizeargfunc)0, /* sq_item */
(ssizessizeargfunc)0, /* sq_slice */
(ssizeobjargproc)0, /* sq_ass_item */
(ssizessizeobjargproc)0, /* sq_ass_slice */
(objobjproc)BTree_contains, /* sq_contains */
0, /* sq_inplace_concat */
0, /* sq_inplace_repeat */
};
static int
TreeSet_init(PyObject *self, PyObject *args, PyObject *kwds)
{
PyObject *v = NULL;
if (!PyArg_ParseTuple(args, "|O:" MOD_NAME_PREFIX "TreeSet", &v))
return -1;
if (v)
return _TreeSet_update((BTree *)self, v);
else
return 0;
}
static PyTypeObject TreeSetType =
{
PyVarObject_HEAD_INIT(NULL, 0)
MODULE_NAME MOD_NAME_PREFIX "TreeSet", /* tp_name */
sizeof(BTree), /* tp_basicsize */
0, /* tp_itemsize */
(destructor)BTree_dealloc, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_compare */
0, /* tp_repr */
&BTree_as_number_for_nonzero, /* tp_as_number */
&TreeSet_as_sequence, /* tp_as_sequence */
&TreeSet_as_mapping, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT |
Py_TPFLAGS_HAVE_GC |
Py_TPFLAGS_BASETYPE, /* tp_flags */
0, /* tp_doc */
(traverseproc)BTree_traverse, /* tp_traverse */
(inquiry)BTree_tp_clear, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
(getiterfunc)BTree_getiter, /* tp_iter */
0, /* tp_iternext */
TreeSet_methods, /* tp_methods */
BTree_members, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
TreeSet_init, /* tp_init */
0, /* tp_alloc */
0, /*PyType_GenericNew,*/ /* tp_new */
};
@@ -0,0 +1,41 @@
/*############################################################################
#
# Copyright (c) 2004 Zope Foundation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
############################################################################*/
#define MASTER_ID "$Id$\n"
/* IFBTree - int key, float value BTree
Implements a collection using int type keys
and float type values
*/
/* Setup template macros */
#define PERSISTENT
#define MOD_NAME_PREFIX "IF"
#define DEFAULT_MAX_BUCKET_SIZE 120
#define DEFAULT_MAX_BTREE_SIZE 500
#include "_compat.h"
#include "intkeymacros.h"
#include "floatvaluemacros.h"
#ifdef PY3K
#define INITMODULE PyInit__IFBTree
#else
#define INITMODULE init_IFBTree
#endif
#include "BTreeModuleTemplate.c"
@@ -0,0 +1,41 @@
/*############################################################################
#
# Copyright (c) 2004 Zope Foundation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
############################################################################*/
#define MASTER_ID "$Id$\n"
/* IIBTree - int key, int value BTree
Implements a collection using int type keys
and int type values
*/
/* Setup template macros */
#define PERSISTENT
#define MOD_NAME_PREFIX "II"
#define DEFAULT_MAX_BUCKET_SIZE 120
#define DEFAULT_MAX_BTREE_SIZE 500
#include "_compat.h"
#include "intkeymacros.h"
#include "intvaluemacros.h"
#ifdef PY3K
#define INITMODULE PyInit__IIBTree
#else
#define INITMODULE init_IIBTree
#endif
#include "BTreeModuleTemplate.c"
@@ -0,0 +1,39 @@
/*############################################################################
#
# Copyright (c) 2004 Zope Foundation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
############################################################################*/
#define MASTER_ID "$Id$\n"
/* IOBTree - int key, object value BTree
Implements a collection using int type keys
and object type values
*/
#define PERSISTENT
#define MOD_NAME_PREFIX "IO"
#define DEFAULT_MAX_BUCKET_SIZE 60
#define DEFAULT_MAX_BTREE_SIZE 500
#include "_compat.h"
#include "intkeymacros.h"
#include "objectvaluemacros.h"
#ifdef PY3K
#define INITMODULE PyInit__IOBTree
#else
#define INITMODULE init_IOBTree
#endif
#include "BTreeModuleTemplate.c"
@@ -0,0 +1,43 @@
/*############################################################################
#
# Copyright (c) 2004 Zope Foundation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
############################################################################*/
#define MASTER_ID "$Id: _IFBTree.c 67074 2006-04-17 19:13:39Z fdrake $\n"
/* IFBTree - int key, float value BTree
Implements a collection using int type keys
and float type values
*/
/* Setup template macros */
#define PERSISTENT
#define MOD_NAME_PREFIX "LF"
#define DEFAULT_MAX_BUCKET_SIZE 120
#define DEFAULT_MAX_BTREE_SIZE 500
#define ZODB_64BIT_INTS
#include "_compat.h"
#include "intkeymacros.h"
#include "floatvaluemacros.h"
#ifdef PY3K
#define INITMODULE PyInit__LFBTree
#else
#define INITMODULE init_LFBTree
#endif
#include "BTreeModuleTemplate.c"
@@ -0,0 +1,43 @@
/*############################################################################
#
# Copyright (c) 2004 Zope Foundation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
############################################################################*/
#define MASTER_ID "$Id: _IIBTree.c 25186 2004-06-02 15:07:33Z jim $\n"
/* IIBTree - int key, int value BTree
Implements a collection using int type keys
and int type values
*/
/* Setup template macros */
#define PERSISTENT
#define MOD_NAME_PREFIX "LL"
#define DEFAULT_MAX_BUCKET_SIZE 120
#define DEFAULT_MAX_BTREE_SIZE 500
#define ZODB_64BIT_INTS
#include "_compat.h"
#include "intkeymacros.h"
#include "intvaluemacros.h"
#ifdef PY3K
#define INITMODULE PyInit__LLBTree
#else
#define INITMODULE init_LLBTree
#endif
#include "BTreeModuleTemplate.c"
@@ -0,0 +1,41 @@
/*############################################################################
#
# Copyright (c) 2004 Zope Foundation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
############################################################################*/
#define MASTER_ID "$Id: _IOBTree.c 25186 2004-06-02 15:07:33Z jim $\n"
/* IOBTree - int key, object value BTree
Implements a collection using int type keys
and object type values
*/
#define PERSISTENT
#define MOD_NAME_PREFIX "LO"
#define DEFAULT_MAX_BUCKET_SIZE 60
#define DEFAULT_MAX_BTREE_SIZE 500
#define ZODB_64BIT_INTS
#include "_compat.h"
#include "intkeymacros.h"
#include "objectvaluemacros.h"
#ifdef PY3K
#define INITMODULE PyInit__LOBTree
#else
#define INITMODULE init_LOBTree
#endif
#include "BTreeModuleTemplate.c"
@@ -0,0 +1,39 @@
/*############################################################################
#
# Copyright (c) 2004 Zope Foundation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
############################################################################*/
#define MASTER_ID "$Id$\n"
/* OIBTree - object key, int value BTree
Implements a collection using object type keys
and int type values
*/
#define PERSISTENT
#define MOD_NAME_PREFIX "OI"
#define DEFAULT_MAX_BUCKET_SIZE 60
#define DEFAULT_MAX_BTREE_SIZE 250
#include "_compat.h"
#include "objectkeymacros.h"
#include "intvaluemacros.h"
#ifdef PY3K
#define INITMODULE PyInit__OIBTree
#else
#define INITMODULE init_OIBTree
#endif
#include "BTreeModuleTemplate.c"
@@ -0,0 +1,41 @@
/*############################################################################
#
# Copyright (c) 2004 Zope Foundation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
############################################################################*/
#define MASTER_ID "$Id: _OIBTree.c 25186 2004-06-02 15:07:33Z jim $\n"
/* OIBTree - object key, int value BTree
Implements a collection using object type keys
and int type values
*/
#define PERSISTENT
#define MOD_NAME_PREFIX "OL"
#define DEFAULT_MAX_BUCKET_SIZE 60
#define DEFAULT_MAX_BTREE_SIZE 250
#define ZODB_64BIT_INTS
#include "_compat.h"
#include "objectkeymacros.h"
#include "intvaluemacros.h"
#ifdef PY3K
#define INITMODULE PyInit__OLBTree
#else
#define INITMODULE init_OLBTree
#endif
#include "BTreeModuleTemplate.c"
@@ -0,0 +1,39 @@
/*############################################################################
#
# Copyright (c) 2004 Zope Foundation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
############################################################################*/
#define MASTER_ID "$Id$\n"
/* OOBTree - object key, object value BTree
Implements a collection using object type keys
and object type values
*/
#define PERSISTENT
#define MOD_NAME_PREFIX "OO"
#define DEFAULT_MAX_BUCKET_SIZE 30
#define DEFAULT_MAX_BTREE_SIZE 250
#include "_compat.h"
#include "objectkeymacros.h"
#include "objectvaluemacros.h"
#ifdef PY3K
#define INITMODULE PyInit__OOBTree
#else
#define INITMODULE init_OOBTree
#endif
#include "BTreeModuleTemplate.c"
@@ -0,0 +1,69 @@
#############################################################################
#
# Copyright (c) 2007 Zope Foundation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
#############################################################################
import zope.interface
import BTrees.Interfaces
@zope.interface.implementer(BTrees.Interfaces.IBTreeFamily)
class _Family(object):
from BTrees import OOBTree as OO
class _Family32(_Family):
from BTrees import OIBTree as OI
from BTrees import IIBTree as II
from BTrees import IOBTree as IO
from BTrees import IFBTree as IF
maxint = int(2**31-1)
minint = -maxint - 1
def __reduce__(self):
return _family32, ()
class _Family64(_Family):
from BTrees import OLBTree as OI
from BTrees import LLBTree as II
from BTrees import LOBTree as IO
from BTrees import LFBTree as IF
maxint = 2**63-1
minint = -maxint - 1
def __reduce__(self):
return _family64, ()
def _family32():
return family32
_family32.__safe_for_unpickling__ = True
def _family64():
return family64
_family64.__safe_for_unpickling__ = True
family32 = _Family32()
family64 = _Family64()
BTrees.family64.IO.family = family64
BTrees.family64.OI.family = family64
BTrees.family64.IF.family = family64
BTrees.family64.II.family = family64
BTrees.family32.IO.family = family32
BTrees.family32.OI.family = family32
BTrees.family32.IF.family = family32
BTrees.family32.II.family = family32
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,55 @@
/* Straddle Python 2 / 3 */
#ifndef BTREES__COMPAT_H
#define BTREES__COMPAT_H
#include "Python.h"
#ifdef INTERN
#undef INTERN
#endif
#ifdef INT_FROM_LONG
#undef INT_FROM_LONG
#endif
#ifdef INT_CHECK
#undef INT_CHECK
#endif
#if PY_MAJOR_VERSION >= 3
#define PY3K
#define INTERN PyUnicode_InternFromString
#define INT_FROM_LONG(x) PyLong_FromLong(x)
#define INT_CHECK(x) PyLong_Check(x)
#define INT_AS_LONG(x) PyLong_AS_LONG(x)
#define TEXT_FROM_STRING PyUnicode_FromString
#define TEXT_FORMAT PyUnicode_Format
/* Note that the second comparison is skipped if the first comparison returns:
1 -> There was no error and the answer is -1
-1 -> There was an error, which the caller will detect with PyError_Occurred.
*/
#define COMPARE(lhs, rhs) \
(lhs == Py_None ? (rhs == Py_None ? 0 : -1) : (rhs == Py_None ? 1 : \
(PyObject_RichCompareBool((lhs), (rhs), Py_LT) != 0 ? -1 : \
(PyObject_RichCompareBool((lhs), (rhs), Py_EQ) > 0 ? 0 : 1))))
#else
#define INTERN PyString_InternFromString
#define INT_FROM_LONG(x) PyInt_FromLong(x)
#define INT_CHECK(x) PyInt_Check(x)
#define INT_AS_LONG(x) PyInt_AS_LONG(x)
#define TEXT_FROM_STRING PyString_FromString
#define TEXT_FORMAT PyString_Format
#define COMPARE(lhs, rhs) \
(lhs == Py_None ? (rhs == Py_None ? 0 : -1) : (rhs == Py_None ? 1 : \
PyObject_Compare((lhs), (rhs))))
#endif
#endif /* BTREES__COMPAT_H */
@@ -0,0 +1,100 @@
##############################################################################
#
# Copyright (c) 2001-2012 Zope Foundation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
import os
import sys
PYPY = hasattr(sys, 'pypy_version_info')
# We can and do build the C extensions on PyPy, but
# as of Persistent 4.2.5 the persistent C extension is not
# built on PyPy, so importing our C extension will fail anyway.
PURE_PYTHON = os.environ.get('PURE_PYTHON', PYPY)
if sys.version_info[0] < 3: #pragma NO COVER Python2
PY2 = True
PY3 = False
int_types = int, long
xrange = xrange
def compare(x, y):
if x is None:
if y is None:
return 0
else:
return -1
elif y is None:
return 1
else:
return cmp(x, y)
_bytes = str
def _ascii(x):
return bytes(x)
else: #pragma NO COVER Python3
PY2 = False
PY3 = True
int_types = int,
xrange = range
def compare(x, y):
if x is None:
if y is None:
return 0
else:
return -1
elif y is None:
return 1
else:
return (x > y) - (y > x)
_bytes = bytes
def _ascii(x):
return bytes(x, 'ascii')
def import_c_extension(mod_globals):
import importlib
c_module = None
module_name = mod_globals['__name__']
assert module_name.startswith('BTrees.')
module_name = module_name.split('.')[1]
if not PURE_PYTHON:
try:
c_module = importlib.import_module('BTrees._' + module_name)
except ImportError:
pass
if c_module is not None:
new_values = dict(c_module.__dict__)
new_values.pop("__name__", None)
new_values.pop('__file__', None)
new_values.pop('__doc__', None)
mod_globals.update(new_values)
else:
# No C extension, make the Py versions available without that
# extension. The list comprehension both filters and prevents
# concurrent modification errors.
for py in [k for k in mod_globals if k.endswith('Py')]:
mod_globals[py[:-2]] = mod_globals[py]
# Assign the global aliases
prefix = module_name[:2]
for name in ('Bucket', 'Set', 'BTree', 'TreeSet'):
mod_globals[name] = mod_globals[prefix + name]
# Cleanup
del mod_globals['import_c_extension']
@@ -0,0 +1,164 @@
/*############################################################################
#
# Copyright (c) 2004 Zope Foundation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
############################################################################*/
#define MASTER_ID "$Id$\n"
/* fsBTree - FileStorage index BTree
This BTree implements a mapping from 2-character strings
to six-character strings. This allows us to efficiently store
a FileStorage index as a nested mapping of 6-character oid prefix
to mapping of 2-character oid suffix to 6-character (byte) file
positions.
*/
typedef unsigned char char2[2];
typedef unsigned char char6[6];
/* Setup template macros */
#define PERSISTENT
#define MOD_NAME_PREFIX "fs"
#define DEFAULT_MAX_BUCKET_SIZE 500
#define DEFAULT_MAX_BTREE_SIZE 500
#include "_compat.h"
/*#include "intkeymacros.h"*/
#define KEYMACROS_H "$Id$\n"
#define KEY_TYPE char2
#undef KEY_TYPE_IS_PYOBJECT
#define KEY_CHECK(K) (PyBytes_Check(K) && PyBytes_GET_SIZE(K)==2)
#define TEST_KEY_SET_OR(V, K, T) if ( ( (V) = ((*(K) < *(T) || (*(K) == *(T) && (K)[1] < (T)[1])) ? -1 : ((*(K) == *(T) && (K)[1] == (T)[1]) ? 0 : 1)) ), 0 )
#define DECREF_KEY(KEY)
#define INCREF_KEY(k)
#define COPY_KEY(KEY, E) (*(KEY)=*(E), (KEY)[1]=(E)[1])
#define COPY_KEY_TO_OBJECT(O, K) O=PyBytes_FromStringAndSize((const char*)K,2)
#define COPY_KEY_FROM_ARG(TARGET, ARG, STATUS) \
if (KEY_CHECK(ARG)) memcpy(TARGET, PyBytes_AS_STRING(ARG), 2); else { \
PyErr_SetString(PyExc_TypeError, "expected two-character string key"); \
(STATUS)=0; }
/*#include "intvaluemacros.h"*/
#define VALUEMACROS_H "$Id$\n"
#define VALUE_TYPE char6
#undef VALUE_TYPE_IS_PYOBJECT
#define TEST_VALUE(K, T) memcmp(K,T,6)
#define DECREF_VALUE(k)
#define INCREF_VALUE(k)
#define COPY_VALUE(V, E) (memcpy(V, E, 6))
#define COPY_VALUE_TO_OBJECT(O, K) O=PyBytes_FromStringAndSize((const char*)K,6)
#define COPY_VALUE_FROM_ARG(TARGET, ARG, STATUS) \
if ((PyBytes_Check(ARG) && PyBytes_GET_SIZE(ARG)==6)) \
memcpy(TARGET, PyBytes_AS_STRING(ARG), 6); else { \
PyErr_SetString(PyExc_TypeError, "expected six-character string key"); \
(STATUS)=0; }
#define NORMALIZE_VALUE(V, MIN)
#include "Python.h"
static PyObject *bucket_toBytes(PyObject *self);
static PyObject *bucket_fromBytes(PyObject *self, PyObject *state);
#define EXTRA_BUCKET_METHODS \
{"toBytes", (PyCFunction) bucket_toBytes, METH_NOARGS, \
"toBytes() -- Return the state as a bytes array"}, \
{"fromBytes", (PyCFunction) bucket_fromBytes, METH_O, \
"fromSBytes(s) -- Set the state of the object from a bytes array"}, \
{"toString", (PyCFunction) bucket_toBytes, METH_NOARGS, \
"toString() -- Deprecated alias for 'toBytes'"}, \
{"fromString", (PyCFunction) bucket_fromBytes, METH_O, \
"fromString(s) -- Deprecated alias for 'fromBytes'"}, \
#ifdef PY3K
#define INITMODULE PyInit__fsBTree
#else
#define INITMODULE init_fsBTree
#endif
#include "BTreeModuleTemplate.c"
static PyObject *
bucket_toBytes(PyObject *oself)
{
Bucket *self = (Bucket *)oself;
PyObject *items = NULL;
int len;
PER_USE_OR_RETURN(self, NULL);
len = self->len;
items = PyBytes_FromStringAndSize(NULL, len*8);
if (items == NULL)
goto err;
memcpy(PyBytes_AS_STRING(items), self->keys, len*2);
memcpy(PyBytes_AS_STRING(items)+len*2, self->values, len*6);
PER_UNUSE(self);
return items;
err:
PER_UNUSE(self);
Py_XDECREF(items);
return NULL;
}
static PyObject *
bucket_fromBytes(PyObject *oself, PyObject *state)
{
Bucket *self = (Bucket *)oself;
int len;
KEY_TYPE *keys;
VALUE_TYPE *values;
len = PyBytes_Size(state);
if (len < 0)
return NULL;
if (len%8)
{
PyErr_SetString(PyExc_ValueError, "state string of wrong size");
return NULL;
}
len /= 8;
if (self->next) {
Py_DECREF(self->next);
self->next = NULL;
}
if (len > self->size) {
keys = BTree_Realloc(self->keys, sizeof(KEY_TYPE)*len);
if (keys == NULL)
return NULL;
values = BTree_Realloc(self->values, sizeof(VALUE_TYPE)*len);
if (values == NULL)
return NULL;
self->keys = keys;
self->values = values;
self->size = len;
}
memcpy(self->keys, PyBytes_AS_STRING(state), len*2);
memcpy(self->values, PyBytes_AS_STRING(state)+len*2, len*6);
self->len = len;
Py_INCREF(self);
return (PyObject *)self;
}
@@ -0,0 +1,428 @@
##############################################################################
#
# Copyright (c) 2003 Zope Foundation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
"""
Utilities for working with BTrees (TreeSets, Buckets, and Sets) at a low
level.
The primary function is check(btree), which performs value-based consistency
checks of a kind btree._check() does not perform. See the function docstring
for details.
display(btree) displays the internal structure of a BTree (TreeSet, etc) to
stdout.
CAUTION: When a BTree node has only a single bucket child, it can be
impossible to get at the bucket from Python code (__getstate__() may squash
the bucket object out of existence, as a pickling storage optimization). In
such a case, the code here synthesizes a temporary bucket with the same keys
(and values, if the bucket is of a mapping type). This has no first-order
consequences, but can mislead if you pay close attention to reported object
addresses and/or object identity (the synthesized bucket has an address
that doesn't exist in the actual BTree).
"""
from BTrees.IFBTree import IFBTree, IFBucket, IFSet, IFTreeSet
from BTrees.IFBTree import IFBTreePy, IFBucketPy, IFSetPy, IFTreeSetPy
from BTrees.IIBTree import IIBTree, IIBucket, IISet, IITreeSet
from BTrees.IIBTree import IIBTreePy, IIBucketPy, IISetPy, IITreeSetPy
from BTrees.IOBTree import IOBTree, IOBucket, IOSet, IOTreeSet
from BTrees.IOBTree import IOBTreePy, IOBucketPy, IOSetPy, IOTreeSetPy
from BTrees.LFBTree import LFBTree, LFBucket, LFSet, LFTreeSet
from BTrees.LFBTree import LFBTreePy, LFBucketPy, LFSetPy, LFTreeSetPy
from BTrees.LLBTree import LLBTree, LLBucket, LLSet, LLTreeSet
from BTrees.LLBTree import LLBTreePy, LLBucketPy, LLSetPy, LLTreeSetPy
from BTrees.LOBTree import LOBTree, LOBucket, LOSet, LOTreeSet
from BTrees.LOBTree import LOBTreePy, LOBucketPy, LOSetPy, LOTreeSetPy
from BTrees.OIBTree import OIBTree, OIBucket, OISet, OITreeSet
from BTrees.OIBTree import OIBTreePy, OIBucketPy, OISetPy, OITreeSetPy
from BTrees.OLBTree import OLBTree, OLBucket, OLSet, OLTreeSet
from BTrees.OLBTree import OLBTreePy, OLBucketPy, OLSetPy, OLTreeSetPy
from BTrees.OOBTree import OOBTree, OOBucket, OOSet, OOTreeSet
from BTrees.OOBTree import OOBTreePy, OOBucketPy, OOSetPy, OOTreeSetPy
from BTrees.utils import positive_id
from BTrees.utils import oid_repr
TYPE_UNKNOWN, TYPE_BTREE, TYPE_BUCKET = range(3)
from ._compat import compare
_type2kind = {}
for kv in ('OO',
'II', 'IO', 'OI', 'IF',
'LL', 'LO', 'OL', 'LF',
):
for name, kind in (
('BTree', (TYPE_BTREE, True)),
('Bucket', (TYPE_BUCKET, True)),
('TreeSet', (TYPE_BTREE, False)),
('Set', (TYPE_BUCKET, False)),
):
_type2kind[globals()[kv+name]] = kind
py = kv + name + 'Py'
_type2kind[globals()[py]] = kind
# Return pair
#
# TYPE_BTREE or TYPE_BUCKET, is_mapping
def classify(obj):
return _type2kind[type(obj)]
BTREE_EMPTY, BTREE_ONE, BTREE_NORMAL = range(3)
# If the BTree is empty, returns
#
# BTREE_EMPTY, [], []
#
# If the BTree has only one bucket, sometimes returns
#
# BTREE_ONE, bucket_state, None
#
# Else returns
#
# BTREE_NORMAL, list of keys, list of kids
#
# and the list of kids has one more entry than the list of keys.
#
# BTree.__getstate__() docs:
#
# For an empty BTree (self->len == 0), None.
#
# For a BTree with one child (self->len == 1), and that child is a bucket,
# and that bucket has a NULL oid, a one-tuple containing a one-tuple
# containing the bucket's state:
#
# (
# (
# child[0].__getstate__(),
# ),
# )
#
# Else a two-tuple. The first element is a tuple interleaving the BTree's
# keys and direct children, of size 2*self->len - 1 (key[0] is unused and
# is not saved). The second element is the firstbucket:
#
# (
# (child[0], key[1], child[1], key[2], child[2], ...,
# key[len-1], child[len-1]),
# self->firstbucket
# )
_btree2bucket = {}
for kv in ('OO',
'II', 'IO', 'OI', 'IF',
'LL', 'LO', 'OL', 'LF',
):
_btree2bucket[globals()[kv+'BTree']] = globals()[kv+'Bucket']
py = kv + 'BTreePy'
_btree2bucket[globals()[py]] = globals()[kv+'BucketPy']
_btree2bucket[globals()[kv+'TreeSet']] = globals()[kv+'Set']
py = kv + 'TreeSetPy'
_btree2bucket[globals()[kv+'TreeSetPy']] = globals()[kv+'SetPy']
def crack_btree(t, is_mapping):
state = t.__getstate__()
if state is None:
return BTREE_EMPTY, [], []
assert isinstance(state, tuple)
if len(state) == 1:
state = state[0]
assert isinstance(state, tuple) and len(state) == 1
state = state[0]
return BTREE_ONE, state, None
assert len(state) == 2
data, firstbucket = state
n = len(data)
assert n & 1
kids = []
keys = []
i = 0
for x in data:
if i & 1:
keys.append(x)
else:
kids.append(x)
i += 1
return BTREE_NORMAL, keys, kids
# Returns
#
# keys, values # for a mapping; len(keys) == len(values) in this case
# or
# keys, [] # for a set
#
# bucket.__getstate__() docs:
#
# For a set bucket (self->values is NULL), a one-tuple or two-tuple. The
# first element is a tuple of keys, of length self->len. The second element
# is the next bucket, present if and only if next is non-NULL:
#
# (
# (keys[0], keys[1], ..., keys[len-1]),
# <self->next iff non-NULL>
# )
#
# For a mapping bucket (self->values is not NULL), a one-tuple or two-tuple.
# The first element is a tuple interleaving keys and values, of length
# 2 * self->len. The second element is the next bucket, present iff next is
# non-NULL:
#
# (
# (keys[0], values[0], keys[1], values[1], ...,
# keys[len-1], values[len-1]),
# <self->next iff non-NULL>
# )
def crack_bucket(b, is_mapping):
state = b.__getstate__()
assert isinstance(state, tuple)
assert 1 <= len(state) <= 2
data = state[0]
if not is_mapping:
return data, []
keys = []
values = []
n = len(data)
assert n & 1 == 0
i = 0
for x in data:
if i & 1:
values.append(x)
else:
keys.append(x)
i += 1
return keys, values
def type_and_adr(obj):
if hasattr(obj, '_p_oid'):
oid = oid_repr(obj._p_oid)
else:
oid = 'None'
return "%s (0x%x oid=%s)" % (type(obj).__name__, positive_id(obj), oid)
# Walker implements a depth-first search of a BTree (or TreeSet or Set or
# Bucket). Subclasses must implement the visit_btree() and visit_bucket()
# methods, and arrange to call the walk() method. walk() calls the
# visit_XYZ() methods once for each node in the tree, in depth-first
# left-to-right order.
class Walker:
def __init__(self, obj):
self.obj = obj
# obj is the BTree (BTree or TreeSet).
# path is a list of indices, from the root. For example, if a BTree node
# is child[5] of child[3] of the root BTree, [3, 5].
# parent is the parent BTree object, or None if this is the root BTree.
# is_mapping is True for a BTree and False for a TreeSet.
# keys is a list of the BTree's internal keys.
# kids is a list of the BTree's children.
# If the BTree is an empty root node, keys == kids == [].
# Else len(kids) == len(keys) + 1.
# lo and hi are slice bounds on the values the elements of keys *should*
# lie in (lo inclusive, hi exclusive). lo is None if there is no lower
# bound known, and hi is None if no upper bound is known.
def visit_btree(self, obj, path, parent, is_mapping,
keys, kids, lo, hi):
raise NotImplementedError
# obj is the bucket (Bucket or Set).
# path is a list of indices, from the root. For example, if a bucket
# node is child[5] of child[3] of the root BTree, [3, 5].
# parent is the parent BTree object.
# is_mapping is True for a Bucket and False for a Set.
# keys is a list of the bucket's keys.
# values is a list of the bucket's values.
# If is_mapping is false, values == []. Else len(keys) == len(values).
# lo and hi are slice bounds on the values the elements of keys *should*
# lie in (lo inclusive, hi exclusive). lo is None if there is no lower
# bound known, and hi is None if no upper bound is known.
def visit_bucket(self, obj, path, parent, is_mapping,
keys, values, lo, hi):
raise NotImplementedError
def walk(self):
obj = self.obj
path = []
stack = [(obj, path, None, None, None)]
while stack:
obj, path, parent, lo, hi = stack.pop()
kind, is_mapping = classify(obj)
if kind is TYPE_BTREE:
bkind, keys, kids = crack_btree(obj, is_mapping)
if bkind is BTREE_NORMAL:
# push the kids, in reverse order (so they're popped off
# the stack in forward order)
n = len(kids)
for i in range(len(kids)-1, -1, -1):
newlo, newhi = lo, hi
if i < n-1:
newhi = keys[i]
if i > 0:
newlo = keys[i-1]
stack.append((kids[i],
path + [i],
obj,
newlo,
newhi))
elif bkind is BTREE_EMPTY:
pass
else:
assert bkind is BTREE_ONE
# Yuck. There isn't a bucket object to pass on, as
# the bucket state is embedded directly in the BTree
# state. Synthesize a bucket.
assert kids is None # and "keys" is really the bucket
# state
bucket = _btree2bucket[type(obj)]()
bucket.__setstate__(keys)
stack.append((bucket,
path + [0],
obj,
lo,
hi))
keys = []
kids = [bucket]
self.visit_btree(obj,
path,
parent,
is_mapping,
keys,
kids,
lo,
hi)
else:
assert kind is TYPE_BUCKET
keys, values = crack_bucket(obj, is_mapping)
self.visit_bucket(obj,
path,
parent,
is_mapping,
keys,
values,
lo,
hi)
class Checker(Walker):
def __init__(self, obj):
Walker.__init__(self, obj)
self.errors = []
def check(self):
self.walk()
if self.errors:
s = "Errors found in %s:" % type_and_adr(self.obj)
self.errors.insert(0, s)
s = "\n".join(self.errors)
raise AssertionError(s)
def visit_btree(self, obj, path, parent, is_mapping,
keys, kids, lo, hi):
self.check_sorted(obj, path, keys, lo, hi)
def visit_bucket(self, obj, path, parent, is_mapping,
keys, values, lo, hi):
self.check_sorted(obj, path, keys, lo, hi)
def check_sorted(self, obj, path, keys, lo, hi):
i, n = 0, len(keys)
for x in keys:
# lo or hi are ommitted by supplying None. Thus the not
# None checkes below.
if lo is not None and not compare(lo, x) <= 0:
s = "key %r < lower bound %r at index %d" % (x, lo, i)
self.complain(s, obj, path)
if hi is not None and not compare(x, hi) < 0:
s = "key %r >= upper bound %r at index %d" % (x, hi, i)
self.complain(s, obj, path)
if i < n-1 and not compare(x, keys[i+1]) < 0:
s = "key %r at index %d >= key %r at index %d" % (
x, i, keys[i+1], i+1)
self.complain(s, obj, path)
i += 1
def complain(self, msg, obj, path):
s = "%s, in %s, path from root %s" % (
msg,
type_and_adr(obj),
".".join(map(str, path)))
self.errors.append(s)
class Printer(Walker): #pragma NO COVER
def __init__(self, obj):
Walker.__init__(self, obj)
def display(self):
self.walk()
def visit_btree(self, obj, path, parent, is_mapping,
keys, kids, lo, hi):
indent = " " * len(path)
print("%s%s %s with %d children" % (
indent,
".".join(map(str, path)),
type_and_adr(obj),
len(kids)))
indent += " "
n = len(keys)
for i in range(n):
print("%skey %d: %r" % (indent, i, keys[i]))
def visit_bucket(self, obj, path, parent, is_mapping,
keys, values, lo, hi):
indent = " " * len(path)
print("%s%s %s with %d keys" % (
indent,
".".join(map(str, path)),
type_and_adr(obj),
len(keys)))
indent += " "
n = len(keys)
for i in range(n):
print("%skey %d: %r" % (indent, i, keys[i]),)
if is_mapping:
print("value %r" % (values[i],))
def check(btree):
"""Check internal value-based invariants in a BTree or TreeSet.
The btree._check() method checks internal C-level pointer consistency.
The check() function here checks value-based invariants: whether the
keys in leaf bucket and internal nodes are in strictly increasing order,
and whether they all lie in their expected range. The latter is a subtle
invariant that can't be checked locally -- it requires propagating
range info down from the root of the tree, and modifying it at each
level for each child.
Raises AssertionError if anything is wrong, with a string detail
explaining the problems. The entire tree is checked before
AssertionError is raised, and the string detail may be large (depending
on how much went wrong).
"""
Checker(btree).check()
def display(btree): #pragma NO COVER
"Display the internal structure of a BTree, Bucket, TreeSet or Set."
Printer(btree).display()
@@ -0,0 +1,25 @@
#define VALUEMACROS_H "$Id$\n"
#define VALUE_TYPE float
#undef VALUE_TYPE_IS_PYOBJECT
#define TEST_VALUE(K, T) (((K) < (T)) ? -1 : (((K) > (T)) ? 1: 0))
#define VALUE_SAME(VALUE, TARGET) ( (VALUE) == (TARGET) )
#define DECLARE_VALUE(NAME) VALUE_TYPE NAME
#define VALUE_PARSE "f"
#define DECREF_VALUE(k)
#define INCREF_VALUE(k)
#define COPY_VALUE(V, E) (V=(E))
#define COPY_VALUE_TO_OBJECT(O, K) O=PyFloat_FromDouble(K)
#define COPY_VALUE_FROM_ARG(TARGET, ARG, STATUS) \
if (PyFloat_Check(ARG)) TARGET = (float)PyFloat_AsDouble(ARG); \
else if (INT_CHECK(ARG)) TARGET = (float)INT_AS_LONG(ARG); \
else { \
PyErr_SetString(PyExc_TypeError, "expected float or int value"); \
(STATUS)=0; (TARGET)=0; }
#define NORMALIZE_VALUE(V, MIN) ((MIN) > 0) ? ((V)/=(MIN)) : 0
#define MERGE_DEFAULT 1.0f
#define MERGE(O1, w1, O2, w2) ((O1)*(w1)+(O2)*(w2))
#define MERGE_WEIGHT(O, w) ((O)*(w))
@@ -0,0 +1,110 @@
##############################################################################
#
# Copyright (c) 2001-2012 Zope Foundation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
# fsBTrees are data structures used for ZODB FileStorage. They are not
# expected to be "public" excpect to FileStorage.
# Each item in an fsBTree maps a two-byte key to a six-byte value.
__all__ = ('Bucket', 'Set', 'BTree', 'TreeSet',
'fsBucket', 'fsSet', 'fsBTree', 'fsTreeSet',
'union', 'intersection', 'difference',
)
from zope.interface import moduleProvides
from .Interfaces import IIntegerObjectBTreeModule
from ._base import Bucket
from ._base import Set
from ._base import Tree as BTree
from ._base import TreeSet
from ._base import difference as _difference
from ._base import intersection as _intersection
from ._base import set_operation as _set_operation
from ._base import to_bytes as _to_bytes
from ._base import union as _union
from ._base import _fix_pickle
from ._compat import import_c_extension
_BUCKET_SIZE = 500
_TREE_SIZE = 500
using64bits = False
_to_key = _to_bytes(2)
_to_value = _to_bytes(6)
class fsBucketPy(Bucket):
_to_key = _to_key
_to_value = _to_value
def toString(self):
return b''.join(self._keys) + b''.join(self._values)
def fromString(self, v):
length = len(v)
if length % 8 != 0:
raise ValueError()
count = length // 8
keys, values = v[:count*2], v[count*2:]
self.clear()
while keys and values:
key, keys = keys[:2], keys[2:]
value, values = values[:6], values[6:]
self._keys.append(key)
self._values.append(value)
return self
class fsSetPy(Set):
_to_key = _to_key
class fsBTreePy(BTree):
max_leaf_size = _BUCKET_SIZE
max_internal_size = _TREE_SIZE
_to_key = _to_key
_to_value = _to_value
class fsTreeSetPy(TreeSet):
max_leaf_size = _BUCKET_SIZE
max_internal_size = _TREE_SIZE
_to_key = _to_key
# Can't declare forward refs, so fix up afterwards:
fsBucketPy._mapping_type = fsBucketPy._bucket_type = fsBucketPy
fsBucketPy._set_type = fsSetPy
fsSetPy._mapping_type = fsBucketPy
fsSetPy._set_type = fsSetPy._bucket_type = fsSetPy
fsBTreePy._mapping_type = fsBTreePy._bucket_type = fsBucketPy
fsBTreePy._set_type = fsSetPy
fsTreeSetPy._mapping_type = fsBucketPy
fsTreeSetPy._set_type = fsTreeSetPy._bucket_type = fsSetPy
differencePy = _set_operation(_difference, fsSetPy)
unionPy = _set_operation(_union, fsSetPy)
intersectionPy = _set_operation(_intersection, fsSetPy)
import_c_extension(globals())
_fix_pickle(globals(), __name__)
moduleProvides(IIntegerObjectBTreeModule)
@@ -0,0 +1,40 @@
#define KEYMACROS_H "$Id$\n"
#ifdef ZODB_64BIT_INTS
/* PY_LONG_LONG as key */
#define NEED_LONG_LONG_SUPPORT
#define NEED_LONG_LONG_KEYS
#define KEY_TYPE PY_LONG_LONG
#define KEY_CHECK longlong_check
#define COPY_KEY_TO_OBJECT(O, K) O=longlong_as_object(K)
#define COPY_KEY_FROM_ARG(TARGET, ARG, STATUS) \
if (!longlong_convert((ARG), &TARGET)) \
{ \
(STATUS)=0; (TARGET)=0; \
}
#else
/* C int as key */
#define KEY_TYPE int
#define KEY_CHECK INT_CHECK
#define COPY_KEY_TO_OBJECT(O, K) O=INT_FROM_LONG(K)
#define COPY_KEY_FROM_ARG(TARGET, ARG, STATUS) \
if (INT_CHECK(ARG)) { \
long vcopy = INT_AS_LONG(ARG); \
if (PyErr_Occurred()) { (STATUS)=0; (TARGET)=0; } \
else if ((int)vcopy != vcopy) { \
PyErr_SetString(PyExc_TypeError, "integer out of range"); \
(STATUS)=0; (TARGET)=0; \
} \
else TARGET = vcopy; \
} else { \
PyErr_SetString(PyExc_TypeError, "expected integer key"); \
(STATUS)=0; (TARGET)=0; }
#endif
#undef KEY_TYPE_IS_PYOBJECT
#define TEST_KEY_SET_OR(V, K, T) if ( ( (V) = (((K) < (T)) ? -1 : (((K) > (T)) ? 1: 0)) ) , 0 )
#define DECREF_KEY(KEY)
#define INCREF_KEY(k)
#define COPY_KEY(KEY, E) (KEY=(E))
#define MULTI_INT_UNION 1
@@ -0,0 +1,46 @@
#define VALUEMACROS_H "$Id$\n"
#ifdef ZODB_64BIT_INTS
#define NEED_LONG_LONG_SUPPORT
#define VALUE_TYPE PY_LONG_LONG
#define VALUE_PARSE "L"
#define COPY_VALUE_TO_OBJECT(O, K) O=longlong_as_object(K)
#define COPY_VALUE_FROM_ARG(TARGET, ARG, STATUS) \
if (!longlong_convert((ARG), &TARGET)) \
{ \
(STATUS)=0; (TARGET)=0; \
}
#else
#define VALUE_TYPE int
#define VALUE_PARSE "i"
#define COPY_VALUE_TO_OBJECT(O, K) O=INT_FROM_LONG(K)
#define COPY_VALUE_FROM_ARG(TARGET, ARG, STATUS) \
if (INT_CHECK(ARG)) { \
long vcopy = INT_AS_LONG(ARG); \
if (PyErr_Occurred()) { (STATUS)=0; (TARGET)=0; } \
else if ((int)vcopy != vcopy) { \
PyErr_SetString(PyExc_TypeError, "integer out of range"); \
(STATUS)=0; (TARGET)=0; \
} \
else TARGET = vcopy; \
} else { \
PyErr_SetString(PyExc_TypeError, "expected integer key"); \
(STATUS)=0; (TARGET)=0; }
#endif
#undef VALUE_TYPE_IS_PYOBJECT
#define TEST_VALUE(K, T) (((K) < (T)) ? -1 : (((K) > (T)) ? 1: 0))
#define VALUE_SAME(VALUE, TARGET) ( (VALUE) == (TARGET) )
#define DECLARE_VALUE(NAME) VALUE_TYPE NAME
#define DECREF_VALUE(k)
#define INCREF_VALUE(k)
#define COPY_VALUE(V, E) (V=(E))
#define NORMALIZE_VALUE(V, MIN) ((MIN) > 0) ? ((V)/=(MIN)) : 0
#define MERGE_DEFAULT 1
#define MERGE(O1, w1, O2, w2) ((O1)*(w1)+(O2)*(w2))
#define MERGE_WEIGHT(O, w) ((O)*(w))
@@ -0,0 +1,44 @@
#define KEYMACROS_H "$Id$\n"
#define KEY_TYPE PyObject *
#define KEY_TYPE_IS_PYOBJECT
#include "Python.h"
#include "_compat.h"
static PyObject *object_; /* initialized in BTreeModuleTemplate init */
static int
check_argument_cmp(PyObject *arg)
{
/* printf("check cmp %p %p %p %p\n", */
/* arg->ob_type->tp_richcompare, */
/* ((PyTypeObject *)object_)->ob_type->tp_richcompare, */
/* arg->ob_type->tp_compare, */
/* ((PyTypeObject *)object_)->ob_type->tp_compare); */
if (arg == Py_None) {
return 1;
}
#ifdef PY3K
if (Py_TYPE(arg)->tp_richcompare == Py_TYPE(object_)->tp_richcompare)
#else
if (Py_TYPE(arg)->tp_richcompare == NULL
&& Py_TYPE(arg)->tp_compare == Py_TYPE(object_)->tp_compare)
#endif
{
PyErr_SetString(PyExc_TypeError, "Object has default comparison");
return 0;
}
return 1;
}
#define TEST_KEY_SET_OR(V, KEY, TARGET) \
if ( ( (V) = COMPARE((KEY),(TARGET)) ), PyErr_Occurred() )
#define INCREF_KEY(k) Py_INCREF(k)
#define DECREF_KEY(KEY) Py_DECREF(KEY)
#define COPY_KEY(KEY, E) KEY=(E)
#define COPY_KEY_TO_OBJECT(O, K) O=(K); Py_INCREF(O)
#define COPY_KEY_FROM_ARG(TARGET, ARG, S) \
TARGET=(ARG); \
(S) = 1;
#define KEY_CHECK_ON_SET check_argument_cmp
@@ -0,0 +1,12 @@
#define VALUEMACROS_H "$Id$\n"
#define VALUE_TYPE PyObject *
#define VALUE_TYPE_IS_PYOBJECT
#define TEST_VALUE(VALUE, TARGET) (COMPARE((VALUE),(TARGET)))
#define DECLARE_VALUE(NAME) VALUE_TYPE NAME
#define INCREF_VALUE(k) Py_INCREF(k)
#define DECREF_VALUE(k) Py_DECREF(k)
#define COPY_VALUE(k,e) k=(e)
#define COPY_VALUE_TO_OBJECT(O, K) O=(K); Py_INCREF(O)
#define COPY_VALUE_FROM_ARG(TARGET, ARG, S) TARGET=(ARG)
#define NORMALIZE_VALUE(V, MIN) Py_INCREF(V)
@@ -0,0 +1,542 @@
/*****************************************************************************
Copyright (c) 2002 Zope Foundation and Contributors.
All Rights Reserved.
This software is subject to the provisions of the Zope Public License,
Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
FOR A PARTICULAR PURPOSE
****************************************************************************/
/* Revision information: $Id$ */
/* The only routine here intended to be used outside the file is
size_t sort_int_nodups(int *p, size_t n)
Sort the array of n ints pointed at by p, in place, and also remove
duplicates. Return the number of unique elements remaining, which occupy
a contiguous and monotonically increasing slice of the array starting at p.
Example: If the input array is [3, 1, 2, 3, 1, 5, 2], sort_int_nodups
returns 4, and the first 4 elements of the array are changed to
[1, 2, 3, 5]. The content of the remaining array positions is not defined.
Notes:
+ This is specific to n-byte signed ints, with endianness natural to the
platform. `n` is determined based on ZODB_64BIT_INTS.
+ 4*n bytes of available heap memory are required for best speed
(8*n when ZODB_64BIT_INTS is defined).
*/
#include <stdlib.h>
#include <stddef.h>
#include <memory.h>
#include <string.h>
#include <assert.h>
/* The type of array elements to be sorted. Most of the routines don't
care about the type, and will work fine for any scalar C type (provided
they're recompiled with element_type appropriately redefined). However,
the radix sort has to know everything about the type's internal
representation.
*/
typedef KEY_TYPE element_type;
/* The radixsort is faster than the quicksort for large arrays, but radixsort
has high fixed overhead, making it a poor choice for small arrays. The
crossover point isn't critical, and is sensitive to things like compiler
and machine cache structure, so don't worry much about this.
*/
#define QUICKSORT_BEATS_RADIXSORT 800U
/* In turn, the quicksort backs off to an insertion sort for very small
slices. MAX_INSERTION is the largest slice quicksort leaves entirely to
insertion. Because this version of quicksort uses a median-of-3 rule for
selecting a pivot, MAX_INSERTION must be at least 2 (so that quicksort
has at least 3 values to look at in a slice). Again, the exact value here
isn't critical.
*/
#define MAX_INSERTION 25U
#if MAX_INSERTION < 2U
# error "MAX_INSERTION must be >= 2"
#endif
/* LSB-first radix sort of the n elements in 'in'.
'work' is work storage at least as large as 'in'. Depending on how many
swaps are done internally, the final result may come back in 'in' or 'work';
and that pointer is returned.
radixsort_int is specific to signed n-byte ints, with natural machine
endianness. `n` is determined based on ZODB_64BIT_INTS.
*/
static element_type*
radixsort_int(element_type *in, element_type *work, size_t n)
{
/* count[i][j] is the number of input elements that have byte value j
in byte position i, where byte position 0 is the LSB. Note that
holding i fixed, the sum of count[i][j] over all j in range(256)
is n.
*/
#ifdef ZODB_64BIT_INTS
size_t count[8][256];
#else
size_t count[4][256];
#endif
size_t i;
int offset, offsetinc;
/* Which byte position are we working on now? 0=LSB, 1, 2, ... */
size_t bytenum;
#ifdef ZODB_64BIT_INTS
assert(sizeof(element_type) == 8);
#else
assert(sizeof(element_type) == 4);
#endif
assert(in);
assert(work);
/* Compute all of count in one pass. */
memset(count, 0, sizeof(count));
for (i = 0; i < n; ++i) {
element_type const x = in[i];
++count[0][(x ) & 0xff];
++count[1][(x >> 8) & 0xff];
++count[2][(x >> 16) & 0xff];
++count[3][(x >> 24) & 0xff];
#ifdef ZODB_64BIT_INTS
++count[4][(x >> 32) & 0xff];
++count[5][(x >> 40) & 0xff];
++count[6][(x >> 48) & 0xff];
++count[7][(x >> 56) & 0xff];
#endif
}
/* For p an element_type* cast to char*, offset is how much farther we
have to go to get to the LSB of the element; this is 0 for little-
endian boxes and sizeof(element_type)-1 for big-endian.
offsetinc is 1 or -1, respectively, telling us which direction to go
from p+offset to get to the element's more-significant bytes.
*/
{
element_type one = 1;
if (*(char*)&one) {
/* Little endian. */
offset = 0;
offsetinc = 1;
}
else {
/* Big endian. */
offset = sizeof(element_type) - 1;
offsetinc = -1;
}
}
/* The radix sort. */
for (bytenum = 0;
bytenum < sizeof(element_type);
++bytenum, offset += offsetinc) {
/* Do a stable distribution sort on byte position bytenum,
from in to work. index[i] tells us the work index at which
to store the next in element with byte value i. pinbyte
points to the correct byte in the input array.
*/
size_t index[256];
unsigned char* pinbyte;
size_t total = 0;
size_t *pcount = count[bytenum];
/* Compute the correct output starting index for each possible
byte value.
*/
if (bytenum < sizeof(element_type) - 1) {
for (i = 0; i < 256; ++i) {
const size_t icount = pcount[i];
index[i] = total;
total += icount;
if (icount == n)
break;
}
if (i < 256) {
/* All bytes in the current position have value
i, so there's nothing to do on this pass.
*/
continue;
}
}
else {
/* The MSB of signed ints needs to be distributed
differently than the other bytes, in order
0x80, 0x81, ... 0xff, 0x00, 0x01, ... 0x7f
*/
for (i = 128; i < 256; ++i) {
const size_t icount = pcount[i];
index[i] = total;
total += icount;
if (icount == n)
break;
}
if (i < 256)
continue;
for (i = 0; i < 128; ++i) {
const size_t icount = pcount[i];
index[i] = total;
total += icount;
if (icount == n)
break;
}
if (i < 128)
continue;
}
assert(total == n);
/* Distribute the elements according to byte value. Note that
this is where most of the time is spent.
Note: The loop is unrolled 4x by hand, for speed. This
may be a pessimization someday, but was a significant win
on my MSVC 6.0 timing tests.
*/
pinbyte = (unsigned char *)in + offset;
i = 0;
/* Reduce number of elements to copy to a multiple of 4. */
while ((n - i) & 0x3) {
unsigned char byte = *pinbyte;
work[index[byte]++] = in[i];
++i;
pinbyte += sizeof(element_type);
}
for (; i < n; i += 4, pinbyte += 4 * sizeof(element_type)) {
unsigned char byte1 = *(pinbyte );
unsigned char byte2 = *(pinbyte + sizeof(element_type));
unsigned char byte3 = *(pinbyte + 2 * sizeof(element_type));
unsigned char byte4 = *(pinbyte + 3 * sizeof(element_type));
element_type in1 = in[i ];
element_type in2 = in[i+1];
element_type in3 = in[i+2];
element_type in4 = in[i+3];
work[index[byte1]++] = in1;
work[index[byte2]++] = in2;
work[index[byte3]++] = in3;
work[index[byte4]++] = in4;
}
/* Swap in and work (just a pointer swap). */
{
element_type *temp = in;
in = work;
work = temp;
}
}
return in;
}
/* Remove duplicates from sorted array in, storing exactly one of each distinct
element value into sorted array out. It's OK (and expected!) for in == out,
but otherwise the n elements beginning at in must not overlap with the n
beginning at out.
Return the number of elements in out.
*/
static size_t
uniq(element_type *out, element_type *in, size_t n)
{
size_t i;
element_type lastelt;
element_type *pout;
assert(out);
assert(in);
if (n == 0)
return 0;
/* i <- first index in 'in' that contains a duplicate.
in[0], in[1], ... in[i-1] are unique, but in[i-1] == in[i].
Set i to n if everything is unique.
*/
for (i = 1; i < n; ++i) {
if (in[i-1] == in[i])
break;
}
/* in[:i] is unique; copy to out[:i] if needed. */
assert(i > 0);
if (in != out)
memcpy(out, in, i * sizeof(element_type));
pout = out + i;
lastelt = in[i-1]; /* safe even when i == n */
for (++i; i < n; ++i) {
element_type elt = in[i];
if (elt != lastelt)
*pout++ = lastelt = elt;
}
return pout - out;
}
#if 0
/* insertionsort is no longer referenced directly, but I'd like to keep
* the code here just in case.
*/
/* Straight insertion sort of the n elements starting at 'in'. */
static void
insertionsort(element_type *in, size_t n)
{
element_type *p, *q;
element_type minimum; /* smallest seen so far */
element_type *plimit = in + n;
assert(in);
if (n < 2)
return;
minimum = *in;
for (p = in+1; p < plimit; ++p) {
/* *in <= *(in+1) <= ... <= *(p-1). Slide *p into place. */
element_type thiselt = *p;
if (thiselt < minimum) {
/* This is a new minimum. This saves p-in compares
when it happens, but should happen so rarely that
it's not worth checking for its own sake: the
point is that the far more popular 'else' branch can
exploit that thiselt is *not* the smallest so far.
*/
memmove(in+1, in, (p - in) * sizeof(*in));
*in = minimum = thiselt;
}
else {
/* thiselt >= minimum, so the loop will find a q
with *q <= thiselt. This saves testing q >= in
on each trip. It's such a simple loop that saving
a per-trip test is a major speed win.
*/
for (q = p-1; *q > thiselt; --q)
*(q+1) = *q;
*(q+1) = thiselt;
}
}
}
#endif
/* The maximum number of elements in the pending-work stack quicksort
maintains. The maximum stack depth is approximately log2(n), so
arrays of size up to approximately MAX_INSERTION * 2**STACKSIZE can be
sorted. The memory burden for the stack is small, so better safe than
sorry.
*/
#define STACKSIZE 60
/* A _stacknode remembers a contiguous slice of an array that needs to sorted.
lo must be <= hi, and, unlike Python array slices, this includes both ends.
*/
struct _stacknode {
element_type *lo;
element_type *hi;
};
static void
quicksort(element_type *plo, size_t n)
{
element_type *phi;
/* Swap two array elements. */
element_type _temp;
#define SWAP(P, Q) (_temp = *(P), *(P) = *(Q), *(Q) = _temp)
/* Stack of pending array slices to be sorted. */
struct _stacknode stack[STACKSIZE];
struct _stacknode *stackfree = stack; /* available stack slot */
/* Push an array slice on the pending-work stack. */
#define PUSH(PLO, PHI) \
do { \
assert(stackfree - stack < STACKSIZE); \
assert((PLO) <= (PHI)); \
stackfree->lo = (PLO); \
stackfree->hi = (PHI); \
++stackfree; \
} while(0)
assert(plo);
phi = plo + n - 1;
for (;;) {
element_type pivot;
element_type *pi, *pj;
assert(plo <= phi);
n = phi - plo + 1;
if (n <= MAX_INSERTION) {
/* Do a small insertion sort. Contra Knuth, we do
this now instead of waiting until the end, because
this little slice is likely still in cache now.
*/
element_type *p, *q;
element_type minimum = *plo;
for (p = plo+1; p <= phi; ++p) {
/* *plo <= *(plo+1) <= ... <= *(p-1).
Slide *p into place. */
element_type thiselt = *p;
if (thiselt < minimum) {
/* New minimum. */
memmove(plo+1,
plo,
(p - plo) * sizeof(*p));
*plo = minimum = thiselt;
}
else {
/* thiselt >= minimum, so the loop will
find a q with *q <= thiselt.
*/
for (q = p-1; *q > thiselt; --q)
*(q+1) = *q;
*(q+1) = thiselt;
}
}
/* Pop another slice off the stack. */
if (stack == stackfree)
break; /* no more slices -- we're done */
--stackfree;
plo = stackfree->lo;
phi = stackfree->hi;
continue;
}
/* Parition the slice.
For pivot, take the median of the leftmost, rightmost, and
middle elements. First sort those three; then the median
is the middle one. For technical reasons, the middle
element is swapped to plo+1 first (see Knuth Vol 3 Ed 2
section 5.2.2 exercise 55 -- reverse-sorted arrays can
take quadratic time otherwise!).
*/
{
element_type *plop1 = plo + 1;
element_type *pmid = plo + (n >> 1);
assert(plo < pmid && pmid < phi);
SWAP(plop1, pmid);
/* Sort plo, plop1, phi. */
/* Smaller of rightmost two -> middle. */
if (*plop1 > *phi)
SWAP(plop1, phi);
/* Smallest of all -> left; if plo is already the
smallest, the sort is complete.
*/
if (*plo > *plop1) {
SWAP(plo, plop1);
/* Largest of all -> right. */
if (*plop1 > *phi)
SWAP(plop1, phi);
}
pivot = *plop1;
pi = plop1;
}
assert(*plo <= pivot);
assert(*pi == pivot);
assert(*phi >= pivot);
pj = phi;
/* Partition wrt pivot. This is the time-critical part, and
nearly every decision in the routine aims at making this
loop as fast as possible -- even small points like
arranging that all loop tests can be done correctly at the
bottoms of loops instead of the tops, and that pointers can
be derefenced directly as-is (without fiddly +1 or -1).
The aim is to make the C here so simple that a compiler
has a good shot at doing as well as hand-crafted assembler.
*/
for (;;) {
/* Invariants:
1. pi < pj.
2. All elements at plo, plo+1 .. pi are <= pivot.
3. All elements at pj, pj+1 .. phi are >= pivot.
4. There is an element >= pivot to the right of pi.
5. There is an element <= pivot to the left of pj.
Note that #4 and #5 save us from needing to check
that the pointers stay in bounds.
*/
assert(pi < pj);
do { ++pi; } while (*pi < pivot);
assert(pi <= pj);
do { --pj; } while (*pj > pivot);
assert(pj >= pi - 1);
if (pi < pj)
SWAP(pi, pj);
else
break;
}
assert(plo+1 < pi && pi <= phi);
assert(plo < pj && pj < phi);
assert(*pi >= pivot);
assert( (pi == pj && *pj == pivot) ||
(pj + 1 == pi && *pj <= pivot) );
/* Swap pivot into its final position, pj. */
assert(plo[1] == pivot);
plo[1] = *pj;
*pj = pivot;
/* Subfiles are from plo to pj-1 inclusive, and pj+1 to phi
inclusive. Push the larger one, and loop back to do the
smaller one directly.
*/
if (pj - plo >= phi - pj) {
PUSH(plo, pj-1);
plo = pj+1;
}
else {
PUSH(pj+1, phi);
phi = pj-1;
}
}
#undef PUSH
#undef SWAP
}
/* Sort p and remove duplicates, as fast as we can. */
static size_t
sort_int_nodups(KEY_TYPE *p, size_t n)
{
size_t nunique;
element_type *work;
assert(sizeof(KEY_TYPE) == sizeof(element_type));
assert(p);
/* Use quicksort if the array is small, OR if malloc can't find
enough temp memory for radixsort.
*/
work = NULL;
if (n > QUICKSORT_BEATS_RADIXSORT)
work = (element_type *)malloc(n * sizeof(element_type));
if (work) {
element_type *out = radixsort_int(p, work, n);
nunique = uniq(p, out, n);
free(work);
}
else {
quicksort(p, n);
nunique = uniq(p, p, n);
}
return nunique;
}
@@ -0,0 +1 @@
# If tests is a package, debugging is a bit easier.
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,534 @@
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Foundation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
import unittest
from BTrees.tests.common import permutations
class DegenerateBTree(unittest.TestCase):
# Build a degenerate tree (set). Boxes are BTree nodes. There are
# 5 leaf buckets, each containing a single int. Keys in the BTree
# nodes don't appear in the buckets. Seven BTree nodes are purely
# indirection nodes (no keys). Buckets aren't all at the same depth:
#
# +------------------------+
# | 4 |
# +------------------------+
# | |
# | v
# | +-+
# | | |
# | +-+
# | |
# v v
# +-------+ +-------------+
# | 2 | | 6 10 |
# +-------+ +-------------+
# | | | | |
# v v v v v
# +-+ +-+ +-+ +-+ +-+
# | | | | | | | | | |
# +-+ +-+ +-+ +-+ +-+
# | | | | |
# v v v v v
# 1 3 +-+ 7 11
# | |
# +-+
# |
# v
# 5
#
# This is nasty for many algorithms. Consider a high-end range search
# for 4. The BTree nodes direct it to the 5 bucket, but the correct
# answer is the 3 bucket, which requires going in a different direction
# at the very top node already. Consider a low-end range search for
# 9. The BTree nodes direct it to the 7 bucket, but the correct answer
# is the 11 bucket. This is also a nasty-case tree for deletions.
def _build_degenerate_tree(self):
# Build the buckets and chain them together.
from BTrees.IIBTree import IISet
from BTrees.IIBTree import IITreeSet
from BTrees.check import check
bucket11 = IISet([11])
bucket7 = IISet()
bucket7.__setstate__(((7,), bucket11))
bucket5 = IISet()
bucket5.__setstate__(((5,), bucket7))
bucket3 = IISet()
bucket3.__setstate__(((3,), bucket5))
bucket1 = IISet()
bucket1.__setstate__(((1,), bucket3))
# Build the deepest layers of indirection nodes.
ts = IITreeSet
tree1 = ts()
tree1.__setstate__(((bucket1,), bucket1))
tree3 = ts()
tree3.__setstate__(((bucket3,), bucket3))
tree5lower = ts()
tree5lower.__setstate__(((bucket5,), bucket5))
tree5 = ts()
tree5.__setstate__(((tree5lower,), bucket5))
tree7 = ts()
tree7.__setstate__(((bucket7,), bucket7))
tree11 = ts()
tree11.__setstate__(((bucket11,), bucket11))
# Paste together the middle layers.
tree13 = ts()
tree13.__setstate__(((tree1, 2, tree3), bucket1))
tree5711lower = ts()
tree5711lower.__setstate__(((tree5, 6, tree7, 10, tree11), bucket5))
tree5711 = ts()
tree5711.__setstate__(((tree5711lower,), bucket5))
# One more.
t = ts()
t.__setstate__(((tree13, 4, tree5711), bucket1))
t._check()
check(t)
return t, [1, 3, 5, 7, 11]
def testBasicOps(self):
t, keys = self._build_degenerate_tree()
self.assertEqual(len(t), len(keys))
self.assertEqual(list(t.keys()), keys)
# has_key actually returns the depth of a bucket.
self.assertEqual(t.has_key(1), 4)
self.assertEqual(t.has_key(3), 4)
self.assertEqual(t.has_key(5), 6)
self.assertEqual(t.has_key(7), 5)
self.assertEqual(t.has_key(11), 5)
for i in 0, 2, 4, 6, 8, 9, 10, 12:
self.assertTrue(i not in t)
def _checkRanges(self, tree, keys):
self.assertEqual(len(tree), len(keys))
sorted_keys = keys[:]
sorted_keys.sort()
self.assertEqual(list(tree.keys()), sorted_keys)
for k in keys:
self.assertTrue(k in tree)
if keys:
lokey = sorted_keys[0]
hikey = sorted_keys[-1]
self.assertEqual(lokey, tree.minKey())
self.assertEqual(hikey, tree.maxKey())
else:
lokey = hikey = 42
# Try all range searches.
for lo in range(lokey - 1, hikey + 2):
for hi in range(lo - 1, hikey + 2):
for skipmin in False, True:
for skipmax in False, True:
wantlo, wanthi = lo, hi
if skipmin:
wantlo += 1
if skipmax:
wanthi -= 1
want = [k for k in keys if wantlo <= k <= wanthi]
got = list(tree.keys(lo, hi, skipmin, skipmax))
self.assertEqual(want, got)
def testRanges(self):
t, keys = self._build_degenerate_tree()
self._checkRanges(t, keys)
def testDeletes(self):
# Delete keys in all possible orders, checking each tree along
# the way.
# This is a tough test. Previous failure modes included:
# 1. A variety of assertion failures in _checkRanges.
# 2. Assorted "Invalid firstbucket pointer" failures at
# seemingly random times, coming out of the BTree destructor.
# 3. Under Python 2.3 CVS, some baffling
# RuntimeWarning: tp_compare didn't return -1 or -2 for exception
# warnings, possibly due to memory corruption after a BTree
# goes insane.
# On CPython in PURE_PYTHON mode, this is a *slow* test, taking 15+s
# on a 2015 laptop.
from BTrees.check import check
t, keys = self._build_degenerate_tree()
for oneperm in permutations(keys):
t, keys = self._build_degenerate_tree()
for key in oneperm:
t.remove(key)
keys.remove(key)
t._check()
check(t)
self._checkRanges(t, keys)
# We removed all the keys, so the tree should be empty now.
self.assertEqual(t.__getstate__(), None)
# A damaged tree may trigger an "invalid firstbucket pointer"
# failure at the time its destructor is invoked. Try to force
# that to happen now, so it doesn't look like a baffling failure
# at some unrelated line.
del t # trigger destructor
LP294788_ids = {}
class ToBeDeleted(object):
def __init__(self, id):
assert isinstance(id, int) #we don't want to store any object ref here
self.id = id
global LP294788_ids
LP294788_ids[id] = 1
def __del__(self):
global LP294788_ids
LP294788_ids.pop(self.id, None)
def __cmp__(self, other):
return cmp(self.id, other.id)
def __le__(self, other):
return self.id <= other.id
def __lt__(self, other):
return self.id < other.id
def __eq__(self, other):
return self.id == other.id
def __ne__(self, other):
return self.id != other.id
def __gt__(self, other):
return self.id > other.id
def __ge__(self, other):
return self.id >= other.id
def __hash__(self):
return hash(self.id)
class BugFixes(unittest.TestCase):
# Collector 1843. Error returns were effectively ignored in
# Bucket_rangeSearch(), leading to "delayed" errors, or worse.
def testFixed1843(self):
from BTrees.IIBTree import IISet
t = IISet()
t.insert(1)
# This one used to fail to raise the TypeError when it occurred.
self.assertRaises(TypeError, t.keys, "")
# This one used to segfault.
self.assertRaises(TypeError, t.keys, 0, "")
def test_LP294788(self):
# https://bugs.launchpad.net/bugs/294788
# BTree keeps some deleted objects referenced
# The logic here together with the ToBeDeleted class is that
# a separate reference dict is populated on object creation
# and removed in __del__
# That means what's left in the reference dict is never GC'ed
# therefore referenced somewhere
# To simulate real life, some random data is used to exercise the tree
import gc
import random
from BTrees.OOBTree import OOBTree
t = OOBTree()
trandom = random.Random('OOBTree')
global LP294788_ids
# /// BTree keys are integers, value is an object
LP294788_ids = {}
ids = {}
for i in range(1024):
if trandom.random() > 0.1 or not ids:
#add
id = None
while id is None or id in ids:
id = trandom.randint(0, 1000000)
ids[id] = 1
t[id] = ToBeDeleted(id)
else:
#del
keys = list(ids.keys())
if keys:
id = trandom.choice(list(ids.keys()))
del t[id]
del ids[id]
ids = ids.keys()
trandom.shuffle(list(ids))
for id in ids:
del t[id]
ids = None
#to be on the safe side run a full GC
gc.collect()
#print LP294788_ids
self.assertEqual(len(t), 0)
self.assertEqual(len(LP294788_ids), 0)
# \\\
# /// BTree keys are integers, value is a tuple having an object
LP294788_ids = {}
ids = {}
for i in range(1024):
if trandom.random() > 0.1 or not ids:
#add
id = None
while id is None or id in ids:
id = trandom.randint(0, 1000000)
ids[id] = 1
t[id] = (id, ToBeDeleted(id), u'somename')
else:
#del
keys = list(ids.keys())
if keys:
id = trandom.choice(keys)
del t[id]
del ids[id]
ids = ids.keys()
trandom.shuffle(list(ids))
for id in ids:
del t[id]
ids = None
#to be on the safe side run a full GC
gc.collect()
#print LP294788_ids
self.assertEqual(len(t), 0)
self.assertEqual(len(LP294788_ids), 0)
# \\\
# /// BTree keys are objects, value is an int
t = OOBTree()
LP294788_ids = {}
ids = {}
for i in range(1024):
if trandom.random() > 0.1 or not ids:
#add
id = None
while id is None or id in ids:
id = ToBeDeleted(trandom.randint(0, 1000000))
ids[id] = 1
t[id] = 1
else:
#del
id = trandom.choice(list(ids.keys()))
del ids[id]
del t[id]
ids = ids.keys()
trandom.shuffle(list(ids))
for id in ids:
del t[id]
#release all refs
ids = id = None
#to be on the safe side run a full GC
gc.collect()
#print LP294788_ids
self.assertEqual(len(t), 0)
self.assertEqual(len(LP294788_ids), 0)
# /// BTree keys are tuples having objects, value is an int
t = OOBTree()
LP294788_ids = {}
ids = {}
for i in range(1024):
if trandom.random() > 0.1 or not ids:
#add
id = None
while id is None or id in ids:
id = trandom.randint(0, 1000000)
id = (id, ToBeDeleted(id), u'somename')
ids[id] = 1
t[id] = 1
else:
#del
id = trandom.choice(list(ids.keys()))
del ids[id]
del t[id]
ids = ids.keys()
trandom.shuffle(list(ids))
for id in ids:
del t[id]
#release all refs
ids = id = key = None
#to be on the safe side run a full GC
gc.collect()
#print LP294788_ids
self.assertEqual(len(t), 0)
self.assertEqual(len(LP294788_ids), 0)
# cmp error propagation tests
class DoesntLikeBeingCompared:
def __cmp__(self, other):
raise ValueError('incomparable')
__lt__ = __le__ = __eq__ = __ne__ = __ge__ = __gt__ = __cmp__
class TestCmpError(unittest.TestCase):
def testFoo(self):
from BTrees.OOBTree import OOBTree
t = OOBTree()
t['hello world'] = None
try:
t[DoesntLikeBeingCompared()] = None
except ValueError as e:
self.assertEqual(str(e), 'incomparable')
else:
self.fail('incomarable objects should not be allowed into '
'the tree')
class FamilyTest(unittest.TestCase):
def test32(self):
from zope.interface.verify import verifyObject
import BTrees
from BTrees.IOBTree import IOTreeSet
verifyObject(BTrees.Interfaces.IBTreeFamily, BTrees.family32)
self.assertEqual(
BTrees.family32.IO, BTrees.IOBTree)
self.assertEqual(
BTrees.family32.OI, BTrees.OIBTree)
self.assertEqual(
BTrees.family32.II, BTrees.IIBTree)
self.assertEqual(
BTrees.family32.IF, BTrees.IFBTree)
self.assertEqual(
BTrees.family32.OO, BTrees.OOBTree)
s = IOTreeSet()
s.insert(BTrees.family32.maxint)
self.assertTrue(BTrees.family32.maxint in s)
s = IOTreeSet()
s.insert(BTrees.family32.minint)
self.assertTrue(BTrees.family32.minint in s)
s = IOTreeSet()
# this next bit illustrates an, um, "interesting feature". If
# the characteristics change to match the 64 bit version, please
# feel free to change.
with self.assertRaises((TypeError, OverflowError)):
s.insert(BTrees.family32.maxint + 1)
with self.assertRaises((TypeError, OverflowError)):
s.insert(BTrees.family32.minint - 1)
self.check_pickling(BTrees.family32)
def test64(self):
from zope.interface.verify import verifyObject
import BTrees
from BTrees.LOBTree import LOTreeSet
verifyObject(BTrees.Interfaces.IBTreeFamily, BTrees.family64)
self.assertEqual(
BTrees.family64.IO, BTrees.LOBTree)
self.assertEqual(
BTrees.family64.OI, BTrees.OLBTree)
self.assertEqual(
BTrees.family64.II, BTrees.LLBTree)
self.assertEqual(
BTrees.family64.IF, BTrees.LFBTree)
self.assertEqual(
BTrees.family64.OO, BTrees.OOBTree)
s = LOTreeSet()
s.insert(BTrees.family64.maxint)
self.assertTrue(BTrees.family64.maxint in s)
s = LOTreeSet()
s.insert(BTrees.family64.minint)
self.assertTrue(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)
def check_pickling(self, family):
# The "family" objects are singletons; they can be pickled and
# unpickled, and the same instances will always be returned on
# unpickling, whether from the same unpickler or different
# unpicklers.
import pickle
from io import BytesIO
s = pickle.dumps((family, family))
(f1, f2) = pickle.loads(s)
self.assertIs(f1, family)
self.assertIs(f2, family)
# Using a single memo across multiple pickles:
sio = BytesIO()
p = pickle.Pickler(sio)
p.dump(family)
p.dump([family])
u = pickle.Unpickler(BytesIO(sio.getvalue()))
f1 = u.load()
f2, = u.load()
self.assertTrue(f1 is family)
self.assertTrue(f2 is family)
# Using separate memos for each pickle:
sio = BytesIO()
p = pickle.Pickler(sio)
p.dump(family)
p.clear_memo()
p.dump([family])
u = pickle.Unpickler(BytesIO(sio.getvalue()))
f1 = u.load()
f2, = u.load()
self.assertTrue(f1 is family)
self.assertTrue(f2 is family)
def test_suite():
return unittest.TestSuite((
unittest.makeSuite(DegenerateBTree),
unittest.makeSuite(BugFixes),
unittest.makeSuite(TestCmpError),
unittest.makeSuite(FamilyTest),
))
@@ -0,0 +1,76 @@
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Foundation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
import unittest
from .common import _skip_under_Py3k
# When an OOBtree contains unicode strings as keys,
# it is neccessary accessing non-unicode strings are
# either ascii strings or encoded as unicoded using the
# corresponding encoding
encoding = 'ISO-8859-1'
class TestBTreesUnicode(unittest.TestCase):
""" test unicode"""
def setUp(self):
#setup an OOBTree with some unicode strings
from BTrees.OOBTree import OOBTree
from BTrees._compat import _bytes
self.s = b'dreit\xe4gigen'.decode('latin1')
self.data = [(b'alien', 1),
(b'k\xf6nnten', 2),
(b'fox', 3),
(b'future', 4),
(b'quick', 5),
(b'zerst\xf6rt', 6),
(u'dreit\xe4gigen', 7),
]
self.tree = OOBTree()
for k, v in self.data:
if isinstance(k, _bytes):
k = k.decode('latin1')
self.tree[k] = v
@_skip_under_Py3k
def testAllKeys(self):
# check every item of the tree
from BTrees._compat import _bytes
for k, v in self.data:
if isinstance(k, _bytes):
k = k.decode(encoding)
self.assertTrue(k in self.tree)
self.assertEqual(self.tree[k], v)
@_skip_under_Py3k
def testUnicodeKeys(self):
# try to access unicode keys in tree
k, v = self.data[-1]
self.assertEqual(k, self.s)
self.assertEqual(self.tree[k], v)
self.assertEqual(self.tree[self.s], v)
@_skip_under_Py3k
def testAsciiKeys(self):
# try to access some "plain ASCII" keys in the tree
for k, v in self.data[0], self.data[2]:
self.assertTrue(isinstance(k, str))
self.assertEqual(self.tree[k], v)
def test_suite():
return unittest.makeSuite(TestBTreesUnicode)
@@ -0,0 +1,573 @@
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Foundation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
import unittest
from .common import _skip_wo_ZODB
from .common import ConflictTestBase
class NastyConfictFunctionalTests(ConflictTestBase, unittest.TestCase):
# FUNCTESTS: Provoke various conflict scenarios using ZODB + transaction
def _getTargetClass(self):
from BTrees.OOBTree import OOBTree
return OOBTree
def openDB(self):
# The conflict tests tend to open two or more connections
# and then try to commit them. A standard FileStorage
# is not MVCC aware, and so each connection would have the same
# instance of the storage, leading to the error
# "Duplicate tpc_begin calls for same transaction" on commit;
# thus we use a MVCCMappingStorage for these tests, ensuring each
# connection has its own storage.
# Unfortunately, it wants to acquire the identically same
# non-recursive lock in each of its *its* tpc_* methods, which deadlocks.
# The solution is to give each instance its own lock, and trust in the
# serialization (ordering) of the datamanager, and the fact that these tests are
# single-threaded.
import threading
from ZODB.tests.MVCCMappingStorage import MVCCMappingStorage
class _MVCCMappingStorage(MVCCMappingStorage):
def new_instance(self):
inst = MVCCMappingStorage.new_instance(self)
inst._commit_lock = threading.Lock()
return inst
from ZODB.DB import DB
self.storage = _MVCCMappingStorage()
self.db = DB(self.storage)
return self.db
@_skip_wo_ZODB
def testSimpleConflict(self):
# Invoke conflict resolution by committing a transaction and
# catching a conflict in the storage.
import transaction
self.openDB()
r1 = self.db.open().root()
r1["t"] = t = self._makeOne()
transaction.commit()
r2 = self.db.open().root()
copy = r2["t"]
list(copy) # unghostify
self.assertEqual(t._p_serial, copy._p_serial)
t.update({1:2, 2:3})
transaction.commit()
copy.update({3:4})
transaction.commit()
# This tests a problem that cropped up while trying to write
# testBucketSplitConflict (below): conflict resolution wasn't
# working at all in non-trivial cases. Symptoms varied from
# strange complaints about pickling (despite that the test isn't
# doing any *directly*), thru SystemErrors from Python and
# AssertionErrors inside the BTree code.
@_skip_wo_ZODB
def testResolutionBlowsUp(self):
import transaction
b = self._makeOne()
for i in range(0, 200, 4):
b[i] = i
# bucket 0 has 15 values: 0, 4 .. 56
# bucket 1 has 15 values: 60, 64 .. 116
# bucket 2 has 20 values: 120, 124 .. 196
state = b.__getstate__()
# Looks like: ((bucket0, 60, bucket1, 120, bucket2), firstbucket)
# If these fail, the *preconditions* for running the test aren't
# satisfied -- the test itself hasn't been run yet.
self.assertEqual(len(state), 2)
self.assertEqual(len(state[0]), 5)
self.assertEqual(state[0][1], 60)
self.assertEqual(state[0][3], 120)
# Invoke conflict resolution by committing a transaction.
self.openDB()
r1 = self.db.open().root()
r1["t"] = b
transaction.commit()
r2 = self.db.open().root()
copy = r2["t"]
# Make sure all of copy is loaded.
list(copy.values())
self.assertEqual(b._p_serial, copy._p_serial)
b.update({1:2, 2:3})
transaction.commit()
copy.update({3:4})
transaction.commit() # if this doesn't blow up
list(copy.values()) # and this doesn't either, then fine
@_skip_wo_ZODB
def testBucketSplitConflict(self):
# Tests that a bucket split is viewed as a conflict.
# It's (almost necessarily) a white-box test, and sensitive to
# implementation details.
import transaction
from ZODB.POSException import ConflictError
b = orig = self._makeOne()
for i in range(0, 200, 4):
b[i] = i
# bucket 0 has 15 values: 0, 4 .. 56
# bucket 1 has 15 values: 60, 64 .. 116
# bucket 2 has 20 values: 120, 124 .. 196
state = b.__getstate__()
# Looks like: ((bucket0, 60, bucket1, 120, bucket2), firstbucket)
# If these fail, the *preconditions* for running the test aren't
# satisfied -- the test itself hasn't been run yet.
self.assertEqual(len(state), 2)
self.assertEqual(len(state[0]), 5)
self.assertEqual(state[0][1], 60)
self.assertEqual(state[0][3], 120)
# Invoke conflict resolution by committing a transaction.
self.openDB()
tm1 = transaction.TransactionManager()
r1 = self.db.open(transaction_manager=tm1).root()
r1["t"] = b
tm1.commit()
tm2 = transaction.TransactionManager()
r2 = self.db.open(transaction_manager=tm2).root()
copy = r2["t"]
# Make sure all of copy is loaded.
list(copy.values())
self.assertEqual(orig._p_serial, copy._p_serial)
# In one transaction, add 16 new keys to bucket1, to force a bucket
# split.
b = orig
numtoadd = 16
candidate = 60
while numtoadd:
if candidate not in b:
b[candidate] = candidate
numtoadd -= 1
candidate += 1
# bucket 0 has 15 values: 0, 4 .. 56
# bucket 1 has 15 values: 60, 61 .. 74
# bucket 2 has 16 values: [75, 76 .. 81] + [84, 88 ..116]
# bucket 3 has 20 values: 120, 124 .. 196
state = b.__getstate__()
# Looks like: ((b0, 60, b1, 75, b2, 120, b3), firstbucket)
# The next block is still verifying preconditions.
self.assertEqual(len(state) , 2)
self.assertEqual(len(state[0]), 7)
self.assertEqual(state[0][1], 60)
self.assertEqual(state[0][3], 75)
self.assertEqual(state[0][5], 120)
tm1.commit()
# In the other transaction, add 3 values near the tail end of bucket1.
# This doesn't cause a split.
b = copy
for i in range(112, 116):
b[i] = i
# bucket 0 has 15 values: 0, 4 .. 56
# bucket 1 has 18 values: 60, 64 .. 112, 113, 114, 115, 116
# bucket 2 has 20 values: 120, 124 .. 196
state = b.__getstate__()
# Looks like: ((bucket0, 60, bucket1, 120, bucket2), firstbucket)
# The next block is still verifying preconditions.
self.assertEqual(len(state), 2)
self.assertEqual(len(state[0]), 5)
self.assertEqual(state[0][1], 60)
self.assertEqual(state[0][3], 120)
self.assertRaises(ConflictError, tm2.commit)
@_skip_wo_ZODB
def testEmptyBucketConflict(self):
# Tests that an emptied bucket *created by* conflict resolution is
# viewed as a conflict: conflict resolution doesn't have enough
# info to unlink the empty bucket from the BTree correctly.
import transaction
from ZODB.POSException import ConflictError
b = orig = self._makeOne()
for i in range(0, 200, 4):
b[i] = i
# bucket 0 has 15 values: 0, 4 .. 56
# bucket 1 has 15 values: 60, 64 .. 116
# bucket 2 has 20 values: 120, 124 .. 196
state = b.__getstate__()
# Looks like: ((bucket0, 60, bucket1, 120, bucket2), firstbucket)
# If these fail, the *preconditions* for running the test aren't
# satisfied -- the test itself hasn't been run yet.
self.assertEqual(len(state), 2)
self.assertEqual(len(state[0]), 5)
self.assertEqual(state[0][1], 60)
self.assertEqual(state[0][3], 120)
# Invoke conflict resolution by committing a transaction.
self.openDB()
tm1 = transaction.TransactionManager()
r1 = self.db.open(transaction_manager=tm1).root()
r1["t"] = b
tm1.commit()
tm2 = transaction.TransactionManager()
r2 = self.db.open(transaction_manager=tm2).root()
copy = r2["t"]
# Make sure all of copy is loaded.
list(copy.values())
self.assertEqual(orig._p_serial, copy._p_serial)
# In one transaction, delete half of bucket 1.
b = orig
for k in 60, 64, 68, 72, 76, 80, 84, 88:
del b[k]
# bucket 0 has 15 values: 0, 4 .. 56
# bucket 1 has 7 values: 92, 96, 100, 104, 108, 112, 116
# bucket 2 has 20 values: 120, 124 .. 196
state = b.__getstate__()
# Looks like: ((bucket0, 60, bucket1, 120, bucket2), firstbucket)
# The next block is still verifying preconditions.
self.assertEqual(len(state) , 2)
self.assertEqual(len(state[0]), 5)
self.assertEqual(state[0][1], 92)
self.assertEqual(state[0][3], 120)
tm1.commit()
# In the other transaction, delete the other half of bucket 1.
b = copy
for k in 92, 96, 100, 104, 108, 112, 116:
del b[k]
# bucket 0 has 15 values: 0, 4 .. 56
# bucket 1 has 8 values: 60, 64, 68, 72, 76, 80, 84, 88
# bucket 2 has 20 values: 120, 124 .. 196
state = b.__getstate__()
# Looks like: ((bucket0, 60, bucket1, 120, bucket2), firstbucket)
# The next block is still verifying preconditions.
self.assertEqual(len(state), 2)
self.assertEqual(len(state[0]), 5)
self.assertEqual(state[0][1], 60)
self.assertEqual(state[0][3], 120)
# Conflict resolution empties bucket1 entirely. This used to
# create an "insane" BTree (a legit BTree cannot contain an empty
# bucket -- it contains NULL pointers the BTree code doesn't
# expect, and segfaults result).
self.assertRaises(ConflictError, tm2.commit)
@_skip_wo_ZODB
def testEmptyBucketNoConflict(self):
# Tests that a plain empty bucket (on input) is not viewed as a
# conflict.
import transaction
b = orig = self._makeOne()
for i in range(0, 200, 4):
b[i] = i
# bucket 0 has 15 values: 0, 4 .. 56
# bucket 1 has 15 values: 60, 64 .. 116
# bucket 2 has 20 values: 120, 124 .. 196
state = b.__getstate__()
# Looks like: ((bucket0, 60, bucket1, 120, bucket2), firstbucket)
# If these fail, the *preconditions* for running the test aren't
# satisfied -- the test itself hasn't been run yet.
self.assertEqual(len(state), 2)
self.assertEqual(len(state[0]), 5)
self.assertEqual(state[0][1], 60)
self.assertEqual(state[0][3], 120)
# Invoke conflict resolution by committing a transaction.
self.openDB()
r1 = self.db.open().root()
r1["t"] = orig
transaction.commit()
r2 = self.db.open().root()
copy = r2["t"]
# Make sure all of copy is loaded.
list(copy.values())
self.assertEqual(orig._p_serial, copy._p_serial)
# In one transaction, just add a key.
b = orig
b[1] = 1
# bucket 0 has 16 values: [0, 1] + [4, 8 .. 56]
# bucket 1 has 15 values: 60, 64 .. 116
# bucket 2 has 20 values: 120, 124 .. 196
state = b.__getstate__()
# Looks like: ((bucket0, 60, bucket1, 120, bucket2), firstbucket)
# The next block is still verifying preconditions.
self.assertEqual(len(state), 2)
self.assertEqual(len(state[0]), 5)
self.assertEqual(state[0][1], 60)
self.assertEqual(state[0][3], 120)
transaction.commit()
# In the other transaction, delete bucket 2.
b = copy
for k in range(120, 200, 4):
del b[k]
# bucket 0 has 15 values: 0, 4 .. 56
# bucket 1 has 15 values: 60, 64 .. 116
state = b.__getstate__()
# Looks like: ((bucket0, 60, bucket1), firstbucket)
# The next block is still verifying preconditions.
self.assertEqual(len(state), 2)
self.assertEqual(len(state[0]), 3)
self.assertEqual(state[0][1], 60)
# This shouldn't create a ConflictError.
transaction.commit()
# And the resulting BTree shouldn't have internal damage.
b._check()
# The snaky control flow in _bucket__p_resolveConflict ended up trying
# to decref a NULL pointer if conflict resolution was fed 3 empty
# buckets. http://collector.zope.org/Zope/553
def testThreeEmptyBucketsNoSegfault(self):
# Note that the conflict is raised by our C extension, rather than
# indirectly via the storage, and hence is a more specialized type.
# This test therefore does not require ZODB.
from BTrees.Interfaces import BTreesConflictError
t = self._makeOne()
t[1] = 1
bucket = t._firstbucket
del t[1]
state1 = bucket.__getstate__()
state2 = bucket.__getstate__()
state3 = bucket.__getstate__()
self.assertTrue(state2 is not state1 and
state2 is not state3 and
state3 is not state1)
self.assertTrue(state2 == state1 and
state3 == state1)
self.assertRaises(BTreesConflictError, bucket._p_resolveConflict,
state1, state2, state3)
# When an empty BTree resolves conflicts, it computes the
# bucket state as None, so...
self.assertRaises(BTreesConflictError, bucket._p_resolveConflict,
None, None, None)
@_skip_wo_ZODB
def testCantResolveBTreeConflict(self):
# Test that a conflict involving two different changes to
# an internal BTree node is unresolvable. An internal node
# only changes when there are enough additions or deletions
# to a child bucket that the bucket is split or removed.
# It's (almost necessarily) a white-box test, and sensitive to
# implementation details.
import transaction
from ZODB.POSException import ConflictError
b = orig = self._makeOne()
for i in range(0, 200, 4):
b[i] = i
# bucket 0 has 15 values: 0, 4 .. 56
# bucket 1 has 15 values: 60, 64 .. 116
# bucket 2 has 20 values: 120, 124 .. 196
state = b.__getstate__()
# Looks like: ((bucket0, 60, bucket1, 120, bucket2), firstbucket)
# If these fail, the *preconditions* for running the test aren't
# satisfied -- the test itself hasn't been run yet.
self.assertEqual(len(state), 2)
self.assertEqual(len(state[0]), 5)
self.assertEqual(state[0][1], 60)
self.assertEqual(state[0][3], 120)
# Set up database connections to provoke conflict.
self.openDB()
tm1 = transaction.TransactionManager()
r1 = self.db.open(transaction_manager=tm1).root()
r1["t"] = orig
tm1.commit()
tm2 = transaction.TransactionManager()
r2 = self.db.open(transaction_manager=tm2).root()
copy = r2["t"]
# Make sure all of copy is loaded.
list(copy.values())
self.assertEqual(orig._p_serial, copy._p_serial)
# Now one transaction should add enough keys to cause a split,
# and another should remove all the keys in one bucket.
for k in range(200, 300, 4):
orig[k] = k
tm1.commit()
for k in range(0, 60, 4):
del copy[k]
self.assertRaises(ConflictError, tm2.commit)
@_skip_wo_ZODB
def testConflictWithOneEmptyBucket(self):
# If one transaction empties a bucket, while another adds an item
# to the bucket, all the changes "look resolvable": bucket conflict
# resolution returns a bucket containing (only) the item added by
# the latter transaction, but changes from the former transaction
# removing the bucket are uncontested: the bucket is removed from
# the BTree despite that resolution thinks it's non-empty! This
# was first reported by Dieter Maurer, to zodb-dev on 22 Mar 2005.
import transaction
from ZODB.POSException import ConflictError
b = orig = self._makeOne()
for i in range(0, 200, 4):
b[i] = i
# bucket 0 has 15 values: 0, 4 .. 56
# bucket 1 has 15 values: 60, 64 .. 116
# bucket 2 has 20 values: 120, 124 .. 196
state = b.__getstate__()
# Looks like: ((bucket0, 60, bucket1, 120, bucket2), firstbucket)
# If these fail, the *preconditions* for running the test aren't
# satisfied -- the test itself hasn't been run yet.
self.assertEqual(len(state), 2)
self.assertEqual(len(state[0]), 5)
self.assertEqual(state[0][1], 60)
self.assertEqual(state[0][3], 120)
# Set up database connections to provoke conflict.
self.openDB()
tm1 = transaction.TransactionManager()
r1 = self.db.open(transaction_manager=tm1).root()
r1["t"] = orig
tm1.commit()
tm2 = transaction.TransactionManager()
r2 = self.db.open(transaction_manager=tm2).root()
copy = r2["t"]
# Make sure all of copy is loaded.
list(copy.values())
self.assertEqual(orig._p_serial, copy._p_serial)
# Now one transaction empties the first bucket, and another adds a
# key to the first bucket.
for k in range(0, 60, 4):
del orig[k]
tm1.commit()
copy[1] = 1
self.assertRaises(ConflictError, tm2.commit)
# Same thing, except commit the transactions in the opposite order.
b = self._makeOne()
for i in range(0, 200, 4):
b[i] = i
tm1 = transaction.TransactionManager()
r1 = self.db.open(transaction_manager=tm1).root()
r1["t"] = b
tm1.commit()
tm2 = transaction.TransactionManager()
r2 = self.db.open(transaction_manager=tm2).root()
copy = r2["t"]
# Make sure all of copy is loaded.
list(copy.values())
self.assertEqual(b._p_serial, copy._p_serial)
# Now one transaction empties the first bucket, and another adds a
# key to the first bucket.
b[1] = 1
tm1.commit()
for k in range(0, 60, 4):
del copy[k]
self.assertRaises(ConflictError, tm2.commit)
@_skip_wo_ZODB
def testConflictOfInsertAndDeleteOfFirstBucketItem(self):
# Recently, BTrees became careful about removing internal keys
# (keys in internal aka BTree nodes) when they were deleted from
# buckets. This poses a problem for conflict resolution.
# We want to guard against a case in which the first key in a
# bucket is removed in one transaction while a key is added
# after that key but before the next key in another transaction
# with the result that the added key is unreachable.
# original:
# Bucket(...), k1, Bucket((k1, v1), (k3, v3), ...)
# tran1
# Bucket(...), k3, Bucket(k3, v3), ...)
# tran2
# Bucket(...), k1, Bucket((k1, v1), (k2, v2), (k3, v3), ...)
# where k1 < k2 < k3
# We don't want:
# Bucket(...), k3, Bucket((k2, v2), (k3, v3), ...)
# as k2 would be unfindable, so we want a conflict.
import transaction
from ZODB.POSException import ConflictError
mytype = self._getTargetClass()
db = self.openDB()
tm1 = transaction.TransactionManager()
conn1 = db.open(tm1)
conn1.root.t = t = mytype()
for i in range(0, 200, 2):
t[i] = i
tm1.commit()
k = t.__getstate__()[0][1]
assert t.__getstate__()[0][2].keys()[0] == k
tm2 = transaction.TransactionManager()
conn2 = db.open(tm2)
t[k+1] = k+1
del conn2.root.t[k]
for i in range(200,300):
conn2.root.t[i] = i
tm1.commit()
self.assertRaises(ConflictError, tm2.commit)
tm2.abort()
k = t.__getstate__()[0][1]
t[k+1] = k+1
del conn2.root.t[k]
tm2.commit()
self.assertRaises(ConflictError, tm1.commit)
tm1.abort()
def test_suite():
return unittest.TestSuite((
unittest.makeSuite(NastyConfictFunctionalTests),
))
@@ -0,0 +1,378 @@
##############################################################################
#
# Copyright (c) 2001-2012 Zope Foundation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
import unittest
from .common import BTreeTests
from .common import ExtendedSetTests
from .common import InternalKeysMappingTest
from .common import InternalKeysSetTest
from .common import MappingBase
from .common import MappingConflictTestBase
from .common import ModuleTest
from .common import MultiUnion
from .common import NormalSetTests
from .common import SetConflictTestBase
from .common import SetResult
from .common import TestLongIntKeys
from .common import makeBuilder
from BTrees.IIBTree import using64bits #XXX Ugly, but unavoidable
class IFBTreeInternalKeyTest(InternalKeysMappingTest, unittest.TestCase):
def _getTargetClass(self):
from BTrees.IFBTree import IFBTree
return IFBTree
class IFBTreePyInternalKeyTest(InternalKeysMappingTest, unittest.TestCase):
def _getTargetClass(self):
from BTrees.IFBTree import IFBTreePy
return IFBTreePy
class IFTreeSetInternalKeyTest(InternalKeysSetTest, unittest.TestCase):
def _getTargetClass(self):
from BTrees.IFBTree import IFTreeSet
return IFTreeSet
class IFTreeSetPyInternalKeyTest(InternalKeysSetTest, unittest.TestCase):
def _getTargetClass(self):
from BTrees.IFBTree import IFTreeSetPy
return IFTreeSetPy
class IFBucketTest(MappingBase, unittest.TestCase):
def _getTargetClass(self):
from BTrees.IFBTree import IFBucket
return IFBucket
class IFBucketPyTest(MappingBase, unittest.TestCase):
def _getTargetClass(self):
from BTrees.IFBTree import IFBucketPy
return IFBucketPy
class IFTreeSetTest(NormalSetTests, unittest.TestCase):
def _getTargetClass(self):
from BTrees.IFBTree import IFTreeSet
return IFTreeSet
class IFTreeSetPyTest(NormalSetTests, unittest.TestCase):
def _getTargetClass(self):
from BTrees.IFBTree import IFTreeSetPy
return IFTreeSetPy
class IFSetTest(ExtendedSetTests, unittest.TestCase):
def _getTargetClass(self):
from BTrees.IFBTree import IFSet
return IFSet
class IFSetPyTest(ExtendedSetTests, unittest.TestCase):
def _getTargetClass(self):
from BTrees.IFBTree import IFSetPy
return IFSetPy
class IFBTreeTest(BTreeTests, unittest.TestCase):
def _makeOne(self):
from BTrees.IFBTree import IFBTree
return IFBTree()
class IFBTreePyTest(BTreeTests, unittest.TestCase):
def _makeOne(self):
from BTrees.IFBTree import IFBTreePy
return IFBTreePy()
if using64bits:
class IFBTreeTest(BTreeTests, TestLongIntKeys, unittest.TestCase):
def _makeOne(self):
from BTrees.IFBTree import IFBTree
return IFBTree()
def getTwoValues(self):
return 0.5, 1.5
class IFBTreePyTest(BTreeTests, TestLongIntKeys, unittest.TestCase):
def _makeOne(self):
from BTrees.IFBTree import IFBTreePy
return IFBTreePy()
def getTwoValues(self):
return 0.5, 1.5
class _TestIFBTreesBase(object):
def testNonIntegerKeyRaises(self):
self.assertRaises(TypeError, self._stringraiseskey)
self.assertRaises(TypeError, self._floatraiseskey)
self.assertRaises(TypeError, self._noneraiseskey)
def testNonNumericValueRaises(self):
self.assertRaises(TypeError, self._stringraisesvalue)
self.assertRaises(TypeError, self._noneraisesvalue)
self._makeOne()[1] = 1
self._makeOne()[1] = 1.0
def _stringraiseskey(self):
self._makeOne()['c'] = 1
def _floatraiseskey(self):
self._makeOne()[2.5] = 1
def _noneraiseskey(self):
self._makeOne()[None] = 1
def _stringraisesvalue(self):
self._makeOne()[1] = 'c'
def _floatraisesvalue(self):
self._makeOne()[1] = 1.4
def _noneraisesvalue(self):
self._makeOne()[1] = None
class TestIFBTrees(_TestIFBTreesBase, unittest.TestCase):
def _makeOne(self):
from BTrees.IFBTree import IFBTree
return IFBTree()
class TestIFBTreesPy(_TestIFBTreesBase, unittest.TestCase):
def _makeOne(self):
from BTrees.IFBTree import IFBTreePy
return IFBTreePy()
class TestIFMultiUnion(MultiUnion, unittest.TestCase):
def multiunion(self, *args):
from BTrees.IFBTree import multiunion
return multiunion(*args)
def union(self, *args):
from BTrees.IFBTree import union
return union(*args)
def mkset(self, *args):
from BTrees.IFBTree import IFSet as mkset
return mkset(*args)
def mktreeset(self, *args):
from BTrees.IFBTree import IFTreeSet as mktreeset
return mktreeset(*args)
def mkbucket(self, *args):
from BTrees.IFBTree import IFBucket as mkbucket
return mkbucket(*args)
def mkbtree(self, *args):
from BTrees.IFBTree import IFBTree as mkbtree
return mkbtree(*args)
class TestIFMultiUnionPy(MultiUnion, unittest.TestCase):
def multiunion(self, *args):
from BTrees.IFBTree import multiunionPy
return multiunionPy(*args)
def union(self, *args):
from BTrees.IFBTree import unionPy
return unionPy(*args)
def mkset(self, *args):
from BTrees.IFBTree import IFSetPy as mkset
return mkset(*args)
def mktreeset(self, *args):
from BTrees.IFBTree import IFTreeSetPy as mktreeset
return mktreeset(*args)
def mkbucket(self, *args):
from BTrees.IFBTree import IFBucketPy as mkbucket
return mkbucket(*args)
def mkbtree(self, *args):
from BTrees.IFBTree import IFBTreePy as mkbtree
return mkbtree(*args)
class PureIF(SetResult, unittest.TestCase):
def union(self, *args):
from BTrees.IFBTree import union
return union(*args)
def intersection(self, *args):
from BTrees.IFBTree import intersection
return intersection(*args)
def difference(self, *args):
from BTrees.IFBTree import difference
return difference(*args)
def builders(self):
from BTrees.IFBTree import IFBTree
from BTrees.IFBTree import IFBucket
from BTrees.IFBTree import IFTreeSet
from BTrees.IFBTree import IFSet
return IFSet, IFTreeSet, makeBuilder(IFBTree), makeBuilder(IFBucket)
class PureIFPy(SetResult, unittest.TestCase):
def union(self, *args):
from BTrees.IFBTree import unionPy
return unionPy(*args)
def intersection(self, *args):
from BTrees.IFBTree import intersectionPy
return intersectionPy(*args)
def difference(self, *args):
from BTrees.IFBTree import differencePy
return differencePy(*args)
def builders(self):
from BTrees.IFBTree import IFBTreePy
from BTrees.IFBTree import IFBucketPy
from BTrees.IFBTree import IFTreeSetPy
from BTrees.IFBTree import IFSetPy
return (IFSetPy, IFTreeSetPy,
makeBuilder(IFBTreePy), makeBuilder(IFBucketPy))
class IFBTreeConflictTests(MappingConflictTestBase, unittest.TestCase):
def _getTargetClass(self):
from BTrees.IFBTree import IFBTree
return IFBTree
class IFBTreePyConflictTests(MappingConflictTestBase, unittest.TestCase):
def _getTargetClass(self):
from BTrees.IFBTree import IFBTreePy
return IFBTreePy
class IFBucketConflictTests(MappingConflictTestBase, unittest.TestCase):
def _getTargetClass(self):
from BTrees.IFBTree import IFBucket
return IFBucket
class IFBucketPyConflictTests(MappingConflictTestBase, unittest.TestCase):
def _getTargetClass(self):
from BTrees.IFBTree import IFBucketPy
return IFBucketPy
class IFTreeSetConflictTests(SetConflictTestBase, unittest.TestCase):
def _getTargetClass(self):
from BTrees.IFBTree import IFTreeSet
return IFTreeSet
class IFTreeSetPyConflictTests(SetConflictTestBase, unittest.TestCase):
def _getTargetClass(self):
from BTrees.IFBTree import IFTreeSetPy
return IFTreeSetPy
class IFSetConflictTests(SetConflictTestBase, unittest.TestCase):
def _getTargetClass(self):
from BTrees.IFBTree import IFSet
return IFSet
class IFSetPyConflictTests(SetConflictTestBase, unittest.TestCase):
def _getTargetClass(self):
from BTrees.IFBTree import IFSetPy
return IFSetPy
class IFModuleTest(ModuleTest, unittest.TestCase):
prefix = 'IF'
def _getModule(self):
import BTrees
return BTrees.IFBTree
def _getInterface(self):
import BTrees.Interfaces
return BTrees.Interfaces.IIntegerFloatBTreeModule
def test_suite():
return unittest.TestSuite((
unittest.makeSuite(IFBTreeInternalKeyTest),
unittest.makeSuite(IFBTreePyInternalKeyTest),
unittest.makeSuite(IFTreeSetInternalKeyTest),
unittest.makeSuite(IFTreeSetPyInternalKeyTest),
unittest.makeSuite(IFBucketTest),
unittest.makeSuite(IFBucketPyTest),
unittest.makeSuite(IFTreeSetTest),
unittest.makeSuite(IFTreeSetPyTest),
unittest.makeSuite(IFSetTest),
unittest.makeSuite(IFSetPyTest),
unittest.makeSuite(IFBTreeTest),
unittest.makeSuite(IFBTreePyTest),
unittest.makeSuite(TestIFBTrees),
unittest.makeSuite(TestIFBTreesPy),
unittest.makeSuite(TestIFMultiUnion),
unittest.makeSuite(TestIFMultiUnionPy),
unittest.makeSuite(PureIF),
unittest.makeSuite(PureIFPy),
unittest.makeSuite(IFBTreeConflictTests),
unittest.makeSuite(IFBTreePyConflictTests),
unittest.makeSuite(IFBucketConflictTests),
unittest.makeSuite(IFBucketPyConflictTests),
unittest.makeSuite(IFTreeSetConflictTests),
unittest.makeSuite(IFTreeSetPyConflictTests),
unittest.makeSuite(IFSetConflictTests),
unittest.makeSuite(IFSetPyConflictTests),
unittest.makeSuite(IFModuleTest),
))
@@ -0,0 +1,507 @@
##############################################################################
#
# Copyright (c) 2001-2012 Zope Foundation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
import unittest
from .common import BTreeTests
from .common import ExtendedSetTests
from .common import I_SetsBase
from .common import InternalKeysMappingTest
from .common import InternalKeysSetTest
from .common import MappingBase
from .common import MappingConflictTestBase
from .common import ModuleTest
from .common import MultiUnion
from .common import NormalSetTests
from .common import SetConflictTestBase
from .common import SetResult
from .common import TestLongIntKeys
from .common import TestLongIntValues
from .common import Weighted
from .common import itemsToSet
from .common import makeBuilder
from BTrees.IIBTree import using64bits #XXX Ugly, but unavoidable
class IIBTreeInternalKeyTest(InternalKeysMappingTest, unittest.TestCase):
def _getTargetClass(self):
from BTrees.IIBTree import IIBTree
return IIBTree
class IIBTreePyInternalKeyTest(InternalKeysMappingTest, unittest.TestCase):
def _getTargetClass(self):
from BTrees.IIBTree import IIBTreePy
return IIBTreePy
class IITreeSetInternalKeyTest(InternalKeysSetTest, unittest.TestCase):
def _getTargetClass(self):
from BTrees.IIBTree import IITreeSet
return IITreeSet
class IITreeSetPyInternalKeyTest(InternalKeysSetTest, unittest.TestCase):
def _getTargetClass(self):
from BTrees.IIBTree import IITreeSetPy
return IITreeSetPy
class IIBucketTest(MappingBase, unittest.TestCase):
def _getTargetClass(self):
from BTrees.IIBTree import IIBucket
return IIBucket
class IIBucketPyTest(MappingBase, unittest.TestCase):
def _getTargetClass(self):
from BTrees.IIBTree import IIBucketPy
return IIBucketPy
class IITreeSetTest(NormalSetTests, unittest.TestCase):
def _getTargetClass(self):
from BTrees.IIBTree import IITreeSet
return IITreeSet
class IITreeSetPyTest(NormalSetTests, unittest.TestCase):
def _getTargetClass(self):
from BTrees.IIBTree import IITreeSetPy
return IITreeSetPy
class IISetTest(ExtendedSetTests, unittest.TestCase):
def _getTargetClass(self):
from BTrees.IIBTree import IISet
return IISet
class IISetPyTest(ExtendedSetTests, unittest.TestCase):
def _getTargetClass(self):
from BTrees.IIBTree import IISetPy
return IISetPy
class _IIBTreeTestBase(BTreeTests):
def testIIBTreeOverflow(self):
good = set()
b = self._makeOne()
def trial(i):
i = int(i)
try:
b[i] = 0
except OverflowError:
self.assertRaises(OverflowError, b.__setitem__, 0, i)
except TypeError:
self.assertRaises(TypeError, b.__setitem__, 0, i)
else:
good.add(i)
b[0] = i
self.assertEqual(b[0], i)
for i in range((1<<31) - 3, (1<<31) + 3):
trial(i)
trial(-i)
del b[0]
self.assertEqual(sorted(good), sorted(b))
class IIBTreeTest(_IIBTreeTestBase, unittest.TestCase):
def _makeOne(self):
from BTrees.IIBTree import IIBTree
return IIBTree()
class IIBTreeTestPy(_IIBTreeTestBase, unittest.TestCase):
def _makeOne(self):
from BTrees.IIBTree import IIBTreePy
return IIBTreePy()
if using64bits:
class IIBTreeTest(BTreeTests, TestLongIntKeys, TestLongIntValues,
unittest.TestCase):
def _makeOne(self):
from BTrees.IIBTree import IIBTree
return IIBTree()
def getTwoValues(self):
return 1, 2
class IIBTreeTest(BTreeTests, TestLongIntKeys, TestLongIntValues,
unittest.TestCase):
def _makeOne(self):
from BTrees.IIBTree import IIBTreePy
return IIBTreePy()
def getTwoValues(self):
return 1, 2
class _TestIIBTreesBase(object):
def testNonIntegerKeyRaises(self):
self.assertRaises(TypeError, self._stringraiseskey)
self.assertRaises(TypeError, self._floatraiseskey)
self.assertRaises(TypeError, self._noneraiseskey)
def testNonIntegerValueRaises(self):
self.assertRaises(TypeError, self._stringraisesvalue)
self.assertRaises(TypeError, self._floatraisesvalue)
self.assertRaises(TypeError, self._noneraisesvalue)
def _stringraiseskey(self):
self._makeOne()['c'] = 1
def _floatraiseskey(self):
self._makeOne()[2.5] = 1
def _noneraiseskey(self):
self._makeOne()[None] = 1
def _stringraisesvalue(self):
self._makeOne()[1] = 'c'
def _floatraisesvalue(self):
self._makeOne()[1] = 1.4
def _noneraisesvalue(self):
self._makeOne()[1] = None
class TestIIBTrees(_TestIIBTreesBase, unittest.TestCase):
def _makeOne(self):
from BTrees.IIBTree import IIBTree
return IIBTree()
class TestIIBTreesPy(_TestIIBTreesBase, unittest.TestCase):
def _makeOne(self):
from BTrees.IIBTree import IIBTreePy
return IIBTreePy()
class TestIISets(I_SetsBase, unittest.TestCase):
def _makeOne(self):
from BTrees.IIBTree import IISet
return IISet()
class TestIISetsPy(I_SetsBase, unittest.TestCase):
def _makeOne(self):
from BTrees.IIBTree import IISetPy
return IISetPy()
class TestIITreeSets(I_SetsBase, unittest.TestCase):
def _makeOne(self):
from BTrees.IIBTree import IITreeSet
return IITreeSet()
class TestIITreeSetsPy(I_SetsBase, unittest.TestCase):
def _makeOne(self):
from BTrees.IIBTree import IITreeSetPy
return IITreeSetPy()
class PureII(SetResult, unittest.TestCase):
def union(self, *args):
from BTrees.IIBTree import union
return union(*args)
def intersection(self, *args):
from BTrees.IIBTree import intersection
return intersection(*args)
def difference(self, *args):
from BTrees.IIBTree import difference
return difference(*args)
def builders(self):
from BTrees.IIBTree import IIBTree
from BTrees.IIBTree import IIBucket
from BTrees.IIBTree import IITreeSet
from BTrees.IIBTree import IISet
return IISet, IITreeSet, makeBuilder(IIBTree), makeBuilder(IIBucket)
class PureIIPy(SetResult, unittest.TestCase):
def union(self, *args):
from BTrees.IIBTree import unionPy
return unionPy(*args)
def intersection(self, *args):
from BTrees.IIBTree import intersectionPy
return intersectionPy(*args)
def difference(self, *args):
from BTrees.IIBTree import differencePy
return differencePy(*args)
def builders(self):
from BTrees.IIBTree import IIBTreePy
from BTrees.IIBTree import IIBucketPy
from BTrees.IIBTree import IITreeSetPy
from BTrees.IIBTree import IISetPy
return (IISetPy, IITreeSetPy,
makeBuilder(IIBTreePy), makeBuilder(IIBucketPy))
class TestIIMultiUnion(MultiUnion, unittest.TestCase):
def multiunion(self, *args):
from BTrees.IIBTree import multiunion
return multiunion(*args)
def union(self, *args):
from BTrees.IIBTree import union
return union(*args)
def mkset(self, *args):
from BTrees.IIBTree import IISet as mkset
return mkset(*args)
def mktreeset(self, *args):
from BTrees.IIBTree import IITreeSet as mktreeset
return mktreeset(*args)
def mkbucket(self, *args):
from BTrees.IIBTree import IIBucket as mkbucket
return mkbucket(*args)
def mkbtree(self, *args):
from BTrees.IIBTree import IIBTree as mkbtree
return mkbtree(*args)
class TestIIMultiUnionPy(MultiUnion, unittest.TestCase):
def multiunion(self, *args):
from BTrees.IIBTree import multiunionPy
return multiunionPy(*args)
def union(self, *args):
from BTrees.IIBTree import unionPy
return unionPy(*args)
def mkset(self, *args):
from BTrees.IIBTree import IISetPy as mkset
return mkset(*args)
def mktreeset(self, *args):
from BTrees.IIBTree import IITreeSetPy as mktreeset
return mktreeset(*args)
def mkbucket(self, *args):
from BTrees.IIBTree import IIBucketPy as mkbucket
return mkbucket(*args)
def mkbtree(self, *args):
from BTrees.IIBTree import IIBTreePy as mkbtree
return mkbtree(*args)
class TestWeightedII(Weighted, unittest.TestCase):
def weightedUnion(self):
from BTrees.IIBTree import weightedUnion
return weightedUnion
def weightedIntersection(self):
from BTrees.IIBTree import weightedIntersection
return weightedIntersection
def union(self):
from BTrees.IIBTree import union
return union
def intersection(self):
from BTrees.IIBTree import intersection
return intersection
def mkbucket(self, *args):
from BTrees.IIBTree import IIBucket as mkbucket
return mkbucket(*args)
def builders(self):
from BTrees.IIBTree import IIBTree
from BTrees.IIBTree import IIBucket
from BTrees.IIBTree import IITreeSet
from BTrees.IIBTree import IISet
return IIBucket, IIBTree, itemsToSet(IISet), itemsToSet(IITreeSet)
class TestWeightedIIPy(Weighted, unittest.TestCase):
def weightedUnion(self):
from BTrees.IIBTree import weightedUnionPy
return weightedUnionPy
def weightedIntersection(self):
from BTrees.IIBTree import weightedIntersectionPy
return weightedIntersectionPy
def union(self):
from BTrees.IIBTree import unionPy
return unionPy
def intersection(self):
from BTrees.IIBTree import intersectionPy
return intersectionPy
def mkbucket(self, *args):
from BTrees.IIBTree import IIBucketPy as mkbucket
return mkbucket(*args)
def builders(self):
from BTrees.IIBTree import IIBTreePy
from BTrees.IIBTree import IIBucketPy
from BTrees.IIBTree import IITreeSetPy
from BTrees.IIBTree import IISetPy
return (IIBucketPy, IIBTreePy,
itemsToSet(IISetPy), itemsToSet(IITreeSetPy))
class IIBTreeConflictTests(MappingConflictTestBase, unittest.TestCase):
def _getTargetClass(self):
from BTrees.IIBTree import IIBTree
return IIBTree
class IIBTreeConflictTestsPy(MappingConflictTestBase, unittest.TestCase):
def _getTargetClass(self):
from BTrees.IIBTree import IIBTreePy
return IIBTreePy
class IIBucketConflictTests(MappingConflictTestBase, unittest.TestCase):
def _getTargetClass(self):
from BTrees.IIBTree import IIBucket
return IIBucket
class IIBucketConflictTestsPy(MappingConflictTestBase, unittest.TestCase):
def _getTargetClass(self):
from BTrees.IIBTree import IIBucketPy
return IIBucketPy
class IITreeSetConflictTests(SetConflictTestBase, unittest.TestCase):
def _getTargetClass(self):
from BTrees.IIBTree import IITreeSet
return IITreeSet
class IITreeSetConflictTestsPy(SetConflictTestBase, unittest.TestCase):
def _getTargetClass(self):
from BTrees.IIBTree import IITreeSetPy
return IITreeSetPy
class IISetConflictTests(SetConflictTestBase, unittest.TestCase):
def _getTargetClass(self):
from BTrees.IIBTree import IISet
return IISet
class IISetConflictTestsPy(SetConflictTestBase, unittest.TestCase):
def _getTargetClass(self):
from BTrees.IIBTree import IISetPy
return IISetPy
class IIModuleTest(ModuleTest, unittest.TestCase):
prefix = 'II'
def _getModule(self):
import BTrees
return BTrees.IIBTree
def _getInterface(self):
import BTrees.Interfaces
return BTrees.Interfaces.IIntegerIntegerBTreeModule
def test_suite():
return unittest.TestSuite((
unittest.makeSuite(IIBTreeInternalKeyTest),
unittest.makeSuite(IIBTreePyInternalKeyTest),
unittest.makeSuite(IITreeSetInternalKeyTest),
unittest.makeSuite(IITreeSetPyInternalKeyTest),
unittest.makeSuite(IIBucketTest),
unittest.makeSuite(IIBucketPyTest),
unittest.makeSuite(IITreeSetTest),
unittest.makeSuite(IITreeSetPyTest),
unittest.makeSuite(IISetTest),
unittest.makeSuite(IISetPyTest),
unittest.makeSuite(IIBTreeTest),
unittest.makeSuite(IIBTreeTestPy),
unittest.makeSuite(TestIIBTrees),
unittest.makeSuite(TestIIBTreesPy),
unittest.makeSuite(TestIISets),
unittest.makeSuite(TestIISetsPy),
unittest.makeSuite(TestIITreeSets),
unittest.makeSuite(TestIITreeSetsPy),
unittest.makeSuite(TestIIMultiUnion),
unittest.makeSuite(TestIIMultiUnionPy),
unittest.makeSuite(PureII),
unittest.makeSuite(PureIIPy),
unittest.makeSuite(TestWeightedII),
unittest.makeSuite(TestWeightedIIPy),
unittest.makeSuite(IIBTreeConflictTests),
unittest.makeSuite(IIBTreeConflictTestsPy),
unittest.makeSuite(IIBucketConflictTests),
unittest.makeSuite(IIBucketConflictTestsPy),
unittest.makeSuite(IITreeSetConflictTests),
unittest.makeSuite(IITreeSetConflictTestsPy),
unittest.makeSuite(IISetConflictTests),
unittest.makeSuite(IISetConflictTestsPy),
unittest.makeSuite(IIModuleTest),
))
@@ -0,0 +1,414 @@
##############################################################################
#
# Copyright (c) 2001-2012 Zope Foundation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
import unittest
from .common import BTreeTests
from .common import ExtendedSetTests
from .common import I_SetsBase
from .common import InternalKeysMappingTest
from .common import InternalKeysSetTest
from .common import MappingBase
from .common import MappingConflictTestBase
from .common import ModuleTest
from .common import MultiUnion
from .common import NormalSetTests
from .common import SetConflictTestBase
from .common import SetResult
from .common import TypeTest
from .common import TestLongIntKeys
from .common import makeBuilder
from BTrees.IIBTree import using64bits #XXX Ugly, but unavoidable
class IOBTreeInternalKeyTest(InternalKeysMappingTest, unittest.TestCase):
def _getTargetClass(self):
from BTrees.IOBTree import IOBTree
return IOBTree
class IOBTreePyInternalKeyTest(InternalKeysMappingTest, unittest.TestCase):
def _getTargetClass(self):
from BTrees.IOBTree import IOBTreePy
return IOBTreePy
class IOTreeSetInternalKeyTest(InternalKeysSetTest, unittest.TestCase):
def _getTargetClass(self):
from BTrees.IOBTree import IOTreeSet
return IOTreeSet
class IOTreeSetPyInternalKeyTest(InternalKeysSetTest, unittest.TestCase):
def _getTargetClass(self):
from BTrees.IOBTree import IOTreeSetPy
return IOTreeSetPy
class IOBucketTest(MappingBase, unittest.TestCase):
def _getTargetClass(self):
from BTrees.IOBTree import IOBucket
return IOBucket
class IOBucketPyTest(MappingBase, unittest.TestCase):
def _getTargetClass(self):
from BTrees.IOBTree import IOBucketPy
return IOBucketPy
class IOTreeSetTest(NormalSetTests, unittest.TestCase):
def _getTargetClass(self):
from BTrees.IOBTree import IOTreeSet
return IOTreeSet
class IOTreeSetPyTest(NormalSetTests, unittest.TestCase):
def _getTargetClass(self):
from BTrees.IOBTree import IOTreeSetPy
return IOTreeSetPy
class IOSetTest(ExtendedSetTests, unittest.TestCase):
def _getTargetClass(self):
from BTrees.IOBTree import IOSet
return IOSet
class IOSetPyTest(ExtendedSetTests, unittest.TestCase):
def _getTargetClass(self):
from BTrees.IOBTree import IOSetPy
return IOSetPy
class IOBTreeTest(BTreeTests, unittest.TestCase):
def _makeOne(self):
from BTrees.IOBTree import IOBTree
return IOBTree()
class IOBTreePyTest(BTreeTests, unittest.TestCase):
def _makeOne(self):
from BTrees.IOBTree import IOBTreePy
return IOBTreePy()
if using64bits:
class IOBTreeTest(BTreeTests, TestLongIntKeys, unittest.TestCase):
def _makeOne(self):
from BTrees.IOBTree import IOBTree
return IOBTree()
class IOBTreePyTest(BTreeTests, TestLongIntKeys, unittest.TestCase):
def _makeOne(self):
from BTrees.IOBTree import IOBTreePy
return IOBTreePy()
class _TestIOBTreesBase(TypeTest):
def _stringraises(self):
self._makeOne()['c'] = 1
def _floatraises(self):
self._makeOne()[2.5] = 1
def _noneraises(self):
self._makeOne()[None] = 1
def testStringAllowedInContains(self):
self.assertFalse('key' in self._makeOne())
def testStringKeyRaisesKeyErrorWhenMissing(self):
self.assertRaises(KeyError, self._makeOne().__getitem__, 'key')
def testStringKeyReturnsDefaultFromGetWhenMissing(self):
self.assertEqual(self._makeOne().get('key', 42), 42)
class TestIOBTrees(_TestIOBTreesBase, unittest.TestCase):
def _makeOne(self):
from BTrees.IOBTree import IOBTree
return IOBTree()
class TestIOBTreesPy(_TestIOBTreesBase, unittest.TestCase):
def _makeOne(self):
from BTrees.IOBTree import IOBTreePy
return IOBTreePy()
class TestIOSets(I_SetsBase, unittest.TestCase):
def _makeOne(self):
from BTrees.IOBTree import IOSet
return IOSet()
class TestIOSetsPy(I_SetsBase, unittest.TestCase):
def _makeOne(self):
from BTrees.IOBTree import IOSetPy
return IOSetPy()
class TestIOTreeSets(I_SetsBase, unittest.TestCase):
def _makeOne(self):
from BTrees.IOBTree import IOTreeSet
return IOTreeSet()
class TestIOTreeSetsPy(I_SetsBase, unittest.TestCase):
def _makeOne(self):
from BTrees.IOBTree import IOTreeSetPy
return IOTreeSetPy()
class PureIO(SetResult, unittest.TestCase):
def union(self, *args):
from BTrees.IOBTree import union
return union(*args)
def intersection(self, *args):
from BTrees.IOBTree import intersection
return intersection(*args)
def difference(self, *args):
from BTrees.IOBTree import difference
return difference(*args)
def builders(self):
from BTrees.IOBTree import IOBTree
from BTrees.IOBTree import IOBucket
from BTrees.IOBTree import IOTreeSet
from BTrees.IOBTree import IOSet
return IOSet, IOTreeSet, makeBuilder(IOBTree), makeBuilder(IOBucket)
class PureIOPy(SetResult, unittest.TestCase):
def union(self, *args):
from BTrees.IOBTree import unionPy
return unionPy(*args)
def intersection(self, *args):
from BTrees.IOBTree import intersectionPy
return intersectionPy(*args)
def difference(self, *args):
from BTrees.IOBTree import differencePy
return differencePy(*args)
def builders(self):
from BTrees.IOBTree import IOBTreePy
from BTrees.IOBTree import IOBucketPy
from BTrees.IOBTree import IOTreeSetPy
from BTrees.IOBTree import IOSetPy
return (IOSetPy, IOTreeSetPy,
makeBuilder(IOBTreePy), makeBuilder(IOBucketPy))
class TestIOMultiUnion(MultiUnion, unittest.TestCase):
def multiunion(self, *args):
from BTrees.IOBTree import multiunion
return multiunion(*args)
def union(self, *args):
from BTrees.IOBTree import union
return union(*args)
def mkset(self, *args):
from BTrees.IOBTree import IOSet as mkset
return mkset(*args)
def mktreeset(self, *args):
from BTrees.IOBTree import IOTreeSet as mktreeset
return mktreeset(*args)
def mkbucket(self, *args):
from BTrees.IOBTree import IOBucket as mkbucket
return mkbucket(*args)
def mkbtree(self, *args):
from BTrees.IOBTree import IOBTree as mkbtree
return mkbtree(*args)
class TestIOMultiUnionPy(MultiUnion, unittest.TestCase):
def multiunion(self, *args):
from BTrees.IOBTree import multiunionPy
return multiunionPy(*args)
def union(self, *args):
from BTrees.IOBTree import unionPy
return unionPy(*args)
def mkset(self, *args):
from BTrees.IOBTree import IOSetPy as mkset
return mkset(*args)
def mktreeset(self, *args):
from BTrees.IOBTree import IOTreeSetPy as mktreeset
return mktreeset(*args)
def mkbucket(self, *args):
from BTrees.IOBTree import IOBucketPy as mkbucket
return mkbucket(*args)
def mkbtree(self, *args):
from BTrees.IOBTree import IOBTreePy as mkbtree
return mkbtree(*args)
class IOBTreeConflictTests(MappingConflictTestBase, unittest.TestCase):
def _getTargetClass(self):
from BTrees.IOBTree import IOBTree
return IOBTree
class IOBTreeConflictTestsPy(MappingConflictTestBase, unittest.TestCase):
def _getTargetClass(self):
from BTrees.IOBTree import IOBTreePy
return IOBTreePy
class IOBucketConflictTests(MappingConflictTestBase, unittest.TestCase):
def _getTargetClass(self):
from BTrees.IOBTree import IOBucket
return IOBucket
class IOBucketConflictTestsPy(MappingConflictTestBase, unittest.TestCase):
def _getTargetClass(self):
from BTrees.IOBTree import IOBucketPy
return IOBucketPy
class IOTreeSetConflictTests(SetConflictTestBase, unittest.TestCase):
def _getTargetClass(self):
from BTrees.IOBTree import IOTreeSet
return IOTreeSet
class IOTreeSetConflictTestsPy(SetConflictTestBase, unittest.TestCase):
def _getTargetClass(self):
from BTrees.IOBTree import IOTreeSetPy
return IOTreeSetPy
class IOSetConflictTests(SetConflictTestBase, unittest.TestCase):
def _getTargetClass(self):
from BTrees.IOBTree import IOSet
return IOSet
class IOSetConflictTestsPy(SetConflictTestBase, unittest.TestCase):
def _getTargetClass(self):
from BTrees.IOBTree import IOSetPy
return IOSetPy
class IOModuleTest(ModuleTest, unittest.TestCase):
prefix = 'IO'
def _getModule(self):
import BTrees
return BTrees.IOBTree
def _getInterface(self):
import BTrees.Interfaces
return BTrees.Interfaces.IIntegerObjectBTreeModule
def test_weightedUnion_not_present(self):
try:
from BTrees.IOBTree import weightedUnion
except ImportError:
pass
else:
self.fail("IOBTree shouldn't have weightedUnion")
def test_weightedIntersection_not_present(self):
try:
from BTrees.IOBTree import weightedIntersection
except ImportError:
pass
else:
self.fail("IOBTree shouldn't have weightedIntersection")
def test_suite():
return unittest.TestSuite((
unittest.makeSuite(IOBTreeInternalKeyTest),
unittest.makeSuite(IOBTreePyInternalKeyTest),
unittest.makeSuite(IOTreeSetInternalKeyTest),
unittest.makeSuite(IOTreeSetPyInternalKeyTest),
unittest.makeSuite(IOBucketTest),
unittest.makeSuite(IOBucketPyTest),
unittest.makeSuite(IOTreeSetTest),
unittest.makeSuite(IOTreeSetPyTest),
unittest.makeSuite(IOSetTest),
unittest.makeSuite(IOSetPyTest),
unittest.makeSuite(IOBTreeTest),
unittest.makeSuite(IOBTreePyTest),
unittest.makeSuite(TestIOBTrees),
unittest.makeSuite(TestIOBTreesPy),
unittest.makeSuite(TestIOSets),
unittest.makeSuite(TestIOSetsPy),
unittest.makeSuite(TestIOTreeSets),
unittest.makeSuite(TestIOTreeSetsPy),
unittest.makeSuite(TestIOMultiUnion),
unittest.makeSuite(TestIOMultiUnionPy),
unittest.makeSuite(PureIO),
unittest.makeSuite(PureIOPy),
unittest.makeSuite(IOBTreeConflictTests),
unittest.makeSuite(IOBTreeConflictTestsPy),
unittest.makeSuite(IOBucketConflictTests),
unittest.makeSuite(IOBucketConflictTestsPy),
unittest.makeSuite(IOTreeSetConflictTests),
unittest.makeSuite(IOTreeSetConflictTestsPy),
unittest.makeSuite(IOSetConflictTests),
unittest.makeSuite(IOSetConflictTestsPy),
unittest.makeSuite(IOModuleTest),
))
@@ -0,0 +1,316 @@
##############################################################################
#
# Copyright (c) 2001-2012 Zope Foundation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
import unittest
from .common import BTreeTests
from .common import ExtendedSetTests
from .common import InternalKeysMappingTest
from .common import InternalKeysSetTest
from .common import MappingBase
from .common import MappingConflictTestBase
from .common import ModuleTest
from .common import MultiUnion
from .common import NormalSetTests
from .common import SetConflictTestBase
from .common import SetResult
from .common import TestLongIntKeys
from .common import makeBuilder
class LFBTreeInternalKeyTest(InternalKeysMappingTest, unittest.TestCase):
def _getTargetClass(self):
from BTrees.LFBTree import LFBTree
return LFBTree
class LFBTreePyInternalKeyTest(InternalKeysMappingTest, unittest.TestCase):
def _getTargetClass(self):
from BTrees.LFBTree import LFBTreePy
return LFBTreePy
class LFTreeSetInternalKeyTest(InternalKeysSetTest, unittest.TestCase):
def _getTargetClass(self):
from BTrees.LFBTree import LFTreeSet
return LFTreeSet
class LFTreeSetPyInternalKeyTest(InternalKeysSetTest, unittest.TestCase):
def _getTargetClass(self):
from BTrees.LFBTree import LFTreeSetPy
return LFTreeSetPy
class LFBucketTest(MappingBase, unittest.TestCase):
def _getTargetClass(self):
from BTrees.LFBTree import LFBucket
return LFBucket
class LFBucketPyTest(MappingBase, unittest.TestCase):
def _getTargetClass(self):
from BTrees.LFBTree import LFBucketPy
return LFBucketPy
class LFTreeSetTest(NormalSetTests, unittest.TestCase):
def _getTargetClass(self):
from BTrees.LFBTree import LFTreeSet
return LFTreeSet
class LFTreeSetPyTest(NormalSetTests, unittest.TestCase):
def _getTargetClass(self):
from BTrees.LFBTree import LFTreeSetPy
return LFTreeSetPy
class LFSetTest(ExtendedSetTests, unittest.TestCase):
def _getTargetClass(self):
from BTrees.LFBTree import LFSet
return LFSet
class LFSetPyTest(ExtendedSetTests, unittest.TestCase):
def _getTargetClass(self):
from BTrees.LFBTree import LFSetPy
return LFSetPy
class LFBTreeTest(BTreeTests, TestLongIntKeys, unittest.TestCase):
def _makeOne(self):
from BTrees.LFBTree import LFBTree
return LFBTree()
def getTwoValues(self):
return 0.5, 1.5
class LFBTreePyTest(BTreeTests, TestLongIntKeys, unittest.TestCase):
def _makeOne(self):
from BTrees.LFBTree import LFBTreePy
return LFBTreePy()
def getTwoValues(self):
return 0.5, 1.5
class TestLFMultiUnion(MultiUnion, unittest.TestCase):
def multiunion(self, *args):
from BTrees.LFBTree import multiunionPy
return multiunionPy(*args)
def union(self, *args):
from BTrees.LFBTree import unionPy
return unionPy(*args)
def mkset(self, *args):
from BTrees.LFBTree import LFSetPy as mkset
return mkset(*args)
def mktreeset(self, *args):
from BTrees.LFBTree import LFTreeSetPy as mktreeset
return mktreeset(*args)
def mkbucket(self, *args):
from BTrees.LFBTree import LFBucketPy as mkbucket
return mkbucket(*args)
def mkbtree(self, *args):
from BTrees.LFBTree import LFBTreePy as mkbtree
return mkbtree(*args)
class TestLFMultiUnionPy(MultiUnion, unittest.TestCase):
def multiunion(self, *args):
from BTrees.LFBTree import multiunionPy
return multiunionPy(*args)
def union(self, *args):
from BTrees.LFBTree import unionPy
return unionPy(*args)
def mkset(self, *args):
from BTrees.LFBTree import LFSetPy as mkset
return mkset(*args)
def mktreeset(self, *args):
from BTrees.LFBTree import LFTreeSetPy as mktreeset
return mktreeset(*args)
def mkbucket(self, *args):
from BTrees.LFBTree import LFBucketPy as mkbucket
return mkbucket(*args)
def mkbtree(self, *args):
from BTrees.LFBTree import LFBTreePy as mkbtree
return mkbtree(*args)
class PureLF(SetResult, unittest.TestCase):
def union(self, *args):
from BTrees.LFBTree import union
return union(*args)
def intersection(self, *args):
from BTrees.LFBTree import intersection
return intersection(*args)
def difference(self, *args):
from BTrees.LFBTree import difference
return difference(*args)
def builders(self):
from BTrees.LFBTree import LFBTree
from BTrees.LFBTree import LFBucket
from BTrees.LFBTree import LFTreeSet
from BTrees.LFBTree import LFSet
return LFSet, LFTreeSet, makeBuilder(LFBTree), makeBuilder(LFBucket)
class PureLFPy(SetResult, unittest.TestCase):
def union(self, *args):
from BTrees.LFBTree import unionPy
return unionPy(*args)
def intersection(self, *args):
from BTrees.LFBTree import intersectionPy
return intersectionPy(*args)
def difference(self, *args):
from BTrees.LFBTree import differencePy
return differencePy(*args)
def builders(self):
from BTrees.LFBTree import LFBTreePy
from BTrees.LFBTree import LFBucketPy
from BTrees.LFBTree import LFTreeSetPy
from BTrees.LFBTree import LFSetPy
return (LFSetPy, LFTreeSetPy,
makeBuilder(LFBTreePy), makeBuilder(LFBucketPy))
class LFBTreeConflictTests(MappingConflictTestBase, unittest.TestCase):
def _getTargetClass(self):
from BTrees.LFBTree import LFBTree
return LFBTree
class LFBTreeConflictTestsPy(MappingConflictTestBase, unittest.TestCase):
def _getTargetClass(self):
from BTrees.LFBTree import LFBTreePy
return LFBTreePy
class LFBucketConflictTests(MappingConflictTestBase, unittest.TestCase):
def _getTargetClass(self):
from BTrees.LFBTree import LFBucket
return LFBucket
class LFBucketConflictTestsPy(MappingConflictTestBase, unittest.TestCase):
def _getTargetClass(self):
from BTrees.LFBTree import LFBucketPy
return LFBucketPy
class LFTreeSetConflictTests(SetConflictTestBase, unittest.TestCase):
def _getTargetClass(self):
from BTrees.LFBTree import LFTreeSet
return LFTreeSet
class LFTreeSetConflictTestsPy(SetConflictTestBase, unittest.TestCase):
def _getTargetClass(self):
from BTrees.LFBTree import LFTreeSetPy
return LFTreeSetPy
class LFSetConflictTests(SetConflictTestBase, unittest.TestCase):
def _getTargetClass(self):
from BTrees.LFBTree import LFSet
return LFSet
class LFSetConflictTestsPy(SetConflictTestBase, unittest.TestCase):
def _getTargetClass(self):
from BTrees.LFBTree import LFSetPy
return LFSetPy
class LFModuleTest(ModuleTest, unittest.TestCase):
prefix = 'LF'
def _getModule(self):
import BTrees
return BTrees.LFBTree
def _getInterface(self):
import BTrees.Interfaces
return BTrees.Interfaces.IIntegerFloatBTreeModule
def test_suite():
return unittest.TestSuite((
unittest.makeSuite(LFBTreeInternalKeyTest),
unittest.makeSuite(LFBTreePyInternalKeyTest),
unittest.makeSuite(LFTreeSetInternalKeyTest),
unittest.makeSuite(LFTreeSetPyInternalKeyTest),
unittest.makeSuite(LFBucketTest),
unittest.makeSuite(LFBucketPyTest),
unittest.makeSuite(LFTreeSetTest),
unittest.makeSuite(LFTreeSetPyTest),
unittest.makeSuite(LFSetTest),
unittest.makeSuite(LFSetPyTest),
unittest.makeSuite(LFBTreeTest),
unittest.makeSuite(LFBTreePyTest),
unittest.makeSuite(TestLFMultiUnion),
unittest.makeSuite(TestLFMultiUnionPy),
unittest.makeSuite(PureLF),
unittest.makeSuite(PureLFPy),
unittest.makeSuite(LFBTreeConflictTests),
unittest.makeSuite(LFBTreeConflictTestsPy),
unittest.makeSuite(LFBucketConflictTests),
unittest.makeSuite(LFBucketConflictTestsPy),
unittest.makeSuite(LFTreeSetConflictTests),
unittest.makeSuite(LFTreeSetConflictTestsPy),
unittest.makeSuite(LFSetConflictTests),
unittest.makeSuite(LFSetConflictTestsPy),
unittest.makeSuite(LFModuleTest),
))
@@ -0,0 +1,415 @@
##############################################################################
#
# Copyright (c) 2001-2012 Zope Foundation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
import unittest
from .common import BTreeTests
from .common import ExtendedSetTests
from .common import I_SetsBase
from .common import InternalKeysMappingTest
from .common import InternalKeysSetTest
from .common import MappingBase
from .common import MappingConflictTestBase
from .common import ModuleTest
from .common import MultiUnion
from .common import NormalSetTests
from .common import SetConflictTestBase
from .common import SetResult
from .common import TestLongIntKeys
from .common import TestLongIntValues
from .common import Weighted
from .common import itemsToSet
from .common import makeBuilder
class LLBTreeInternalKeyTest(InternalKeysMappingTest, unittest.TestCase):
def _getTargetClass(self):
from BTrees.LLBTree import LLBTree
return LLBTree
class LLBTreePyInternalKeyTest(InternalKeysMappingTest, unittest.TestCase):
def _getTargetClass(self):
from BTrees.LLBTree import LLBTreePy
return LLBTreePy
class LLTreeSetInternalKeyTest(InternalKeysSetTest, unittest.TestCase):
def _getTargetClass(self):
from BTrees.LLBTree import LLTreeSet
return LLTreeSet
class LLTreeSetPyInternalKeyTest(InternalKeysSetTest, unittest.TestCase):
def _getTargetClass(self):
from BTrees.LLBTree import LLTreeSetPy
return LLTreeSetPy
class LLBucketTest(MappingBase, unittest.TestCase):
def _getTargetClass(self):
from BTrees.LLBTree import LLBucket
return LLBucket
class LLBucketTestPy(MappingBase, unittest.TestCase):
def _getTargetClass(self):
from BTrees.LLBTree import LLBucketPy
return LLBucketPy
class LLTreeSetTest(NormalSetTests, unittest.TestCase):
def _getTargetClass(self):
from BTrees.LLBTree import LLTreeSet
return LLTreeSet
class LLTreeSetTestPy(NormalSetTests, unittest.TestCase):
def _getTargetClass(self):
from BTrees.LLBTree import LLTreeSetPy
return LLTreeSetPy
class LLSetTest(ExtendedSetTests, unittest.TestCase):
def _getTargetClass(self):
from BTrees.LLBTree import LLSet
return LLSet
class LLSetTestPy(ExtendedSetTests, unittest.TestCase):
def _getTargetClass(self):
from BTrees.LLBTree import LLSetPy
return LLSetPy
class LLBTreeTest(BTreeTests, TestLongIntKeys, TestLongIntValues,
unittest.TestCase):
def _makeOne(self):
from BTrees.LLBTree import LLBTree
return LLBTree()
def getTwoValues(self):
return 1, 2
class LLBTreeTestPy(BTreeTests, TestLongIntKeys, TestLongIntValues,
unittest.TestCase):
def _makeOne(self):
from BTrees.LLBTree import LLBTreePy
return LLBTreePy()
def getTwoValues(self):
return 1, 2
class TestLLSets(I_SetsBase, unittest.TestCase):
def _makeOne(self):
from BTrees.LLBTree import LLSet
return LLSet()
class TestLLSetsPy(I_SetsBase, unittest.TestCase):
def _makeOne(self):
from BTrees.LLBTree import LLSetPy
return LLSetPy()
class TestLLTreeSets(I_SetsBase, unittest.TestCase):
def _makeOne(self):
from BTrees.LLBTree import LLTreeSet
return LLTreeSet()
class TestLLTreeSetsPy(I_SetsBase, unittest.TestCase):
def _makeOne(self):
from BTrees.LLBTree import LLTreeSetPy
return LLTreeSetPy()
class PureLL(SetResult, unittest.TestCase):
def union(self, *args):
from BTrees.LLBTree import union
return union(*args)
def intersection(self, *args):
from BTrees.LLBTree import intersection
return intersection(*args)
def difference(self, *args):
from BTrees.LLBTree import difference
return difference(*args)
def builders(self):
from BTrees.LLBTree import LLBTree
from BTrees.LLBTree import LLBucket
from BTrees.LLBTree import LLTreeSet
from BTrees.LLBTree import LLSet
return LLSet, LLTreeSet, makeBuilder(LLBTree), makeBuilder(LLBucket)
class PureLLPy(SetResult, unittest.TestCase):
def union(self, *args):
from BTrees.LLBTree import unionPy
return unionPy(*args)
def intersection(self, *args):
from BTrees.LLBTree import intersectionPy
return intersectionPy(*args)
def difference(self, *args):
from BTrees.LLBTree import differencePy
return differencePy(*args)
def builders(self):
from BTrees.LLBTree import LLBTreePy
from BTrees.LLBTree import LLBucketPy
from BTrees.LLBTree import LLTreeSetPy
from BTrees.LLBTree import LLSetPy
return (LLSetPy, LLTreeSetPy,
makeBuilder(LLBTreePy), makeBuilder(LLBucketPy))
class TestLLMultiUnion(MultiUnion, unittest.TestCase):
def multiunion(self, *args):
from BTrees.LLBTree import multiunion
return multiunion(*args)
def union(self, *args):
from BTrees.LLBTree import union
return union(*args)
def mkset(self, *args):
from BTrees.LLBTree import LLSet as mkset
return mkset(*args)
def mktreeset(self, *args):
from BTrees.LLBTree import LLTreeSet as mktreeset
return mktreeset(*args)
def mkbucket(self, *args):
from BTrees.LLBTree import LLBucket as mkbucket
return mkbucket(*args)
def mkbtree(self, *args):
from BTrees.LLBTree import LLBTree as mkbtree
return mkbtree(*args)
class TestLLMultiUnionPy(MultiUnion, unittest.TestCase):
def multiunion(self, *args):
from BTrees.LLBTree import multiunionPy
return multiunionPy(*args)
def union(self, *args):
from BTrees.LLBTree import unionPy
return unionPy(*args)
def mkset(self, *args):
from BTrees.LLBTree import LLSetPy as mkset
return mkset(*args)
def mktreeset(self, *args):
from BTrees.LLBTree import LLTreeSetPy as mktreeset
return mktreeset(*args)
def mkbucket(self, *args):
from BTrees.LLBTree import LLBucketPy as mkbucket
return mkbucket(*args)
def mkbtree(self, *args):
from BTrees.LLBTree import LLBTreePy as mkbtree
return mkbtree(*args)
class TestWeightedLL(Weighted, unittest.TestCase):
def weightedUnion(self):
from BTrees.LLBTree import weightedUnion
return weightedUnion
def weightedIntersection(self):
from BTrees.LLBTree import weightedIntersection
return weightedIntersection
def union(self):
from BTrees.LLBTree import union
return union
def intersection(self):
from BTrees.LLBTree import intersection
return intersection
def mkbucket(self, *args):
from BTrees.LLBTree import LLBucket as mkbucket
return mkbucket(*args)
def builders(self):
from BTrees.LLBTree import LLBTree
from BTrees.LLBTree import LLBucket
from BTrees.LLBTree import LLTreeSet
from BTrees.LLBTree import LLSet
return LLBucket, LLBTree, itemsToSet(LLSet), itemsToSet(LLTreeSet)
class TestWeightedLLPy(Weighted, unittest.TestCase):
def weightedUnion(self):
from BTrees.LLBTree import weightedUnionPy
return weightedUnionPy
def weightedIntersection(self):
from BTrees.LLBTree import weightedIntersectionPy
return weightedIntersectionPy
def union(self):
from BTrees.LLBTree import unionPy
return unionPy
def intersection(self):
from BTrees.LLBTree import intersectionPy
return intersectionPy
def mkbucket(self, *args):
from BTrees.LLBTree import LLBucketPy as mkbucket
return mkbucket(*args)
def builders(self):
from BTrees.LLBTree import LLBTreePy
from BTrees.LLBTree import LLBucketPy
from BTrees.LLBTree import LLTreeSetPy
from BTrees.LLBTree import LLSetPy
return (LLBucketPy, LLBTreePy,
itemsToSet(LLSetPy), itemsToSet(LLTreeSetPy))
class LLBTreeConflictTests(MappingConflictTestBase, unittest.TestCase):
def _getTargetClass(self):
from BTrees.LLBTree import LLBTree
return LLBTree
class LLBTreeConflictTestsPy(MappingConflictTestBase, unittest.TestCase):
def _getTargetClass(self):
from BTrees.LLBTree import LLBTreePy
return LLBTreePy
class LLBucketConflictTests(MappingConflictTestBase, unittest.TestCase):
def _getTargetClass(self):
from BTrees.LLBTree import LLBucket
return LLBucket
class LLBucketConflictTestsPy(MappingConflictTestBase, unittest.TestCase):
def _getTargetClass(self):
from BTrees.LLBTree import LLBucketPy
return LLBucketPy
class LLTreeSetConflictTests(SetConflictTestBase, unittest.TestCase):
def _getTargetClass(self):
from BTrees.LLBTree import LLTreeSet
return LLTreeSet
class LLTreeSetConflictTestsPy(SetConflictTestBase, unittest.TestCase):
def _getTargetClass(self):
from BTrees.LLBTree import LLTreeSetPy
return LLTreeSetPy
class LLSetConflictTests(SetConflictTestBase, unittest.TestCase):
def _getTargetClass(self):
from BTrees.LLBTree import LLSet
return LLSet
class LLSetConflictTestsPy(SetConflictTestBase, unittest.TestCase):
def _getTargetClass(self):
from BTrees.LLBTree import LLSetPy
return LLSetPy
class LLModuleTest(ModuleTest, unittest.TestCase):
prefix = 'LL'
def _getModule(self):
import BTrees
return BTrees.LLBTree
def _getInterface(self):
import BTrees.Interfaces
return BTrees.Interfaces.IIntegerIntegerBTreeModule
def test_suite():
return unittest.TestSuite((
unittest.makeSuite(LLBTreeInternalKeyTest),
unittest.makeSuite(LLBTreeInternalKeyTest),
unittest.makeSuite(LLTreeSetInternalKeyTest),
unittest.makeSuite(LLTreeSetInternalKeyTest),
unittest.makeSuite(LLBucketTest),
unittest.makeSuite(LLBucketTest),
unittest.makeSuite(LLTreeSetTest),
unittest.makeSuite(LLTreeSetTest),
unittest.makeSuite(LLSetTest),
unittest.makeSuite(LLSetTest),
unittest.makeSuite(LLBTreeTest),
unittest.makeSuite(LLBTreeTest),
unittest.makeSuite(TestLLSets),
unittest.makeSuite(TestLLSets),
unittest.makeSuite(TestLLTreeSets),
unittest.makeSuite(TestLLTreeSets),
unittest.makeSuite(TestLLMultiUnion),
unittest.makeSuite(TestLLMultiUnion),
unittest.makeSuite(PureLL),
unittest.makeSuite(PureLL),
unittest.makeSuite(TestWeightedLL),
unittest.makeSuite(TestWeightedLL),
unittest.makeSuite(LLBTreeConflictTests),
unittest.makeSuite(LLBTreeConflictTests),
unittest.makeSuite(LLBucketConflictTests),
unittest.makeSuite(LLBucketConflictTests),
unittest.makeSuite(LLTreeSetConflictTests),
unittest.makeSuite(LLTreeSetConflictTests),
unittest.makeSuite(LLSetConflictTests),
unittest.makeSuite(LLSetConflictTests),
unittest.makeSuite(LLModuleTest),
))
@@ -0,0 +1,359 @@
##############################################################################
#
# Copyright (c) 2001-2012 Zope Foundation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
import unittest
from .common import BTreeTests
from .common import ExtendedSetTests
from .common import I_SetsBase
from .common import InternalKeysMappingTest
from .common import InternalKeysSetTest
from .common import MappingBase
from .common import MappingConflictTestBase
from .common import ModuleTest
from .common import MultiUnion
from .common import NormalSetTests
from .common import SetConflictTestBase
from .common import SetResult
from .common import TestLongIntKeys
from .common import makeBuilder
class LOBTreeInternalKeyTest(InternalKeysMappingTest, unittest.TestCase):
def _getTargetClass(self):
from BTrees.LOBTree import LOBTree
return LOBTree
class LOBTreePyInternalKeyTest(InternalKeysMappingTest, unittest.TestCase):
def _getTargetClass(self):
from BTrees.LOBTree import LOBTreePy
return LOBTreePy
class LOTreeSetInternalKeyTest(InternalKeysSetTest, unittest.TestCase):
def _getTargetClass(self):
from BTrees.LOBTree import LOTreeSet
return LOTreeSet
class LOTreeSetPyInternalKeyTest(InternalKeysSetTest, unittest.TestCase):
def _getTargetClass(self):
from BTrees.LOBTree import LOTreeSetPy
return LOTreeSetPy
class LOBucketTest(MappingBase, unittest.TestCase):
def _getTargetClass(self):
from BTrees.LOBTree import LOBucket
return LOBucket
class LOBucketPyTest(MappingBase, unittest.TestCase):
def _getTargetClass(self):
from BTrees.LOBTree import LOBucketPy
return LOBucketPy
class LOTreeSetTest(NormalSetTests, unittest.TestCase):
def _getTargetClass(self):
from BTrees.LOBTree import LOTreeSet
return LOTreeSet
class LOTreeSetPyTest(NormalSetTests, unittest.TestCase):
def _getTargetClass(self):
from BTrees.LOBTree import LOTreeSetPy
return LOTreeSetPy
class LOSetTest(ExtendedSetTests, unittest.TestCase):
def _getTargetClass(self):
from BTrees.LOBTree import LOSet
return LOSet
class LOSetPyTest(ExtendedSetTests, unittest.TestCase):
def _getTargetClass(self):
from BTrees.LOBTree import LOSetPy
return LOSetPy
class LOBTreeTest(BTreeTests, TestLongIntKeys, unittest.TestCase):
def _makeOne(self):
from BTrees.LOBTree import LOBTree
return LOBTree()
class LOBTreePyTest(BTreeTests, TestLongIntKeys, unittest.TestCase):
def _makeOne(self):
from BTrees.LOBTree import LOBTreePy
return LOBTreePy()
class TestLOSets(I_SetsBase, unittest.TestCase):
def _makeOne(self):
from BTrees.LOBTree import LOSet
return LOSet()
class TestLOSetsPy(I_SetsBase, unittest.TestCase):
def _makeOne(self):
from BTrees.LOBTree import LOSetPy
return LOSetPy()
class TestLOTreeSets(I_SetsBase, unittest.TestCase):
def _makeOne(self):
from BTrees.LOBTree import LOTreeSet
return LOTreeSet()
class TestLOTreeSetsPy(I_SetsBase, unittest.TestCase):
def _makeOne(self):
from BTrees.LOBTree import LOTreeSetPy
return LOTreeSetPy()
class TestLOMultiUnion(MultiUnion, unittest.TestCase):
def multiunion(self, *args):
from BTrees.LOBTree import multiunion
return multiunion(*args)
def union(self, *args):
from BTrees.LOBTree import union
return union(*args)
def mkset(self, *args):
from BTrees.LOBTree import LOSet as mkset
return mkset(*args)
def mktreeset(self, *args):
from BTrees.LOBTree import LOTreeSet as mktreeset
return mktreeset(*args)
def mkbucket(self, *args):
from BTrees.LOBTree import LOBucket as mkbucket
return mkbucket(*args)
def mkbtree(self, *args):
from BTrees.LOBTree import LOBTree as mkbtree
return mkbtree(*args)
class TestLOMultiUnionPy(MultiUnion, unittest.TestCase):
def multiunion(self, *args):
from BTrees.LOBTree import multiunionPy
return multiunionPy(*args)
def union(self, *args):
from BTrees.LOBTree import unionPy
return unionPy(*args)
def mkset(self, *args):
from BTrees.LOBTree import LOSetPy as mkset
return mkset(*args)
def mktreeset(self, *args):
from BTrees.LOBTree import LOTreeSetPy as mktreeset
return mktreeset(*args)
def mkbucket(self, *args):
from BTrees.LOBTree import LOBucketPy as mkbucket
return mkbucket(*args)
def mkbtree(self, *args):
from BTrees.LOBTree import LOBTreePy as mkbtree
return mkbtree(*args)
class PureLO(SetResult, unittest.TestCase):
def union(self, *args):
from BTrees.LOBTree import union
return union(*args)
def intersection(self, *args):
from BTrees.LOBTree import intersection
return intersection(*args)
def difference(self, *args):
from BTrees.LOBTree import difference
return difference(*args)
def builders(self):
from BTrees.LOBTree import LOBTree
from BTrees.LOBTree import LOBucket
from BTrees.LOBTree import LOTreeSet
from BTrees.LOBTree import LOSet
return LOSet, LOTreeSet, makeBuilder(LOBTree), makeBuilder(LOBucket)
class PureLOPy(SetResult, unittest.TestCase):
def union(self, *args):
from BTrees.LOBTree import unionPy
return unionPy(*args)
def intersection(self, *args):
from BTrees.LOBTree import intersectionPy
return intersectionPy(*args)
def difference(self, *args):
from BTrees.LOBTree import differencePy
return differencePy(*args)
def builders(self):
from BTrees.LOBTree import LOBTreePy
from BTrees.LOBTree import LOBucketPy
from BTrees.LOBTree import LOTreeSetPy
from BTrees.LOBTree import LOSetPy
return (LOSetPy, LOTreeSetPy,
makeBuilder(LOBTreePy), makeBuilder(LOBucketPy))
class LOBTreeConflictTests(MappingConflictTestBase, unittest.TestCase):
def _getTargetClass(self):
from BTrees.LOBTree import LOBTree
return LOBTree
class LOBTreeConflictTestsPy(MappingConflictTestBase, unittest.TestCase):
def _getTargetClass(self):
from BTrees.LOBTree import LOBTreePy
return LOBTreePy
class LOBucketConflictTests(MappingConflictTestBase, unittest.TestCase):
def _getTargetClass(self):
from BTrees.LOBTree import LOBucket
return LOBucket
class LOBucketConflictTestsPy(MappingConflictTestBase, unittest.TestCase):
def _getTargetClass(self):
from BTrees.LOBTree import LOBucketPy
return LOBucketPy
class LOTreeSetConflictTests(SetConflictTestBase, unittest.TestCase):
def _getTargetClass(self):
from BTrees.LOBTree import LOTreeSet
return LOTreeSet
class LOTreeSetConflictTestsPy(SetConflictTestBase, unittest.TestCase):
def _getTargetClass(self):
from BTrees.LOBTree import LOTreeSetPy
return LOTreeSetPy
class LOSetConflictTests(SetConflictTestBase, unittest.TestCase):
def _getTargetClass(self):
from BTrees.LOBTree import LOSet
return LOSet
class LOSetConflictTestsPy(SetConflictTestBase, unittest.TestCase):
def _getTargetClass(self):
from BTrees.LOBTree import LOSetPy
return LOSetPy
class LOModuleTest(ModuleTest, unittest.TestCase):
prefix = 'LO'
def _getModule(self):
import BTrees
return BTrees.LOBTree
def _getInterface(self):
import BTrees.Interfaces
return BTrees.Interfaces.IIntegerObjectBTreeModule
def test_weightedUnion_not_present(self):
try:
from BTrees.LOBTree import weightedUnion
except ImportError:
pass
else:
self.fail("LOBTree shouldn't have weightedUnion")
def test_weightedIntersection_not_present(self):
try:
from BTrees.LOBTree import weightedIntersection
except ImportError:
pass
else:
self.fail("LOBTree shouldn't have weightedIntersection")
def test_suite():
return unittest.TestSuite((
unittest.makeSuite(LOBTreeInternalKeyTest),
unittest.makeSuite(LOBTreePyInternalKeyTest),
unittest.makeSuite(LOTreeSetInternalKeyTest),
unittest.makeSuite(LOTreeSetPyInternalKeyTest),
unittest.makeSuite(LOBucketTest),
unittest.makeSuite(LOBucketPyTest),
unittest.makeSuite(LOTreeSetTest),
unittest.makeSuite(LOTreeSetPyTest),
unittest.makeSuite(LOSetTest),
unittest.makeSuite(LOSetPyTest),
unittest.makeSuite(LOBTreeTest),
unittest.makeSuite(LOBTreePyTest),
unittest.makeSuite(TestLOSets),
unittest.makeSuite(TestLOSetsPy),
unittest.makeSuite(TestLOTreeSets),
unittest.makeSuite(TestLOTreeSetsPy),
unittest.makeSuite(TestLOMultiUnion),
unittest.makeSuite(TestLOMultiUnionPy),
unittest.makeSuite(PureLO),
unittest.makeSuite(PureLOPy),
unittest.makeSuite(LOBTreeConflictTests),
unittest.makeSuite(LOBTreeConflictTestsPy),
unittest.makeSuite(LOBucketConflictTests),
unittest.makeSuite(LOBucketConflictTestsPy),
unittest.makeSuite(LOTreeSetConflictTests),
unittest.makeSuite(LOTreeSetConflictTestsPy),
unittest.makeSuite(LOSetConflictTests),
unittest.makeSuite(LOSetConflictTestsPy),
unittest.makeSuite(LOModuleTest),
))
@@ -0,0 +1,110 @@
##############################################################################
#
# Copyright (c) 2008 Zope Foundation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
import unittest
_marker = object()
class LengthTestCase(unittest.TestCase):
def _getTargetClass(self):
from BTrees.Length import Length
return Length
def _makeOne(self, value=_marker):
if value is _marker:
return self._getTargetClass()()
return self._getTargetClass()(value)
def test_ctor_default(self):
length = self._makeOne()
self.assertEqual(length.value, 0)
def test_ctor_explict(self):
length = self._makeOne(42)
self.assertEqual(length.value, 42)
def test___getstate___(self):
length = self._makeOne(42)
self.assertEqual(length.__getstate__(), 42)
def test___setstate__(self):
length = self._makeOne()
length.__setstate__(42)
self.assertEqual(length.value, 42)
def test_set(self):
length = self._makeOne()
length.set(42)
self.assertEqual(length.value, 42)
def test__p_resolveConflict(self):
length = self._makeOne()
self.assertEqual(length._p_resolveConflict(5, 7, 9), 11)
def test_change_w_positive_delta(self):
length = self._makeOne()
length.change(3)
self.assertEqual(length.value, 3)
def test_change_w_negative_delta(self):
length = self._makeOne()
length.change(-3)
self.assertEqual(length.value, -3)
def test_change_overflows_to_long(self):
import sys
try:
length = self._makeOne(sys.maxint)
except AttributeError: #pragma NO COVER Py3k
return
else: #pragma NO COVER Py2
self.assertEqual(length(), sys.maxint)
self.assertTrue(type(length()) is int)
length.change(+1)
self.assertEqual(length(), sys.maxint + 1)
self.assertTrue(type(length()) is long)
def test_change_underflows_to_long(self):
import sys
try:
minint = (-sys.maxint) - 1
except AttributeError: #pragma NO COVER Py3k
return
else: #pragma NO COVER Py2
length = self._makeOne(minint)
self.assertEqual(length(), minint)
self.assertTrue(type(length()) is int)
length.change(-1)
self.assertEqual(length(), minint - 1)
self.assertTrue(type(length()) is long)
def test___call___no_args(self):
length = self._makeOne(42)
self.assertEqual(length(), 42)
def test___call___w_args(self):
length = self._makeOne(42)
self.assertEqual(length(0, None, (), [], {}), 42)
def test_lp_516653(self):
# Test for https://bugs.launchpad.net/zodb/+bug/516653
import copy
length = self._makeOne()
other = copy.copy(length)
self.assertEqual(other(), 0)
def test_suite():
return unittest.makeSuite(LengthTestCase)
@@ -0,0 +1,383 @@
##############################################################################
#
# Copyright (c) 2001-2012 Zope Foundation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
import unittest
from .common import BTreeTests
from .common import ExtendedSetTests
from .common import InternalKeysMappingTest
from .common import InternalKeysSetTest
from .common import MappingBase
from .common import MappingConflictTestBase
from .common import ModuleTest
from .common import NormalSetTests
from .common import SetConflictTestBase
from .common import SetResult
from .common import TestLongIntValues
from .common import TypeTest
from .common import Weighted
from .common import itemsToSet
from .common import makeBuilder
from BTrees.IIBTree import using64bits #XXX Ugly, but necessary
class OIBTreeInternalKeyTest(InternalKeysMappingTest, unittest.TestCase):
def _getTargetClass(self):
from BTrees.OIBTree import OIBTree
return OIBTree
class OIBTreePyInternalKeyTest(InternalKeysMappingTest, unittest.TestCase):
def _getTargetClass(self):
from BTrees.OIBTree import OIBTreePy
return OIBTreePy
class OITreeSetInternalKeyTest(InternalKeysSetTest, unittest.TestCase):
def _getTargetClass(self):
from BTrees.OIBTree import OITreeSet
return OITreeSet
class OITreeSetPyInternalKeyTest(InternalKeysSetTest, unittest.TestCase):
def _getTargetClass(self):
from BTrees.OIBTree import OITreeSetPy
return OITreeSetPy
class OIBucketTest(MappingBase, unittest.TestCase):
def _getTargetClass(self):
from BTrees.OIBTree import OIBucket
return OIBucket
class OIBucketPyTest(MappingBase, unittest.TestCase):
def _getTargetClass(self):
from BTrees.OIBTree import OIBucketPy
return OIBucketPy
class OITreeSetTest(NormalSetTests, unittest.TestCase):
def _getTargetClass(self):
from BTrees.OIBTree import OITreeSet
return OITreeSet
class OITreeSetPyTest(NormalSetTests, unittest.TestCase):
def _getTargetClass(self):
from BTrees.OIBTree import OITreeSetPy
return OITreeSetPy
class OISetTest(ExtendedSetTests, unittest.TestCase):
def _getTargetClass(self):
from BTrees.OIBTree import OISet
return OISet
class OISetPyTest(ExtendedSetTests, unittest.TestCase):
def _getTargetClass(self):
from BTrees.OIBTree import OISetPy
return OISetPy
class OIBTreeTest(BTreeTests, unittest.TestCase):
def _makeOne(self):
from BTrees.OIBTree import OIBTree
return OIBTree()
class OIBTreePyTest(BTreeTests, unittest.TestCase):
def _makeOne(self):
from BTrees.OIBTree import OIBTreePy
return OIBTreePy()
if using64bits:
class OIBTreeTest(BTreeTests, TestLongIntValues, unittest.TestCase):
def _makeOne(self):
from BTrees.OIBTree import OIBTree
return OIBTree()
def getTwoKeys(self):
return object(), object()
class OIBTreePyTest(BTreeTests, TestLongIntValues, unittest.TestCase):
def _makeOne(self):
from BTrees.OIBTree import OIBTreePy
return OIBTreePy()
def getTwoKeys(self):
return object(), object()
class _TestOIBTreesBase(TypeTest):
def _stringraises(self):
self._makeOne()[1] = 'c'
def _floatraises(self):
self._makeOne()[1] = 1.4
def _noneraises(self):
self._makeOne()[1] = None
def testEmptyFirstBucketReportedByGuido(self):
from .._compat import xrange
b = self._makeOne()
for i in xrange(29972): # reduce to 29971 and it works
b[i] = i
for i in xrange(30): # reduce to 29 and it works
del b[i]
b[i+40000] = i
self.assertEqual(b.keys()[0], 30)
class TestOIBTrees(_TestOIBTreesBase, unittest.TestCase):
def _makeOne(self):
from BTrees.OIBTree import OIBTree
return OIBTree()
class TestOIBTreesPy(_TestOIBTreesBase, unittest.TestCase):
def _makeOne(self):
from BTrees.OIBTree import OIBTreePy
return OIBTreePy()
class PureOI(SetResult, unittest.TestCase):
def union(self, *args):
from BTrees.OIBTree import union
return union(*args)
def intersection(self, *args):
from BTrees.OIBTree import intersection
return intersection(*args)
def difference(self, *args):
from BTrees.OIBTree import difference
return difference(*args)
def builders(self):
from BTrees.OIBTree import OIBTree
from BTrees.OIBTree import OIBucket
from BTrees.OIBTree import OITreeSet
from BTrees.OIBTree import OISet
return OISet, OITreeSet, makeBuilder(OIBTree), makeBuilder(OIBucket)
class PureOIPy(SetResult, unittest.TestCase):
def union(self, *args):
from BTrees.OIBTree import unionPy
return unionPy(*args)
def intersection(self, *args):
from BTrees.OIBTree import intersectionPy
return intersectionPy(*args)
def difference(self, *args):
from BTrees.OIBTree import differencePy
return differencePy(*args)
def builders(self):
from BTrees.OIBTree import OIBTreePy
from BTrees.OIBTree import OIBucketPy
from BTrees.OIBTree import OITreeSetPy
from BTrees.OIBTree import OISetPy
return (OISetPy, OITreeSetPy,
makeBuilder(OIBTreePy), makeBuilder(OIBucketPy))
class TestWeightedOI(Weighted, unittest.TestCase):
def weightedUnion(self):
from BTrees.OIBTree import weightedUnion
return weightedUnion
def weightedIntersection(self):
from BTrees.OIBTree import weightedIntersection
return weightedIntersection
def union(self):
from BTrees.OIBTree import union
return union
def intersection(self):
from BTrees.OIBTree import intersection
return intersection
def mkbucket(self, *args):
from BTrees.OIBTree import OIBucket as mkbucket
return mkbucket(*args)
def builders(self):
from BTrees.OIBTree import OIBTree
from BTrees.OIBTree import OIBucket
from BTrees.OIBTree import OITreeSet
from BTrees.OIBTree import OISet
return OIBucket, OIBTree, itemsToSet(OISet), itemsToSet(OITreeSet)
class TestWeightedOIPy(Weighted, unittest.TestCase):
def weightedUnion(self):
from BTrees.OIBTree import weightedUnionPy
return weightedUnionPy
def weightedIntersection(self):
from BTrees.OIBTree import weightedIntersectionPy
return weightedIntersectionPy
def union(self):
from BTrees.OIBTree import unionPy
return unionPy
def intersection(self):
from BTrees.OIBTree import intersectionPy
return intersectionPy
def mkbucket(self, *args):
from BTrees.OIBTree import OIBucketPy as mkbucket
return mkbucket(*args)
def builders(self):
from BTrees.OIBTree import OIBTreePy
from BTrees.OIBTree import OIBucketPy
from BTrees.OIBTree import OITreeSetPy
from BTrees.OIBTree import OISetPy
return (OIBucketPy, OIBTreePy,
itemsToSet(OISetPy), itemsToSet(OITreeSetPy))
class OIBucketConflictTests(MappingConflictTestBase, unittest.TestCase):
def _getTargetClass(self):
from BTrees.OIBTree import OIBucket
return OIBucket
class OIBucketConflictTestsPy(MappingConflictTestBase, unittest.TestCase):
def _getTargetClass(self):
from BTrees.OIBTree import OIBucketPy
return OIBucketPy
class OISetConflictTests(SetConflictTestBase, unittest.TestCase):
def _getTargetClass(self):
from BTrees.OIBTree import OISet
return OISet
class OISetConflictTestsPy(SetConflictTestBase, unittest.TestCase):
def _getTargetClass(self):
from BTrees.OIBTree import OISetPy
return OISetPy
class OIBTreeConflictTests(MappingConflictTestBase, unittest.TestCase):
def _getTargetClass(self):
from BTrees.OIBTree import OIBTree
return OIBTree
class OIBTreeConflictTestsPy(MappingConflictTestBase, unittest.TestCase):
def _getTargetClass(self):
from BTrees.OIBTree import OIBTreePy
return OIBTreePy
class OITreeSetConflictTests(SetConflictTestBase, unittest.TestCase):
def _getTargetClass(self):
from BTrees.OIBTree import OITreeSet
return OITreeSet
class OITreeSetConflictTestsPy(SetConflictTestBase, unittest.TestCase):
def _getTargetClass(self):
from BTrees.OIBTree import OITreeSetPy
return OITreeSetPy
class OIModuleTest(ModuleTest, unittest.TestCase):
prefix = 'OI'
def _getModule(self):
import BTrees
return BTrees.OIBTree
def _getInterface(self):
import BTrees.Interfaces
return BTrees.Interfaces.IObjectIntegerBTreeModule
def test_multiunion_not_present(self):
try:
from BTrees.OIBTree import multiunion
except ImportError:
pass
else:
self.fail("OIBTree shouldn't have multiunion")
def test_suite():
return unittest.TestSuite((
unittest.makeSuite(OIBTreeInternalKeyTest),
unittest.makeSuite(OIBTreePyInternalKeyTest),
unittest.makeSuite(OITreeSetInternalKeyTest),
unittest.makeSuite(OITreeSetPyInternalKeyTest),
unittest.makeSuite(OIBucketTest),
unittest.makeSuite(OIBucketPyTest),
unittest.makeSuite(OITreeSetTest),
unittest.makeSuite(OITreeSetPyTest),
unittest.makeSuite(OISetTest),
unittest.makeSuite(OISetPyTest),
unittest.makeSuite(OIBTreeTest),
unittest.makeSuite(OIBTreePyTest),
unittest.makeSuite(TestOIBTrees),
unittest.makeSuite(TestOIBTreesPy),
unittest.makeSuite(PureOI),
unittest.makeSuite(PureOIPy),
unittest.makeSuite(TestWeightedOI),
unittest.makeSuite(TestWeightedOIPy),
unittest.makeSuite(OIBucketConflictTests),
unittest.makeSuite(OIBucketConflictTestsPy),
unittest.makeSuite(OISetConflictTests),
unittest.makeSuite(OISetConflictTestsPy),
unittest.makeSuite(OIBTreeConflictTests),
unittest.makeSuite(OIBTreeConflictTestsPy),
unittest.makeSuite(OITreeSetConflictTests),
unittest.makeSuite(OITreeSetConflictTestsPy),
unittest.makeSuite(OIModuleTest),
))
@@ -0,0 +1,348 @@
##############################################################################
#
# Copyright (c) 2001-2012 Zope Foundation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
import unittest
from .common import BTreeTests
from .common import ExtendedSetTests
from .common import InternalKeysMappingTest
from .common import InternalKeysSetTest
from .common import MappingBase
from .common import MappingConflictTestBase
from .common import ModuleTest
from .common import NormalSetTests
from .common import SetConflictTestBase
from .common import SetResult
from .common import TestLongIntValues
from .common import Weighted
from .common import itemsToSet
from .common import makeBuilder
from .common import _skip_on_32_bits
class OLBTreeInternalKeyTest(InternalKeysMappingTest, unittest.TestCase):
def _getTargetClass(self):
from BTrees.OLBTree import OLBTree
return OLBTree
class OLBTreePyInternalKeyTest(InternalKeysMappingTest, unittest.TestCase):
def _getTargetClass(self):
from BTrees.OLBTree import OLBTreePy
return OLBTreePy
class OLTreeSetInternalKeyTest(InternalKeysSetTest, unittest.TestCase):
def _getTargetClass(self):
from BTrees.OLBTree import OLTreeSet
return OLTreeSet
class OLTreeSetPyInternalKeyTest(InternalKeysSetTest, unittest.TestCase):
def _getTargetClass(self):
from BTrees.OLBTree import OLTreeSetPy
return OLTreeSetPy
class OLBucketTest(MappingBase, unittest.TestCase):
def _getTargetClass(self):
from BTrees.OLBTree import OLBucket
return OLBucket
class OLBucketPyTest(MappingBase, unittest.TestCase):
def _getTargetClass(self):
from BTrees.OLBTree import OLBucketPy
return OLBucketPy
class OLTreeSetTest(NormalSetTests, unittest.TestCase):
def _getTargetClass(self):
from BTrees.OLBTree import OLTreeSet
return OLTreeSet
class OLTreeSetPyTest(NormalSetTests, unittest.TestCase):
def _getTargetClass(self):
from BTrees.OLBTree import OLTreeSetPy
return OLTreeSetPy
class OLSetTest(ExtendedSetTests, unittest.TestCase):
def _getTargetClass(self):
from BTrees.OLBTree import OLSet
return OLSet
class OLSetPyTest(ExtendedSetTests, unittest.TestCase):
def _getTargetClass(self):
from BTrees.OLBTree import OLSetPy
return OLSetPy
class OLBTreeTest(BTreeTests, TestLongIntValues, unittest.TestCase):
def _makeOne(self):
from BTrees.OLBTree import OLBTree
return OLBTree()
def getTwoKeys(self):
return "abc", "def"
@_skip_on_32_bits
def test_extremes(self):
from BTrees.tests.common import SMALLEST_64_BITS
from BTrees.tests.common import SMALLEST_POSITIVE_65_BITS
from BTrees.tests.common import LARGEST_64_BITS
from BTrees.tests.common import LARGEST_NEGATIVE_65_BITS
btree = self._makeOne()
btree['ZERO'] = 0
btree['SMALLEST_64_BITS'] = SMALLEST_64_BITS
btree['LARGEST_64_BITS'] = LARGEST_64_BITS
self.assertRaises((ValueError, OverflowError), btree.__setitem__,
'SMALLEST_POSITIVE_65_BITS', SMALLEST_POSITIVE_65_BITS)
self.assertRaises((ValueError, OverflowError), btree.__setitem__,
'LARGEST_NEGATIVE_65_BITS', LARGEST_NEGATIVE_65_BITS)
class OLBTreePyTest(BTreeTests, TestLongIntValues, unittest.TestCase):
def _makeOne(self):
from BTrees.OLBTree import OLBTreePy
return OLBTreePy()
def getTwoKeys(self):
return "abc", "def"
class PureOL(SetResult, unittest.TestCase):
def union(self, *args):
from BTrees.OLBTree import union
return union(*args)
def intersection(self, *args):
from BTrees.OLBTree import intersection
return intersection(*args)
def difference(self, *args):
from BTrees.OLBTree import difference
return difference(*args)
def builders(self):
from BTrees.OLBTree import OLBTree
from BTrees.OLBTree import OLBucket
from BTrees.OLBTree import OLTreeSet
from BTrees.OLBTree import OLSet
return OLSet, OLTreeSet, makeBuilder(OLBTree), makeBuilder(OLBucket)
class PureOLPy(SetResult, unittest.TestCase):
def union(self, *args):
from BTrees.OLBTree import unionPy
return unionPy(*args)
def intersection(self, *args):
from BTrees.OLBTree import intersectionPy
return intersectionPy(*args)
def difference(self, *args):
from BTrees.OLBTree import differencePy
return differencePy(*args)
def builders(self):
from BTrees.OLBTree import OLBTreePy
from BTrees.OLBTree import OLBucketPy
from BTrees.OLBTree import OLTreeSetPy
from BTrees.OLBTree import OLSetPy
return (OLSetPy, OLTreeSetPy,
makeBuilder(OLBTreePy), makeBuilder(OLBucketPy))
class TestWeightedOL(Weighted, unittest.TestCase):
def weightedUnion(self):
from BTrees.OLBTree import weightedUnion
return weightedUnion
def weightedIntersection(self):
from BTrees.OLBTree import weightedIntersection
return weightedIntersection
def union(self):
from BTrees.OLBTree import union
return union
def intersection(self):
from BTrees.OLBTree import intersection
return intersection
def mkbucket(self, *args):
from BTrees.OLBTree import OLBucket as mkbucket
return mkbucket(*args)
def builders(self):
from BTrees.OLBTree import OLBTree
from BTrees.OLBTree import OLBucket
from BTrees.OLBTree import OLTreeSet
from BTrees.OLBTree import OLSet
return OLBucket, OLBTree, itemsToSet(OLSet), itemsToSet(OLTreeSet)
class TestWeightedOLPy(Weighted, unittest.TestCase):
def weightedUnion(self):
from BTrees.OLBTree import weightedUnionPy
return weightedUnionPy
def weightedIntersection(self):
from BTrees.OLBTree import weightedIntersectionPy
return weightedIntersectionPy
def union(self):
from BTrees.OLBTree import unionPy
return unionPy
def intersection(self):
from BTrees.OLBTree import intersectionPy
return intersectionPy
def mkbucket(self, *args):
from BTrees.OLBTree import OLBucketPy as mkbucket
return mkbucket(*args)
def builders(self):
from BTrees.OLBTree import OLBTreePy
from BTrees.OLBTree import OLBucketPy
from BTrees.OLBTree import OLTreeSetPy
from BTrees.OLBTree import OLSetPy
return (OLBucketPy, OLBTreePy,
itemsToSet(OLSetPy), itemsToSet(OLTreeSetPy))
class OLBucketConflictTests(MappingConflictTestBase, unittest.TestCase):
def _getTargetClass(self):
from BTrees.OLBTree import OLBucket
return OLBucket
class OLBucketPyConflictTests(MappingConflictTestBase, unittest.TestCase):
def _getTargetClass(self):
from BTrees.OLBTree import OLBucketPy
return OLBucketPy
class OLSetConflictTests(SetConflictTestBase, unittest.TestCase):
def _getTargetClass(self):
from BTrees.OLBTree import OLSet
return OLSet
class OLSetPyConflictTests(SetConflictTestBase, unittest.TestCase):
def _getTargetClass(self):
from BTrees.OLBTree import OLSetPy
return OLSetPy
class OLBTreeConflictTests(MappingConflictTestBase, unittest.TestCase):
def _getTargetClass(self):
from BTrees.OLBTree import OLBTree
return OLBTree
class OLBTreePyConflictTests(MappingConflictTestBase, unittest.TestCase):
def _getTargetClass(self):
from BTrees.OLBTree import OLBTreePy
return OLBTreePy
class OLTreeSetConflictTests(SetConflictTestBase, unittest.TestCase):
def _getTargetClass(self):
from BTrees.OLBTree import OLTreeSet
return OLTreeSet
class OLTreeSetPyConflictTests(SetConflictTestBase, unittest.TestCase):
def _getTargetClass(self):
from BTrees.OLBTree import OLTreeSetPy
return OLTreeSetPy
class OLModuleTest(ModuleTest, unittest.TestCase):
prefix = 'OL'
def _getModule(self):
import BTrees
return BTrees.OLBTree
def _getInterface(self):
import BTrees.Interfaces
return BTrees.Interfaces.IObjectIntegerBTreeModule
def test_multiunion_not_present(self):
try:
from BTrees.OLBTree import multiunion
except ImportError:
pass
else:
self.fail("OLBTree shouldn't have multiunion")
def test_suite():
return unittest.TestSuite((
unittest.makeSuite(OLBTreeInternalKeyTest),
unittest.makeSuite(OLBTreePyInternalKeyTest),
unittest.makeSuite(OLTreeSetInternalKeyTest),
unittest.makeSuite(OLTreeSetPyInternalKeyTest),
unittest.makeSuite(OLBucketTest),
unittest.makeSuite(OLBucketPyTest),
unittest.makeSuite(OLTreeSetTest),
unittest.makeSuite(OLTreeSetPyTest),
unittest.makeSuite(OLSetTest),
unittest.makeSuite(OLSetPyTest),
unittest.makeSuite(OLBTreeTest),
unittest.makeSuite(OLBTreePyTest),
unittest.makeSuite(PureOL),
unittest.makeSuite(PureOLPy),
unittest.makeSuite(TestWeightedOL),
unittest.makeSuite(TestWeightedOLPy),
unittest.makeSuite(OLBucketConflictTests),
unittest.makeSuite(OLBucketPyConflictTests),
unittest.makeSuite(OLSetConflictTests),
unittest.makeSuite(OLSetPyConflictTests),
unittest.makeSuite(OLBTreeConflictTests),
unittest.makeSuite(OLBTreePyConflictTests),
unittest.makeSuite(OLTreeSetConflictTests),
unittest.makeSuite(OLTreeSetPyConflictTests),
unittest.makeSuite(OLModuleTest),
))
@@ -0,0 +1,419 @@
##############################################################################
#
# Copyright (c) 2001-2012 Zope Foundation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
import unittest
from .common import _skip_under_Py3k
from .common import BTreeTests
from .common import ExtendedSetTests
from .common import InternalKeysMappingTest
from .common import InternalKeysSetTest
from .common import MappingBase
from .common import MappingConflictTestBase
from .common import ModuleTest
from .common import NormalSetTests
from .common import SetResult
from .common import SetConflictTestBase
from .common import makeBuilder
class OOBTreeInternalKeyTest(InternalKeysMappingTest, unittest.TestCase):
def _getTargetClass(self):
from BTrees.OOBTree import OOBTreePy
return OOBTreePy
class OOBTreePyInternalKeyTest(InternalKeysMappingTest, unittest.TestCase):
def _getTargetClass(self):
from BTrees.OOBTree import OOBTree
return OOBTree
class OOTreeSetInternalKeyTest(InternalKeysSetTest, unittest.TestCase):
def _getTargetClass(self):
from BTrees.OOBTree import OOTreeSet
return OOTreeSet
class OOTreeSetPyInternalKeyTest(InternalKeysSetTest, unittest.TestCase):
def _getTargetClass(self):
from BTrees.OOBTree import OOTreeSetPy
return OOTreeSetPy
class OOBucketTest(MappingBase, unittest.TestCase):
def _getTargetClass(self):
from BTrees.OOBTree import OOBucket
return OOBucket
class OOBucketPyTest(MappingBase, unittest.TestCase):
def _getTargetClass(self):
from BTrees.OOBTree import OOBucketPy
return OOBucketPy
class OOTreeSetTest(NormalSetTests, unittest.TestCase):
def _getTargetClass(self):
from BTrees.OOBTree import OOTreeSet
return OOTreeSet
class OOTreeSetPyTest(NormalSetTests, unittest.TestCase):
def _getTargetClass(self):
from BTrees.OOBTree import OOTreeSetPy
return OOTreeSetPy
class OOSetTest(ExtendedSetTests, unittest.TestCase):
def _getTargetClass(self):
from BTrees.OOBTree import OOSet
return OOSet
class OOSetPyTest(ExtendedSetTests, unittest.TestCase):
def _getTargetClass(self):
from BTrees.OOBTree import OOSetPy
return OOSetPy
class OOBTreeTest(BTreeTests, unittest.TestCase):
def _makeOne(self, *args):
from BTrees.OOBTree import OOBTree
return OOBTree(*args)
def test_byValue(self):
ITEMS = [(y, x) for x, y in enumerate('abcdefghijklmnopqrstuvwxyz')]
tree = self._makeOne(ITEMS)
self.assertEqual(list(tree.byValue(22)),
[(y, x) for x, y in reversed(ITEMS[22:])])
def testRejectDefaultComparisonOnSet(self):
# Check that passing int keys w default comparison fails.
# Only applies to new-style class instances. Old-style
# instances are too hard to introspect.
# This is white box because we know that the check is being
# used in a function that's used in lots of places.
# Otherwise, there are many permutations that would have to be
# checked.
from .._compat import PY2
t = self._makeOne()
class C(object):
pass
self.assertRaises(TypeError, lambda : t.__setitem__(C(), 1))
with self.assertRaises(TypeError) as raising:
t[C()] = 1
self.assertEqual(raising.exception.args[0], "Object has default comparison")
if PY2: # we only check for __cmp__ on Python2
class With___cmp__(object):
def __cmp__(*args):
return 1
c = With___cmp__()
t[c] = 1
t.clear()
class With___lt__(object):
def __lt__(*args):
return 1
c = With___lt__()
t[c] = 1
t.clear()
def testAcceptDefaultComparisonOnGet(self):
# Issue #42
t = self._makeOne()
class C(object):
pass
self.assertEqual(t.get(C(), 42), 42)
self.assertRaises(KeyError, t.__getitem__, C())
self.assertFalse(C() in t)
def test_None_is_smallest(self):
t = self._makeOne()
for i in range(999): # Make sure we multiple buckets
t[i] = i*i
t[None] = -1
for i in range(-99,0): # Make sure we multiple buckets
t[i] = i*i
self.assertEqual(list(t), [None] + list(range(-99, 999)))
self.assertEqual(list(t.values()),
[-1] + [i*i for i in range(-99, 999)])
self.assertEqual(t[2], 4)
self.assertEqual(t[-2], 4)
self.assertEqual(t[None], -1)
t[None] = -2
self.assertEqual(t[None], -2)
t2 = t.__class__(t)
del t[None]
self.assertEqual(list(t), list(range(-99, 999)))
if 'Py' in self.__class__.__name__:
return
from BTrees.OOBTree import difference, union, intersection
self.assertEqual(list(difference(t2, t).items()), [(None, -2)])
self.assertEqual(list(union(t, t2)), list(t2))
self.assertEqual(list(intersection(t, t2)), list(t))
@_skip_under_Py3k
def testDeleteNoneKey(self):
# Check that a None key can be deleted in Python 2.
# This doesn't work on Python 3 because None is unorderable,
# so the tree can't be searched. But None also can't be inserted,
# and we don't support migrating Python 2 databases to Python 3.
t = self._makeOne()
bucket_state = ((None, 42),)
tree_state = ((bucket_state,),)
t.__setstate__(tree_state)
self.assertEqual(t[None], 42)
del t[None]
def testUnpickleNoneKey(self):
# All versions (py2 and py3, C and Python) can unpickle
# data that looks like this: {None: 42}, even though None
# is unorderable..
# This pickle was captured in BTree/ZODB3 3.10.7
data = b'ccopy_reg\n__newobj__\np0\n(cBTrees.OOBTree\nOOBTree\np1\ntp2\nRp3\n((((NI42\ntp4\ntp5\ntp6\ntp7\nb.'
import pickle
t = pickle.loads(data)
keys = list(t)
self.assertEqual([None], keys)
def testIdentityTrumpsBrokenComparison(self):
# Identical keys always match, even if their comparison is
# broken. See https://github.com/zopefoundation/BTrees/issues/50
from functools import total_ordering
@total_ordering
class Bad(object):
def __eq__(self, other):
return False
def __cmp__(self, other):
return 1
def __lt__(self, other):
return False
t = self._makeOne()
bad_key = Bad()
t[bad_key] = 42
self.assertIn(bad_key, t)
self.assertEqual(list(t), [bad_key])
del t[bad_key]
self.assertNotIn(bad_key, t)
self.assertEqual(list(t), [])
class OOBTreePyTest(OOBTreeTest):
#
# Right now, we can't match the C extension's test / prohibition of the
# default 'object' comparison semantics.
#class OOBTreePyTest(BTreeTests, unittest.TestCase):
def _makeOne(self, *args):
from BTrees.OOBTree import OOBTreePy
return OOBTreePy(*args)
class PureOO(SetResult, unittest.TestCase):
def union(self, *args):
from BTrees.OOBTree import union
return union(*args)
def intersection(self, *args):
from BTrees.OOBTree import intersection
return intersection(*args)
def difference(self, *args):
from BTrees.OOBTree import difference
return difference(*args)
def builders(self):
from BTrees.OOBTree import OOBTree
from BTrees.OOBTree import OOBucket
from BTrees.OOBTree import OOTreeSet
from BTrees.OOBTree import OOSet
return OOSet, OOTreeSet, makeBuilder(OOBTree), makeBuilder(OOBucket)
class PureOOPy(SetResult, unittest.TestCase):
def union(self, *args):
from BTrees.OOBTree import unionPy
return unionPy(*args)
def intersection(self, *args):
from BTrees.OOBTree import intersectionPy
return intersectionPy(*args)
def difference(self, *args):
from BTrees.OOBTree import differencePy
return differencePy(*args)
def builders(self):
from BTrees.OOBTree import OOBTreePy
from BTrees.OOBTree import OOBucketPy
from BTrees.OOBTree import OOTreeSetPy
from BTrees.OOBTree import OOSetPy
return (OOSetPy, OOTreeSetPy,
makeBuilder(OOBTreePy), makeBuilder(OOBucketPy))
class OOBucketConflictTests(MappingConflictTestBase, unittest.TestCase):
def _getTargetClass(self):
from BTrees.OOBTree import OOBucket
return OOBucket
class OOBucketPyConflictTests(MappingConflictTestBase, unittest.TestCase):
def _getTargetClass(self):
from BTrees.OOBTree import OOBucketPy
return OOBucketPy
class OOSetConflictTests(SetConflictTestBase, unittest.TestCase):
def _getTargetClass(self):
from BTrees.OOBTree import OOSet
return OOSet
class OOSetPyConflictTests(SetConflictTestBase, unittest.TestCase):
def _getTargetClass(self):
from BTrees.OOBTree import OOSetPy
return OOSetPy
class OOBTreeConflictTests(MappingConflictTestBase, unittest.TestCase):
def _getTargetClass(self):
from BTrees.OOBTree import OOBTree
return OOBTree
class OOBTreePyConflictTests(MappingConflictTestBase, unittest.TestCase):
def _getTargetClass(self):
from BTrees.OOBTree import OOBTreePy
return OOBTreePy
class OOTreeSetConflictTests(SetConflictTestBase, unittest.TestCase):
def _getTargetClass(self):
from BTrees.OOBTree import OOTreeSet
return OOTreeSet
class OOTreeSetPyConflictTests(SetConflictTestBase, unittest.TestCase):
def _getTargetClass(self):
from BTrees.OOBTree import OOTreeSetPy
return OOTreeSetPy
class OOModuleTest(ModuleTest, unittest.TestCase):
prefix = 'OO'
def _getModule(self):
import BTrees
return BTrees.OOBTree
def _getInterface(self):
import BTrees.Interfaces
return BTrees.Interfaces.IObjectObjectBTreeModule
def test_weightedUnion_not_present(self):
try:
from BTrees.OOBTree import weightedUnion
except ImportError:
pass
else:
self.fail("OOBTree shouldn't have weightedUnion")
def test_weightedIntersection_not_present(self):
try:
from BTrees.OOBTree import weightedIntersection
except ImportError:
pass
else:
self.fail("OOBTree shouldn't have weightedIntersection")
def test_multiunion_not_present(self):
try:
from BTrees.OOBTree import multiunion
except ImportError:
pass
else:
self.fail("OOBTree shouldn't have multiunion")
def test_suite():
return unittest.TestSuite((
unittest.makeSuite(OOBTreeInternalKeyTest),
unittest.makeSuite(OOBTreePyInternalKeyTest),
unittest.makeSuite(OOTreeSetInternalKeyTest),
unittest.makeSuite(OOTreeSetPyInternalKeyTest),
unittest.makeSuite(OOBucketTest),
unittest.makeSuite(OOBucketPyTest),
unittest.makeSuite(OOTreeSetTest),
unittest.makeSuite(OOTreeSetPyTest),
unittest.makeSuite(OOSetTest),
unittest.makeSuite(OOSetPyTest),
unittest.makeSuite(OOBTreeTest),
unittest.makeSuite(OOBTreePyTest),
unittest.makeSuite(PureOO),
unittest.makeSuite(PureOOPy),
unittest.makeSuite(OOBucketConflictTests),
unittest.makeSuite(OOBucketPyConflictTests),
unittest.makeSuite(OOSetConflictTests),
unittest.makeSuite(OOSetPyConflictTests),
unittest.makeSuite(OOBTreeConflictTests),
unittest.makeSuite(OOBTreePyConflictTests),
unittest.makeSuite(OOTreeSetConflictTests),
unittest.makeSuite(OOTreeSetPyConflictTests),
unittest.makeSuite(OOModuleTest),
))
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,54 @@
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Foundation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
from BTrees.OOBTree import OOBTree, OOBucket
class B(OOBucket):
pass
class T(OOBTree):
_bucket_type = B
max_leaf_size = 2
max_internal_size = 3
class S(T):
pass
import unittest
class SubclassTest(unittest.TestCase):
def testSubclass(self):
# test that a subclass that defines _bucket_type gets buckets
# of that type
t = T()
t[0] = 0
self.assertTrue(t._firstbucket.__class__ is B)
def testCustomNodeSizes(self):
# We override btree and bucket split sizes in BTree subclasses.
t = S()
for i in range(8):
t[i] = i
state = t.__getstate__()[0]
self.assertEqual(len(state), 5)
sub = state[0]
self.assertEqual(sub.__class__, S)
sub = sub.__getstate__()[0]
self.assertEqual(len(sub), 5)
sub = sub[0]
self.assertEqual(sub.__class__, B)
self.assertEqual(len(sub), 1)
def test_suite():
return unittest.makeSuite(SubclassTest)
@@ -0,0 +1,425 @@
##############################################################################
#
# Copyright (c) 2003 Zope Foundation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
import unittest
def _assertRaises(self, e_type, checked, *args, **kw):
try:
checked(*args, **kw)
except e_type as e:
return e
self.fail("Didn't raise: %s" % e_type.__name__)
class Test_classify(unittest.TestCase):
def _callFUT(self, obj):
from BTrees.check import classify
return classify(obj)
def test_classify_w_unknown(self):
class NotClassified(object):
pass
self.assertRaises(KeyError, self._callFUT, NotClassified())
def test_classify_w_bucket(self):
from BTrees.OOBTree import OOBucketPy
from BTrees.check import TYPE_BUCKET
kind, is_mapping = self._callFUT(OOBucketPy())
self.assertEqual(kind, TYPE_BUCKET)
self.assertTrue(is_mapping)
def test_classify_w_set(self):
from BTrees.OOBTree import OOSetPy
from BTrees.check import TYPE_BUCKET
kind, is_mapping = self._callFUT(OOSetPy())
self.assertEqual(kind, TYPE_BUCKET)
self.assertFalse(is_mapping)
def test_classify_w_tree(self):
from BTrees.OOBTree import OOBTreePy
from BTrees.check import TYPE_BTREE
kind, is_mapping = self._callFUT(OOBTreePy())
self.assertEqual(kind, TYPE_BTREE)
self.assertTrue(is_mapping)
def test_classify_w_treeset(self):
from BTrees.OOBTree import OOTreeSetPy
from BTrees.check import TYPE_BTREE
kind, is_mapping = self._callFUT(OOTreeSetPy())
self.assertEqual(kind, TYPE_BTREE)
self.assertFalse(is_mapping)
class Test_crack_btree(unittest.TestCase):
def _callFUT(self, obj, is_mapping):
from BTrees.check import crack_btree
return crack_btree(obj, is_mapping)
def test_w_empty_tree(self):
from BTrees.check import BTREE_EMPTY
class Empty(object):
def __getstate__(self):
return None
kind, keys, kids = self._callFUT(Empty(), True)
self.assertEqual(kind, BTREE_EMPTY)
self.assertEqual(keys, [])
self.assertEqual(kids, [])
def test_w_degenerate_tree(self):
from BTrees.check import BTREE_ONE
class Degenerate(object):
def __getstate__(self):
return ((('a', 1, 'b', 2),),)
kind, keys, kids = self._callFUT(Degenerate(), True)
self.assertEqual(kind, BTREE_ONE)
self.assertEqual(keys, ('a', 1, 'b', 2))
self.assertEqual(kids, None)
def test_w_normal_tree(self):
from BTrees.check import BTREE_NORMAL
first_bucket = [object()] * 8
second_bucket = [object()] * 8
class Normal(object):
def __getstate__(self):
return ((first_bucket, 'b', second_bucket), first_bucket)
kind, keys, kids = self._callFUT(Normal(), True)
self.assertEqual(kind, BTREE_NORMAL)
self.assertEqual(keys, ['b'])
self.assertEqual(kids, [first_bucket, second_bucket])
class Test_crack_bucket(unittest.TestCase):
def _callFUT(self, obj, is_mapping):
from BTrees.check import crack_bucket
return crack_bucket(obj, is_mapping)
def test_w_empty_set(self):
class EmptySet(object):
def __getstate__(self):
return ([],)
keys, values = self._callFUT(EmptySet(), False)
self.assertEqual(keys, [])
self.assertEqual(values, [])
def test_w_non_empty_set(self):
class NonEmptySet(object):
def __getstate__(self):
return (['a', 'b', 'c'],)
keys, values = self._callFUT(NonEmptySet(), False)
self.assertEqual(keys, ['a', 'b', 'c'])
self.assertEqual(values, [])
def test_w_empty_mapping(self):
class EmptyMapping(object):
def __getstate__(self):
return ([], object())
keys, values = self._callFUT(EmptyMapping(), True)
self.assertEqual(keys, [])
self.assertEqual(values, [])
def test_w_non_empty_mapping(self):
class NonEmptyMapping(object):
def __getstate__(self):
return (['a', 1, 'b', 2, 'c', 3], object())
keys, values = self._callFUT(NonEmptyMapping(), True)
self.assertEqual(keys, ['a', 'b', 'c'])
self.assertEqual(values, [1, 2, 3])
class Test_type_and_adr(unittest.TestCase):
def _callFUT(self, obj):
from BTrees.check import type_and_adr
return type_and_adr(obj)
def test_type_and_adr_w_oid(self):
from BTrees.utils import oid_repr
class WithOid(object):
_p_oid = b'DEADBEEF'
t_and_a = self._callFUT(WithOid())
self.assertTrue(t_and_a.startswith('WithOid (0x'))
self.assertTrue(t_and_a.endswith('oid=%s)' % oid_repr(b'DEADBEEF')))
def test_type_and_adr_wo_oid(self):
class WithoutOid(object):
pass
t_and_a = self._callFUT(WithoutOid())
self.assertTrue(t_and_a.startswith('WithoutOid (0x'))
self.assertTrue(t_and_a.endswith('oid=None)'))
class WalkerTests(unittest.TestCase):
def _getTargetClass(self):
from BTrees.check import Walker
return Walker
def _makeOne(self, obj):
return self._getTargetClass()(obj)
def test_visit_btree_abstract(self):
walker = self._makeOne(object())
obj = object()
path = '/'
parent = object()
is_mapping = True
keys = []
kids = []
lo = 0
hi = None
self.assertRaises(NotImplementedError, walker.visit_btree,
obj, path, parent, is_mapping, keys, kids, lo, hi)
def test_visit_bucket_abstract(self):
walker = self._makeOne(object())
obj = object()
path = '/'
parent = object()
is_mapping = True
keys = []
kids = []
lo = 0
hi = None
self.assertRaises(NotImplementedError, walker.visit_bucket,
obj, path, parent, is_mapping, keys, kids, lo, hi)
def test_walk_w_empty_bucket(self):
from BTrees.OOBTree import OOBucket
obj = OOBucket()
walker = self._makeOne(obj)
path = '/'
parent = object()
is_mapping = True
keys = []
kids = []
lo = 0
hi = None
self.assertRaises(NotImplementedError, walker.walk)
def test_walk_w_empty_btree(self):
from BTrees.OOBTree import OOBTree
obj = OOBTree()
walker = self._makeOne(obj)
path = '/'
parent = object()
is_mapping = True
keys = []
kids = []
lo = 0
hi = None
self.assertRaises(NotImplementedError, walker.walk)
def test_walk_w_degenerate_btree(self):
from BTrees.OOBTree import OOBTree
obj = OOBTree()
obj['a'] = 1
walker = self._makeOne(obj)
path = '/'
parent = object()
is_mapping = True
keys = []
kids = []
lo = 0
hi = None
self.assertRaises(NotImplementedError, walker.walk)
def test_walk_w_normal_btree(self):
from BTrees.IIBTree import IIBTree
obj = IIBTree()
for i in range(1000):
obj[i] = i
walker = self._makeOne(obj)
path = '/'
parent = object()
is_mapping = True
keys = []
kids = []
lo = 0
hi = None
self.assertRaises(NotImplementedError, walker.walk)
class CheckerTests(unittest.TestCase):
assertRaises = _assertRaises
def _getTargetClass(self):
from BTrees.check import Checker
return Checker
def _makeOne(self, obj):
return self._getTargetClass()(obj)
def test_walk_w_empty_bucket(self):
from BTrees.OOBTree import OOBucket
obj = OOBucket()
checker = self._makeOne(obj)
path = '/'
parent = object()
is_mapping = True
keys = []
kids = []
lo = 0
hi = None
checker.check() #noraise
def test_walk_w_empty_btree(self):
obj = _makeTree(False)
checker = self._makeOne(obj)
path = '/'
parent = object()
is_mapping = True
keys = []
kids = []
lo = 0
hi = None
checker.check() #noraise
def test_walk_w_degenerate_btree(self):
obj = _makeTree(False)
obj['a'] = 1
checker = self._makeOne(obj)
path = '/'
parent = object()
is_mapping = True
keys = []
kids = []
lo = 0
hi = None
checker.check() #noraise
def test_walk_w_normal_btree(self):
obj = _makeTree(False)
checker = self._makeOne(obj)
path = '/'
parent = object()
is_mapping = True
keys = []
kids = []
lo = 0
hi = None
checker.check() #noraise
def test_walk_w_key_too_large(self):
obj = _makeTree(True)
state = obj.__getstate__()
# Damage an invariant by dropping the BTree key to 14.
new_state = (state[0][0], 14, state[0][2]), state[1]
obj.__setstate__(new_state)
checker = self._makeOne(obj)
path = '/'
parent = object()
is_mapping = True
keys = []
kids = []
lo = 0
hi = None
e = self.assertRaises(AssertionError, checker.check)
self.assertTrue(">= upper bound" in str(e))
def test_walk_w_key_too_small(self):
obj = _makeTree(True)
state = obj.__getstate__()
# Damage an invariant by bumping the BTree key to 16.
new_state = (state[0][0], 16, state[0][2]), state[1]
obj.__setstate__(new_state)
checker = self._makeOne(obj)
path = '/'
parent = object()
is_mapping = True
keys = []
kids = []
lo = 0
hi = None
e = self.assertRaises(AssertionError, checker.check)
self.assertTrue("< lower bound" in str(e))
def test_walk_w_keys_swapped(self):
obj = _makeTree(True)
state = obj.__getstate__()
# Damage an invariant by bumping the BTree key to 16.
(b0, num, b1), firstbucket = state
self.assertEqual(b0[4], 8)
self.assertEqual(b0[5], 10)
b0state = b0.__getstate__()
self.assertEqual(len(b0state), 2)
# b0state looks like
# ((k0, v0, k1, v1, ...), nextbucket)
pairs, nextbucket = b0state
self.assertEqual(pairs[8], 4)
self.assertEqual(pairs[9], 8)
self.assertEqual(pairs[10], 5)
self.assertEqual(pairs[11], 10)
newpairs = pairs[:8] + (5, 10, 4, 8) + pairs[12:]
b0.__setstate__((newpairs, nextbucket))
checker = self._makeOne(obj)
path = '/'
parent = object()
is_mapping = True
keys = []
kids = []
lo = 0
hi = None
e = self.assertRaises(AssertionError, checker.check)
self.assertTrue("key 5 at index 4 >= key 4 at index 5" in str(e))
class Test_check(unittest.TestCase):
def _callFUT(self, tree):
from BTrees.check import check
return check(tree)
def _makeOne(self):
from BTrees.OOBTree import OOBTree
tree = OOBTree()
for i in range(31):
tree[i] = 2*i
return tree
def test_normal(self):
from BTrees.OOBTree import OOBTree
tree = OOBTree()
for i in range(31):
tree[i] = 2*i
state = tree.__getstate__()
self.assertEqual(len(state), 2)
self.assertEqual(len(state[0]), 3)
self.assertEqual(state[0][1], 15)
self._callFUT(tree) #noraise
def _makeTree(fill):
from BTrees.OOBTree import OOBTree
from BTrees.OOBTree import _BUCKET_SIZE
tree = OOBTree()
if fill:
for i in range(_BUCKET_SIZE + 1):
tree[i] = 2*i
return tree
def test_suite():
return unittest.TestSuite((
unittest.makeSuite(Test_classify),
unittest.makeSuite(Test_crack_btree),
unittest.makeSuite(Test_crack_bucket),
unittest.makeSuite(Test_type_and_adr),
unittest.makeSuite(WalkerTests),
unittest.makeSuite(CheckerTests),
unittest.makeSuite(Test_check),
))
@@ -0,0 +1,64 @@
##############################################################################
#
# Copyright (c) 2010 Zope Foundation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
import unittest
class fsBucketBase(object):
def _makeOne(self, *args, **kw):
return self._getTargetClass()(*args, **kw)
def _makeBytesItems(self):
from .._compat import _ascii
return[(_ascii(c*2), _ascii(c*6)) for c in 'abcdef']
def test_toString(self):
bucket = self._makeOne(self._makeBytesItems())
self.assertEqual(bucket.toString(),
b'aabbccddeeffaaaaaabbbbbbccccccddddddeeeeeeffffff')
def test_fromString(self):
before = self._makeOne(self._makeBytesItems())
after = before.fromString(before.toString())
self.assertEqual(before.__getstate__(), after.__getstate__())
def test_fromString_empty(self):
before = self._makeOne(self._makeBytesItems())
after = before.fromString(b'')
self.assertEqual(after.__getstate__(), ((),))
def test_fromString_invalid_length(self):
bucket = self._makeOne(self._makeBytesItems())
self.assertRaises(ValueError, bucket.fromString, b'xxx')
class fsBucketTests(unittest.TestCase, fsBucketBase):
def _getTargetClass(self):
from BTrees.fsBTree import fsBucket
return fsBucket
class fsBucketPyTests(unittest.TestCase, fsBucketBase):
def _getTargetClass(self):
from BTrees.fsBTree import fsBucketPy
return fsBucketPy
def test_suite():
return unittest.TestSuite((
unittest.makeSuite(fsBucketTests),
unittest.makeSuite(fsBucketPyTests),
))
@@ -0,0 +1,83 @@
##############################################################################
#
# Copyright (c) 2001-2012 Zope Foundation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
import unittest
class Test_non_negative(unittest.TestCase):
def _callFUT(self, int_val):
from BTrees.utils import non_negative
return non_negative(int_val)
def test_w_big_negative(self):
self.assertEqual(self._callFUT(-(2**63 - 1)), 1)
def test_w_negative(self):
self.assertEqual(self._callFUT(-1), 2**63 - 1)
def test_w_zero(self):
self.assertEqual(self._callFUT(0), 0)
def test_w_positive(self):
self.assertEqual(self._callFUT(1), 1)
def test_w_big_positive(self):
import sys
try:
self.assertEqual(self._callFUT(sys.maxint), sys.maxint)
except AttributeError: #pragma NO COVER Py3k
pass
class Test_oid_repr(unittest.TestCase):
def _callFUT(self, oid):
from BTrees.utils import oid_repr
return oid_repr(oid)
def test_w_non_strings(self):
self.assertEqual(self._callFUT(None), repr(None))
self.assertEqual(self._callFUT(()), repr(()))
self.assertEqual(self._callFUT([]), repr([]))
self.assertEqual(self._callFUT({}), repr({}))
self.assertEqual(self._callFUT(0), repr(0))
def test_w_short_strings(self):
for length in range(8):
faux = 'x' * length
self.assertEqual(self._callFUT(faux), repr(faux))
def test_w_long_strings(self):
for length in range(9, 1024):
faux = 'x' * length
self.assertEqual(self._callFUT(faux), repr(faux))
def test_w_zero(self):
self.assertEqual(self._callFUT(b'\0\0\0\0\0\0\0\0'), b'0x00')
def test_w_one(self):
self.assertEqual(self._callFUT(b'\0\0\0\0\0\0\0\1'), b'0x01')
def test_w_even_length(self):
self.assertEqual(self._callFUT(b'\0\0\0\0\0\0\xAB\xC4'), b'0xabc4')
def test_w_odd_length(self):
self.assertEqual(self._callFUT(b'\0\0\0\0\0\0\x0D\xEF'), b'0x0def')
def test_suite():
return unittest.TestSuite((
unittest.makeSuite(Test_non_negative),
unittest.makeSuite(Test_oid_repr),
))
@@ -0,0 +1,45 @@
##############################################################################
#
# Copyright (c) 2001-2012 Zope Foundation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
# Copied from ZODB/utils.py
from binascii import hexlify
from ._compat import _bytes
def non_negative(int_val):
if int_val < 0:
# Coerce to non-negative.
int_val &= 0x7FFFFFFFFFFFFFFF
return int_val
def positive_id(obj): #pragma NO COVER
"""Return id(obj) as a non-negative integer."""
return non_negative(id(obj))
def oid_repr(oid):
if isinstance(oid, _bytes) and len(oid) == 8:
# Convert to hex and strip leading zeroes.
as_hex = hexlify(oid).lstrip(b'0')
# Ensure two characters per input byte.
chunks = [b'0x']
if len(as_hex) & 1:
chunks.append(b'0')
elif as_hex == b'':
as_hex = b'00'
chunks.append(as_hex)
return b''.join(chunks)
else:
return repr(oid)