Commit 485a07d5 authored by Jim Fulton's avatar Jim Fulton

- BTrees allowed object keys with insane comparison. (Comparison

  inherited from object, which compares based on in-process address.)
  Now BTrees warn if an attempt is made to save a key with
  comparison inherited from object. (This doesn't apply to old-style
  class instances.) This will become an error in ZODB 3.11.
parent e9eb7743
...@@ -465,6 +465,12 @@ INITMODULE (void) ...@@ -465,6 +465,12 @@ INITMODULE (void)
{ {
PyObject *m, *d, *c; PyObject *m, *d, *c;
#ifdef KEY_TYPE_IS_PYOBJECT
object_ = PyTuple_GetItem(Py_None->ob_type->tp_bases, 0);
if (object_ == NULL)
return;
#endif
sort_str = PyString_InternFromString("sort"); sort_str = PyString_InternFromString("sort");
if (!sort_str) if (!sort_str)
return; return;
......
#define KEYMACROS_H "$Id$\n" #define KEYMACROS_H "$Id$\n"
#define KEY_TYPE PyObject * #define KEY_TYPE PyObject *
#define KEY_TYPE_IS_PYOBJECT #define KEY_TYPE_IS_PYOBJECT
#include "Python.h"
static PyObject *object_;
static int
check_argument_cmp(PyObject *arg)
{
if (arg->ob_type->tp_richcompare == NULL
&&
arg->ob_type->tp_compare ==
((PyTypeObject *)object_)->ob_type->tp_compare
)
{
return PyErr_WarnEx(
PyExc_UserWarning,
"Object has default comparison! This will error in 3.11",
1) >= 0;
}
return 1;
}
#define TEST_KEY_SET_OR(V, KEY, TARGET) if ( ( (V) = PyObject_Compare((KEY),(TARGET)) ), PyErr_Occurred() ) #define TEST_KEY_SET_OR(V, KEY, TARGET) if ( ( (V) = PyObject_Compare((KEY),(TARGET)) ), PyErr_Occurred() )
#define INCREF_KEY(k) Py_INCREF(k) #define INCREF_KEY(k) Py_INCREF(k)
#define DECREF_KEY(KEY) Py_DECREF(KEY) #define DECREF_KEY(KEY) Py_DECREF(KEY)
#define COPY_KEY(KEY, E) KEY=(E) #define COPY_KEY(KEY, E) KEY=(E)
#define COPY_KEY_TO_OBJECT(O, K) O=(K); Py_INCREF(O) #define COPY_KEY_TO_OBJECT(O, K) O=(K); Py_INCREF(O)
#define COPY_KEY_FROM_ARG(TARGET, ARG, S) TARGET=(ARG) #define COPY_KEY_FROM_ARG(TARGET, ARG, S) \
TARGET=(ARG); \
(S) = check_argument_cmp(ARG);
...@@ -15,6 +15,7 @@ import gc ...@@ -15,6 +15,7 @@ import gc
import pickle import pickle
import random import random
import StringIO import StringIO
import sys
from unittest import TestCase, TestSuite, TextTestRunner, makeSuite from unittest import TestCase, TestSuite, TextTestRunner, makeSuite
from types import ClassType from types import ClassType
import zope.interface.verify import zope.interface.verify
...@@ -1884,6 +1885,55 @@ class OOBTreeTest(BTreeTests): ...@@ -1884,6 +1885,55 @@ class OOBTreeTest(BTreeTests):
def setUp(self): def setUp(self):
self.t = OOBTree() self.t = OOBTree()
def testRejectDefaultComparison(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.
import warnings
class C(object):
pass
with warnings.catch_warnings(record=True) as w:
# Cause all warnings to always be triggered.
warnings.simplefilter("always")
self.t[C()] = 1
# Verify some things
n = len(w) # somewhat unpredictable #
for m in w:
self.assertEqual(m.category, UserWarning)
self.assert_("Object has default comparison" in str(m.message))
self.t.clear()
class C(object):
def __cmp__(*args):
return 1
self.t[C()] = 1
self.t.clear()
class C(object):
def __lt__(*args):
return 1
self.t[C()] = 1
self.t.clear()
self.assertEqual(len(w), n)
if sys.version_info[:2] < (2, 6):
del testRejectDefaultComparison
if using64bits: if using64bits:
class IIBTreeTest(BTreeTests, TestLongIntKeys, TestLongIntValues): class IIBTreeTest(BTreeTests, TestLongIntKeys, TestLongIntValues):
def setUp(self): def setUp(self):
...@@ -1922,9 +1972,6 @@ class OLBTreeTest(BTreeTests, TestLongIntValues): ...@@ -1922,9 +1972,6 @@ class OLBTreeTest(BTreeTests, TestLongIntValues):
self.t = OLBTree() self.t = OLBTree()
def getTwoKeys(self): def getTwoKeys(self):
return object(), object() return object(), object()
class OOBTreeTest(BTreeTests):
def setUp(self):
self.t = OOBTree()
# cmp error propagation tests # cmp error propagation tests
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
Change History Change History
================ ================
3.10.1 (2010-10-??) 3.10.1 (2010-10-27)
=================== ===================
Bugs Fixed Bugs Fixed
...@@ -28,6 +28,12 @@ Bugs Fixed ...@@ -28,6 +28,12 @@ Bugs Fixed
don't send invalidations. There's no reason to send them when an don't send invalidations. There's no reason to send them when an
external garbage collector is used. external garbage collector is used.
- BTrees allowed object keys with insane comparison. (Comparison
inherited from object, which compares based on in-process address.)
Now BTrees warn if an attempt is made to save a key with
comparison inherited from object. (This doesn't apply to old-style
class instances.) This will become an error in ZODB 3.11.
- ZEO client cache simulation misshandled invalidations - ZEO client cache simulation misshandled invalidations
causing incorrect statistics and errors. causing incorrect statistics and errors.
......
...@@ -65,6 +65,11 @@ use: ...@@ -65,6 +65,11 @@ use:
from persistent import Persistent from persistent import Persistent
class PCounter(Persistent): class PCounter(Persistent):
'`value` is readonly; increment it with `inc`.' '`value` is readonly; increment it with `inc`.'
def __cmp__(self, other):
'Fool BTree checks for sane comparison :/'
return object.__cmp__(self, other)
_val = 0 _val = 0
def inc(self): def inc(self):
self._val += 1 self._val += 1
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment