[Python-Dev] builtin_id() returns negative numbers
tim.peters at gmail.com
Mon Feb 14 16:41:35 CET 2005
[Troels Walsted Hansen]
> The Python binding in libxml2 uses the following code for __repr__():
> class xmlNode(xmlCore):
> def __init__(self, _obj=None):
> self._o = None
> xmlCore.__init__(self, _obj=_obj)
> def __repr__(self):
> return "<xmlNode (%s) object at 0x%x>" % (self.name, id (self))
> With Python 2.3.4 I'm seeing warnings like the one below:
> <frozen module libxml2>:2357: FutureWarning: %u/%o/%x/%X of negative int
> will return a signed string in Python 2.4 and up
> I believe this is caused by the memory address having the sign bit set,
> causing builtin_id() to return a negative integer.
Yes, that's right.
> I grepped around in the Python standard library and found a rather
> awkward work-around that seems to be slowly propagating to various
> module using the "'%x' % id(self)" idiom:
No, it's not propagating any more: I see that none of these exist in 2.4:
> # On some systems (RH10) id() can be a negative number.
> # work around this.
> MAX = 2L*sys.maxint+1
> return '<%s at %#x>' % (' '.join(status), id(self)&MAX)
> $ grep -r 'can be a negative number' *
> Lib/asyncore.py: # On some systems (RH10) id() can be a negative
> Lib/repr.py: # On some systems (RH10) id() can be a negative
> Lib/tarfile.py: # On some systems (RH10) id() can be a negative
> Lib/test/test_repr.py: # On some systems (RH10) id() can be a
> negative number.
> Lib/xml/dom/minidom.py: # On some systems (RH10) id() can be a
> negative number.
> There are many modules that do not have this work-around in Python 2.3.4.
Not sure, but it looks like this stuff was ripped out in 2.4 simply
because 2.4 no longer produces a FutureWarning in these cases. That
doesn't address that the output changed, or that the output for a
negative id() produced by %x under 2.4 is probably surprising to most.
> Wouldn't it be more elegant to make builtin_id() return an unsigned
> long integer?
I think so. This is the function ZODB 3.3 uses, BTW:
# 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.
"""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
The gives a non-negative result regardless of Python version and
(almost) regardless of platform (the `assert` hasn't triggered on any
ZODB 3.3 platform yet).
> Is the performance impact too great?
For some app, somewhere, maybe. It's a tradeoff. The very widespread
practice of embedding %x output from id() favors getting rid of the
sign issue, IMO.
> A long integer is used on platforms where SIZEOF_VOID_P > SIZEOF_LONG
> (most 64 bit platforms?),
Win64 is probably the only major (meaning likely to be popular among
Python users) platform where sizeof(void*) > sizeof(long).
> so all Python code must be prepared to handle it already...
In theory <wink>.
More information about the Python-Dev