Add weakref support to all bsddb.db objects.

Make DBTxn objects automatically call abort() in their destructor if
not yet finalized and raise a RuntimeWarning to that effect.
diff --git a/Modules/_bsddb.c b/Modules/_bsddb.c
index 2785f28..e00a64c 100644
--- a/Modules/_bsddb.c
+++ b/Modules/_bsddb.c
@@ -194,6 +194,13 @@
 #undef HAVE_WEAKREF
 #endif
 
+/* if Python >= 2.1 better support warnings */
+#if PYTHON_API_VERSION >= 1010
+#define HAVE_WARNINGS
+#else
+#undef HAVE_WARNINGS
+#endif
+
 struct behaviourFlags {
     /* What is the default behaviour when DB->get or DBCursor->get returns a
        DB_NOTFOUND error?  Return None or raise an exception? */
@@ -212,6 +219,9 @@
     u_int32_t   flags;             /* saved flags from open() */
     int         closed;
     struct behaviourFlags moduleFlags;
+#ifdef HAVE_WEAKREF
+    PyObject        *in_weakreflist; /* List of weak references */
+#endif
 } DBEnvObject;
 
 
@@ -227,6 +237,9 @@
     PyObject*       associateCallback;
     int             primaryDBType;
 #endif
+#ifdef HAVE_WEAKREF
+    PyObject        *in_weakreflist; /* List of weak references */
+#endif
 } DBObject;
 
 
@@ -243,12 +256,18 @@
 typedef struct {
     PyObject_HEAD
     DB_TXN*         txn;
+#ifdef HAVE_WEAKREF
+    PyObject        *in_weakreflist; /* List of weak references */
+#endif
 } DBTxnObject;
 
 
 typedef struct {
     PyObject_HEAD
     DB_LOCK         lock;
+#ifdef HAVE_WEAKREF
+    PyObject        *in_weakreflist; /* List of weak references */
+#endif
 } DBLockObject;
 
 
@@ -467,8 +486,7 @@
                 strcat(errTxt, _db_errmsg);
                 _db_errmsg[0] = 0;
             }
-/* if Python 2.1 or better use warning framework */
-#if PYTHON_API_VERSION >= 1010
+#ifdef HAVE_WARNINGS
             exceptionRaised = PyErr_Warn(PyExc_RuntimeWarning, errTxt);
 #else
             fprintf(stderr, errTxt);
@@ -697,6 +715,9 @@
     self->associateCallback = NULL;
     self->primaryDBType = 0;
 #endif
