[Python-bugs-list] [ python-Bugs-715063 ] bsddb.first()/next() raise undocumented exception

SourceForge.net noreply@sourceforge.net
Tue, 08 Jul 2003 23:06:21 -0700


Bugs item #715063, was opened at 2003-04-03 20:03
Message generated for change (Comment added) made by greg
You can respond by visiting: 
https://sourceforge.net/tracker/?func=detail&atid=105470&aid=715063&group_id=5470

Category: Python Library
Group: Python 2.3
>Status: Closed
>Resolution: Fixed
Priority: 5
Submitted By: Christian Stork (cst)
Assigned to: Gregory P. Smith (greg)
Summary: bsddb.first()/next() raise undocumented exception

Initial Comment:
bsddb object's first() & next() methods raise an undocumented 
exception.  Wouldn't returning None make much more sense?  
And shouldn't this be documented? 

Python 2.3a2+ (#2, Mar 21 2003, 22:13:05) 
[GCC 3.2.3 20030316 (Debian prerelease)] on linux2
>>> import bsddb
>>> h.bsddb.hashopen("testdb")
>>> h.first()
------------------------------------------------------------
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
  File "/usr/lib/python2.3/bsddb/__init__.py", line 134, in first
    rv = self.dbc.first()
DBNotFoundError: (-30991, 'DB_NOTFOUND: No matching key/
data pair found')

Note: The same exception appeared for the equivalent dbhash 
methods.  But I assume this should be fixed if this bug is fixed.

----------------------------------------------------------------------

>Comment By: Gregory P. Smith (greg)
Date: 2003-07-08 23:06

Message:
Logged In: YES 
user_id=413

resolved/discussed in greater detail on the mailing list.

I extended set_get_returns_none as barry suggested to take the values 0, 1 and 2.  The current default is 1.  The default will change to 2 in the future (pybsddb 4.2 & python 2.4 timeframe).


----------------------------------------------------------------------

Comment By: Barry A. Warsaw (bwarsaw)
Date: 2003-07-08 05:19

Message:
Logged In: YES 
user_id=12800

I sent a follow message to pybsddb-users@lists.sf.net.  I
made a suggestion about a transition plan and a suggestion
of how to make this settable.  Unfortunately, I think we
should not change the default behavior for Python 2.3.

Short answer: make .set_get_returns_none() take a "level"
argument with the following semantics:

0 == what False means now
1 == what True means now
2 == make cursor.set() return None instead of raising an
exception.

Could we make this change in time for Python 2.4?


----------------------------------------------------------------------

Comment By: Gregory P. Smith (greg)
Date: 2003-07-07 22:43

Message:
Logged In: YES 
user_id=413

hehe.  good point.  you can tell i haven't written pybsddb
cursor code in a while, its api is not fresh in my mind.

... * greg puts on pybsddb hat

I notice that my dbtables.py uses set_range such that it
expects it to raise an exception, not return None.  That
means there are surely others out there whos code that
change would break as well.

How about I change the default to be consistent by having
DBCursor set* methods returns None and add a
set_cursor_set_returns_none() method to enable the behaviour
anyone finds most convenient for their situation.

Or does anyone think the default behaviour should not change?  

----------------------------------------------------------------------

Comment By: Barry A. Warsaw (bwarsaw)
Date: 2003-07-07 12:39

Message:
Logged In: YES 
user_id=12800

Greg, correct me if I'm wrong but cursor.next() already has
the return value issue you point out, i.e. it can return
None or a tuple.  My pattern has been to do stuff like this:

rec = c.first()
while rec:
   key, val = rec
   rec = c.next()

so it doesn't bother me too much if the cursor.set() method
works the same way <wink>.  I think the documentation of
set_get_returns_none is correct and cursor.set() should be
changed to follow those rules.  I'm not too concerned with
backward compatibility either because this is the common
.set() idiom I use:

try:
  rec = c.set('somekey')
except db.DBNotFoundError:
  rec = None
while rec:
  ...

This code would not break by changing .set(), but I would be
able to remove the try/except.


----------------------------------------------------------------------

Comment By: Gregory P. Smith (greg)
Date: 2003-07-07 11:56

Message:
Logged In: YES 
user_id=413

Looking in the code, no, none of the DBCursor set* methods
follow set_get_returns_none() to not raise an exception. 
The documentation for set_get_returns_none implies that they
should as they're all cursor "get" methods.

All of the set methods normally return tuples of (key, data)
rather than a single value.  If we modify them to return
None rather than raise a DBNotFoundError it makes for an
annoying interface because it wouldn't always return a tuple.

I'm inclined to leave the set methods as they are and fix
the set_get_returns_none() documentation unless someone can
come up with good examples why it should be different.

----------------------------------------------------------------------

Comment By: Barry A. Warsaw (bwarsaw)
Date: 2003-07-07 10:49

Message:
Logged In: YES 
user_id=12800

Greg, what about cursor.set() -- I don't think this follows
set_get_returns_none() does it?  I wonder if it should.


----------------------------------------------------------------------

Comment By: Gregory P. Smith (greg)
Date: 2003-07-06 17:01

Message:
Logged In: YES 
user_id=413

DBNotFoundError derives from KeyError.  The old bsddb library also raised KeyError on first/last/next calls when there was no more data rather than returning None so that
interface cannot be changed.  (I agree, returning None would make more sense; but I
can't break the old interface).

The new pybsddb interface's DB and DBEnv set_get_returns_none method does allow you to control this behaviour.

If you are using the old style bsddb interface then you do not have access to the DBEnv before the DB object is created (DB objects inherit the flag setting from the DBEnv when they are created) so you'll need to call set_get_returns_none on the DB object itself:

import bsddb   # python 2.3 bsddb (aka bsddb3 or pybsddb)

hdb = bsddb.hashopen("myhash", 'c')
hdb.db.set_get_returns_none(1)   # will only work on python 2.3 / pybsddb
spam = hdb.first()
while spam:
  spam = hdb.next()

# notice there were no errors.


I've looked at the code for first/last/prev/get and they all use the same internal DBCursor_get wrapper that obeys the set_get_returns_none flag so that you can
write simple "foo = hdb.first();  while not foo:  foo = hdb.next()"  code.


----------------------------------------------------------------------

Comment By: Gregory P. Smith (greg)
Date: 2003-07-06 16:24

Message:
Logged In: YES 
user_id=413

i'm taking a look.

----------------------------------------------------------------------

Comment By: Barry A. Warsaw (bwarsaw)
Date: 2003-04-07 15:46

Message:
Logged In: YES 
user_id=12800

pybsddb has this option to return None from cursor.get()
methods instead of raising DBNotFoundError.  The DBEnv
method set_get_returns_none() can be used to change the
behavior, although the default is to return None.  I agree
that this is very handy!

I suppose .first() and .last() should perhaps honor this
config as well?


----------------------------------------------------------------------

You can respond by visiting: 
https://sourceforge.net/tracker/?func=detail&atid=105470&aid=715063&group_id=5470