From b0b7489e14a4e4e088e26f38a2eaeede5814e62c Mon Sep 17 00:00:00 2001
From: Tim Peters <tim.one@comcast.net>
Date: Tue, 6 Apr 2004 21:47:12 +0000
Subject: [PATCH] ZODB.utils grows a new function positive_id(), which returns
 the id() of an object as a non-negative integer.  Code trying to pass
 addresses to an %x format uses positive_id(), and this avoids a Python
 FutureWarning about applying %x to negative ints.  The primary difference
 between this and the last stab is that positive_id() should work OK on 64-bit
 boxes too.  What we really want here is C's %p format code, but in Python we
 can't even reliably know the width of native addresses.

---
 src/BTrees/check.py               |  6 +++---
 src/ZODB/Connection.py            |  8 +++-----
 src/ZODB/tests/testTransaction.py |  7 +++++--
 src/ZODB/utils.py                 | 23 +++++++++++++++++++++++
 4 files changed, 34 insertions(+), 10 deletions(-)

diff --git a/src/BTrees/check.py b/src/BTrees/check.py
index fb17a29c..e87080d1 100644
--- a/src/BTrees/check.py
+++ b/src/BTrees/check.py
@@ -39,6 +39,8 @@ from BTrees.OIBTree import OIBTree, OIBucket, OISet, OITreeSet
 from BTrees.IOBTree import IOBTree, IOBucket, IOSet, IOTreeSet
 from BTrees.IIBTree import IIBTree, IIBucket, IISet, IITreeSet
 
+from ZODB.utils import positive_id
+
 TYPE_UNKNOWN, TYPE_BTREE, TYPE_BUCKET = range(3)
 
 _type2kind = {IOBTree: (TYPE_BTREE, True),
@@ -198,9 +200,7 @@ def crack_bucket(b, is_mapping):
     return keys, values
 
 def type_and_adr(obj):
-    # Force the address to look positive.  A negative address will
-    # show up as signed in Python 2.4, and in 2.3 raises FutureWarning.
-    return "%s (0x%x)" % (type(obj).__name__, id(obj) & 0xffffffffL)
+    return "%s (0x%x)" % (type(obj).__name__, positive_id(obj))
 
 # Walker implements a depth-first search of a BTree (or TreeSet or Set or
 # Bucket).  Subclasses must implement the visit_btree() and visit_bucket()
diff --git a/src/ZODB/Connection.py b/src/ZODB/Connection.py
index 99496910..e37af274 100644
--- a/src/ZODB/Connection.py
+++ b/src/ZODB/Connection.py
@@ -13,7 +13,7 @@
 ##############################################################################
 """Database connection support
 
-$Id: Connection.py,v 1.143 2004/04/06 20:21:55 tim_one Exp $"""
+$Id: Connection.py,v 1.144 2004/04/06 21:47:12 tim_one Exp $"""
 
 import logging
 import sys
@@ -33,7 +33,7 @@ from ZODB.ExportImport import ExportImport
 from ZODB.POSException \
      import ConflictError, ReadConflictError, InvalidObjectReference
 from ZODB.TmpStore import TmpStore
-from ZODB.utils import oid_repr, z64
+from ZODB.utils import oid_repr, z64, positive_id
 from ZODB.serialize import ObjectWriter, ConnectionObjectReader, myhasattr
 
 global_reset_counter = 0
@@ -223,9 +223,7 @@ class Connection(ExportImport, object):
             ver = ' (in version %s)' % `self._version`
         else:
             ver = ''
-        # Force the address to look positive.  A negative address will
-        # show up as signed in Python 2.4, and in 2.3 raises FutureWarning.
-        return '<Connection at %08x%s>' % (id(self) & 0xffffffffL, ver)
+        return '<Connection at %08x%s>' % (positive_id(self), ver)
 
     def get(self, oid):
         """Return the persistent object with oid 'oid'.
diff --git a/src/ZODB/tests/testTransaction.py b/src/ZODB/tests/testTransaction.py
index c4977273..677c37b3 100644
--- a/src/ZODB/tests/testTransaction.py
+++ b/src/ZODB/tests/testTransaction.py
@@ -36,11 +36,12 @@ TODO
     add in tests for objects which are modified multiple times,
     for example an object that gets modified in multiple sub txns.
 
-$Id: testTransaction.py,v 1.23 2004/04/06 01:06:41 tim_one Exp $
+$Id: testTransaction.py,v 1.24 2004/04/06 21:47:12 tim_one Exp $
 """
 
 import unittest
 import transaction
+from ZODB.utils import positive_id
 
 class TransactionTests(unittest.TestCase):
 
@@ -532,7 +533,9 @@ class BasicJar:
         self.ccommit_sub = 0
 
     def __repr__(self):
-        return "<%s %X %s>" % (self.__class__.__name__, id(self), self.errors)
+        return "<%s %X %s>" % (self.__class__.__name__,
+                               positive_id(self),
+                               self.errors)
 
     def sortKey(self):
         # All these jars use the same sort key, and Python's list.sort()
diff --git a/src/ZODB/utils.py b/src/ZODB/utils.py
index 9dda0cbf..7fa0cf10 100644
--- a/src/ZODB/utils.py
+++ b/src/ZODB/utils.py
@@ -69,3 +69,26 @@ def oid_repr(oid):
         return repr(oid)
 
 serial_repr = oid_repr
+
+# Addresses can "look negative" on some boxes, some of the time.  If you
+# feed a "negative address" to an %x format, Python 2.3 displays it as
+# unsigned, but produces a FutureWarning, because Python 2.4 will display
+# it as signed.  So when you want to prodce an address, use positive_id() to
+# obtain it.
+def positive_id(obj):
+    """Return id(obj) as a non-negative integer."""
+
+    result = id(obj)
+    if result < 0:
+        # This is a puzzle:  there's no way to know the natural width of
+        # addresses on this box (in particular, there's no necessary
+        # relation to sys.maxint).  Try 32 bits first (and on a 32-bit
+        # box, adding 2**32 gives a positive number with the same hex
+        # representation as the original result).
+        result += 1L << 32
+        if result < 0:
+            # Undo that, and try 64 bits.
+            result -= 1L << 32
+            result += 1L << 64
+            assert result >= 0 # else addresses are fatter than 64 bits
+    return result
-- 
2.30.9