Commit 30944096 authored by Tim Peters's avatar Tim Peters

Introduced new exception ConnectionStateError, as a subclass of POSError,

raised when an operation on a Connection can't be performed because the
Connection is in "a wrong state".

Connection.close() now raises this exception if the connection is
currently joined to a transaction (most obviously, if the transaction
the connection is joined to has modified objects waiting for commit
or abort).

Also changed other appropriate instances of RuntimeError to raise
ConnectionStateError instead (e.g., trying to load an object from a
closed connection).
parent 495c6f58
......@@ -13,7 +13,7 @@
##############################################################################
"""Database connection support
$Id: Connection.py,v 1.150 2004/04/16 14:19:11 jeremy Exp $"""
$Id: Connection.py,v 1.151 2004/04/16 19:07:00 tim_one Exp $"""
import logging
import sys
......@@ -31,7 +31,8 @@ import transaction
from ZODB.ConflictResolution import ResolvedSerial
from ZODB.ExportImport import ExportImport
from ZODB.POSException \
import ConflictError, ReadConflictError, InvalidObjectReference
import ConflictError, ReadConflictError, InvalidObjectReference, \
ConnectionStateError
from ZODB.TmpStore import TmpStore
from ZODB.utils import oid_repr, z64, positive_id
from ZODB.serialize import ObjectWriter, ConnectionObjectReader, myhasattr
......@@ -316,11 +317,11 @@ class Connection(ExportImport, object):
object does not exist as of the current transaction, but
existed in the past. It may even exist again in the
future, if the transaction that removed it is undone.
- `RuntimeError`: if the connection is closed.
- `ConnectionStateError`: if the connection is closed.
"""
if self._storage is None:
# XXX Should this be a ZODB-specific exception?
raise RuntimeError("The database connection is closed")
raise ConnectionStateError("The database connection is closed")
obj = self._cache.get(oid, None)
if obj is not None:
......@@ -365,11 +366,10 @@ class Connection(ExportImport, object):
- `TypeError`: if obj is not a persistent object.
- `InvalidObjectReference`: if obj is already associated
with another connection.
- `RuntimeError`: if the connection is closed.
- `ConnectionStateError`: if the connection is closed.
"""
if self._storage is None:
# XXX Should this be a ZODB-specific exception?
raise RuntimeError("The database connection is closed")
raise ConnectionStateError("The database connection is closed")
marker = object()
oid = getattr(obj, "_p_oid", marker)
......@@ -532,6 +532,12 @@ class Connection(ExportImport, object):
onCloseCallback() are invoked and the cache is scanned for
old objects.
"""
if not self._needs_to_join:
# We're currently joined to a transaction.
raise ConnectionStateError("Cannot close a connection joined to "
"a transaction")
if self._cache is not None:
self._cache.incrgc() # This is a good time to do some GC
......@@ -671,14 +677,12 @@ class Connection(ExportImport, object):
def getVersion(self):
if self._storage is None:
# XXX Should this be a ZODB-specific exception?
raise RuntimeError("The database connection is closed")
raise ConnectionStateError("The database connection is closed")
return self._version
def isReadOnly(self):
if self._storage is None:
# XXX Should this be a ZODB-specific exception?
raise RuntimeError("The database connection is closed")
raise ConnectionStateError("The database connection is closed")
return self._storage.isReadOnly()
def invalidate(self, tid, oids):
......@@ -776,7 +780,7 @@ class Connection(ExportImport, object):
msg = ("Shouldn't load state for %s "
"when the connection is closed" % oid_repr(oid))
self._log.error(msg)
raise RuntimeError(msg)
raise ConnectionStateError(msg)
try:
self._setstate(obj)
......
......@@ -13,7 +13,7 @@
##############################################################################
"""ZODB-defined exceptions
$Id: POSException.py,v 1.23 2004/02/27 00:31:53 faassen Exp $"""
$Id: POSException.py,v 1.24 2004/04/16 19:07:00 tim_one Exp $"""
from ZODB.utils import oid_repr, serial_repr
......@@ -268,7 +268,7 @@ class ExportError(POSError):
"""An export file doesn't have the right format."""
class Unsupported(POSError):
"""An feature that is unsupported bt the storage was used."""
"""A feature was used that is not supported by the storage."""
class InvalidObjectReference(POSError):
"""An object contains an invalid reference to another object.
......@@ -281,3 +281,13 @@ class InvalidObjectReference(POSError):
XXX The exception ought to have a member that is the invalid object.
"""
class ConnectionStateError(POSError):
"""A Connection isn't in the required state for an operation.
o An operation such as a load is attempted on a closed connection.
o An attempt to close a connection is made while the connection is
still joined to a transaction (for example, a transaction is in
progress, with uncommitted modifications in the connection).
"""
......@@ -236,12 +236,48 @@ class UserMethodTests(unittest.TestCase):
>>> cn.get(p64(0))
Traceback (most recent call last):
...
RuntimeError: The database connection is closed
ConnectionStateError: The database connection is closed
>>> p = Persistent()
>>> cn.add(p)
Traceback (most recent call last):
...
RuntimeError: The database connection is closed
ConnectionStateError: The database connection is closed
"""
def test_close_with_pending_changes(self):
r"""doctest to ensure close() w/ pending changes complains
>>> import transaction
Just opening and closing is fine.
>>> db = databaseFromString("<zodb>\n<mappingstorage/>\n</zodb>")
>>> cn = db.open()
>>> cn.close()
Opening, making a change, committing, and closing is fine.
>>> cn = db.open()
>>> cn.root()['a'] = 1
>>> transaction.commit()
>>> cn.close()
Opening, making a change, committing, and aborting is fine.
>>> cn = db.open()
>>> cn.root()['a'] = 1
>>> transaction.abort()
>>> cn.close()
But trying to close with a change pending complains.
>>> cn = db.open()
>>> cn.root()['a'] = 1
>>> cn.close()
Traceback (most recent call last):
...
ConnectionStateError: Cannot close a connection joined to a transaction
This leaves the connection as it was, so we can still commit
the change.
>>> transaction.commit()
>>> cn.close()
"""
def test_onCloseCallbacks(self):
......@@ -298,7 +334,7 @@ class UserMethodTests(unittest.TestCase):
>>> cn.isReadOnly()
Traceback (most recent call last):
...
RuntimeError: The database connection is closed
ConnectionStateError: The database connection is closed
An expedient way to create a read-only storage:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment