[ZODB-Dev] 0 is not really equal to my object

Tim Peters tim at zope.com
Tue Feb 15 16:02:59 EST 2005


[Derrick Hudson]
>> At least I can use "float('inf')" for my sentinel value to work around
>> this problem.

[and Derrick replies]
> Actually, I can't.
>
>         File "lib/python/ZODB/Connection.py", line 416, in commit
>             dump(state)
>     SystemError: frexp() result out of range
>
> I guess that's to protect people from saving 'inf' on linux then
> wondering why they can't load their database on windows (or solaris, or
> [other non-GNU-libc system]).

Na, it's just an accident.  All Python behavior in the presence of
infinities, NaNs, and signed zeroes is a platform-dependent accident.
That's because Python's implementation is written to the C89 standard, and
the latter says nothing about any of those IEEE-754 special cases; as a
result, there's no predicting what C will do across platforms in these
cases, and so also no predicting what Python's C code will do.

For example, if you tried the code above on Windows, it would have died much
earlier:

Python 2.3.5 (#62, Feb  8 2005, 16:23:02) [MSC v.1200 32 bit (Intel)] on
win32
>>> float('inf')
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
ValueError: invalid literal for float(): inf

That's because Microsoft C's strtod() implementation doesn't accept "inf"
(in fact, MS's strtod() doesn't support any way to spell infinity, although
its sprintf() produces "1.#INF" when fed an infinity).

There's little hope of that changing before C99 becomes universal, _and_ C99
compiler vendors all agree to support C99's optional 754/IEC floating-point
gimmicks.  Given the current age of the C99 standard, and the weak efforts
across vendors to supply conforming implementations so far, "never" is my
best guess.  Python could solve it itself, but that requires some
knowledgeable volunteers able to commit to more work than anyone would
believe at first (unless they've done this kind of work before -- I have, so
I don't underestimate the pain).

> I also learned that I can't use the singleton trick:
>
>     class Infinite:
>         ...
>     Infinite = Infinite()
>
> because the ZODB stores the classname of the instance, and then tries to
> find the class from the name when unpickling it.

You might find it helpful to study Python's docs for the pickle/cPickle
modules.  It's really pickle that stores a class by storing a pair of
strings (the path to the module the class can be found in, and the name of
the class).  ZODB adds a few tricks of its own, but in general can't work
with any object that cPickle can't work with on its own.

> I reduced my sentinel value down to a basic string, and procedural-style
> "if" blocks in the code that must handle it.

Did you try defining a *non*-Persistent class to model the behavior you want
(an utterly ordinary Python class, preferably "new style")?  Then
ExtensionClass isn't involved, you can use rich comparisons right away, etc.
ZODB is happy to store non-persistent objects too; it stores them by value
instead of by reference, and that may or may not be important to you (can't
guess, although all values of all Python builtin types are stored by value
in ZODB (integers, floats, strings, lists, ...), since no builtin type
derives from Persistent).

> Maybe this message in the archives will help the the next person who
> comes up with these ideas.
>
> BTW, thanks for all the info regarding ZODB 3.2 and 3.3 compatibility. I
> will keep it in mind to use when it is time to upgrade.

I hope that if any upgrade issues exist, we'll have a decent tool by then to
help!



More information about the ZODB-Dev mailing list