dumbdbm module broken in Python2.3?

Changjune Kim juneaftn at REMOVETHIShanmail.net
Thu Mar 13 16:35:01 CET 2003


"Jane Austine" <janeaustine50 at hotmail.com> wrote in message
news:ba1e306f.0303111337.72a696c7 at posting.google.com...
> I used shelve.py and it falls back on dumbdbm when no
> possible alternatives are found on the system.
>
> I found this error, which is recurrent and deterministic:
>
> Exception exceptions.AttributeError: "'NoneType' object has no
> attribute 'error'" in <bound method _Database.__del__ of
> <dumbdbm._Database instance at 0x820c71c>> ignored
>
> The problem seems to reside in the __del__ of dumbdbm._Database:
>
> class _Database:
> ...
>     def __del__(self):
>         if self._index is not None:
>             self._commit()
> ...
>     def _commit(self):
>         try: _os.unlink(self._bakfile)
>         except _os.error: pass
>         try: _os.rename(self._dirfile, self._bakfile)
>         except _os.error: pass
>         f = _open(self._dirfile, 'w', self._mode)
>         for key, (pos, siz) in self._index.items():
>             f.write("%s, (%s, %s)\n" % (`key`, `pos`, `siz`))
>         f.close()
>
> My investigation showed that the error was from _commit. When
> it was called, _os or _open was both None. And the exception
> catch didn't work quite safely cause its in the "except" clause.
>
> The reason I suspect is when the time that _Database.__del__ was
> called the os module(which is imported as _os) is already removed out.
>
> I changed the code as:
>
>     def _commit(self):
>         global _os
>         if _os is None:
>             import os as _os
>             import __builtin__
>             _open = __builtin__.open
>         try: _os.unlink(self._bakfile)
>         except _os.error: pass
>         try: _os.rename(self._dirfile, self._bakfile)
>         except _os.error: pass
>         ......
>
> Now it works without any problems, AFAIK.
>
> Am I right on the track?
>
> Jane

You should be using shelve with auto-garbage-collection. A safer and proper
way is close shelve explicitly.

This is a minimal case code for the same kind of error:

<code>
import types
import types as _types
class A:
    def __del__(self):
        try:
            _types.IntType
        except:
            print "error from A"

class B:
    def __del__(self):
        try:
            types.IntType
        except:
            print "error from B"

foobar=(A(),B())
</code>

This results in "error from A" only. Why? See what happens with -v option:
...
# clear __builtin__._
# clear sys.path
# clear sys.argv
# clear sys.ps1
# clear sys.ps2
# clear sys.exitfunc
# clear sys.exc_type
# clear sys.exc_value
# clear sys.exc_traceback
# clear sys.last_type
# clear sys.last_value
# clear sys.last_traceback
# restore sys.stdin
# restore sys.stdout
# restore sys.stderr
# cleanup __main__
error from A
# cleanup[1] __future__
# cleanup[1] site
# cleanup[1] types
# cleanup[1] sitecustomize
# cleanup[1] nt
# cleanup[1] signal
...
# cleanup[2] os
# cleanup sys
# cleanup __builtin__
# cleanup ints: 2 unfreed ints in 1 out of 1 block
# cleanup floats

__main__ is cleared and _types is removed as __main__ is cleaned up.
However, types.IntType still exists since "types" is cleaned up much later.

One way around it is:
    def __del__(self,_types=_types):

and you get no errors at all.

Still the best way is explicitly del-ing what you don't need anymore.
Explicit is better than implicit in Python world.





More information about the Python-list mailing list