[Zope-dev] ZPatterns: commitSubtransaction() gives AttributeError on commit_sub
Brian Lloyd
brian@digicool.com
Mon, 26 Feb 2001 16:19:59 -0500
This is a multi-part message in MIME format.
------=_NextPart_000_0003_01C0A00F.F6BD7960
Content-Type: text/plain;
charset="iso-8859-1"
Content-Transfer-Encoding: 7bit
> req = context.REQUEST
> context.propertysheets.info.manage_changeProperties(longdesc=req.f
> orm['longdesc'])
> context.commitSubtransaction()
> req.RESPONSE.redirect(req['URL1']+'/edit_longdescForm')
>
> context is a DataSkin instance. When I submit the form, I get an
> Attribute Error on commit_sub (traceback below in case it helps
> anyone). As far as I can see from the ZPattern's source, I'm making
> the call correctly and it is an appropriate call. Any clues?
>
> This is Zope 2.3.1b1 and Steve's convenience release of ZPatterns.
>
> --RDM
>
> Traceback:
> AttributeError: (see above)
I believe we've tracked this down and fixed it properly - the
fix will be in 2.3.1 b2, but I've a attached a copy of the
updated Transaction.py that you can drop into lib/python/ZODB,
then restart. It would be great if you get a chance to confirm
the fix and let me know how it goes...
Note I've cc'ed a few other folks who have run into this
specific problem - if any of you can spare a few moments
to verify the fix and let me know if you still see any
problems, I'd really appreciate it.
Thanks!
Brian Lloyd brian@digicool.com
Software Engineer 540.371.6909
Digital Creations http://www.digicool.com
------=_NextPart_000_0003_01C0A00F.F6BD7960
Content-Type: text/plain;
name="Transaction.py"
Content-Transfer-Encoding: quoted-printable
Content-Disposition: attachment;
filename="Transaction.py"
#########################################################################=
#####=0A=
# =0A=
# Zope Public License (ZPL) Version 1.0=0A=
# -------------------------------------=0A=
# =0A=
# Copyright (c) Digital Creations. All rights reserved.=0A=
# =0A=
# This license has been certified as Open Source(tm).=0A=
# =0A=
# Redistribution and use in source and binary forms, with or without=0A=
# modification, are permitted provided that the following conditions are=0A=
# met:=0A=
# =0A=
# 1. Redistributions in source code must retain the above copyright=0A=
# notice, this list of conditions, and the following disclaimer.=0A=
# =0A=
# 2. Redistributions in binary form must reproduce the above copyright=0A=
# notice, this list of conditions, and the following disclaimer in=0A=
# the documentation and/or other materials provided with the=0A=
# distribution.=0A=
# =0A=
# 3. Digital Creations requests that attribution be given to Zope=0A=
# in any manner possible. Zope includes a "Powered by Zope"=0A=
# button that is installed by default. While it is not a license=0A=
# violation to remove this button, it is requested that the=0A=
# attribution remain. A significant investment has been put=0A=
# into Zope, and this effort will continue if the Zope community=0A=
# continues to grow. This is one way to assure that growth.=0A=
# =0A=
# 4. All advertising materials and documentation mentioning=0A=
# features derived from or use of this software must display=0A=
# the following acknowledgement:=0A=
# =0A=
# "This product includes software developed by Digital Creations=0A=
# for use in the Z Object Publishing Environment=0A=
# (http://www.zope.org/)."=0A=
# =0A=
# In the event that the product being advertised includes an=0A=
# intact Zope distribution (with copyright and license included)=0A=
# then this clause is waived.=0A=
# =0A=
# 5. Names associated with Zope or Digital Creations must not be used to=0A=
# endorse or promote products derived from this software without=0A=
# prior written permission from Digital Creations.=0A=
# =0A=
# 6. Modified redistributions of any form whatsoever must retain=0A=
# the following acknowledgment:=0A=
# =0A=
# "This product includes software developed by Digital Creations=0A=
# for use in the Z Object Publishing Environment=0A=
# (http://www.zope.org/)."=0A=
# =0A=
# Intact (re-)distributions of any official Zope release do not=0A=
# require an external acknowledgement.=0A=
# =0A=
# 7. Modifications are encouraged but must be packaged separately as=0A=
# patches to official Zope releases. Distributions that do not=0A=
# clearly separate the patches from the original work must be clearly=0A=
# labeled as unofficial distributions. Modifications which do not=0A=
# carry the name Zope may be packaged in any form, as long as they=0A=
# conform to all of the clauses above.=0A=
# =0A=
# =0A=
# Disclaimer=0A=
# =0A=
# THIS SOFTWARE IS PROVIDED BY DIGITAL CREATIONS ``AS IS'' AND ANY=0A=
# EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE=0A=
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR=0A=
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL DIGITAL CREATIONS OR ITS=0A=
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,=0A=
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT=0A=
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF=0A=
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND=0A=
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,=0A=
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT=0A=
# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF=0A=
# SUCH DAMAGE.=0A=
# =0A=
# =0A=
# This software consists of contributions made by Digital Creations and=0A=
# many individuals on behalf of Digital Creations. Specific=0A=
# attributions are listed in the accompanying credits file.=0A=
# =0A=
#########################################################################=
#####=0A=
"""Transaction management=0A=
=0A=
$Id: Transaction.py,v 1.24.54.4 2001/02/26 20:32:02 brian Exp $"""=0A=
__version__=3D'$Revision: 1.24.54.4 $'[11:-2]=0A=
=0A=
import time, sys, struct, POSException=0A=
from struct import pack=0A=
from string import split, strip, join=0A=
from zLOG import LOG, ERROR, PANIC=0A=
from POSException import ConflictError=0A=
=0A=
# Flag indicating whether certain errors have occurred.=0A=
hosed=3D0=0A=
=0A=
class Transaction:=0A=
'Simple transaction objects for single-threaded applications.'=0A=
user=3D''=0A=
description=3D''=0A=
_connections=3DNone=0A=
_extension=3DNone=0A=
_sub=3DNone # This is a subtrasaction flag=0A=
=0A=
# The _non_st_objects variable is either None or a list=0A=
# of jars that do not support subtransactions. This is used to=0A=
# manage non-subtransaction-supporting jars during subtransaction=0A=
# commits and aborts to ensure that they are correctly committed=0A=
# or aborted in the "outside" transaction.=0A=
_non_st_objects=3DNone=0A=
=0A=
def __init__(self, id=3DNone):=0A=
self._id=3Did=0A=
self._objects=3D[]=0A=
self._append=3Dself._objects.append=0A=
=0A=
def _init(self):=0A=
self._objects=3D[]=0A=
self._append=3Dself._objects.append=0A=
self.user=3Dself.description=3D''=0A=
if self._connections:=0A=
for c in self._connections.values(): c.close()=0A=
del self._connections=0A=
=0A=
def sub(self):=0A=
# Create a manually managed subtransaction for internal use=0A=
r=3Dself.__class__()=0A=
r.user=3Dself.user=0A=
r.description=3Dself.description=0A=
r._extension=3Dself._extension=0A=
return r=0A=
=0A=
def __str__(self): return "%.3f\t%s" % (self._id, self.user)=0A=
=0A=
def __del__(self):=0A=
if self._objects: self.abort(freeme=3D0)=0A=
=0A=
def abort(self, subtransaction=3D0, freeme=3D1):=0A=
'''Abort the transaction.=0A=
=0A=
This is called from the application. This means that we haven\'t=0A=
entered two-phase commit yet, so no tpc_ messages are sent.=0A=
'''=0A=
=0A=
if subtransaction and (self._non_st_objects is not None):=0A=
raise POSException.TransactionError, (=0A=
"""Attempted to abort a sub-transaction, but a =
participating=0A=
data manager doesn't support partial abort.=0A=
""")=0A=
=0A=
t=3Dv=3Dtb=3DNone=0A=
subj=3Dself._sub=0A=
subjars=3D()=0A=
=0A=
if not subtransaction:=0A=
=0A=
# Must add in any non-subtransaction supporting objects that=0A=
# may have been stowed away from previous subtransaction=0A=
# commits.=0A=
if self._non_st_objects is not None:=0A=
append=3Dself._objects.append=0A=
for object in self._non_st_objects:=0A=
append(object)=0A=
self._non_st_objects =3D None=0A=
=0A=
if subj is not None:=0A=
# Abort of top-level transaction after commiting=0A=
# subtransactions.=0A=
subjars=3Dsubj.values()=0A=
self._sub=3DNone=0A=
=0A=
try:=0A=
# Abort the objects=0A=
for o in self._objects:=0A=
try:=0A=
j=3Dgetattr(o, '_p_jar', o)=0A=
if j is not None: j.abort(o, self)=0A=
except:=0A=
if t is None:=0A=
t,v,tb=3Dsys.exc_info()=0A=
=0A=
# Ugh, we need to abort work done in sub-transactions.=0A=
while subjars:=0A=
j=3Dsubjars.pop()=0A=
j.abort_sub(self) # This should never fail=0A=
=0A=
if t is not None: raise t,v,tb=0A=
=0A=
finally:=0A=
tb=3DNone=0A=
del self._objects[:] # Clear registered=0A=
if not subtransaction and freeme:=0A=
if self._id is not None: free_transaction()=0A=
else: self._init()=0A=
=0A=
def begin(self, info=3DNone, subtransaction=3DNone):=0A=
'''Begin a new transaction.=0A=
=0A=
This aborts any transaction in progres.=0A=
'''=0A=
if self._objects: self.abort(subtransaction, 0)=0A=
if info:=0A=
info=3Dsplit(info,'\t')=0A=
self.user=3Dstrip(info[0])=0A=
self.description=3Dstrip(join(info[1:],'\t'))=0A=
=0A=
def commit(self, subtransaction=3DNone):=0A=
'Finalize the transaction'=0A=
=0A=
objects=3Dself._objects=0A=
jars=3D{}=0A=
jarsv =3D None=0A=
subj=3Dself._sub=0A=
subjars=3D()=0A=
=0A=
if subtransaction:=0A=
if subj is None: self._sub=3Dsubj=3D{}=0A=
else:=0A=
if subj is not None:=0A=
if objects:=0A=
# Do an implicit sub-transaction commit:=0A=
self.commit(1)=0A=
objects=3D[]=0A=
subjars=3Dsubj.values()=0A=
self._sub=3DNone=0A=
=0A=
# If not a subtransaction, then we need to add any non-=0A=
# subtransaction-supporting objects that may have been=0A=
# stowed away during subtransaction commits to _objects.=0A=
if (subtransaction is None) and (self._non_st_objects is not =
None):=0A=
append=3Dobjects.append=0A=
for object in self._non_st_objects:=0A=
append(object)=0A=
self._non_st_objects =3D None=0A=
=0A=
t=3Dv=3Dtb=3DNone=0A=
=0A=
if (objects or subjars) and hosed:=0A=
# Something really bad happened and we don't=0A=
# trust the system state.=0A=
raise POSException.TransactionError, (=0A=
=0A=
"""A serious error, which was probably a system error,=0A=
occurred in a previous database transaction. This=0A=
application may be in an invalid state and must be=0A=
restarted before database updates can be allowed.=0A=
=0A=
Beware though that if the error was due to a serious=0A=
system problem, such as a disk full condition, then=0A=
the application may not come up until you deal with=0A=
the system problem. See your application log for=0A=
information on the error that lead to this problem.=0A=
""")=0A=
=0A=
try:=0A=
=0A=
# It's important that:=0A=
#=0A=
# - Every object in self._objects is either committed=0A=
# or aborted.=0A=
#=0A=
# - For each object that is committed=0A=
# we call tpc_begin on it's jar at least once=0A=
#=0A=
# - For every jar for which we've called tpc_begin on,=0A=
# we either call tpc_abort or tpc_finish. It is OK=0A=
# to call these multiple times, as the storage is=0A=
# required to ignore these calls if tpc_begin has not=0A=
# been called.=0A=
=0A=
ncommitted=3D0=0A=
try:=0A=
for o in objects:=0A=
j=3Dgetattr(o, '_p_jar', o)=0A=
if j is not None:=0A=
i=3Did(j)=0A=
if not jars.has_key(i):=0A=
jars[i]=3Dj=0A=
if subtransaction:=0A=
=0A=
# If a jar does not support =
subtransactions,=0A=
# we need to save it away to be =
committed in=0A=
# the outer transaction.=0A=
try: j.tpc_begin(self, subtransaction)=0A=
except TypeError:=0A=
j.tpc_begin(self)=0A=
=0A=
if hasattr(j, 'commit_sub'):=0A=
subj[i]=3Dj=0A=
else:=0A=
if self._non_st_objects is None:=0A=
self._non_st_objects =3D []=0A=
self._non_st_objects.append(o)=0A=
continue=0A=
=0A=
else:=0A=
j.tpc_begin(self)=0A=
j.commit(o,self)=0A=
ncommitted=3Dncommitted+1=0A=
=0A=
# Commit work done in subtransactions=0A=
while subjars:=0A=
j=3Dsubjars.pop()=0A=
i=3Did(j)=0A=
if not jars.has_key(i):=0A=
jars[i]=3Dj=0A=
=0A=
j.commit_sub(self)=0A=
=0A=
jarsv =3D jars.values()=0A=
for jar in jarsv:=0A=
if not subtransaction:=0A=
try: jar=3Djar.tpc_vote=0A=
except: pass=0A=
else: jar(self) # last chance to bail=0A=
=0A=
try:=0A=
# Try to finish one jar, since we may be able to=0A=
# recover if the first one fails.=0A=
if jarsv:=0A=
jarsv[-1].tpc_finish(self) # This should never =
fail=0A=
jarsv.pop() # It didn't, so it's taken care of.=0A=
except:=0A=
# Bug if it does, we need to keep track of it=0A=
LOG('ZODB', ERROR,=0A=
"A storage error occurred in the last phase of a =
"=0A=
"two-phase commit. This shouldn\'t happen. ",=0A=
error=3Dsys.exc_info())=0A=
raise=0A=
=0A=
try:=0A=
while jarsv:=0A=
jarsv[-1].tpc_finish(self) # This should never =
fail=0A=
jarsv.pop() # It didn't, so it's taken care of.=0A=
except: =0A=
# Bug if it does, we need to yell FIRE!=0A=
# Someone finished, so don't allow any more=0A=
# work without at least a restart!=0A=
global hosed=0A=
hosed=3D1=0A=
LOG('ZODB', PANIC,=0A=
"A storage error occurred in the last phase of a =
"=0A=
"two-phase commit. This shouldn\'t happen. "=0A=
"The application may be in a hosed state, so "=0A=
"transactions will not be allowed to commit "=0A=
"until the site/storage is reset by a restart. ",=0A=
error=3Dsys.exc_info())=0A=
raise=0A=
=0A=
except:=0A=
t,v,tb=3Dsys.exc_info()=0A=
=0A=
# Ugh, we got an got an error during commit, so we=0A=
# have to clean up.=0A=
=0A=
# First, we have to abort any uncommitted objects.=0A=
for o in objects[ncommitted:]:=0A=
try:=0A=
j=3Dgetattr(o, '_p_jar', o)=0A=
if j is not None: j.abort(o, self)=0A=
except: pass=0A=
=0A=
# Then, we unwind TPC for the jars that began it.=0A=
if jarsv is None:=0A=
jarsv =3D jars.values()=0A=
for j in jarsv:=0A=
try: j.tpc_abort(self) # This should never fail=0A=
except: pass=0A=
=0A=
# Ugh, we need to abort work done in sub-transactions.=0A=
while subjars:=0A=
j=3Dsubjars.pop()=0A=
j.abort_sub(self) # This should never fail=0A=
=0A=
raise t,v,tb=0A=
=0A=
finally:=0A=
tb=3DNone=0A=
del objects[:] # clear registered=0A=
if not subtransaction and self._id is not None: =
free_transaction()=0A=
=0A=
def register(self,object):=0A=
'Register the given object for transaction control.'=0A=
self._append(object)=0A=
=0A=
def note(self, text):=0A=
if self.description:=0A=
self.description =3D "%s\n\n%s" % (self.description, =
strip(text))=0A=
else: =0A=
self.description =3D strip(text)=0A=
=0A=
def setUser(self, user_name, path=3D'/'):=0A=
self.user=3D"%s %s" % (path, user_name)=0A=
=0A=
def setExtendedInfo(self, name, value):=0A=
ext=3Dself._extension=0A=
if ext is None:=0A=
ext=3Dself._extension=3D{}=0A=
ext[name]=3Dvalue=0A=
=0A=
=0A=
#########################################################################=
###=0A=
# install get_transaction:=0A=
=0A=
try:=0A=
import thread=0A=
=0A=
except:=0A=
_t=3DTransaction(None)=0A=
def get_transaction(_t=3D_t): return _t=0A=
def free_transaction(_t=3D_t): _t.__init__()=0A=
=0A=
else:=0A=
_t=3D{}=0A=
def get_transaction(_id=3Dthread.get_ident, _t=3D_t, get=3D_t.get, =
None=3DNone):=0A=
id=3D_id()=0A=
t=3Dget(id, None)=0A=
if t is None: _t[id]=3Dt=3DTransaction(id)=0A=
return t=0A=
=0A=
def free_transaction(_id=3Dthread.get_ident, _t=3D_t):=0A=
id=3D_id()=0A=
try: del _t[id]=0A=
except KeyError: pass=0A=
=0A=
del thread=0A=
=0A=
del _t=0A=
=0A=
import __main__ =0A=
__main__.__builtins__.get_transaction=3Dget_transaction=0A=
=0A=
------=_NextPart_000_0003_01C0A00F.F6BD7960--