[Zope-dev] ZPublisher XMLRPC extension for user exceptions
Illarramendi Amilibia, Aitor
aillarramendi@indra.es
Thu, 27 Feb 2003 17:13:38 +0100
This is a multi-part message in MIME format.
------_=_NextPart_001_01C2DE7B.2FEE5FD4
Content-Type: multipart/alternative;
boundary="----_=_NextPart_002_01C2DE7B.2FEE5FD4"
------_=_NextPart_002_01C2DE7B.2FEE5FD4
Content-Type: text/plain;
charset="iso-8859-1"
Content-Transfer-Encoding: quoted-printable
Hello everybody:
=20
I'm making some kind of development related to invoke externally =
from a Java Applet some ZOPE product methods via XML-RPC.
=20
I use ZOPE as the XMLRPC server, as it can 'listen' for XMLRPC =
requests, find the method requested using URL and traversal, invoke the =
method and receive the return value of it and then encapsulate the =
result in a XMLRPC response.
=20
Anyways, I want to report some exceptions from my ZOPE product =
python methods through XML-RPC to the applet. As my methods don't work =
directly with XMLRPC, all is embedded inside ZOPE, all the XMLRPC works =
are transparent for my python methods, so I didn't realize how I could =
do it.
=20
After looking for the ZOPE code responsible of the XMLRPC request =
process and XMLRPC response generation I found that some of the work was =
being done in ZPublisher xmlrpc.py code (Response stuff).
=20
Then when I started to read the comments of the code I found this in =
the Response class of the code:
=20
""" It's probably possible to improve the 'exception' method =
quite a bit. The current implementation, however, should suffice for =
now. """
=20
But as you will see is not enough, at least for my needs.
=20
I want to define my own exceptions in my product, this exceptions =
could be defined as follows:
=20
class MyOwnException:
__init__(self, faultCode, faultString):
self.faultCode =3D faultCode
self.faultString =3D faultString
=20
Obviously I want to map XMLRPC-like fault format.
=20
Then, I could raise this exception from my method, and I will =
want that the class responsible of making the XMLRPC Response for my =
XMLRPC client will use it for composing the XML for XMLRPC using my =
fault code and fault string.
=20
But I found that xmlrpc.py doesn't take into account that (i'm =
talking of ZOPE 2.6.0) so i had to make some changes in the code of =
ZPublisher/xmlrpc.py:
=20
def exception(self, fatal=3D0, info=3DNone,
absuri_match=3DNone, tag_search=3DNone):
# Fetch our exception info. t is type, v is value and =
tb is the
# traceback object.
if type(info) is type(()) and len(info)=3D=3D3: t,v,tb =
=3D info
else:=20
t,v,tb =3D sys.exc_info()
=20
# Don't mask 404 respnses, as some XML-RPC libraries =
rely on the HTTP
# mechanisms for detecting when authentication is =
required. Fixes Zope
# Collector issue 525.
if t =3D=3D 'Unauthorized' or (isinstance(t, =
types.ClassType)
and issubclass(t, Unauthorized)):
return self._real.exception(fatal=3Dfatal, =
info=3Dinfo)
=20
# Create an appropriate Fault object. Containing error =
information
Fault=3Dxmlrpclib.Fault
f=3DNone
try:
# Strip HTML tags from the error value
v =3D str(v)
remove =3D [r"<[^<>]*>", r"&[A-Za-z]+;"]
for pat in remove:
v =3D re.sub(pat, " ", v)
from Globals import DevelopmentMode
if DevelopmentMode:
from traceback import format_exception
value =3D '\n' + ''.join(format_exception(t, =
v, tb))
else:
value =3D '%s - %s' % (t, v)
=20
###### my own extension for catching user =
exceptions START #####
try:
faultCode =3D =
getattr(sys.exc_info()[1],'faultCode')
faultString =3D =
getattr(sys.exc_info()[1],'faultString')
except:
print "no according exception type"
###### my own extension for catching user =
exceptions FINISH #####
if isinstance(v, Fault):
f=3Dv=20
###### my own extension for catching user =
exceptions START #####
elif (faultCode and faultString) :
f =3D xmlrpclib.Fault(faultCode,faultString)
###### my own extension for catching user =
exceptions FINISH ######
elif isinstance(v, Exception):
f=3DFault(-1, 'Unexpected Zope exception: =
%s' % value)
else:
f=3DFault(-2, 'Unexpected Zope error value: =
%s' % value)
except:
f=3DFault(-3, "Unknown Zope fault type")
=20
# Do the damage.
self.setBody(f)
self._real.setStatus(200)
=20
return tb
=20
By that way, I could send my own fault code and fault =
descriptions via XML-RPC.
=20
But as you can see, I have to extend ZOPE code, and I don't want =
to do it, I have to work with standard releases of ZOPE.
=20
Do you think that something like that would be improved in an =
early future for next ZOPE releases?
=20
Any opinion will be really appreciated.
=20
Thanks for advance.
=20
Regards.
=20
Aitor Illarramendi Amilibia=09
=20
Ingeniero de software
Mando y Control=20
<http://www.indra.es/> Indra.bmp=09
=20
Carretera de Loeches, 9
28850 - Torrej=F3n de Ardoz, Madrid (ESPA=D1A)
Tel: +34-91-626.80.60
Fax: +34-91-626.81.14
aillarramendi@indra.es
www.indra.es <http://www.indra.es/>=20
=20
------_=_NextPart_002_01C2DE7B.2FEE5FD4
Content-Type: text/html;
charset="iso-8859-1"
Content-Transfer-Encoding: quoted-printable
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML><HEAD>
<META HTTP-EQUIV=3D"Content-Type" CONTENT=3D"text/html; =
charset=3Diso-8859-1">
<META content=3D"MSHTML 5.50.4134.600" name=3DGENERATOR></HEAD>
<BODY>
<DIV><FONT face=3DArial size=3D2><SPAN class=3D538114715-27022003>Hello=20
everybody:</SPAN></FONT></DIV>
<DIV><FONT face=3DArial size=3D2><SPAN=20
class=3D538114715-27022003></SPAN></FONT> </DIV>
<DIV><FONT face=3DArial size=3D2><SPAN =
class=3D538114715-27022003> =20
I'm making some kind of development related to invoke externally from a =
Java=20
Applet some ZOPE product methods via XML-RPC.</SPAN></FONT></DIV>
<DIV><FONT face=3DArial size=3D2><SPAN=20
class=3D538114715-27022003></SPAN></FONT> </DIV>
<DIV><FONT face=3DArial size=3D2><SPAN =
class=3D538114715-27022003> I=20
use ZOPE as the XMLRPC server, as it can 'listen' for XMLRPC requests, =
find the=20
method requested using URL and traversal, invoke the method and receive =
the=20
return value of it and then encapsulate the result in a XMLRPC=20
response.</SPAN></FONT></DIV>
<DIV><FONT face=3DArial size=3D2><SPAN=20
class=3D538114715-27022003></SPAN></FONT> </DIV>
<DIV><FONT face=3DArial size=3D2><SPAN =
class=3D538114715-27022003> =20
Anyways, I want to report some exceptions from my ZOPE product python =
methods=20
through XML-RPC to the applet. As my methods don't work directly with =
XMLRPC,=20
all is embedded inside ZOPE, all the XMLRPC works are transparent for my =
python=20
methods, so I didn't realize how I could do =
it.</SPAN></FONT></DIV>
<DIV><FONT face=3DArial size=3D2><SPAN=20
class=3D538114715-27022003></SPAN></FONT> </DIV>
<DIV><FONT face=3DArial size=3D2><SPAN =
class=3D538114715-27022003> =20
After looking for the ZOPE code responsible of the XMLRPC request =
process and=20
XMLRPC response generation I found that some of the work was being done =
in=20
ZPublisher xmlrpc.py code (Response stuff).</SPAN></FONT></DIV>
<DIV><FONT face=3DArial size=3D2><SPAN=20
class=3D538114715-27022003></SPAN></FONT> </DIV>
<DIV><FONT face=3DArial size=3D2><SPAN =
class=3D538114715-27022003> =20
Then when I started to read the comments of the code I found this =
in the=20
Response class of the code:</SPAN></FONT></DIV>
<DIV><FONT face=3DArial size=3D2><SPAN=20
class=3D538114715-27022003></SPAN></FONT> </DIV>
<DIV><FONT face=3DArial size=3D2><SPAN=20
class=3D538114715-27022003> &nbs=
p; =20
""" It's probably possible to improve the 'exception' method quite a =
bit. The=20
current implementation, however, should suffice for now. =
"""</SPAN></FONT></DIV>
<DIV><FONT face=3DArial size=3D2><SPAN=20
class=3D538114715-27022003></SPAN></FONT> </DIV>
<DIV><FONT face=3DArial size=3D2><SPAN =
class=3D538114715-27022003> =20
But as you will see is not enough, at least for my =
needs.</SPAN></FONT></DIV>
<DIV><FONT face=3DArial size=3D2><SPAN=20
class=3D538114715-27022003></SPAN></FONT> </DIV>
<DIV><FONT face=3DArial size=3D2><SPAN =
class=3D538114715-27022003> I=20
want to define my own exceptions in my product, this exceptions could be =
defined=20
as follows:</SPAN></FONT></DIV>
<DIV><FONT face=3DArial size=3D2><SPAN=20
class=3D538114715-27022003></SPAN></FONT> </DIV>
<DIV><FONT face=3DArial size=3D2><SPAN=20
class=3D538114715-27022003> =
class=20
MyOwnException:</SPAN></FONT></DIV>
<DIV><FONT face=3DArial size=3D2><SPAN=20
class=3D538114715-27022003> &nbs=
p; =20
__init__(self, faultCode, faultString):</SPAN></FONT></DIV>
<DIV><FONT face=3DArial size=3D2><SPAN=20
class=3D538114715-27022003> &nbs=
p; =20
self.faultCode =3D faultCode</SPAN></FONT></DIV>
<DIV><FONT face=3DArial size=3D2><SPAN=20
class=3D538114715-27022003> &nbs=
p; =20
self.faultString =3D faultString</SPAN></FONT></DIV>
<DIV><FONT face=3DArial size=3D2><SPAN=20
class=3D538114715-27022003></SPAN></FONT> </DIV>
<DIV><FONT face=3DArial size=3D2><SPAN=20
class=3D538114715-27022003> =
Obviously I=20
want to map XMLRPC-like fault format.</SPAN></FONT></DIV>
<DIV><FONT face=3DArial size=3D2><SPAN=20
class=3D538114715-27022003></SPAN></FONT> </DIV>
<DIV><FONT face=3DArial size=3D2><SPAN=20
class=3D538114715-27022003> =
Then, I=20
could raise this exception from my method, and I will want that the =
class=20
responsible of making the XMLRPC Response for my XMLRPC client will use =
it for=20
composing the XML for XMLRPC using my fault code and fault=20
string.</SPAN></FONT></DIV>
<DIV><FONT face=3DArial size=3D2><SPAN=20
class=3D538114715-27022003></SPAN></FONT> </DIV>
<DIV><FONT face=3DArial size=3D2><SPAN=20
class=3D538114715-27022003> =
But I found=20
that xmlrpc.py doesn't take into account that (i'm talking of ZOPE =
2.6.0) so i=20
had to make some changes in the code of=20
ZPublisher/xmlrpc.py:</SPAN></FONT></DIV>
<DIV><FONT face=3DArial size=3D2><SPAN=20
class=3D538114715-27022003></SPAN></FONT> </DIV>
<DIV><FONT face=3DArial size=3D2><SPAN=20
class=3D538114715-27022003> &nbs=
p; =20
<FONT face=3D"Times New Roman" color=3D#ff0000 size=3D1>def =
exception(self, fatal=3D0,=20
info=3DNone,<BR> &nb=
sp; =20
absuri_match=3DNone,=20
tag_search=3DNone):<BR> &n=
bsp; #=20
Fetch our exception info. t is type, v is value and tb is=20
the<BR> =
=20
# traceback=20
object.<BR> &n=
bsp; =20
if type(info) is type(()) and len(info)=3D=3D3: t,v,tb =3D=20
info<BR>  =
; =20
else: <BR> &nbs=
p; =20
t,v,tb =3D=20
sys.exc_info()<BR> <BR> &nb=
sp; #=20
Don't mask 404 respnses, as some XML-RPC libraries rely on the=20
HTTP<BR>  =
; =20
# mechanisms for detecting when authentication is required. Fixes=20
Zope<BR>  =
; =20
# Collector issue=20
525.<BR>  =
; =20
if t =3D=3D 'Unauthorized' or (isinstance(t,=20
types.ClassType)<BR>  =
; =
&=
nbsp;=20
and issubclass(t,=20
Unauthorized)):<BR> =
=20
return self._real.exception(fatal=3Dfatal, =
info=3Dinfo)</FONT></SPAN></FONT></DIV>
<DIV><FONT color=3D#ff0000 size=3D1></FONT> </DIV>
<DIV><FONT color=3D#ff0000 size=3D1><SPAN=20
class=3D538114715-27022003> &nbs=
p; #=20
Create an appropriate Fault object. Containing error=20
information<BR> &nbs=
p; =20
Fault=3Dxmlrpclib.Fault<BR> &nbs=
p; f=3DNone<BR=
> =
=20
try:<BR>  =
; =20
# Strip HTML tags from the error=20
value<BR> &nbs=
p; =20
v =3D=20
str(v)<BR> &nb=
sp; =20
remove =3D [r"<[^<>]*>",=20
r"&[A-Za-z]+;"]<BR> &n=
bsp; =20
for pat in=20
remove:<BR> &n=
bsp; &nb=
sp;=20
v =3D re.sub(pat, " ",=20
v)<BR> &=
nbsp; =20
from Globals import=20
DevelopmentMode<BR> =
&=
nbsp; =20
if=20
DevelopmentMode:<BR>  =
; =
=20
from traceback import=20
format_exception<BR>  =
; =
=20
value =3D '\n' + ''.join(format_exception(t, v,=20
tb))<BR>  =
; =
=20
else:<BR> &nbs=
p;  =
; =20
value =3D '%s - %s' % (t,=20
v)<BR> &=
nbsp; <BR> &nbs=
p;  =
; <FONT=20
color=3D#0000ff>###### my own extension for catching user =
exceptions=20
START #####<BR>  =
; =
=20
try:<BR>  =
; =
=20
faultCode =3D=20
getattr(sys.exc_info()[1],'faultCode')<BR> &=
nbsp; &n=
bsp; =20
faultString =3D=20
getattr(sys.exc_info()[1],'faultString')<BR>  =
; =
except:<BR> &n=
bsp; &nb=
sp; =20
print "no according exception=20
type"<BR> &nbs=
p;  =
; ###### =20
my own extension for catching user exceptions FINISH=20
#####</FONT></SPAN></FONT><FONT color=3D#ff0000 size=3D1></FONT></DIV>
<DIV><FONT color=3D#ff0000 size=3D1><SPAN=20
class=3D538114715-27022003> &nbs=
p;  =
; if=20
isinstance(v,=20
Fault):<BR> &n=
bsp; &nb=
sp; =20
f=3Dv <BR> &nbs=
p;  =
; <FONT=20
color=3D#0000ff>###### my own extension for catching user exceptions =
START=20
#####<BR> &nbs=
p;  =
; elif=20
(faultCode and faultString)=20
:<BR> &n=
bsp; &nb=
sp; =20
f =3D=20
xmlrpclib.Fault(faultCode,faultString)<BR> &=
nbsp; &n=
bsp; ######=20
my own extension for catching user exceptions FINISH=20
######<BR></FONT> &n=
bsp; &nb=
sp; =20
elif isinstance(v,=20
Exception):<BR> &nbs=
p;  =
; =20
f=3DFault(-1, 'Unexpected Zope exception: %s' %=20
value)<BR> &nb=
sp; &nbs=
p;=20
else:<BR> &nbs=
p;  =
; =20
f=3DFault(-2, 'Unexpected Zope error value: %s' %=20
value)<BR> &nb=
sp; except:<BR>  =
; =
f=3DFault(-3,=20
"Unknown Zope fault type")</SPAN></FONT></DIV>
<DIV><FONT color=3D#ff0000 size=3D1></FONT> </DIV>
<DIV><FONT color=3D#ff0000 size=3D1><SPAN=20
class=3D538114715-27022003> &nbs=
p; #=20
Do the=20
damage.<BR> &n=
bsp; self.setBody(f)<BR> &n=
bsp; &nb=
sp; self._real.setStatus(200)</SPAN></FONT></DIV>
<DIV><FONT color=3D#ff0000 size=3D1></FONT> </DIV>
<DIV><FONT color=3D#ff0000 size=3D1><SPAN=20
class=3D538114715-27022003> &nbs=
p; =20
return tb</SPAN></FONT></DIV>
<DIV><FONT face=3DArial size=3D2><SPAN=20
class=3D538114715-27022003></SPAN></FONT> </DIV>
<DIV><FONT face=3DArial size=3D2><SPAN=20
class=3D538114715-27022003> By =
that way,=20
I could send my own fault code and fault descriptions via=20
XML-RPC.</SPAN></FONT></DIV>
<DIV><FONT face=3DArial size=3D2><SPAN=20
class=3D538114715-27022003></SPAN></FONT> </DIV>
<DIV><FONT face=3DArial size=3D2><SPAN=20
class=3D538114715-27022003> =
But as you=20
can see, I have to extend ZOPE code, and I don't want to do it, I have =
to work=20
with standard releases of ZOPE.</SPAN></FONT></DIV>
<DIV><FONT face=3DArial size=3D2><SPAN=20
class=3D538114715-27022003></SPAN></FONT> </DIV>
<DIV><FONT face=3DArial size=3D2><SPAN=20
class=3D538114715-27022003> Do =
you think=20
that something like that would be improved in an early future for next =
ZOPE=20
releases?</SPAN></FONT></DIV>
<DIV><FONT face=3DArial size=3D2><SPAN=20
class=3D538114715-27022003></SPAN></FONT> </DIV>
<DIV><FONT face=3DArial size=3D2><SPAN=20
class=3D538114715-27022003> =
Any opinion=20
will be really appreciated.</SPAN></FONT></DIV>
<DIV><FONT face=3DArial size=3D2><SPAN=20
class=3D538114715-27022003></SPAN></FONT> </DIV>
<DIV><FONT face=3DArial size=3D2><SPAN=20
class=3D538114715-27022003> =
Thanks for=20
advance.</SPAN></FONT></DIV>
<DIV><FONT face=3DArial size=3D2><SPAN=20
class=3D538114715-27022003></SPAN></FONT> </DIV>
<DIV><FONT face=3DArial size=3D2><SPAN=20
class=3D538114715-27022003>Regards.</SPAN></FONT></DIV>
<DIV><FONT face=3DArial size=3D2><SPAN=20
class=3D538114715-27022003></SPAN></FONT> </DIV>
<DIV><FONT face=3DArial size=3D2>
<TABLE style=3D"WIDTH: 500px" width=3D500 border=3D0>
<TBODY>
<TR>
<TD width=3D40></TD>
<TD=20
style=3D"FONT-WEIGHT: bold; FONT-SIZE: 12px; COLOR: navy; =
FONT-FAMILY: Verdana"=20
vAlign=3Dcenter>Aitor Illarramendi Amilibia</TD></TR>
<TR>
<TD width=3D40> </TD>
<TD=20
style=3D"FONT-WEIGHT: bold; FONT-SIZE: 10px; COLOR: navy; =
FONT-FAMILY: Verdana"=20
vAlign=3Dcenter>
<DIV>Ingeniero de software</DIV>
<DIV>Mando y Control </DIV></TD></TR>
<TR>
<TD style=3D"HEIGHT: 20px" vAlign=3Dcenter width=3D100 =
colSpan=3D2><A=20
href=3D"http://www.indra.es/"></A> <IMG alt=3DIndra.bmp =
hspace=3D0=20
src=3D"cid:538114715@27022003-18e0" align=3Dbaseline =
border=3D0></TD></TR>
<TR>
<TD width=3D40> </TD>
<TD=20
style=3D"FONT-WEIGHT: bold; FONT-SIZE: 10px; COLOR: navy; =
FONT-FAMILY: Verdana"=20
vAlign=3Dtop>
<DIV>Carretera de Loeches, 9<BR>28850 - Torrej=F3n de Ardoz, =
Madrid=20
(ESPA=D1A)<BR>Tel: +34-91-626.80.60<BR>Fax: +34-91-626.81.14</DIV>
<DIV><A=20
=
href=3D"mailto:aillarramendi@indra.es">aillarramendi@indra.es</A><BR><A=20
=
href=3D"http://www.indra.es/">www.indra.es</A><BR></DIV></TD></TR></TBODY=
></TABLE></FONT></DIV>
<DIV><FONT face=3DArial size=3D2></FONT> </DIV></BODY></HTML>
------_=_NextPart_002_01C2DE7B.2FEE5FD4--
------_=_NextPart_001_01C2DE7B.2FEE5FD4
Content-Type: image/bmp;
name="Indra.bmp"
Content-Transfer-Encoding: base64
Content-ID: <538114715@27022003-18e0>
Content-Description: Indra.bmp
Content-Location: Indra.bmp
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
AA==
------_=_NextPart_001_01C2DE7B.2FEE5FD4--