+#ifdef HAVE_WEAKREF
+    self->in_weakreflist = NULL;
+#endif
 
     /* keep a reference to our python DBEnv object */
     if (arg) {
@@ -718,6 +739,9 @@
     self->db->app_private = (void*)self;
 #endif
     MYDB_END_ALLOW_THREADS;
+    /* TODO add a weakref(self) to the self->myenvobj->open_child_weakrefs
+     * list so that a DBEnv can refuse to close without aborting any open
+     * open DBTxns and closing any open DBs first. */
     if (makeDBError(err)) {
         if (self->myenvobj) {
             Py_DECREF(self->myenvobj);
@@ -741,8 +765,7 @@
             MYDB_BEGIN_ALLOW_THREADS;
             self->db->close(self->db, 0);
             MYDB_END_ALLOW_THREADS;
- /* if Python 2.1 or better use warning framework */
-#if PYTHON_API_VERSION >= 1010
+#ifdef HAVE_WARNINGS
         } else {
             PyErr_Warn(PyExc_RuntimeWarning,
                 "DB could not be closed in destructor: DBEnv already closed");
@@ -750,6 +773,11 @@
         }
         self->db = NULL;
     }
+#ifdef HAVE_WEAKREF
+    if (self->in_weakreflist != NULL) {
+        PyObject_ClearWeakRefs((PyObject *) self);
+    }
+#endif
     if (self->myenvobj) {
         Py_DECREF(self->myenvobj);
         self->myenvobj = NULL;
@@ -842,6 +870,9 @@
     self->flags = flags;
     self->moduleFlags.getReturnsNone = DEFAULT_GET_RETURNS_NONE;
     self->moduleFlags.cursorSetReturnsNone = DEFAULT_CURSOR_SET_RETURNS_NONE;
+#ifdef HAVE_WEAKREF
+    self->in_weakreflist = NULL;
+#endif
 
     MYDB_BEGIN_ALLOW_THREADS;
     err = db_env_create(&self->db_env, flags);
@@ -859,6 +890,12 @@
 static void
 DBEnv_dealloc(DBEnvObject* self)
 {
+#ifdef HAVE_WEAKREF
+    if (self->in_weakreflist != NULL) {
+        PyObject_ClearWeakRefs((PyObject *) self);
+    }
+#endif
+
     if (!self->closed) {
         MYDB_BEGIN_ALLOW_THREADS;
         self->db_env->close(self->db_env, 0);
@@ -885,6 +922,9 @@
 #endif
     if (self == NULL)
         return NULL;
+#ifdef HAVE_WEAKREF
+    self->in_weakreflist = NULL;
+#endif
 
     MYDB_BEGIN_ALLOW_THREADS;
 #if (DBVER >= 40)
@@ -892,6 +932,9 @@
 #else
     err = txn_begin(myenv->db_env, parent, &(self->txn), flags);
 #endif
+    /* TODO add a weakref(self) to the self->myenvobj->open_child_weakrefs
+     * list so that a DBEnv can refuse to close without aborting any open
+     * open DBTxns and closing any open DBs first. */
     MYDB_END_ALLOW_THREADS;
     if (makeDBError(err)) {
         self = NULL;
@@ -903,9 +946,26 @@
 static void
 DBTxn_dealloc(DBTxnObject* self)
 {
-    /* XXX nothing to do for transaction objects?!? */
+#ifdef HAVE_WEAKREF
+    if (self->in_weakreflist != NULL) {
+        PyObject_ClearWeakRefs((PyObject *) self);
+    }
+#endif
 
-    /* TODO: if it hasn't been commited, should we abort it? */
+#ifdef HAVE_WARNINGS
+    if (self->txn) {
+        /* it hasn't been finalized, abort it! */
+        MYDB_BEGIN_ALLOW_THREADS;
+#if (DBVER >= 40)
+        self->txn->abort(self->txn);
+#else
+        txn_abort(self->txn);
+#endif
+        MYDB_END_ALLOW_THREADS;
+        PyErr_Warn(PyExc_RuntimeWarning,
+            "DBTxn aborted in destructor.  No prior commit() or abort().");
+    }
+#endif
 
 #if PYTHON_API_VERSION <= 1007
     PyMem_DEL(self);
@@ -929,6 +989,9 @@
 #endif
     if (self == NULL)
         return NULL;
+#ifdef HAVE_WEAKREF
+    self->in_weakreflist = NULL;
+#endif
 
     MYDB_BEGIN_ALLOW_THREADS;
 #if (DBVER >= 40)
@@ -949,7 +1012,12 @@
 static void
 DBLock_dealloc(DBLockObject* self)
 {
-    /* TODO: if it hasn't been released, should we do it? */
+#ifdef HAVE_WEAKREF
+    if (self->in_weakreflist != NULL) {
+        PyObject_ClearWeakRefs((PyObject *) self);
+    }
+#endif
+    /* TODO: is this lock held? should we release it? */
 
 #if PYTHON_API_VERSION <= 1007
     PyMem_DEL(self);
@@ -4305,6 +4373,19 @@
     0,          /*tp_as_sequence*/
     &DB_mapping,/*tp_as_mapping*/
     0,          /*tp_hash*/
+#ifdef HAVE_WEAKREF
+    0,			/* tp_call */
+    0,			/* tp_str */
+    0,  		/* tp_getattro */
+    0,                  /* tp_setattro */
+    0,			/* tp_as_buffer */
+    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_WEAKREFS,      /* tp_flags */
+    0,                  /* tp_doc */
+    0,		        /* tp_traverse */
+    0,			/* tp_clear */
+    0,			/* tp_richcompare */
+    offsetof(DBObject, in_weakreflist),   /* tp_weaklistoffset */
+#endif
 };
 
 
@@ -4358,6 +4439,19 @@
     0,          /*tp_as_sequence*/
     0,          /*tp_as_mapping*/
     0,          /*tp_hash*/
+#ifdef HAVE_WEAKREF
+    0,			/* tp_call */
+    0,			/* tp_str */
+    0,  		/* tp_getattro */
+    0,                  /* tp_setattro */
+    0,			/* tp_as_buffer */
+    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_WEAKREFS,      /* tp_flags */
+    0,                  /* tp_doc */
+    0,		        /* tp_traverse */
+    0,			/* tp_clear */
+    0,			/* tp_richcompare */
+    offsetof(DBEnvObject, in_weakreflist),   /* tp_weaklistoffset */
+#endif
 };
 
 statichere PyTypeObject DBTxn_Type = {
@@ -4377,6 +4471,19 @@
     0,          /*tp_as_sequence*/
     0,          /*tp_as_mapping*/
     0,          /*tp_hash*/
+#ifdef HAVE_WEAKREF
+    0,			/* tp_call */
+    0,			/* tp_str */
+    0,  		/* tp_getattro */
+    0,                  /* tp_setattro */
+    0,			/* tp_as_buffer */
+    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_WEAKREFS,      /* tp_flags */
+    0,                  /* tp_doc */
+    0,		        /* tp_traverse */
+    0,			/* tp_clear */
+    0,			/* tp_richcompare */
+    offsetof(DBTxnObject, in_weakreflist),   /* tp_weaklistoffset */
+#endif
 };
 
 
@@ -4397,6 +4504,19 @@
     0,          /*tp_as_sequence*/
     0,          /*tp_as_mapping*/
     0,          /*tp_hash*/
+#ifdef HAVE_WEAKREF
+    0,			/* tp_call */
+    0,			/* tp_str */
+    0,  		/* tp_getattro */
+    0,                  /* tp_setattro */
+    0,			/* tp_as_buffer */
+    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_WEAKREFS,      /* tp_flags */
+    0,                  /* tp_doc */
+    0,		        /* tp_traverse */
+    0,			/* tp_clear */
+    0,			/* tp_richcompare */
+    offsetof(DBLockObject, in_weakreflist),   /* tp_weaklistoffset */
+#endif
 };